# HG changeset patch # User Chris Cannam # Date 1373189148 -7200 # Node ID e3e11437ecea42de2eac7f4bd2f3c66f5608368b # Parent 72f59aa7e5039fe28048a1546f803b64d6e3bbd2 Add forum code diff -r 72f59aa7e503 -r e3e11437ecea forum/Packages/.htaccess --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Packages/.htaccess Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,5 @@ + + Order Deny,Allow + Deny from all + Allow from localhost + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Packages/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Packages/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,16 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/SSI.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/SSI.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1999 @@ +', $context['user']['name'], '', allowedTo('pm_read') ? ', ' . $txt['msg_alert_you_have'] . ' ' . $context['user']['messages'] . ' ' . ($context['user']['messages'] == '1' ? $txt['message_lowercase'] : $txt['msg_alert_messages']) . '' . $txt['newmessages4'] . ' ' . $context['user']['unread_messages'] . ' ' . ($context['user']['unread_messages'] == '1' ? $txt['newmessages0'] : $txt['newmessages1']) : '', '.'; + } + // Don't echo... then do what?! + else + return $context['user']; +} + +// Display a menu bar, like is displayed at the top of the forum. +function ssi_menubar($output_method = 'echo') +{ + global $context; + + if ($output_method == 'echo') + template_menu(); + // What else could this do? + else + return $context['menu_buttons']; +} + +// Show a logout link. +function ssi_logout($redirect_to = '', $output_method = 'echo') +{ + global $context, $txt, $scripturl; + + if ($redirect_to != '') + $_SESSION['logout_url'] = $redirect_to; + + // Guests can't log out. + if ($context['user']['is_guest']) + return false; + + $link = '' . $txt['logout'] . ''; + + if ($output_method == 'echo') + echo $link; + else + return $link; +} + +// Recent post list: [board] Subject by Poster Date +function ssi_recentPosts($num_recent = 8, $exclude_boards = null, $include_boards = null, $output_method = 'echo', $limit_body = true) +{ + global $context, $settings, $scripturl, $txt, $db_prefix, $user_info; + global $modSettings, $smcFunc; + + // Excluding certain boards... + if ($exclude_boards === null && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0) + $exclude_boards = array($modSettings['recycle_board']); + else + $exclude_boards = empty($exclude_boards) ? array() : (is_array($exclude_boards) ? $exclude_boards : array($exclude_boards)); + + // What about including certain boards - note we do some protection here as pre-2.0 didn't have this parameter. + if (is_array($include_boards) || (int) $include_boards === $include_boards) + { + $include_boards = is_array($include_boards) ? $include_boards : array($include_boards); + } + elseif ($include_boards != null) + { + $include_boards = array(); + } + + // Let's restrict the query boys (and girls) + $query_where = ' + m.id_msg >= {int:min_message_id} + ' . (empty($exclude_boards) ? '' : ' + AND b.id_board NOT IN ({array_int:exclude_boards})') . ' + ' . ($include_boards === null ? '' : ' + AND b.id_board IN ({array_int:include_boards})') . ' + AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? ' + AND m.approved = {int:is_approved}' : ''); + + $query_where_params = array( + 'is_approved' => 1, + 'include_boards' => $include_boards === null ? '' : $include_boards, + 'exclude_boards' => empty($exclude_boards) ? '' : $exclude_boards, + 'min_message_id' => $modSettings['maxMsgID'] - 25 * min($num_recent, 5), + ); + + // Past to this simpleton of a function... + return ssi_queryPosts($query_where, $query_where_params, $num_recent, 'm.id_msg DESC', $output_method, $limit_body); +} + +// Fetch a post with a particular ID. By default will only show if you have permission to the see the board in question - this can be overriden. +function ssi_fetchPosts($post_ids = array(), $override_permissions = false, $output_method = 'echo') +{ + global $user_info, $modSettings; + + if (empty($post_ids)) + return; + + // Allow the user to request more than one - why not? + $post_ids = is_array($post_ids) ? $post_ids : array($post_ids); + + // Restrict the posts required... + $query_where = ' + m.id_msg IN ({array_int:message_list})' . ($override_permissions ? '' : ' + AND {query_wanna_see_board}') . ($modSettings['postmod_active'] ? ' + AND m.approved = {int:is_approved}' : ''); + $query_where_params = array( + 'message_list' => $post_ids, + 'is_approved' => 1, + ); + + // Then make the query and dump the data. + return ssi_queryPosts($query_where, $query_where_params, '', 'm.id_msg DESC', $output_method); +} + +// This removes code duplication in other queries - don't call it direct unless you really know what you're up to. +function ssi_queryPosts($query_where = '', $query_where_params = array(), $query_limit = 10, $query_order = 'm.id_msg DESC', $output_method = 'echo', $limit_body = false, $override_permissions = false) +{ + global $context, $settings, $scripturl, $txt, $db_prefix, $user_info; + global $modSettings, $smcFunc; + + // Find all the posts. Newer ones will have higher IDs. + $request = $smcFunc['db_query']('substring', ' + SELECT + m.poster_time, m.subject, m.id_topic, m.id_member, m.id_msg, m.id_board, b.name AS board_name, + IFNULL(mem.real_name, m.poster_name) AS poster_name, ' . ($user_info['is_guest'] ? '1 AS is_read, 0 AS new_from' : ' + IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) >= m.id_msg_modified AS is_read, + IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from') . ', ' . ($limit_body ? 'SUBSTRING(m.body, 1, 384) AS body' : 'm.body') . ', m.smileys_enabled + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (!$user_info['is_guest'] ? ' + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = m.id_topic AND lt.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = m.id_board AND lmr.id_member = {int:current_member})' : '') . ' + WHERE 1=1 ' . ($override_permissions ? '' : ' + AND {query_wanna_see_board}') . ($modSettings['postmod_active'] ? ' + AND m.approved = {int:is_approved}' : '') . ' + ' . (empty($query_where) ? '' : 'AND ' . $query_where) . ' + ORDER BY ' . $query_order . ' + ' . ($query_limit == '' ? '' : 'LIMIT ' . $query_limit), + array_merge($query_where_params, array( + 'current_member' => $user_info['id'], + 'is_approved' => 1, + )) + ); + $posts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); + + // Censor it! + censorText($row['subject']); + censorText($row['body']); + + $preview = strip_tags(strtr($row['body'], array('
' => ' '))); + + // Build the array. + $posts[] = array( + 'id' => $row['id_msg'], + 'board' => array( + 'id' => $row['id_board'], + 'name' => $row['board_name'], + 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'link' => '' . $row['board_name'] . '' + ), + 'topic' => $row['id_topic'], + 'poster' => array( + 'id' => $row['id_member'], + 'name' => $row['poster_name'], + 'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => empty($row['id_member']) ? $row['poster_name'] : '' . $row['poster_name'] . '' + ), + 'subject' => $row['subject'], + 'short_subject' => shorten_subject($row['subject'], 25), + 'preview' => $smcFunc['strlen']($preview) > 128 ? $smcFunc['substr']($preview, 0, 128) . '...' : $preview, + 'body' => $row['body'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . ';topicseen#new', + 'link' => '' . $row['subject'] . '', + 'new' => !empty($row['is_read']), + 'is_new' => empty($row['is_read']), + 'new_from' => $row['new_from'], + ); + } + $smcFunc['db_free_result']($request); + + // Just return it. + if ($output_method != 'echo' || empty($posts)) + return $posts; + + echo ' + '; + foreach ($posts as $post) + echo ' + + + + + '; + echo ' +
+ [', $post['board']['link'], '] + + ', $post['subject'], ' + ', $txt['by'], ' ', $post['poster']['link'], ' + ', $post['is_new'] ? '' . $txt['new'] . '' : '', ' + + ', $post['time'], ' +
'; +} + +// Recent topic list: [board] Subject by Poster Date +function ssi_recentTopics($num_recent = 8, $exclude_boards = null, $include_boards = null, $output_method = 'echo') +{ + global $context, $settings, $scripturl, $txt, $db_prefix, $user_info; + global $modSettings, $smcFunc; + + if ($exclude_boards === null && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0) + $exclude_boards = array($modSettings['recycle_board']); + else + $exclude_boards = empty($exclude_boards) ? array() : (is_array($exclude_boards) ? $exclude_boards : array($exclude_boards)); + + // Only some boards?. + if (is_array($include_boards) || (int) $include_boards === $include_boards) + { + $include_boards = is_array($include_boards) ? $include_boards : array($include_boards); + } + elseif ($include_boards != null) + { + $output_method = $include_boards; + $include_boards = array(); + } + + $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless'); + $icon_sources = array(); + foreach ($stable_icons as $icon) + $icon_sources[$icon] = 'images_url'; + + // Find all the posts in distinct topics. Newer ones will have higher IDs. + $request = $smcFunc['db_query']('substring', ' + SELECT + m.poster_time, ms.subject, m.id_topic, m.id_member, m.id_msg, b.id_board, b.name AS board_name, t.num_replies, t.num_views, + IFNULL(mem.real_name, m.poster_name) AS poster_name, ' . ($user_info['is_guest'] ? '1 AS is_read, 0 AS new_from' : ' + IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) >= m.id_msg_modified AS is_read, + IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from') . ', SUBSTRING(m.body, 1, 384) AS body, m.smileys_enabled, m.icon + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_last_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (!$user_info['is_guest'] ? ' + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = b.id_board AND lmr.id_member = {int:current_member})' : '') . ' + WHERE t.id_last_msg >= {int:min_message_id} + ' . (empty($exclude_boards) ? '' : ' + AND b.id_board NOT IN ({array_int:exclude_boards})') . ' + ' . (empty($include_boards) ? '' : ' + AND b.id_board IN ({array_int:include_boards})') . ' + AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved} + AND m.approved = {int:is_approved}' : '') . ' + ORDER BY t.id_last_msg DESC + LIMIT ' . $num_recent, + array( + 'current_member' => $user_info['id'], + 'include_boards' => empty($include_boards) ? '' : $include_boards, + 'exclude_boards' => empty($exclude_boards) ? '' : $exclude_boards, + 'min_message_id' => $modSettings['maxMsgID'] - 35 * min($num_recent, 5), + 'is_approved' => 1, + ) + ); + $posts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $row['body'] = strip_tags(strtr(parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']), array('
' => ' '))); + if ($smcFunc['strlen']($row['body']) > 128) + $row['body'] = $smcFunc['substr']($row['body'], 0, 128) . '...'; + + // Censor the subject. + censorText($row['subject']); + censorText($row['body']); + + if (empty($modSettings['messageIconChecks_disable']) && !isset($icon_sources[$row['icon']])) + $icon_sources[$row['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['icon'] . '.gif') ? 'images_url' : 'default_images_url'; + + // Build the array. + $posts[] = array( + 'board' => array( + 'id' => $row['id_board'], + 'name' => $row['board_name'], + 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'link' => '' . $row['board_name'] . '' + ), + 'topic' => $row['id_topic'], + 'poster' => array( + 'id' => $row['id_member'], + 'name' => $row['poster_name'], + 'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => empty($row['id_member']) ? $row['poster_name'] : '' . $row['poster_name'] . '' + ), + 'subject' => $row['subject'], + 'replies' => $row['num_replies'], + 'views' => $row['num_views'], + 'short_subject' => shorten_subject($row['subject'], 25), + 'preview' => $row['body'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . ';topicseen#new', + 'link' => '' . $row['subject'] . '', + // Retained for compatibility - is technically incorrect! + 'new' => !empty($row['is_read']), + 'is_new' => empty($row['is_read']), + 'new_from' => $row['new_from'], + 'icon' => '' . $row['icon'] . '', + ); + } + $smcFunc['db_free_result']($request); + + // Just return it. + if ($output_method != 'echo' || empty($posts)) + return $posts; + + echo ' + '; + foreach ($posts as $post) + echo ' + + + + + '; + echo ' +
+ [', $post['board']['link'], '] + + ', $post['subject'], ' + ', $txt['by'], ' ', $post['poster']['link'], ' + ', !$post['is_new'] ? '' : '' . $txt['new'] . '', ' + + ', $post['time'], ' +
'; +} + +// Show the top poster's name and profile link. +function ssi_topPoster($topNumber = 1, $output_method = 'echo') +{ + global $db_prefix, $scripturl, $smcFunc; + + // Find the latest poster. + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, posts + FROM {db_prefix}members + ORDER BY posts DESC + LIMIT ' . $topNumber, + array( + ) + ); + $return = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $return[] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => '' . $row['real_name'] . '', + 'posts' => $row['posts'] + ); + $smcFunc['db_free_result']($request); + + // Just return all the top posters. + if ($output_method != 'echo') + return $return; + + // Make a quick array to list the links in. + $temp_array = array(); + foreach ($return as $member) + $temp_array[] = $member['link']; + + echo implode(', ', $temp_array); +} + +// Show boards by activity. +function ssi_topBoards($num_top = 10, $output_method = 'echo') +{ + global $context, $settings, $db_prefix, $txt, $scripturl, $user_info, $modSettings, $smcFunc; + + // Find boards with lots of posts. + $request = $smcFunc['db_query']('', ' + SELECT + b.name, b.num_topics, b.num_posts, b.id_board,' . (!$user_info['is_guest'] ? ' 1 AS is_read' : ' + (IFNULL(lb.id_msg, 0) >= b.id_last_msg) AS is_read') . ' + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member}) + WHERE {query_wanna_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_board}' : '') . ' + ORDER BY b.num_posts DESC + LIMIT ' . $num_top, + array( + 'current_member' => $user_info['id'], + 'recycle_board' => (int) $modSettings['recycle_board'], + ) + ); + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards[] = array( + 'id' => $row['id_board'], + 'num_posts' => $row['num_posts'], + 'num_topics' => $row['num_topics'], + 'name' => $row['name'], + 'new' => empty($row['is_read']), + 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'link' => '' . $row['name'] . '' + ); + $smcFunc['db_free_result']($request); + + // If we shouldn't output or have nothing to output, just jump out. + if ($output_method != 'echo' || empty($boards)) + return $boards; + + echo ' + + + + + + '; + foreach ($boards as $board) + echo ' + + + + + '; + echo ' +
', $txt['board'], '', $txt['board_topics'], '', $txt['posts'], '
', $board['link'], $board['new'] ? ' ' . $txt['new'] . '' : '', '', comma_format($board['num_topics']), '', comma_format($board['num_posts']), '
'; +} + +// Shows the top topics. +function ssi_topTopics($type = 'replies', $num_topics = 10, $output_method = 'echo') +{ + global $db_prefix, $txt, $scripturl, $user_info, $modSettings, $smcFunc, $context; + + if ($modSettings['totalMessages'] > 100000) + { + // !!! Why don't we use {query(_wanna)_see_board}? + $request = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}topics + WHERE num_' . ($type != 'replies' ? 'views' : 'replies') . ' != 0' . ($modSettings['postmod_active'] ? ' + AND approved = {int:is_approved}' : '') . ' + ORDER BY num_' . ($type != 'replies' ? 'views' : 'replies') . ' DESC + LIMIT {int:limit}', + array( + 'is_approved' => 1, + 'limit' => $num_topics > 100 ? ($num_topics + ($num_topics / 2)) : 100, + ) + ); + $topic_ids = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topic_ids[] = $row['id_topic']; + $smcFunc['db_free_result']($request); + } + else + $topic_ids = array(); + + $request = $smcFunc['db_query']('', ' + SELECT m.subject, m.id_topic, t.num_views, t.num_replies + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE {query_wanna_see_board}' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : '') . (!empty($topic_ids) ? ' + AND t.id_topic IN ({array_int:topic_list})' : '') . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_enable}' : '') . ' + ORDER BY t.num_' . ($type != 'replies' ? 'views' : 'replies') . ' DESC + LIMIT {int:limit}', + array( + 'topic_list' => $topic_ids, + 'is_approved' => 1, + 'recycle_enable' => $modSettings['recycle_board'], + 'limit' => $num_topics, + ) + ); + $topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + censorText($row['subject']); + + $topics[] = array( + 'id' => $row['id_topic'], + 'subject' => $row['subject'], + 'num_replies' => $row['num_replies'], + 'num_views' => $row['num_views'], + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'link' => '' . $row['subject'] . '', + ); + } + $smcFunc['db_free_result']($request); + + if ($output_method != 'echo' || empty($topics)) + return $topics; + + echo ' + + + + + + '; + foreach ($topics as $topic) + echo ' + + + + + '; + echo ' +
', $txt['views'], '', $txt['replies'], '
+ ', $topic['link'], ' + ', comma_format($topic['num_views']), '', comma_format($topic['num_replies']), '
'; +} + +// Shows the top topics, by replies. +function ssi_topTopicsReplies($num_topics = 10, $output_method = 'echo') +{ + return ssi_topTopics('replies', $num_topics, $output_method); +} + +// Shows the top topics, by views. +function ssi_topTopicsViews($num_topics = 10, $output_method = 'echo') +{ + return ssi_topTopics('views', $num_topics, $output_method); +} + +// Show a link to the latest member: Please welcome, Someone, out latest member. +function ssi_latestMember($output_method = 'echo') +{ + global $db_prefix, $txt, $scripturl, $context; + + if ($output_method == 'echo') + echo ' + ', $txt['welcome_member'], ' ', $context['common_stats']['latest_member']['link'], '', $txt['newest_member'], '
'; + else + return $context['common_stats']['latest_member']; +} + +// Fetch a random member - if type set to 'day' will only change once a day! +function ssi_randomMember($random_type = '', $output_method = 'echo') +{ + global $modSettings; + + // If we're looking for something to stay the same each day then seed the generator. + if ($random_type == 'day') + { + // Set the seed to change only once per day. + mt_srand(floor(time() / 86400)); + } + + // Get the lowest ID we're interested in. + $member_id = mt_rand(1, $modSettings['latestMember']); + + $where_query = ' + id_member >= {int:selected_member} + AND is_activated = {int:is_activated}'; + + $query_where_params = array( + 'selected_member' => $member_id, + 'is_activated' => 1, + ); + + $result = ssi_queryMembers($where_query, $query_where_params, 1, 'id_member ASC', $output_method); + + // If we got nothing do the reverse - in case of unactivated members. + if (empty($result)) + { + $where_query = ' + id_member <= {int:selected_member} + AND is_activated = {int:is_activated}'; + + $query_where_params = array( + 'selected_member' => $member_id, + 'is_activated' => 1, + ); + + $result = ssi_queryMembers($where_query, $query_where_params, 1, 'id_member DESC', $output_method); + } + + // Just to be sure put the random generator back to something... random. + if ($random_type != '') + mt_srand(time()); + + return $result; +} + +// Fetch a specific member. +function ssi_fetchMember($member_ids = array(), $output_method = 'echo') +{ + if (empty($member_ids)) + return; + + // Can have more than one member if you really want... + $member_ids = is_array($member_ids) ? $member_ids : array($member_ids); + + // Restrict it right! + $query_where = ' + id_member IN ({array_int:member_list})'; + + $query_where_params = array( + 'member_list' => $member_ids, + ); + + // Then make the query and dump the data. + return ssi_queryMembers($query_where, $query_where_params, '', 'id_member', $output_method); +} + +// Get all members of a group. +function ssi_fetchGroupMembers($group_id = null, $output_method = 'echo') +{ + if ($group_id === null) + return; + + $query_where = ' + id_group = {int:id_group} + OR id_post_group = {int:id_group} + OR FIND_IN_SET({int:id_group}, additional_groups)'; + + $query_where_params = array( + 'id_group' => $group_id, + ); + + return ssi_queryMembers($query_where, $query_where_params, '', 'real_name', $output_method); +} + +// Fetch some member data! +function ssi_queryMembers($query_where = null, $query_where_params = array(), $query_limit = '', $query_order = 'id_member DESC', $output_method = 'echo') +{ + global $context, $settings, $scripturl, $txt, $db_prefix, $user_info; + global $modSettings, $smcFunc, $memberContext; + + if ($query_where === null) + return; + + // Fetch the members in question. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE ' . $query_where . ' + ORDER BY ' . $query_order . ' + ' . ($query_limit == '' ? '' : 'LIMIT ' . $query_limit), + array_merge($query_where_params, array( + )) + ); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[] = $row['id_member']; + $smcFunc['db_free_result']($request); + + if (empty($members)) + return array(); + + // Load the members. + loadMemberData($members); + + // Draw the table! + if ($output_method == 'echo') + echo ' + '; + + $query_members = array(); + foreach ($members as $member) + { + // Load their context data. + if (!loadMemberContext($member)) + continue; + + // Store this member's information. + $query_members[$member] = $memberContext[$member]; + + // Only do something if we're echo'ing. + if ($output_method == 'echo') + echo ' + + + '; + } + + // End the table if appropriate. + if ($output_method == 'echo') + echo ' +
+ ', $query_members[$member]['link'], ' +
', $query_members[$member]['blurb'], ' +
', $query_members[$member]['avatar']['image'], ' +
'; + + // Send back the data. + return $query_members; +} + +// Show some basic stats: Total This: XXXX, etc. +function ssi_boardStats($output_method = 'echo') +{ + global $db_prefix, $txt, $scripturl, $modSettings, $smcFunc; + + $totals = array( + 'members' => $modSettings['totalMembers'], + 'posts' => $modSettings['totalMessages'], + 'topics' => $modSettings['totalTopics'] + ); + + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}boards', + array( + ) + ); + list ($totals['boards']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}categories', + array( + ) + ); + list ($totals['categories']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + if ($output_method != 'echo') + return $totals; + + echo ' + ', $txt['total_members'], ': ', comma_format($totals['members']), '
+ ', $txt['total_posts'], ': ', comma_format($totals['posts']), '
+ ', $txt['total_topics'], ': ', comma_format($totals['topics']), '
+ ', $txt['total_cats'], ': ', comma_format($totals['categories']), '
+ ', $txt['total_boards'], ': ', comma_format($totals['boards']); +} + +// Shows a list of online users: YY Guests, ZZ Users and then a list... +function ssi_whosOnline($output_method = 'echo') +{ + global $user_info, $txt, $sourcedir, $settings, $modSettings; + + require_once($sourcedir . '/Subs-MembersOnline.php'); + $membersOnlineOptions = array( + 'show_hidden' => allowedTo('moderate_forum'), + 'sort' => 'log_time', + 'reverse_sort' => true, + ); + $return = getMembersOnlineStats($membersOnlineOptions); + + // Add some redundancy for backwards compatibility reasons. + if ($output_method != 'echo') + return $return + array( + 'users' => $return['users_online'], + 'guests' => $return['num_guests'], + 'hidden' => $return['num_users_hidden'], + 'buddies' => $return['num_buddies'], + 'num_users' => $return['num_users_online'], + 'total_users' => $return['num_users_online'] + $return['num_guests'] + $return['num_spiders'], + ); + + echo ' + ', comma_format($return['num_guests']), ' ', $return['num_guests'] == 1 ? $txt['guest'] : $txt['guests'], ', ', comma_format($return['num_users_online']), ' ', $return['num_users_online'] == 1 ? $txt['user'] : $txt['users']; + + $bracketList = array(); + if (!empty($user_info['buddies'])) + $bracketList[] = comma_format($return['num_buddies']) . ' ' . ($return['num_buddies'] == 1 ? $txt['buddy'] : $txt['buddies']); + if (!empty($return['num_spiders'])) + $bracketList[] = comma_format($return['num_spiders']) . ' ' . ($return['num_spiders'] == 1 ? $txt['spider'] : $txt['spiders']); + if (!empty($return['num_users_hidden'])) + $bracketList[] = comma_format($return['num_users_hidden']) . ' ' . $txt['hidden']; + + if (!empty($bracketList)) + echo ' (' . implode(', ', $bracketList) . ')'; + + echo '
+ ', implode(', ', $return['list_users_online']); + + // Showing membergroups? + if (!empty($settings['show_group_key']) && !empty($return['membergroups'])) + echo '
+ [' . implode(']  [', $return['membergroups']) . ']'; +} + +// Just like whosOnline except it also logs the online presence. +function ssi_logOnline($output_method = 'echo') +{ + writeLog(); + + if ($output_method != 'echo') + return ssi_whosOnline($output_method); + else + ssi_whosOnline($output_method); +} + +// Shows a login box. +function ssi_login($redirect_to = '', $output_method = 'echo') +{ + global $scripturl, $txt, $user_info, $context, $modSettings; + + if ($redirect_to != '') + $_SESSION['login_url'] = $redirect_to; + + if ($output_method != 'echo' || !$user_info['is_guest']) + return $user_info['is_guest']; + + echo ' +
+ + + + + + + + '; + + // Open ID? + if (!empty($modSettings['enableOpenID'])) + echo ' + + + + + '; + + echo ' + + + +
 
 
—', $txt['or'], '—
 
+
'; + +} + +// Show the most-voted-in poll. +function ssi_topPoll($output_method = 'echo') +{ + // Just use recentPoll, no need to duplicate code... + return ssi_recentPoll(true, $output_method); +} + +// Show the most recently posted poll. +function ssi_recentPoll($topPollInstead = false, $output_method = 'echo') +{ + global $db_prefix, $txt, $settings, $boardurl, $user_info, $context, $smcFunc, $modSettings; + + $boardsAllowed = array_intersect(boardsAllowedTo('poll_view'), boardsAllowedTo('poll_vote')); + + if (empty($boardsAllowed)) + return array(); + + $request = $smcFunc['db_query']('', ' + SELECT p.id_poll, p.question, t.id_topic, p.max_votes, p.guest_vote, p.hide_results, p.expire_time + FROM {db_prefix}polls AS p + INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ') + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)' . ($topPollInstead ? ' + INNER JOIN {db_prefix}poll_choices AS pc ON (pc.id_poll = p.id_poll)' : '') . ' + LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_poll = p.id_poll AND lp.id_member > {int:no_member} AND lp.id_member = {int:current_member}) + WHERE p.voting_locked = {int:voting_opened} + AND (p.expire_time = {int:no_expiration} OR {int:current_time} < p.expire_time) + AND ' . ($user_info['is_guest'] ? 'p.guest_vote = {int:guest_vote_allowed}' : 'lp.id_choice IS NULL') . ' + AND {query_wanna_see_board}' . (!in_array(0, $boardsAllowed) ? ' + AND b.id_board IN ({array_int:boards_allowed_list})' : '') . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_enable}' : '') . ' + ORDER BY ' . ($topPollInstead ? 'pc.votes' : 'p.id_poll') . ' DESC + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'boards_allowed_list' => $boardsAllowed, + 'is_approved' => 1, + 'guest_vote_allowed' => 1, + 'no_member' => 0, + 'voting_opened' => 0, + 'no_expiration' => 0, + 'current_time' => time(), + 'recycle_enable' => $modSettings['recycle_board'], + ) + ); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // This user has voted on all the polls. + if ($row === false) + return array(); + + // If this is a guest who's voted we'll through ourselves to show poll to show the results. + if ($user_info['is_guest'] && (!$row['guest_vote'] || (isset($_COOKIE['guest_poll_vote']) && in_array($row['id_poll'], explode(',', $_COOKIE['guest_poll_vote']))))) + return ssi_showPoll($row['id_topic'], $output_method); + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(DISTINCT id_member) + FROM {db_prefix}log_polls + WHERE id_poll = {int:current_poll}', + array( + 'current_poll' => $row['id_poll'], + ) + ); + list ($total) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT id_choice, label, votes + FROM {db_prefix}poll_choices + WHERE id_poll = {int:current_poll}', + array( + 'current_poll' => $row['id_poll'], + ) + ); + $options = array(); + while ($rowChoice = $smcFunc['db_fetch_assoc']($request)) + { + censorText($rowChoice['label']); + + $options[$rowChoice['id_choice']] = array($rowChoice['label'], $rowChoice['votes']); + } + $smcFunc['db_free_result']($request); + + // Can they view it? + $is_expired = !empty($row['expire_time']) && $row['expire_time'] < time(); + $allow_view_results = allowedTo('moderate_board') || $row['hide_results'] == 0 || $is_expired; + + $return = array( + 'id' => $row['id_poll'], + 'image' => 'poll', + 'question' => $row['question'], + 'total_votes' => $total, + 'is_locked' => false, + 'topic' => $row['id_topic'], + 'allow_view_results' => $allow_view_results, + 'options' => array() + ); + + // Calculate the percentages and bar lengths... + $divisor = $return['total_votes'] == 0 ? 1 : $return['total_votes']; + foreach ($options as $i => $option) + { + $bar = floor(($option[1] * 100) / $divisor); + $barWide = $bar == 0 ? 1 : floor(($bar * 5) / 3); + $return['options'][$i] = array( + 'id' => 'options-' . ($topPollInstead ? 'top-' : 'recent-') . $i, + 'percent' => $bar, + 'votes' => $option[1], + 'bar' => '-', + 'option' => parse_bbc($option[0]), + 'vote_button' => '' + ); + } + + $return['allowed_warning'] = $row['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($options), $row['max_votes'])) : ''; + + if ($output_method != 'echo') + return $return; + + if ($allow_view_results) + { + echo ' +
+ ', $return['question'], '
+ ', !empty($return['allowed_warning']) ? $return['allowed_warning'] . '
' : ''; + + foreach ($return['options'] as $option) + echo ' +
'; + + echo ' + + + +
'; + } + else + echo $txt['poll_cannot_see']; +} + +function ssi_showPoll($topic = null, $output_method = 'echo') +{ + global $db_prefix, $txt, $settings, $boardurl, $user_info, $context, $smcFunc, $modSettings; + + $boardsAllowed = boardsAllowedTo('poll_view'); + + if (empty($boardsAllowed)) + return array(); + + if ($topic === null && isset($_REQUEST['ssi_topic'])) + $topic = (int) $_REQUEST['ssi_topic']; + else + $topic = (int) $topic; + + $request = $smcFunc['db_query']('', ' + SELECT + p.id_poll, p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.guest_vote, b.id_board + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE t.id_topic = {int:current_topic} + AND {query_see_board}' . (!in_array(0, $boardsAllowed) ? ' + AND b.id_board IN ({array_int:boards_allowed_see})' : '') . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : '') . ' + LIMIT 1', + array( + 'current_topic' => $topic, + 'boards_allowed_see' => $boardsAllowed, + 'is_approved' => 1, + ) + ); + + // Either this topic has no poll, or the user cannot view it. + if ($smcFunc['db_num_rows']($request) == 0) + return array(); + + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Check if they can vote. + if (!empty($row['expire_time']) && $row['expire_time'] < time()) + $allow_vote = false; + elseif ($user_info['is_guest'] && $row['guest_vote'] && (!isset($_COOKIE['guest_poll_vote']) || !in_array($row['id_poll'], explode(',', $_COOKIE['guest_poll_vote'])))) + $allow_vote = true; + elseif ($user_info['is_guest']) + $allow_vote = false; + elseif (!empty($row['voting_locked']) || !allowedTo('poll_vote', $row['id_board'])) + $allow_vote = false; + else + { + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}log_polls + WHERE id_poll = {int:current_poll} + AND id_member = {int:current_member} + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'current_poll' => $row['id_poll'], + ) + ); + $allow_vote = $smcFunc['db_num_rows']($request) == 0; + $smcFunc['db_free_result']($request); + } + + // Can they view? + $is_expired = !empty($row['expire_time']) && $row['expire_time'] < time(); + $allow_view_results = allowedTo('moderate_board') || $row['hide_results'] == 0 || ($row['hide_results'] == 1 && !$allow_vote) || $is_expired; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(DISTINCT id_member) + FROM {db_prefix}log_polls + WHERE id_poll = {int:current_poll}', + array( + 'current_poll' => $row['id_poll'], + ) + ); + list ($total) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT id_choice, label, votes + FROM {db_prefix}poll_choices + WHERE id_poll = {int:current_poll}', + array( + 'current_poll' => $row['id_poll'], + ) + ); + $options = array(); + $total_votes = 0; + while ($rowChoice = $smcFunc['db_fetch_assoc']($request)) + { + censorText($rowChoice['label']); + + $options[$rowChoice['id_choice']] = array($rowChoice['label'], $rowChoice['votes']); + $total_votes += $rowChoice['votes']; + } + $smcFunc['db_free_result']($request); + + $return = array( + 'id' => $row['id_poll'], + 'image' => empty($pollinfo['voting_locked']) ? 'poll' : 'locked_poll', + 'question' => $row['question'], + 'total_votes' => $total, + 'is_locked' => !empty($pollinfo['voting_locked']), + 'allow_vote' => $allow_vote, + 'allow_view_results' => $allow_view_results, + 'topic' => $topic + ); + + // Calculate the percentages and bar lengths... + $divisor = $total_votes == 0 ? 1 : $total_votes; + foreach ($options as $i => $option) + { + $bar = floor(($option[1] * 100) / $divisor); + $barWide = $bar == 0 ? 1 : floor(($bar * 5) / 3); + $return['options'][$i] = array( + 'id' => 'options-' . $i, + 'percent' => $bar, + 'votes' => $option[1], + 'bar' => '-', + 'option' => parse_bbc($option[0]), + 'vote_button' => '' + ); + } + + $return['allowed_warning'] = $row['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($options), $row['max_votes'])) : ''; + + if ($output_method != 'echo') + return $return; + + if ($return['allow_vote']) + { + echo ' +
+ ', $return['question'], '
+ ', !empty($return['allowed_warning']) ? $return['allowed_warning'] . '
' : ''; + + foreach ($return['options'] as $option) + echo ' +
'; + + echo ' + + + +
'; + } + elseif ($return['allow_view_results']) + { + echo ' +
+ ', $return['question'], ' +
'; + + foreach ($return['options'] as $option) + echo ' +
', $option['option'], '
+
+
+
+
+
+ ', $option['votes'], ' (', $option['percent'], '%) +
'; + echo ' +
+ ', $txt['poll_total_voters'], ': ', $return['total_votes'], ' +
'; + } + // Cannot see it I'm afraid! + else + echo $txt['poll_cannot_see']; +} + +// Takes care of voting - don't worry, this is done automatically. +function ssi_pollVote() +{ + global $context, $db_prefix, $user_info, $sc, $smcFunc, $sourcedir, $modSettings; + + if (!isset($_POST[$context['session_var']]) || $_POST[$context['session_var']] != $sc || empty($_POST['options']) || !isset($_POST['poll'])) + { + echo ' + + + + +« +'; + return; + } + + // This can cause weird errors! (ie. copyright missing.) + checkSession(); + + $_POST['poll'] = (int) $_POST['poll']; + + // Check if they have already voted, or voting is locked. + $request = $smcFunc['db_query']('', ' + SELECT + p.id_poll, p.voting_locked, p.expire_time, p.max_votes, p.guest_vote, + t.id_topic, + IFNULL(lp.id_choice, -1) AS selected + FROM {db_prefix}polls AS p + INNER JOIN {db_prefix}topics AS t ON (t.id_poll = {int:current_poll}) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_poll = p.id_poll AND lp.id_member = {int:current_member}) + WHERE p.id_poll = {int:current_poll} + AND {query_see_board}' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : '') . ' + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'current_poll' => $_POST['poll'], + 'is_approved' => 1, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + die; + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + if (!empty($row['voting_locked']) || ($row['selected'] != -1 && !$user_info['is_guest']) || (!empty($row['expire_time']) && time() > $row['expire_time'])) + redirectexit('topic=' . $row['id_topic'] . '.0'); + + // Too many options checked? + if (count($_REQUEST['options']) > $row['max_votes']) + redirectexit('topic=' . $row['id_topic'] . '.0'); + + // It's a guest who has already voted? + if ($user_info['is_guest']) + { + // Guest voting disabled? + if (!$row['guest_vote']) + redirectexit('topic=' . $row['id_topic'] . '.0'); + // Already voted? + elseif (isset($_COOKIE['guest_poll_vote']) && in_array($row['id_poll'], explode(',', $_COOKIE['guest_poll_vote']))) + redirectexit('topic=' . $row['id_topic'] . '.0'); + } + + $options = array(); + $inserts = array(); + foreach ($_REQUEST['options'] as $id) + { + $id = (int) $id; + + $options[] = $id; + $inserts[] = array($_POST['poll'], $user_info['id'], $id); + } + + // Add their vote in to the tally. + $smcFunc['db_insert']('insert', + $db_prefix . 'log_polls', + array('id_poll' => 'int', 'id_member' => 'int', 'id_choice' => 'int'), + $inserts, + array('id_poll', 'id_member', 'id_choice') + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}poll_choices + SET votes = votes + 1 + WHERE id_poll = {int:current_poll} + AND id_choice IN ({array_int:option_list})', + array( + 'option_list' => $options, + 'current_poll' => $_POST['poll'], + ) + ); + + // Track the vote if a guest. + if ($user_info['is_guest']) + { + $_COOKIE['guest_poll_vote'] = !empty($_COOKIE['guest_poll_vote']) ? ($_COOKIE['guest_poll_vote'] . ',' . $row['id_poll']) : $row['id_poll']; + + require_once($sourcedir . '/Subs-Auth.php'); + $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); + setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], 0); + } + + redirectexit('topic=' . $row['id_topic'] . '.0'); +} + +// Show a search box. +function ssi_quickSearch($output_method = 'echo') +{ + global $scripturl, $txt, $context; + + if ($output_method != 'echo') + return $scripturl . '?action=search'; + + echo ' +
+ +
'; +} + +// Show what would be the forum news. +function ssi_news($output_method = 'echo') +{ + global $context; + + if ($output_method != 'echo') + return $context['random_news_line']; + + echo $context['random_news_line']; +} + +// Show today's birthdays. +function ssi_todaysBirthdays($output_method = 'echo') +{ + global $scripturl, $modSettings, $user_info; + + if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view') || !allowedTo('profile_view_any')) + return; + + $eventOptions = array( + 'include_birthdays' => true, + 'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'], + ); + $return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'Subs-Calendar.php', 'cache_getRecentEvents', array($eventOptions)); + + if ($output_method != 'echo') + return $return['calendar_birthdays']; + + foreach ($return['calendar_birthdays'] as $member) + echo ' + ' . $member['name'] . (isset($member['age']) ? ' (' . $member['age'] . ')' : '') . '' . (!$member['is_last'] ? ', ' : ''); +} + +// Show today's holidays. +function ssi_todaysHolidays($output_method = 'echo') +{ + global $modSettings, $user_info; + + if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view')) + return; + + $eventOptions = array( + 'include_holidays' => true, + 'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'], + ); + $return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'Subs-Calendar.php', 'cache_getRecentEvents', array($eventOptions)); + + if ($output_method != 'echo') + return $return['calendar_holidays']; + + echo ' + ', implode(', ', $return['calendar_holidays']); +} + +// Show today's events. +function ssi_todaysEvents($output_method = 'echo') +{ + global $modSettings, $user_info; + + if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view')) + return; + + $eventOptions = array( + 'include_events' => true, + 'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'], + ); + $return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'Subs-Calendar.php', 'cache_getRecentEvents', array($eventOptions)); + + if ($output_method != 'echo') + return $return['calendar_events']; + + foreach ($return['calendar_events'] as $event) + { + if ($event['can_edit']) + echo ' + * '; + echo ' + ' . $event['link'] . (!$event['is_last'] ? ', ' : ''); + } +} + +// Show all calendar entires for today. (birthdays, holodays, and events.) +function ssi_todaysCalendar($output_method = 'echo') +{ + global $modSettings, $txt, $scripturl, $user_info; + + $eventOptions = array( + 'include_birthdays' => allowedTo('profile_view_any'), + 'include_holidays' => true, + 'include_events' => true, + 'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'], + ); + $return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'Subs-Calendar.php', 'cache_getRecentEvents', array($eventOptions)); + + if ($output_method != 'echo') + return $return; + + if (!empty($return['calendar_holidays'])) + echo ' + ' . $txt['calendar_prompt'] . ' ' . implode(', ', $return['calendar_holidays']) . '
'; + if (!empty($return['calendar_birthdays'])) + { + echo ' + ' . $txt['birthdays_upcoming'] . ' '; + foreach ($return['calendar_birthdays'] as $member) + echo ' + ', $member['name'], isset($member['age']) ? ' (' . $member['age'] . ')' : '', '', !$member['is_last'] ? ', ' : ''; + echo ' +
'; + } + if (!empty($return['calendar_events'])) + { + echo ' + ' . $txt['events_upcoming'] . ' '; + foreach ($return['calendar_events'] as $event) + { + if ($event['can_edit']) + echo ' + * '; + echo ' + ' . $event['link'] . (!$event['is_last'] ? ', ' : ''); + } + } +} + +// Show the latest news, with a template... by board. +function ssi_boardNews($board = null, $limit = null, $start = null, $length = null, $output_method = 'echo') +{ + global $scripturl, $db_prefix, $txt, $settings, $modSettings, $context; + global $smcFunc; + + loadLanguage('Stats'); + + // Must be integers.... + if ($limit === null) + $limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 5; + else + $limit = (int) $limit; + + if ($start === null) + $start = isset($_GET['start']) ? (int) $_GET['start'] : 0; + else + $start = (int) $start; + + if ($board !== null) + $board = (int) $board; + elseif (isset($_GET['board'])) + $board = (int) $_GET['board']; + + if ($length === null) + $length = isset($_GET['length']) ? (int) $_GET['length'] : 0; + else + $length = (int) $length; + + $limit = max(0, $limit); + $start = max(0, $start); + + // Make sure guests can see this board. + $request = $smcFunc['db_query']('', ' + SELECT id_board + FROM {db_prefix}boards + WHERE ' . ($board === null ? '' : 'id_board = {int:current_board} + AND ') . 'FIND_IN_SET(-1, member_groups) + LIMIT 1', + array( + 'current_board' => $board, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + { + if ($output_method == 'echo') + die($txt['ssi_no_guests']); + else + return array(); + } + list ($board) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Load the message icons - the usual suspects. + $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless'); + $icon_sources = array(); + foreach ($stable_icons as $icon) + $icon_sources[$icon] = 'images_url'; + + // Find the post ids. + $request = $smcFunc['db_query']('', ' + SELECT t.id_first_msg + FROM {db_prefix}topics as t + LEFT JOIN {db_prefix}boards as b ON (b.id_board = t.id_board) + WHERE t.id_board = {int:current_board}' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : '') . ' + AND {query_see_board} + ORDER BY t.id_first_msg DESC + LIMIT ' . $start . ', ' . $limit, + array( + 'current_board' => $board, + 'is_approved' => 1, + ) + ); + $posts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $posts[] = $row['id_first_msg']; + $smcFunc['db_free_result']($request); + + if (empty($posts)) + return array(); + + // Find the posts. + $request = $smcFunc['db_query']('', ' + SELECT + m.icon, m.subject, m.body, IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time, + t.num_replies, t.id_topic, m.id_member, m.smileys_enabled, m.id_msg, t.locked, t.id_last_msg + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE t.id_first_msg IN ({array_int:post_list}) + ORDER BY t.id_first_msg DESC + LIMIT ' . count($posts), + array( + 'post_list' => $posts, + ) + ); + $return = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If we want to limit the length of the post. + if (!empty($length) && $smcFunc['strlen']($row['body']) > $length) + { + $row['body'] = $smcFunc['substr']($row['body'], 0, $length); + + // The first space or line break. (
, etc.) + $cutoff = max(strrpos($row['body'], ' '), strrpos($row['body'], '<')); + + if ($cutoff !== false) + $row['body'] = $smcFunc['substr']($row['body'], 0, $cutoff); + $row['body'] .= '...'; + } + + $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); + + // Check that this message icon is there... + if (empty($modSettings['messageIconChecks_disable']) && !isset($icon_sources[$row['icon']])) + $icon_sources[$row['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['icon'] . '.gif') ? 'images_url' : 'default_images_url'; + + censorText($row['subject']); + censorText($row['body']); + + $return[] = array( + 'id' => $row['id_topic'], + 'message_id' => $row['id_msg'], + 'icon' => '' . $row['icon'] . '', + 'subject' => $row['subject'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'body' => $row['body'], + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'link' => '' . $row['num_replies'] . ' ' . ($row['num_replies'] == 1 ? $txt['ssi_comment'] : $txt['ssi_comments']) . '', + 'replies' => $row['num_replies'], + 'comment_href' => !empty($row['locked']) ? '' : $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . ';last_msg=' . $row['id_last_msg'], + 'comment_link' => !empty($row['locked']) ? '' : '' . $txt['ssi_write_comment'] . '', + 'new_comment' => !empty($row['locked']) ? '' : '' . $txt['ssi_write_comment'] . '', + 'poster' => array( + 'id' => $row['id_member'], + 'name' => $row['poster_name'], + 'href' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '', + 'link' => !empty($row['id_member']) ? '' . $row['poster_name'] . '' : $row['poster_name'] + ), + 'locked' => !empty($row['locked']), + 'is_last' => false + ); + } + $smcFunc['db_free_result']($request); + + if (empty($return)) + return $return; + + $return[count($return) - 1]['is_last'] = true; + + if ($output_method != 'echo') + return $return; + + foreach ($return as $news) + { + echo ' +
+

+ ', $news['icon'], ' + ', $news['subject'], ' +

+
', $news['time'], ' ', $txt['by'], ' ', $news['poster']['link'], '
+
', $news['body'], '
+ ', $news['link'], $news['locked'] ? '' : ' | ' . $news['comment_link'], ' +
'; + + if (!$news['is_last']) + echo ' +
'; + } +} + +// Show the most recent events. +function ssi_recentEvents($max_events = 7, $output_method = 'echo') +{ + global $db_prefix, $user_info, $scripturl, $modSettings, $txt, $context, $smcFunc; + + if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view')) + return; + + // Find all events which are happening in the near future that the member can see. + $request = $smcFunc['db_query']('', ' + SELECT + cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, cal.id_topic, + cal.id_board, t.id_first_msg, t.approved + FROM {db_prefix}calendar AS cal + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = cal.id_board) + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic) + WHERE cal.start_date <= {date:current_date} + AND cal.end_date >= {date:current_date} + AND (cal.id_board = {int:no_board} OR {query_wanna_see_board}) + ORDER BY cal.start_date DESC + LIMIT ' . $max_events, + array( + 'current_date' => strftime('%Y-%m-%d', forum_time(false)), + 'no_board' => 0, + ) + ); + $return = array(); + $duplicates = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Check if we've already come by an event linked to this same topic with the same title... and don't display it if we have. + if (!empty($duplicates[$row['title'] . $row['id_topic']])) + continue; + + // Censor the title. + censorText($row['title']); + + if ($row['start_date'] < strftime('%Y-%m-%d', forum_time(false))) + $date = strftime('%Y-%m-%d', forum_time(false)); + else + $date = $row['start_date']; + + // If the topic it is attached to is not approved then don't link it. + if (!empty($row['id_first_msg']) && !$row['approved']) + $row['id_board'] = $row['id_topic'] = $row['id_first_msg'] = 0; + + $return[$date][] = array( + 'id' => $row['id_event'], + 'title' => $row['title'], + 'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')), + 'modify_href' => $scripturl . '?action=' . ($row['id_board'] == 0 ? 'calendar;sa=post;' : 'post;msg=' . $row['id_first_msg'] . ';topic=' . $row['id_topic'] . '.0;calendar;') . 'eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], + 'href' => $row['id_board'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'link' => $row['id_board'] == 0 ? $row['title'] : '' . $row['title'] . '', + 'start_date' => $row['start_date'], + 'end_date' => $row['end_date'], + 'is_last' => false + ); + + // Let's not show this one again, huh? + $duplicates[$row['title'] . $row['id_topic']] = true; + } + $smcFunc['db_free_result']($request); + + foreach ($return as $mday => $array) + $return[$mday][count($array) - 1]['is_last'] = true; + + if ($output_method != 'echo' || empty($return)) + return $return; + + // Well the output method is echo. + echo ' + ' . $txt['events'] . ' '; + foreach ($return as $mday => $array) + foreach ($array as $event) + { + if ($event['can_edit']) + echo ' + * '; + + echo ' + ' . $event['link'] . (!$event['is_last'] ? ', ' : ''); + } +} + +// Check the passed id_member/password. If $is_username is true, treats $id as a username. +function ssi_checkPassword($id = null, $password = null, $is_username = false) +{ + global $db_prefix, $sourcedir, $smcFunc; + + // If $id is null, this was most likely called from a query string and should do nothing. + if ($id === null) + return; + + $request = $smcFunc['db_query']('', ' + SELECT passwd, member_name, is_activated + FROM {db_prefix}members + WHERE ' . ($is_username ? 'member_name' : 'id_member') . ' = {string:id} + LIMIT 1', + array( + 'id' => $id, + ) + ); + list ($pass, $user, $active) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return sha1(strtolower($user) . $password) == $pass && $active == 1; +} + +// We want to show the recent attachments outside of the forum. +function ssi_recentAttachments($num_attachments = 10, $attachment_ext = array(), $output_method = 'echo') +{ + global $smcFunc, $context, $modSettings, $scripturl, $txt, $settings; + + // We want to make sure that we only get attachments for boards that we can see *if* any. + $attachments_boards = boardsAllowedTo('view_attachments'); + + // No boards? Adios amigo. + if (empty($attachments_boards)) + return array(); + + // Is it an array? + if (!is_array($attachment_ext)) + $attachment_ext = array($attachment_ext); + + // Lets build the query. + $request = $smcFunc['db_query']('', ' + SELECT + att.id_attach, att.id_msg, att.filename, IFNULL(att.size, 0) AS filesize, att.downloads, mem.id_member, + IFNULL(mem.real_name, m.poster_name) AS poster_name, m.id_topic, m.subject, t.id_board, m.poster_time, + att.width, att.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ', IFNULL(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . ' + FROM {db_prefix}attachments AS att + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = att.id_msg) + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ' + LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = att.id_thumb)') . ' + WHERE att.attachment_type = 0' . ($attachments_boards === array(0) ? '' : ' + AND m.id_board IN ({array_int:boards_can_see})') . (!empty($attachment_ext) ? ' + AND att.fileext IN ({array_string:attachment_ext})' : '') . + (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND t.approved = {int:is_approved} + AND m.approved = {int:is_approved} + AND att.approved = {int:is_approved}') . ' + ORDER BY att.id_attach DESC + LIMIT {int:num_attachments}', + array( + 'boards_can_see' => $attachments_boards, + 'attachment_ext' => $attachment_ext, + 'num_attachments' => $num_attachments, + 'is_approved' => 1, + ) + ); + + // We have something. + $attachments = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $filename = preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($row['filename'])); + + // Is it an image? + $attachments[$row['id_attach']] = array( + 'member' => array( + 'id' => $row['id_member'], + 'name' => $row['poster_name'], + 'link' => empty($row['id_member']) ? $row['poster_name'] : '' . $row['poster_name'] . '', + ), + 'file' => array( + 'filename' => $filename, + 'filesize' => round($row['filesize'] /1024, 2) . $txt['kilobyte'], + 'downloads' => $row['downloads'], + 'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'], + 'link' => ' ' . $filename . '', + 'is_image' => !empty($row['width']) && !empty($row['height']) && !empty($modSettings['attachmentShowImages']), + ), + 'topic' => array( + 'id' => $row['id_topic'], + 'subject' => $row['subject'], + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + 'link' => '' . $row['subject'] . '', + 'time' => timeformat($row['poster_time']), + ), + ); + + // Images. + if ($attachments[$row['id_attach']]['file']['is_image']) + { + $id_thumb = empty($row['id_thumb']) ? $row['id_attach'] : $row['id_thumb']; + $attachments[$row['id_attach']]['file']['image'] = array( + 'id' => $id_thumb, + 'width' => $row['width'], + 'height' => $row['height'], + 'img' => '' . $filename . '', + 'thumb' => '' . $filename . '', + 'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $id_thumb . ';image', + 'link' => '' . $filename . '', + ); + } + } + $smcFunc['db_free_result']($request); + + // So you just want an array? Here you can have it. + if ($output_method == 'array' || empty($attachments)) + return $attachments; + + // Give them the default. + echo ' + + + + + + + '; + foreach ($attachments as $attach) + echo ' + + + + + + '; + echo ' +
', $txt['file'], '', $txt['posted_by'], '', $txt['downloads'], '', $txt['filesize'], '
', $attach['file']['link'], '', $attach['member']['link'], '', $attach['file']['downloads'], '', $attach['file']['filesize'], '
'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Settings.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Settings.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,66 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Settings_bak.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Settings_bak.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,67 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/afro.gif Binary file forum/Smileys/aaron/afro.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/angel.gif Binary file forum/Smileys/aaron/angel.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/angry.gif Binary file forum/Smileys/aaron/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/azn.gif Binary file forum/Smileys/aaron/azn.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/blank.gif Binary file forum/Smileys/aaron/blank.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/cheesy.gif Binary file forum/Smileys/aaron/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/cool.gif Binary file forum/Smileys/aaron/cool.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/cry.gif Binary file forum/Smileys/aaron/cry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/embarrassed.gif Binary file forum/Smileys/aaron/embarrassed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/evil.gif Binary file forum/Smileys/aaron/evil.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/grin.gif Binary file forum/Smileys/aaron/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/huh.gif Binary file forum/Smileys/aaron/huh.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Smileys/aaron/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/kiss.gif Binary file forum/Smileys/aaron/kiss.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/laugh.gif Binary file forum/Smileys/aaron/laugh.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/lipsrsealed.gif Binary file forum/Smileys/aaron/lipsrsealed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/police.gif Binary file forum/Smileys/aaron/police.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/rolleyes.gif Binary file forum/Smileys/aaron/rolleyes.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/sad.gif Binary file forum/Smileys/aaron/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/shocked.gif Binary file forum/Smileys/aaron/shocked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/smiley.gif Binary file forum/Smileys/aaron/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/tongue.gif Binary file forum/Smileys/aaron/tongue.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/undecided.gif Binary file forum/Smileys/aaron/undecided.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/wink.gif Binary file forum/Smileys/aaron/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/afro.gif Binary file forum/Smileys/akyhne/afro.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/angel.gif Binary file forum/Smileys/akyhne/angel.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/angry.gif Binary file forum/Smileys/akyhne/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/azn.gif Binary file forum/Smileys/akyhne/azn.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/blank.gif Binary file forum/Smileys/akyhne/blank.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/cheesy.gif Binary file forum/Smileys/akyhne/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/cool.gif Binary file forum/Smileys/akyhne/cool.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/cry.gif Binary file forum/Smileys/akyhne/cry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/embarrassed.gif Binary file forum/Smileys/akyhne/embarrassed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/evil.gif Binary file forum/Smileys/akyhne/evil.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/grin.gif Binary file forum/Smileys/akyhne/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/huh.gif Binary file forum/Smileys/akyhne/huh.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Smileys/akyhne/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/kiss.gif Binary file forum/Smileys/akyhne/kiss.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/laugh.gif Binary file forum/Smileys/akyhne/laugh.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/lipsrsealed.gif Binary file forum/Smileys/akyhne/lipsrsealed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/police.gif Binary file forum/Smileys/akyhne/police.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/rolleyes.gif Binary file forum/Smileys/akyhne/rolleyes.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/sad.gif Binary file forum/Smileys/akyhne/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/shocked.gif Binary file forum/Smileys/akyhne/shocked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/smiley.gif Binary file forum/Smileys/akyhne/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/tongue.gif Binary file forum/Smileys/akyhne/tongue.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/undecided.gif Binary file forum/Smileys/akyhne/undecided.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/wink.gif Binary file forum/Smileys/akyhne/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/afro.gif Binary file forum/Smileys/classic/afro.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/angel.gif Binary file forum/Smileys/classic/angel.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/angry.gif Binary file forum/Smileys/classic/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/azn.gif Binary file forum/Smileys/classic/azn.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/cheesy.gif Binary file forum/Smileys/classic/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/cool.gif Binary file forum/Smileys/classic/cool.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/cry.gif Binary file forum/Smileys/classic/cry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/embarrassed.gif Binary file forum/Smileys/classic/embarrassed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/evil.gif Binary file forum/Smileys/classic/evil.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/grin.gif Binary file forum/Smileys/classic/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/huh.gif Binary file forum/Smileys/classic/huh.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Smileys/classic/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/kiss.gif Binary file forum/Smileys/classic/kiss.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/laugh.gif Binary file forum/Smileys/classic/laugh.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/lipsrsealed.gif Binary file forum/Smileys/classic/lipsrsealed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/police.gif Binary file forum/Smileys/classic/police.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/rolleyes.gif Binary file forum/Smileys/classic/rolleyes.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/sad.gif Binary file forum/Smileys/classic/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/shocked.gif Binary file forum/Smileys/classic/shocked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/smiley.gif Binary file forum/Smileys/classic/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/tongue.gif Binary file forum/Smileys/classic/tongue.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/undecided.gif Binary file forum/Smileys/classic/undecided.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/wink.gif Binary file forum/Smileys/classic/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/afro.gif Binary file forum/Smileys/default/afro.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/angel.gif Binary file forum/Smileys/default/angel.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/angry.gif Binary file forum/Smileys/default/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/azn.gif Binary file forum/Smileys/default/azn.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/blank.gif Binary file forum/Smileys/default/blank.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/cheesy.gif Binary file forum/Smileys/default/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/cool.gif Binary file forum/Smileys/default/cool.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/cry.gif Binary file forum/Smileys/default/cry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/embarrassed.gif Binary file forum/Smileys/default/embarrassed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/evil.gif Binary file forum/Smileys/default/evil.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/grin.gif Binary file forum/Smileys/default/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/huh.gif Binary file forum/Smileys/default/huh.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Smileys/default/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/kiss.gif Binary file forum/Smileys/default/kiss.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/laugh.gif Binary file forum/Smileys/default/laugh.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/lipsrsealed.gif Binary file forum/Smileys/default/lipsrsealed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/police.gif Binary file forum/Smileys/default/police.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/rolleyes.gif Binary file forum/Smileys/default/rolleyes.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/sad.gif Binary file forum/Smileys/default/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/shocked.gif Binary file forum/Smileys/default/shocked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/smiley.gif Binary file forum/Smileys/default/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/tongue.gif Binary file forum/Smileys/default/tongue.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/undecided.gif Binary file forum/Smileys/default/undecided.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/wink.gif Binary file forum/Smileys/default/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Smileys/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,16 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Admin.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Admin.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,999 @@ + array( + 'title' => $txt['admin_main'], + 'permission' => array('admin_forum', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'manage_boards', 'manage_smileys', 'manage_attachments'), + 'areas' => array( + 'index' => array( + 'label' => $txt['admin_center'], + 'function' => 'AdminHome', + 'icon' => 'administration.gif', + ), + 'credits' => array( + 'label' => $txt['support_credits_title'], + 'function' => 'AdminHome', + 'icon' => 'support.gif', + ), + 'news' => array( + 'label' => $txt['news_title'], + 'file' => 'ManageNews.php', + 'function' => 'ManageNews', + 'icon' => 'news.gif', + 'permission' => array('edit_news', 'send_mail', 'admin_forum'), + 'subsections' => array( + 'editnews' => array($txt['admin_edit_news'], 'edit_news'), + 'mailingmembers' => array($txt['admin_newsletters'], 'send_mail'), + 'settings' => array($txt['settings'], 'admin_forum'), + ), + ), + 'packages' => array( + 'label' => $txt['package'], + 'file' => 'Packages.php', + 'function' => 'Packages', + 'permission' => array('admin_forum'), + 'icon' => 'packages.gif', + 'subsections' => array( + 'browse' => array($txt['browse_packages']), + 'packageget' => array($txt['download_packages'], 'url' => $scripturl . '?action=admin;area=packages;sa=packageget;get'), + 'installed' => array($txt['installed_packages']), + 'perms' => array($txt['package_file_perms']), + 'options' => array($txt['package_settings']), + ), + ), + 'search' => array( + 'function' => 'AdminSearch', + 'permission' => array('admin_forum'), + 'select' => 'index' + ), + ), + ), + 'config' => array( + 'title' => $txt['admin_config'], + 'permission' => array('admin_forum'), + 'areas' => array( + 'corefeatures' => array( + 'label' => $txt['core_settings_title'], + 'file' => 'ManageSettings.php', + 'function' => 'ModifyCoreFeatures', + 'icon' => 'corefeatures.gif', + ), + 'featuresettings' => array( + 'label' => $txt['modSettings_title'], + 'file' => 'ManageSettings.php', + 'function' => 'ModifyFeatureSettings', + 'icon' => 'features.gif', + 'subsections' => array( + 'basic' => array($txt['mods_cat_features']), + 'layout' => array($txt['mods_cat_layout']), + 'karma' => array($txt['karma'], 'enabled' => in_array('k', $context['admin_features'])), + 'sig' => array($txt['signature_settings_short']), + 'profile' => array($txt['custom_profile_shorttitle'], 'enabled' => in_array('cp', $context['admin_features'])), + ), + ), + 'securitysettings' => array( + 'label' => $txt['admin_security_moderation'], + 'file' => 'ManageSettings.php', + 'function' => 'ModifySecuritySettings', + 'icon' => 'security.gif', + 'subsections' => array( + 'general' => array($txt['mods_cat_security_general']), + 'spam' => array($txt['antispam_title']), + 'moderation' => array($txt['moderation_settings_short'], 'enabled' => substr($modSettings['warning_settings'], 0, 1) == 1), + ), + ), + 'languages' => array( + 'label' => $txt['language_configuration'], + 'file' => 'ManageServer.php', + 'function' => 'ManageLanguages', + 'icon' => 'languages.gif', + 'subsections' => array( + 'edit' => array($txt['language_edit']), + 'add' => array($txt['language_add']), + 'settings' => array($txt['language_settings']), + ), + ), + 'serversettings' => array( + 'label' => $txt['admin_server_settings'], + 'file' => 'ManageServer.php', + 'function' => 'ModifySettings', + 'icon' => 'server.gif', + 'subsections' => array( + 'general' => array($txt['general_settings']), + 'database' => array($txt['database_paths_settings']), + 'cookie' => array($txt['cookies_sessions_settings']), + 'cache' => array($txt['caching_settings']), + 'loads' => array($txt['load_balancing_settings']), + ), + ), + 'current_theme' => array( + 'label' => $txt['theme_current_settings'], + 'file' => 'Themes.php', + 'function' => 'ThemesMain', + 'custom_url' => $scripturl . '?action=admin;area=theme;sa=settings;th=' . $settings['theme_id'], + 'icon' => 'current_theme.gif', + ), + 'theme' => array( + 'label' => $txt['theme_admin'], + 'file' => 'Themes.php', + 'function' => 'ThemesMain', + 'custom_url' => $scripturl . '?action=admin;area=theme;sa=admin', + 'icon' => 'themes.gif', + 'subsections' => array( + 'admin' => array($txt['themeadmin_admin_title']), + 'list' => array($txt['themeadmin_list_title']), + 'reset' => array($txt['themeadmin_reset_title']), + 'edit' => array($txt['themeadmin_edit_title']), + ), + ), + 'modsettings' => array( + 'label' => $txt['admin_modifications'], + 'file' => 'ManageSettings.php', + 'function' => 'ModifyModSettings', + 'icon' => 'modifications.gif', + 'subsections' => array( + 'general' => array($txt['mods_cat_modifications_misc']), + // Mod Authors for a "ADD AFTER" on this line. Ensure you end your change with a comma. For example: + // 'shout' => array($txt['shout']), + // Note the comma!! The setting with automatically appear with the first mod to be added. + ), + ), + ), + ), + 'layout' => array( + 'title' => $txt['layout_controls'], + 'permission' => array('manage_boards', 'admin_forum', 'manage_smileys', 'manage_attachments', 'moderate_forum'), + 'areas' => array( + 'manageboards' => array( + 'label' => $txt['admin_boards'], + 'file' => 'ManageBoards.php', + 'function' => 'ManageBoards', + 'icon' => 'boards.gif', + 'permission' => array('manage_boards'), + 'subsections' => array( + 'main' => array($txt['boardsEdit']), + 'newcat' => array($txt['mboards_new_cat']), + 'settings' => array($txt['settings'], 'admin_forum'), + ), + ), + 'postsettings' => array( + 'label' => $txt['manageposts'], + 'file' => 'ManagePosts.php', + 'function' => 'ManagePostSettings', + 'permission' => array('admin_forum'), + 'icon' => 'posts.gif', + 'subsections' => array( + 'posts' => array($txt['manageposts_settings']), + 'bbc' => array($txt['manageposts_bbc_settings']), + 'censor' => array($txt['admin_censored_words']), + 'topics' => array($txt['manageposts_topic_settings']), + ), + ), + 'managecalendar' => array( + 'label' => $txt['manage_calendar'], + 'file' => 'ManageCalendar.php', + 'function' => 'ManageCalendar', + 'icon' => 'calendar.gif', + 'permission' => array('admin_forum'), + 'enabled' => in_array('cd', $context['admin_features']), + 'subsections' => array( + 'holidays' => array($txt['manage_holidays'], 'admin_forum', 'enabled' => !empty($modSettings['cal_enabled'])), + 'settings' => array($txt['calendar_settings'], 'admin_forum'), + ), + ), + 'managesearch' => array( + 'label' => $txt['manage_search'], + 'file' => 'ManageSearch.php', + 'function' => 'ManageSearch', + 'icon' => 'search.gif', + 'permission' => array('admin_forum'), + 'subsections' => array( + 'weights' => array($txt['search_weights']), + 'method' => array($txt['search_method']), + 'settings' => array($txt['settings']), + ), + ), + 'smileys' => array( + 'label' => $txt['smileys_manage'], + 'file' => 'ManageSmileys.php', + 'function' => 'ManageSmileys', + 'icon' => 'smiley.gif', + 'permission' => array('manage_smileys'), + 'subsections' => array( + 'editsets' => array($txt['smiley_sets']), + 'addsmiley' => array($txt['smileys_add'], 'enabled' => !empty($modSettings['smiley_enable'])), + 'editsmileys' => array($txt['smileys_edit'], 'enabled' => !empty($modSettings['smiley_enable'])), + 'setorder' => array($txt['smileys_set_order'], 'enabled' => !empty($modSettings['smiley_enable'])), + 'editicons' => array($txt['icons_edit_message_icons'], 'enabled' => !empty($modSettings['messageIcons_enable'])), + 'settings' => array($txt['settings']), + ), + ), + 'manageattachments' => array( + 'label' => $txt['attachments_avatars'], + 'file' => 'ManageAttachments.php', + 'function' => 'ManageAttachments', + 'icon' => 'attachment.gif', + 'permission' => array('manage_attachments'), + 'subsections' => array( + 'browse' => array($txt['attachment_manager_browse']), + 'attachments' => array($txt['attachment_manager_settings']), + 'avatars' => array($txt['attachment_manager_avatar_settings']), + 'maintenance' => array($txt['attachment_manager_maintenance']), + ), + ), + ), + ), + 'members' => array( + 'title' => $txt['admin_manage_members'], + 'permission' => array('moderate_forum', 'manage_membergroups', 'manage_bans', 'manage_permissions', 'admin_forum'), + 'areas' => array( + 'viewmembers' => array( + 'label' => $txt['admin_users'], + 'file' => 'ManageMembers.php', + 'function' => 'ViewMembers', + 'icon' => 'members.gif', + 'permission' => array('moderate_forum'), + 'subsections' => array( + 'all' => array($txt['view_all_members']), + 'search' => array($txt['mlist_search']), + ), + ), + 'membergroups' => array( + 'label' => $txt['admin_groups'], + 'file' => 'ManageMembergroups.php', + 'function' => 'ModifyMembergroups', + 'icon' => 'membergroups.gif', + 'permission' => array('manage_membergroups'), + 'subsections' => array( + 'index' => array($txt['membergroups_edit_groups'], 'manage_membergroups'), + 'add' => array($txt['membergroups_new_group'], 'manage_membergroups'), + 'settings' => array($txt['settings'], 'admin_forum'), + ), + ), + 'permissions' => array( + 'label' => $txt['edit_permissions'], + 'file' => 'ManagePermissions.php', + 'function' => 'ModifyPermissions', + 'icon' => 'permissions.gif', + 'permission' => array('manage_permissions'), + 'subsections' => array( + 'index' => array($txt['permissions_groups'], 'manage_permissions'), + 'board' => array($txt['permissions_boards'], 'manage_permissions'), + 'profiles' => array($txt['permissions_profiles'], 'manage_permissions'), + 'postmod' => array($txt['permissions_post_moderation'], 'manage_permissions', 'enabled' => $modSettings['postmod_active']), + 'settings' => array($txt['settings'], 'admin_forum'), + ), + ), + 'regcenter' => array( + 'label' => $txt['registration_center'], + 'file' => 'ManageRegistration.php', + 'function' => 'RegCenter', + 'icon' => 'regcenter.gif', + 'permission' => array('admin_forum', 'moderate_forum'), + 'subsections' => array( + 'register' => array($txt['admin_browse_register_new'], 'moderate_forum'), + 'agreement' => array($txt['registration_agreement'], 'admin_forum'), + 'reservednames' => array($txt['admin_reserved_set'], 'admin_forum'), + 'settings' => array($txt['settings'], 'admin_forum'), + ), + ), + 'ban' => array( + 'label' => $txt['ban_title'], + 'file' => 'ManageBans.php', + 'function' => 'Ban', + 'icon' => 'ban.gif', + 'permission' => 'manage_bans', + 'subsections' => array( + 'list' => array($txt['ban_edit_list']), + 'add' => array($txt['ban_add_new']), + 'browse' => array($txt['ban_trigger_browse']), + 'log' => array($txt['ban_log']), + ), + ), + 'paidsubscribe' => array( + 'label' => $txt['paid_subscriptions'], + 'enabled' => in_array('ps', $context['admin_features']), + 'file' => 'ManagePaid.php', + 'icon' => 'paid.gif', + 'function' => 'ManagePaidSubscriptions', + 'permission' => 'admin_forum', + 'subsections' => array( + 'view' => array($txt['paid_subs_view']), + 'settings' => array($txt['settings']), + ), + ), + 'sengines' => array( + 'label' => $txt['search_engines'], + 'enabled' => in_array('sp', $context['admin_features']), + 'file' => 'ManageSearchEngines.php', + 'icon' => 'engines.gif', + 'function' => 'SearchEngines', + 'permission' => 'admin_forum', + 'subsections' => array( + 'stats' => array($txt['spider_stats']), + 'logs' => array($txt['spider_logs']), + 'spiders' => array($txt['spiders']), + 'settings' => array($txt['settings']), + ), + ), + ), + ), + 'maintenance' => array( + 'title' => $txt['admin_maintenance'], + 'permission' => array('admin_forum'), + 'areas' => array( + 'maintain' => array( + 'label' => $txt['maintain_title'], + 'file' => 'ManageMaintenance.php', + 'icon' => 'maintain.gif', + 'function' => 'ManageMaintenance', + 'subsections' => array( + 'routine' => array($txt['maintain_sub_routine'], 'admin_forum'), + 'database' => array($txt['maintain_sub_database'], 'admin_forum'), + 'members' => array($txt['maintain_sub_members'], 'admin_forum'), + 'topics' => array($txt['maintain_sub_topics'], 'admin_forum'), + ), + ), + 'scheduledtasks' => array( + 'label' => $txt['maintain_tasks'], + 'file' => 'ManageScheduledTasks.php', + 'icon' => 'scheduled.gif', + 'function' => 'ManageScheduledTasks', + 'subsections' => array( + 'tasks' => array($txt['maintain_tasks'], 'admin_forum'), + 'tasklog' => array($txt['scheduled_log'], 'admin_forum'), + ), + ), + 'mailqueue' => array( + 'label' => $txt['mailqueue_title'], + 'file' => 'ManageMail.php', + 'function' => 'ManageMail', + 'icon' => 'mail.gif', + 'subsections' => array( + 'browse' => array($txt['mailqueue_browse'], 'admin_forum'), + 'settings' => array($txt['mailqueue_settings'], 'admin_forum'), + ), + ), + 'reports' => array( + 'enabled' => in_array('rg', $context['admin_features']), + 'label' => $txt['generate_reports'], + 'file' => 'Reports.php', + 'function' => 'ReportsMain', + 'icon' => 'reports.gif', + ), + 'logs' => array( + 'label' => $txt['logs'], + 'function' => 'AdminLogs', + 'icon' => 'logs.gif', + 'subsections' => array( + 'errorlog' => array($txt['errlog'], 'admin_forum', 'enabled' => !empty($modSettings['enableErrorLogging']), 'url' => $scripturl . '?action=admin;area=logs;sa=errorlog;desc'), + 'adminlog' => array($txt['admin_log'], 'admin_forum', 'enabled' => in_array('ml', $context['admin_features'])), + 'modlog' => array($txt['moderation_log'], 'admin_forum', 'enabled' => in_array('ml', $context['admin_features'])), + 'banlog' => array($txt['ban_log'], 'manage_bans'), + 'spiderlog' => array($txt['spider_logs'], 'admin_forum', 'enabled' => in_array('sp', $context['admin_features'])), + 'tasklog' => array($txt['scheduled_log'], 'admin_forum'), + 'pruning' => array($txt['pruning_title'], 'admin_forum'), + ), + ), + 'repairboards' => array( + 'label' => $txt['admin_repair'], + 'file' => 'RepairBoards.php', + 'function' => 'RepairBoards', + 'select' => 'maintain', + 'hidden' => true, + ), + ), + ), + ); + + // Any files to include for administration? + if (!empty($modSettings['integrate_admin_include'])) + { + $admin_includes = explode(',', $modSettings['integrate_admin_include']); + foreach ($admin_includes as $include) + { + $include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir'])); + if (file_exists($include)) + require_once($include); + } + } + + // Let them modify admin areas easily. + call_integration_hook('integrate_admin_areas', array(&$admin_areas)); + + // Make sure the administrator has a valid session... + validateSession(); + + // Actually create the menu! + $admin_include_data = createMenu($admin_areas); + unset($admin_areas); + + // Nothing valid? + if ($admin_include_data == false) + fatal_lang_error('no_access', false); + + // Build the link tree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=admin', + 'name' => $txt['admin_center'], + ); + if (isset($admin_include_data['current_area']) && $admin_include_data['current_area'] != 'index') + $context['linktree'][] = array( + 'url' => $scripturl . '?action=admin;area=' . $admin_include_data['current_area'] . ';' . $context['session_var'] . '=' . $context['session_id'], + 'name' => $admin_include_data['label'], + ); + if (!empty($admin_include_data['current_subsection']) && $admin_include_data['subsections'][$admin_include_data['current_subsection']][0] != $admin_include_data['label']) + $context['linktree'][] = array( + 'url' => $scripturl . '?action=admin;area=' . $admin_include_data['current_area'] . ';sa=' . $admin_include_data['current_subsection'] . ';' . $context['session_var'] . '=' . $context['session_id'], + 'name' => $admin_include_data['subsections'][$admin_include_data['current_subsection']][0], + ); + + // Make a note of the Unique ID for this menu. + $context['admin_menu_id'] = $context['max_menu_id']; + $context['admin_menu_name'] = 'menu_data_' . $context['admin_menu_id']; + + // Why on the admin are we? + $context['admin_area'] = $admin_include_data['current_area']; + + // Now - finally - call the right place! + if (isset($admin_include_data['file'])) + require_once($sourcedir . '/' . $admin_include_data['file']); + + $admin_include_data['function'](); +} + +// The main administration section. +function AdminHome() +{ + global $sourcedir, $forum_version, $txt, $scripturl, $context, $user_info, $boardurl, $modSettings, $smcFunc; + + // You have to be able to do at least one of the below to see this page. + isAllowedTo(array('admin_forum', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'manage_boards', 'manage_smileys', 'manage_attachments')); + + // Find all of this forum's administrators... + require_once($sourcedir . '/Subs-Membergroups.php'); + if (listMembergroupMembers_Href($context['administrators'], 1, 32) && allowedTo('manage_membergroups')) + { + // Add a 'more'-link if there are more than 32. + $context['more_admins_link'] = '' . $txt['more'] . ''; + } + + // Load the credits stuff. + require_once($sourcedir . '/Who.php'); + Credits(true); + + // This makes it easier to get the latest news with your time format. + $context['time_format'] = urlencode($user_info['time_format']); + + $context['current_versions'] = array( + 'php' => array('title' => $txt['support_versions_php'], 'version' => PHP_VERSION), + 'db' => array('title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), 'version' => ''), + 'server' => array('title' => $txt['support_versions_server'], 'version' => $_SERVER['SERVER_SOFTWARE']), + ); + $context['forum_version'] = $forum_version; + + // Get a list of current server versions. + require_once($sourcedir . '/Subs-Admin.php'); + $checkFor = array( + 'gd', + 'db_server', + 'mmcache', + 'eaccelerator', + 'phpa', + 'apc', + 'memcache', + 'xcache', + 'php', + 'server', + ); + $context['current_versions'] = getServerVersions($checkFor); + + $context['can_admin'] = allowedTo('admin_forum'); + + $context['sub_template'] = $context['admin_area'] == 'credits' ? 'credits' : 'admin'; + $context['page_title'] = $context['admin_area'] == 'credits' ? $txt['support_credits_title'] : $txt['admin_center']; + + // The format of this array is: permission, action, title, description, icon. + $quick_admin_tasks = array( + array('', 'credits', 'support_credits_title', 'support_credits_info', 'support_and_credits.png'), + array('admin_forum', 'featuresettings', 'modSettings_title', 'modSettings_info', 'features_and_options.png'), + array('admin_forum', 'maintain', 'maintain_title', 'maintain_info', 'forum_maintenance.png'), + array('manage_permissions', 'permissions', 'edit_permissions', 'edit_permissions_info', 'permissions.png'), + array('admin_forum', 'theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id'], 'theme_admin', 'theme_admin_info', 'themes_and_layout.png'), + array('admin_forum', 'packages', 'package', 'package_info', 'packages.png'), + array('manage_smileys', 'smileys', 'smileys_manage', 'smileys_manage_info', 'smilies_and_messageicons.png'), + array('moderate_forum', 'viewmembers', 'admin_users', 'member_center_info', 'members.png'), + ); + + $context['quick_admin_tasks'] = array(); + foreach ($quick_admin_tasks as $task) + { + if (!empty($task[0]) && !allowedTo($task[0])) + continue; + + $context['quick_admin_tasks'][] = array( + 'href' => $scripturl . '?action=admin;area=' . $task[1], + 'link' => '' . $txt[$task[2]] . '', + 'title' => $txt[$task[2]], + 'description' => $txt[$task[3]], + 'icon' => $task[4], + 'is_last' => false + ); + } + + if (count($context['quick_admin_tasks']) % 2 == 1) + { + $context['quick_admin_tasks'][] = array( + 'href' => '', + 'link' => '', + 'title' => '', + 'description' => '', + 'is_last' => true + ); + $context['quick_admin_tasks'][count($context['quick_admin_tasks']) - 2]['is_last'] = true; + } + elseif (count($context['quick_admin_tasks']) != 0) + { + $context['quick_admin_tasks'][count($context['quick_admin_tasks']) - 1]['is_last'] = true; + $context['quick_admin_tasks'][count($context['quick_admin_tasks']) - 2]['is_last'] = true; + } + + // Lastly, fill in the blanks in the support resources paragraphs. + $txt['support_resources_p1'] = sprintf($txt['support_resources_p1'], + 'http://wiki.simplemachines.org/', + 'http://wiki.simplemachines.org/smf/features2', + 'http://wiki.simplemachines.org/smf/options2', + 'http://wiki.simplemachines.org/smf/themes2', + 'http://wiki.simplemachines.org/smf/packages2' + ); + $txt['support_resources_p2'] = sprintf($txt['support_resources_p2'], + 'http://www.simplemachines.org/community/', + 'http://www.simplemachines.org/redirect/english_support', + 'http://www.simplemachines.org/redirect/international_support_boards', + 'http://www.simplemachines.org/redirect/smf_support', + 'http://www.simplemachines.org/redirect/customize_support' + ); +} + +// Get one of the admin information files from Simple Machines. +function DisplayAdminFile() +{ + global $context, $modSettings, $smcFunc; + + @ini_set('memory_limit', '32M'); + + if (empty($_REQUEST['filename']) || !is_string($_REQUEST['filename'])) + fatal_lang_error('no_access', false); + + $request = $smcFunc['db_query']('', ' + SELECT data, filetype + FROM {db_prefix}admin_info_files + WHERE filename = {string:current_filename} + LIMIT 1', + array( + 'current_filename' => $_REQUEST['filename'], + ) + ); + + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('admin_file_not_found', true, array($_REQUEST['filename'])); + + list ($file_data, $filetype) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // !!! Temp. + // Figure out if sesc is still being used. + if (strpos($file_data, ';sesc=') !== false) + $file_data = ' +if (!(\'smfForum_sessionvar\' in window)) + window.smfForum_sessionvar = \'sesc\'; +' . strtr($file_data, array(';sesc=' => ';\' + window.smfForum_sessionvar + \'=')); + + $context['template_layers'] = array(); + // 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: ' . $filetype); + echo $file_data; + obExit(false); +} + +// This allocates out all the search stuff. +function AdminSearch() +{ + global $txt, $context, $smcFunc, $sourcedir; + + isAllowedTo('admin_forum'); + + // What can we search for? + $subactions = array( + 'internal' => 'AdminSearchInternal', + 'online' => 'AdminSearchOM', + 'member' => 'AdminSearchMember', + ); + + $context['search_type'] = !isset($_REQUEST['search_type']) || !isset($subactions[$_REQUEST['search_type']]) ? 'internal' : $_REQUEST['search_type']; + $context['search_term'] = isset($_REQUEST['search_term']) ? $smcFunc['htmlspecialchars']($_REQUEST['search_term'], ENT_QUOTES) : ''; + + $context['sub_template'] = 'admin_search_results'; + $context['page_title'] = $txt['admin_search_results']; + + // Keep track of what the admin wants. + if (empty($context['admin_preferences']['sb']) || $context['admin_preferences']['sb'] != $context['search_type']) + { + $context['admin_preferences']['sb'] = $context['search_type']; + + // Update the preferences. + require_once($sourcedir . '/Subs-Admin.php'); + updateAdminPreferences(); + } + + if (trim($context['search_term']) == '') + $context['search_results'] = array(); + else + $subactions[$context['search_type']](); +} + +// A complicated but relatively quick internal search. +function AdminSearchInternal() +{ + global $context, $txt, $helptxt, $scripturl, $sourcedir; + + // Try to get some more memory. + @ini_set('memory_limit', '128M'); + + // Load a lot of language files. + $language_files = array( + 'Help', 'ManageMail', 'ManageSettings', 'ManageCalendar', 'ManageBoards', 'ManagePaid', 'ManagePermissions', 'Search', + 'Login', 'ManageSmileys', + ); + loadLanguage(implode('+', $language_files)); + + // All the files we need to include. + $include_files = array( + 'ManageSettings', 'ManageBoards', 'ManageNews', 'ManageAttachments', 'ManageCalendar', 'ManageMail', 'ManagePaid', 'ManagePermissions', + 'ManagePosts', 'ManageRegistration', 'ManageSearch', 'ManageSearchEngines', 'ManageServer', 'ManageSmileys', + ); + foreach ($include_files as $file) + require_once($sourcedir . '/' . $file . '.php'); + + /* This is the huge array that defines everything... it's a huge array of items formatted as follows: + 0 = Language index (Can be array of indexes) to search through for this setting. + 1 = URL for this indexes page. + 2 = Help index for help associated with this item (If different from 0) + */ + + $search_data = array( + // All the major sections of the forum. + 'sections' => array( + ), + 'settings' => array( + array('COPPA', 'area=regcenter;sa=settings'), + array('CAPTCHA', 'area=securitysettings;sa=spam'), + ), + ); + + // Go through the admin menu structure trying to find suitably named areas! + foreach ($context[$context['admin_menu_name']]['sections'] as $section) + { + foreach ($section['areas'] as $menu_key => $menu_item) + { + $search_data['sections'][] = array($menu_item['label'], 'area=' . $menu_key); + if (!empty($menu_item['subsections'])) + foreach ($menu_item['subsections'] as $key => $sublabel) + { + if (isset($sublabel['label'])) + $search_data['sections'][] = array($sublabel['label'], 'area=' . $menu_key . ';sa=' . $key); + } + } + } + + // This is a special array of functions that contain setting data - we query all these to simply pull all setting bits! + $settings_search = array( + array('ModifyCoreFeatures', 'area=corefeatures'), + array('ModifyBasicSettings', 'area=featuresettings;sa=basic'), + array('ModifyLayoutSettings', 'area=featuresettings;sa=layout'), + array('ModifyKarmaSettings', 'area=featuresettings;sa=karma'), + array('ModifySignatureSettings', 'area=featuresettings;sa=sig'), + array('ModifyGeneralSecuritySettings', 'area=securitysettings;sa=general'), + array('ModifySpamSettings', 'area=securitysettings;sa=spam'), + array('ModifyModerationSettings', 'area=securitysettings;sa=moderation'), + array('ModifyGeneralModSettings', 'area=modsettings;sa=general'), + // Mod authors if you want to be "real freaking good" then add any setting pages for your mod BELOW this line! + array('ManageAttachmentSettings', 'area=manageattachments;sa=attachments'), + array('ManageAvatarSettings', 'area=manageattachments;sa=avatars'), + array('ModifyCalendarSettings', 'area=managecalendar;sa=settings'), + array('EditBoardSettings', 'area=manageboards;sa=settings'), + array('ModifyMailSettings', 'area=mailqueue;sa=settings'), + array('ModifyNewsSettings', 'area=news;sa=settings'), + array('GeneralPermissionSettings', 'area=permissions;sa=settings'), + array('ModifyPostSettings', 'area=postsettings;sa=posts'), + array('ModifyBBCSettings', 'area=postsettings;sa=bbc'), + array('ModifyTopicSettings', 'area=postsettings;sa=topics'), + array('EditSearchSettings', 'area=managesearch;sa=settings'), + array('EditSmileySettings', 'area=smileys;sa=settings'), + array('ModifyGeneralSettings', 'area=serversettings;sa=general'), + array('ModifyDatabaseSettings', 'area=serversettings;sa=database'), + array('ModifyCookieSettings', 'area=serversettings;sa=cookie'), + array('ModifyCacheSettings', 'area=serversettings;sa=cache'), + array('ModifyLanguageSettings', 'area=languages;sa=settings'), + array('ModifyRegistrationSettings', 'area=regcenter;sa=settings'), + array('ManageSearchEngineSettings', 'area=sengines;sa=settings'), + array('ModifySubscriptionSettings', 'area=paidsubscribe;sa=settings'), + array('ModifyPruningSettings', 'area=logs;sa=pruning'), + ); + + foreach ($settings_search as $setting_area) + { + // Get a list of their variables. + $config_vars = $setting_area[0](true); + + foreach ($config_vars as $var) + if (!empty($var[1]) && !in_array($var[0], array('permissions', 'switch'))) + $search_data['settings'][] = array($var[(isset($var[2]) && in_array($var[2], array('file', 'db'))) ? 0 : 1], $setting_area[1]); + } + + $context['page_title'] = $txt['admin_search_results']; + $context['search_results'] = array(); + + $search_term = strtolower($context['search_term']); + // Go through all the search data trying to find this text! + foreach ($search_data as $section => $data) + { + foreach ($data as $item) + { + $found = false; + if (!is_array($item[0])) + $item[0] = array($item[0]); + foreach ($item[0] as $term) + { + $lc_term = strtolower($term); + if (strpos($lc_term, $search_term) !== false || (isset($txt[$term]) && strpos(strtolower($txt[$term]), $search_term) !== false) || (isset($txt['setting_' . $term]) && strpos(strtolower($txt['setting_' . $term]), $search_term) !== false)) + { + $found = $term; + break; + } + } + + if ($found) + { + // Format the name - and remove any descriptions the entry may have. + $name = isset($txt[$found]) ? $txt[$found] : (isset($txt['setting_' . $found]) ? $txt['setting_' . $found] : $found); + $name = preg_replace('~<(?:div|span)\sclass="smalltext">.+?~', '', $name); + + $context['search_results'][] = array( + 'url' => (substr($item[1], 0, 4) == 'area' ? $scripturl . '?action=admin;' . $item[1] : $item[1]) . ';' . $context['session_var'] . '=' . $context['session_id'] . ((substr($item[1], 0, 4) == 'area' && $section == 'settings' ? '#' . $item[0][0] : '')), + 'name' => $name, + 'type' => $section, + 'help' => shorten_subject(isset($item[2]) ? strip_tags($helptxt[$item2]) : (isset($helptxt[$found]) ? strip_tags($helptxt[$found]) : ''), 255), + ); + } + } + } +} + +// All this does is pass through to manage members. +function AdminSearchMember() +{ + global $context, $sourcedir; + + require_once($sourcedir . '/ManageMembers.php'); + $_REQUEST['sa'] = 'query'; + + $_POST['membername'] = $context['search_term']; + + ViewMembers(); +} + +// This file allows the user to search the SM online manual for a little of help. +function AdminSearchOM() +{ + global $context, $sourcedir; + + $docsURL = 'docs.simplemachines.org'; + $context['doc_scripturl'] = 'http://docs.simplemachines.org/index.php'; + + // Set all the parameters search might expect. + $postVars = array( + 'search' => $context['search_term'], + ); + + // Encode the search data. + foreach ($postVars as $k => $v) + $postVars[$k] = urlencode($k) . '=' . urlencode($v); + + // This is what we will send. + $postVars = implode('&', $postVars); + + // Get the results from the doc site. + require_once($sourcedir . '/Subs-Package.php'); + $search_results = fetch_web_data($context['doc_scripturl'] . '?action=search2&xml', $postVars); + + // If we didn't get any xml back we are in trouble - perhaps the doc site is overloaded? + if (!$search_results || preg_match('~<' . '\?xml\sversion="\d+\.\d+"\sencoding=".+?"\?' . '>\s*(.+?)~is', $search_results, $matches) != true) + fatal_lang_error('cannot_connect_doc_site'); + + $search_results = $matches[1]; + + // Otherwise we simply walk through the XML and stick it in context for display. + $context['search_results'] = array(); + loadClassFile('Class-Package.php'); + + // Get the results loaded into an array for processing! + $results = new xmlArray($search_results, false); + + // Move through the smf layer. + if (!$results->exists('smf')) + fatal_lang_error('cannot_connect_doc_site'); + $results = $results->path('smf[0]'); + + // Are there actually some results? + if (!$results->exists('noresults') && !$results->exists('results')) + fatal_lang_error('cannot_connect_doc_site'); + elseif ($results->exists('results')) + { + foreach ($results->set('results/result') as $result) + { + if (!$result->exists('messages')) + continue; + + $context['search_results'][$result->fetch('id')] = array( + 'topic_id' => $result->fetch('id'), + 'relevance' => $result->fetch('relevance'), + 'board' => array( + 'id' => $result->fetch('board/id'), + 'name' => $result->fetch('board/name'), + 'href' => $result->fetch('board/href'), + ), + 'category' => array( + 'id' => $result->fetch('category/id'), + 'name' => $result->fetch('category/name'), + 'href' => $result->fetch('category/href'), + ), + 'messages' => array(), + ); + + // Add the messages. + foreach ($result->set('messages/message') as $message) + $context['search_results'][$result->fetch('id')]['messages'][] = array( + 'id' => $message->fetch('id'), + 'subject' => $message->fetch('subject'), + 'body' => $message->fetch('body'), + 'time' => $message->fetch('time'), + 'timestamp' => $message->fetch('timestamp'), + 'start' => $message->fetch('start'), + 'author' => array( + 'id' => $message->fetch('author/id'), + 'name' => $message->fetch('author/name'), + 'href' => $message->fetch('author/href'), + ), + ); + } + } +} + +// This function decides which log to load. +function AdminLogs() +{ + global $sourcedir, $context, $txt, $scripturl; + + // These are the logs they can load. + $log_functions = array( + 'errorlog' => array('ManageErrors.php', 'ViewErrorLog'), + 'adminlog' => array('Modlog.php', 'ViewModlog'), + 'modlog' => array('Modlog.php', 'ViewModlog'), + 'banlog' => array('ManageBans.php', 'BanLog'), + 'spiderlog' => array('ManageSearchEngines.php', 'SpiderLogs'), + 'tasklog' => array('ManageScheduledTasks.php', 'TaskLog'), + 'pruning' => array('ManageSettings.php', 'ModifyPruningSettings'), + ); + + $sub_action = isset($_REQUEST['sa']) && isset($log_functions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'errorlog'; + // If it's not got a sa set it must have come here for first time, pretend error log should be reversed. + if (!isset($_REQUEST['sa'])) + $_REQUEST['desc'] = true; + + // Setup some tab stuff. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['logs'], + 'help' => '', + 'description' => $txt['maintain_info'], + 'tabs' => array( + 'errorlog' => array( + 'url' => $scripturl . '?action=admin;area=logs;sa=errorlog;desc', + 'description' => sprintf($txt['errlog_desc'], $txt['remove']), + ), + 'adminlog' => array( + 'description' => $txt['admin_log_desc'], + ), + 'modlog' => array( + 'description' => $txt['moderation_log_desc'], + ), + 'banlog' => array( + 'description' => $txt['ban_log_description'], + ), + 'spiderlog' => array( + 'description' => $txt['spider_log_desc'], + ), + 'tasklog' => array( + 'description' => $txt['scheduled_log_desc'], + ), + 'pruning' => array( + 'description' => $txt['pruning_log_desc'], + ), + ), + ); + + require_once($sourcedir . '/' . $log_functions[$sub_action][0]); + $log_functions[$sub_action][1](); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/BoardIndex.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/BoardIndex.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,143 @@ + true, + 'base_level' => 0, + 'parent_id' => 0, + 'set_latest_post' => true, + 'countChildPosts' => !empty($modSettings['countChildPosts']), + ); + $context['categories'] = getBoardIndex($boardIndexOptions); + + // Get the user online list. + require_once($sourcedir . '/Subs-MembersOnline.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.php', 'cache_getMembergroupList', array()); + + // Track most online statistics? (Subs-MembersOnline.php) + if (!empty($modSettings['trackStats'])) + trackStatsUsersOnline($context['num_guests'] + $context['num_spiders'] + $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.php', 'cache_getLastPosts', array($latestPostOptions)); + } + + $settings['display_recent_bar'] = !empty($settings['number_recent_posts']) ? $settings['number_recent_posts'] : 0; + $settings['show_member_bar'] &= allowedTo('view_mlist'); + $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.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'); + } + else + $context['show_calendar'] = false; + + $context['page_title'] = sprintf($txt['forum_index'], $context['forum_name']); +} + +// Collapse or expand a category +function CollapseCategory() +{ + global $user_info, $sourcedir, $context; + + // Just in case, no need, no need. + $context['robot_no_index'] = true; + + checkSession('request'); + + if (!isset($_GET['sa'])) + fatal_lang_error('no_access', false); + + // Check if the input values are correct. + if (in_array($_REQUEST['sa'], array('expand', 'collapse', 'toggle')) && isset($_REQUEST['c'])) + { + // And collapse/expand/toggle the category. + require_once($sourcedir . '/Subs-Categories.php'); + collapseCategories(array((int) $_REQUEST['c']), $_REQUEST['sa'], array($user_info['id'])); + } + + // And go back to the board index. + BoardIndex(); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Calendar.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Calendar.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,487 @@ + 'iCalDownload', + 'post' => 'CalendarPost', + ); + + if (isset($_GET['sa']) && isset($subActions[$_GET['sa']]) && !WIRELESS) + return $subActions[$_GET['sa']](); + + // This is gonna be needed... + loadTemplate('Calendar'); + + // You can't do anything if the calendar is off. + if (empty($modSettings['cal_enabled'])) + fatal_lang_error('calendar_off', false); + + // Set the page title to mention the calendar ;). + $context['page_title'] = $txt['calendar']; + + // Is this a week view? + $context['view_week'] = isset($_GET['viewweek']); + + // Don't let search engines index weekly calendar pages. + if ($context['view_week']) + $context['robot_no_index'] = true; + + // Get the current day of month... + require_once($sourcedir . '/Subs-Calendar.php'); + $today = getTodayInfo(); + + // If the month and year are not passed in, use today's date as a starting point. + $curPage = array( + 'day' => isset($_REQUEST['day']) ? (int) $_REQUEST['day'] : $today['day'], + 'month' => isset($_REQUEST['month']) ? (int) $_REQUEST['month'] : $today['month'], + 'year' => isset($_REQUEST['year']) ? (int) $_REQUEST['year'] : $today['year'] + ); + + // Make sure the year and month are in valid ranges. + if ($curPage['month'] < 1 || $curPage['month'] > 12) + fatal_lang_error('invalid_month', false); + if ($curPage['year'] < $modSettings['cal_minyear'] || $curPage['year'] > $modSettings['cal_maxyear']) + fatal_lang_error('invalid_year', false); + // If we have a day clean that too. + if ($context['view_week']) + { + // Note $isValid is -1 < PHP 5.1 + $isValid = mktime(0, 0, 0, $curPage['month'], $curPage['day'], $curPage['year']); + if ($curPage['day'] > 31 || !$isValid || $isValid == -1) + fatal_lang_error('invalid_day', false); + } + + // Load all the context information needed to show the calendar grid. + $calendarOptions = array( + 'start_day' => !empty($options['calendar_start_day']) ? $options['calendar_start_day'] : 0, + 'show_birthdays' => in_array($modSettings['cal_showbdays'], array(1, 2)), + 'show_events' => in_array($modSettings['cal_showevents'], array(1, 2)), + 'show_holidays' => in_array($modSettings['cal_showholidays'], array(1, 2)), + 'show_week_num' => true, + 'short_day_titles' => false, + 'show_next_prev' => true, + 'show_week_links' => true, + 'size' => 'large', + ); + + // Load up the main view. + if ($context['view_week']) + $context['calendar_grid_main'] = getCalendarWeek($curPage['month'], $curPage['year'], $curPage['day'], $calendarOptions); + else + $context['calendar_grid_main'] = getCalendarGrid($curPage['month'], $curPage['year'], $calendarOptions); + + // Load up the previous and next months. + $calendarOptions['show_birthdays'] = $calendarOptions['show_events'] = $calendarOptions['show_holidays'] = false; + $calendarOptions['short_day_titles'] = true; + $calendarOptions['show_next_prev'] = false; + $calendarOptions['show_week_links'] = false; + $calendarOptions['size'] = 'small'; + $context['calendar_grid_current'] = getCalendarGrid($curPage['month'], $curPage['year'], $calendarOptions); + // Only show previous month if it isn't pre-January of the min-year + if ($context['calendar_grid_current']['previous_calendar']['year'] > $modSettings['cal_minyear'] || $curPage['month'] != 1) + $context['calendar_grid_prev'] = getCalendarGrid($context['calendar_grid_current']['previous_calendar']['month'], $context['calendar_grid_current']['previous_calendar']['year'], $calendarOptions); + // Only show next month if it isn't post-December of the max-year + if ($context['calendar_grid_current']['next_calendar']['year'] < $modSettings['cal_maxyear'] || $curPage['month'] != 12) + $context['calendar_grid_next'] = getCalendarGrid($context['calendar_grid_current']['next_calendar']['month'], $context['calendar_grid_current']['next_calendar']['year'], $calendarOptions); + + // Basic template stuff. + $context['can_post'] = allowedTo('calendar_post'); + $context['current_day'] = $curPage['day']; + $context['current_month'] = $curPage['month']; + $context['current_year'] = $curPage['year']; + $context['show_all_birthdays'] = isset($_GET['showbd']); + + // Set the page title to mention the month or week, too + $context['page_title'] .= ' - ' . ($context['view_week'] ? sprintf($txt['calendar_week_title'], $context['calendar_grid_main']['week_number'], ($context['calendar_grid_main']['week_number'] == 53 ? $context['current_year'] - 1 : $context['current_year'])) : $txt['months'][$context['current_month']] . ' ' . $context['current_year']); + + // Load up the linktree! + $context['linktree'][] = array( + 'url' => $scripturl . '?action=calendar', + 'name' => $txt['calendar'] + ); + // Add the current month to the linktree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=calendar;year=' . $context['current_year'] . ';month=' . $context['current_month'], + 'name' => $txt['months'][$context['current_month']] . ' ' . $context['current_year'] + ); + // If applicable, add the current week to the linktree. + if ($context['view_week']) + $context['linktree'][] = array( + 'url' => $scripturl . '?action=calendar;viewweek;year=' . $context['current_year'] . ';month=' . $context['current_month'] . ';day=' . $context['current_day'], + 'name' => $txt['calendar_week'] . ' ' . $context['calendar_grid_main']['week_number'] + ); +} + +function CalendarPost() +{ + global $context, $txt, $user_info, $sourcedir, $scripturl; + global $modSettings, $topic, $smcFunc; + + // Well - can they? + isAllowedTo('calendar_post'); + + // We need this for all kinds of useful functions. + require_once($sourcedir . '/Subs-Calendar.php'); + + // Cast this for safety... + if (isset($_REQUEST['eventid'])) + $_REQUEST['eventid'] = (int) $_REQUEST['eventid']; + + // Submitting? + if (isset($_POST[$context['session_var']], $_REQUEST['eventid'])) + { + checkSession(); + + // Validate the post... + if (!isset($_POST['link_to_board'])) + validateEventPost(); + + // If you're not allowed to edit any events, you have to be the poster. + if ($_REQUEST['eventid'] > 0 && !allowedTo('calendar_edit_any')) + isAllowedTo('calendar_edit_' . (!empty($user_info['id']) && getEventPoster($_REQUEST['eventid']) == $user_info['id'] ? 'own' : 'any')); + + // New - and directing? + if ($_REQUEST['eventid'] == -1 && isset($_POST['link_to_board'])) + { + $_REQUEST['calendar'] = 1; + require_once($sourcedir . '/Post.php'); + return Post(); + } + // New... + elseif ($_REQUEST['eventid'] == -1) + { + $eventOptions = array( + 'board' => 0, + 'topic' => 0, + 'title' => substr($_REQUEST['evtitle'], 0, 60), + 'member' => $user_info['id'], + 'start_date' => sprintf('%04d-%02d-%02d', $_POST['year'], $_POST['month'], $_POST['day']), + 'span' => isset($_POST['span']) && $_POST['span'] > 0 ? min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1) : 0, + ); + insertEvent($eventOptions); + } + + // Deleting... + elseif (isset($_REQUEST['deleteevent'])) + removeEvent($_REQUEST['eventid']); + + // ... or just update it? + else + { + $eventOptions = array( + 'title' => substr($_REQUEST['evtitle'], 0, 60), + 'span' => empty($modSettings['cal_allowspan']) || empty($_POST['span']) || $_POST['span'] == 1 || empty($modSettings['cal_maxspan']) || $_POST['span'] > $modSettings['cal_maxspan'] ? 0 : min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1), + 'start_date' => strftime('%Y-%m-%d', mktime(0, 0, 0, (int) $_REQUEST['month'], (int) $_REQUEST['day'], (int) $_REQUEST['year'])), + ); + + modifyEvent($_REQUEST['eventid'], $eventOptions); + } + + updateSettings(array( + 'calendar_updated' => time(), + )); + + // No point hanging around here now... + redirectexit($scripturl . '?action=calendar;month=' . $_POST['month'] . ';year=' . $_POST['year']); + } + + // If we are not enabled... we are not enabled. + if (empty($modSettings['cal_allow_unlinked']) && empty($_REQUEST['eventid'])) + { + $_REQUEST['calendar'] = 1; + require_once($sourcedir . '/Post.php'); + return Post(); + } + + // New? + if (!isset($_REQUEST['eventid'])) + { + $today = getdate(); + + $context['event'] = array( + 'boards' => array(), + 'board' => 0, + 'new' => 1, + 'eventid' => -1, + 'year' => isset($_REQUEST['year']) ? $_REQUEST['year'] : $today['year'], + 'month' => isset($_REQUEST['month']) ? $_REQUEST['month'] : $today['mon'], + 'day' => isset($_REQUEST['day']) ? $_REQUEST['day'] : $today['mday'], + 'title' => '', + 'span' => 1, + ); + $context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year'])); + + // Get list of boards that can be posted in. + $boards = boardsAllowedTo('post_new'); + if (empty($boards)) + fatal_lang_error('cannot_post_new', 'permission'); + + // Load the list of boards and categories in the context. + require_once($sourcedir . '/Subs-MessageIndex.php'); + $boardListOptions = array( + 'included_boards' => in_array(0, $boards) ? null : $boards, + 'not_redirection' => true, + 'use_permissions' => true, + 'selected_board' => $modSettings['cal_defaultboard'], + ); + $context['event']['categories'] = getBoardList($boardListOptions); + } + else + { + $context['event'] = getEventProperties($_REQUEST['eventid']); + + if ($context['event'] === false) + fatal_lang_error('no_access', false); + + // If it has a board, then they should be editing it within the topic. + if (!empty($context['event']['topic']['id']) && !empty($context['event']['topic']['first_msg'])) + { + // We load the board up, for a check on the board access rights... + $topic = $context['event']['topic']['id']; + loadBoard(); + } + + // Make sure the user is allowed to edit this event. + if ($context['event']['member'] != $user_info['id']) + isAllowedTo('calendar_edit_any'); + elseif (!allowedTo('calendar_edit_any')) + isAllowedTo('calendar_edit_own'); + } + + // Template, sub template, etc. + loadTemplate('Calendar'); + $context['sub_template'] = 'event_post'; + + $context['page_title'] = isset($_REQUEST['eventid']) ? $txt['calendar_edit'] : $txt['calendar_post_event']; + $context['linktree'][] = array( + 'name' => $context['page_title'], + ); +} + +function iCalDownload() +{ + global $smcFunc, $sourcedir, $forum_version, $context, $modSettings; + + // Goes without saying that this is required. + if (!isset($_REQUEST['eventid'])) + fatal_lang_error('no_access', false); + + // This is kinda wanted. + require_once($sourcedir . '/Subs-Calendar.php'); + + // Load up the event in question and check it exists. + $event = getEventProperties($_REQUEST['eventid']); + + if ($event === false) + fatal_lang_error('no_access', false); + + // Check the title isn't too long - iCal requires some formatting if so. + $title = str_split($event['title'], 30); + foreach ($title as $id => $line) + { + if ($id != 0) + $title[$id] = ' ' . $title[$id]; + $title[$id] .= "\n"; + } + + // Format the date. + $date = $event['year'] . '-' . ($event['month'] < 10 ? '0' . $event['month'] : $event['month']) . '-' . ($event['day'] < 10 ? '0' . $event['day'] : $event['day']) . 'T'; + $date .= '1200:00:00Z'; + + // This is what we will be sending later. + $filecontents = ''; + $filecontents .= 'BEGIN:VCALENDAR' . "\n"; + $filecontents .= 'VERSION:2.0' . "\n"; + $filecontents .= 'PRODID:-//SimpleMachines//SMF ' . (empty($forum_version) ? 1.0 : strtr($forum_version, array('SMF ' => ''))) . '//EN' . "\n"; + $filecontents .= 'BEGIN:VEVENT' . "\n"; + $filecontents .= 'DTSTART:' . $date . "\n"; + $filecontents .= 'DTEND:' . $date . "\n"; + $filecontents .= 'SUMMARY:' . implode('', $title); + $filecontents .= 'END:VEVENT' . "\n"; + $filecontents .= 'END:VCALENDAR'; + + // Send some standard headers. + ob_end_clean(); + if (!empty($modSettings['enableCompressedOutput'])) + @ob_start('ob_gzhandler'); + else + ob_start(); + + // Send the file headers + header('Pragma: '); + header('Cache-Control: no-cache'); + if (!$context['browser']['is_gecko']) + header('Content-Transfer-Encoding: binary'); + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . 'GMT'); + header('Accept-Ranges: bytes'); + header('Connection: close'); + header('Content-Disposition: attachment; filename=' . $event['title'] . '.ics'); + + // How big is it? + if (empty($modSettings['enableCompressedOutput'])) + header('Content-Length: ' . $smcFunc['strlen']($filecontents)); + + // This is a calendar item! + header('Content-Type: text/calendar'); + + // Chuck out the card. + echo $filecontents; + + // Off we pop - lovely! + obExit(false); +} + +// This is not the code you are looking for. +function clock() +{ + global $settings, $context; + $context['onimg'] = $settings['images_url'] . '/bbc/bbc_bg.gif'; + $context['offimg'] = $settings['images_url'] . '/bbc/bbc_hoverbg.gif'; + + $context['page_title'] = 'Anyone know what time it is?'; + $context['robot_no_index'] = true; + + $omfg = isset($_REQUEST['omfg']); + $bcd = !isset($_REQUEST['rb']) && !isset($_REQUEST['omfg']) && !isset($_REQUEST['time']); + + loadTemplate('Calendar'); + + if ($bcd && !$omfg) + { + $context['sub_template'] = 'bcd'; + $context['clockicons'] = unserialize(base64_decode('YTo2OntzOjI6ImgxIjthOjI6e2k6MDtpOjI7aToxO2k6MTt9czoyOiJoMiI7YTo0OntpOjA7aTo4O2k6MTtpOjQ7aToyO2k6MjtpOjM7aToxO31zOjI6Im0xIjthOjM6e2k6MDtpOjQ7aToxO2k6MjtpOjI7aToxO31zOjI6Im0yIjthOjQ6e2k6MDtpOjg7aToxO2k6NDtpOjI7aToyO2k6MztpOjE7fXM6MjoiczEiO2E6Mzp7aTowO2k6NDtpOjE7aToyO2k6MjtpOjE7fXM6MjoiczIiO2E6NDp7aTowO2k6ODtpOjE7aTo0O2k6MjtpOjI7aTozO2k6MTt9fQ==')); + } + elseif (!$omfg && !isset($_REQUEST['time'])) + { + $context['sub_template'] = 'hms'; + $context['clockicons'] = unserialize(base64_decode('YTozOntzOjE6ImgiO2E6NTp7aTowO2k6MTY7aToxO2k6ODtpOjI7aTo0O2k6MztpOjI7aTo0O2k6MTt9czoxOiJtIjthOjY6e2k6MDtpOjMyO2k6MTtpOjE2O2k6MjtpOjg7aTozO2k6NDtpOjQ7aToyO2k6NTtpOjE7fXM6MToicyI7YTo2OntpOjA7aTozMjtpOjE7aToxNjtpOjI7aTo4O2k6MztpOjQ7aTo0O2k6MjtpOjU7aToxO319')); + } + elseif ($omfg) + { + $context['sub_template'] = 'omfg'; + $context['clockicons'] = unserialize(base64_decode('YTo2OntzOjQ6InllYXIiO2E6Nzp7aTowO2k6NjQ7aToxO2k6MzI7aToyO2k6MTY7aTozO2k6ODtpOjQ7aTo0O2k6NTtpOjI7aTo2O2k6MTt9czo1OiJtb250aCI7YTo0OntpOjA7aTo4O2k6MTtpOjQ7aToyO2k6MjtpOjM7aToxO31zOjM6ImRheSI7YTo1OntpOjA7aToxNjtpOjE7aTo4O2k6MjtpOjQ7aTozO2k6MjtpOjQ7aToxO31zOjQ6ImhvdXIiO2E6NTp7aTowO2k6MTY7aToxO2k6ODtpOjI7aTo0O2k6MztpOjI7aTo0O2k6MTt9czozOiJtaW4iO2E6Njp7aTowO2k6MzI7aToxO2k6MTY7aToyO2k6ODtpOjM7aTo0O2k6NDtpOjI7aTo1O2k6MTt9czozOiJzZWMiO2E6Njp7aTowO2k6MzI7aToxO2k6MTY7aToyO2k6ODtpOjM7aTo0O2k6NDtpOjI7aTo1O2k6MTt9fQ==')); + } + elseif (isset($_REQUEST['time'])) + { + $context['sub_template'] = 'thetime'; + $time = getdate($_REQUEST['time'] == 'now' ? time() : (int) $_REQUEST['time']); + + $context['clockicons'] = array( + 'year' => array( + 64 => false, + 32 => false, + 16 => false, + 8 => false, + 4 => false, + 2 => false, + 1 => false + ), + 'month' => array( + 8 => false, + 4 => false, + 2 => false, + 1 => false + ), + 'day' => array( + 16 => false, + 4 => false, + 8 => false, + 2 => false, + 1 => false + ), + 'hour' => array( + 32 => false, + 16 => false, + 8 => false, + 4 => false, + 2 => false, + 1 => false + ), + 'min' => array( + 32 => false, + 16 => false, + 8 => false, + 4 => false, + 2 => false, + 1 => false + ), + 'sec' => array( + 32 => false, + 16 => false, + 8 => false, + 4 => false, + 2 => false, + 1 => false + ), + ); + + $year = $time['year'] % 100; + $month = $time['mon']; + $day = $time['mday']; + $hour = $time['hours']; + $min = $time['minutes']; + $sec = $time['seconds']; + + foreach ($context['clockicons'] as $t => $vs) + foreach ($vs as $v => $dumb) + { + if ($$t >= $v) + { + $$t -= $v; + $context['clockicons'][$t][$v] = true; + } + } + } +} +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Class-Graphics.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Class-Graphics.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,715 @@ +MAX_LZW_BITS = 12; + unset($this->Next, $this->Vals, $this->Stack, $this->Buf); + + $this->Next = range(0, (1 << $this->MAX_LZW_BITS) - 1); + $this->Vals = range(0, (1 << $this->MAX_LZW_BITS) - 1); + $this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1); + $this->Buf = range(0, 279); + } + + public function decompress($data, &$datLen) + { + $stLen = strlen($data); + $datLen = 0; + $ret = ''; + + $this->LZWCommand($data, true); + + while (($iIndex = $this->LZWCommand($data, false)) >= 0) + $ret .= chr($iIndex); + + $datLen = $stLen - strlen($data); + + if ($iIndex != -2) + return false; + + return $ret; + } + + public function LZWCommand(&$data, $bInit) + { + if ($bInit) + { + $this->SetCodeSize = ord($data[0]); + $data = substr($data, 1); + + $this->CodeSize = $this->SetCodeSize + 1; + $this->ClearCode = 1 << $this->SetCodeSize; + $this->EndCode = $this->ClearCode + 1; + $this->MaxCode = $this->ClearCode + 2; + $this->MaxCodeSize = $this->ClearCode << 1; + + $this->GetCode($data, $bInit); + + $this->Fresh = 1; + for ($i = 0; $i < $this->ClearCode; $i++) + { + $this->Next[$i] = 0; + $this->Vals[$i] = $i; + } + + for (; $i < (1 << $this->MAX_LZW_BITS); $i++) + { + $this->Next[$i] = 0; + $this->Vals[$i] = 0; + } + + $this->sp = 0; + return 1; + } + + if ($this->Fresh) + { + $this->Fresh = 0; + do + { + $this->FirstCode = $this->GetCode($data, $bInit); + $this->OldCode = $this->FirstCode; + } + while ($this->FirstCode == $this->ClearCode); + + return $this->FirstCode; + } + + if ($this->sp > 0) + { + $this->sp--; + return $this->Stack[$this->sp]; + } + + while (($Code = $this->GetCode($data, $bInit)) >= 0) + { + if ($Code == $this->ClearCode) + { + for ($i = 0; $i < $this->ClearCode; $i++) + { + $this->Next[$i] = 0; + $this->Vals[$i] = $i; + } + + for (; $i < (1 << $this->MAX_LZW_BITS); $i++) + { + $this->Next[$i] = 0; + $this->Vals[$i] = 0; + } + + $this->CodeSize = $this->SetCodeSize + 1; + $this->MaxCodeSize = $this->ClearCode << 1; + $this->MaxCode = $this->ClearCode + 2; + $this->sp = 0; + $this->FirstCode = $this->GetCode($data, $bInit); + $this->OldCode = $this->FirstCode; + + return $this->FirstCode; + } + + if ($Code == $this->EndCode) + return -2; + + $InCode = $Code; + if ($Code >= $this->MaxCode) + { + $this->Stack[$this->sp] = $this->FirstCode; + $this->sp++; + $Code = $this->OldCode; + } + + while ($Code >= $this->ClearCode) + { + $this->Stack[$this->sp] = $this->Vals[$Code]; + $this->sp++; + + if ($Code == $this->Next[$Code]) // Circular table entry, big GIF Error! + return -1; + + $Code = $this->Next[$Code]; + } + + $this->FirstCode = $this->Vals[$Code]; + $this->Stack[$this->sp] = $this->FirstCode; + $this->sp++; + + if (($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS)) + { + $this->Next[$Code] = $this->OldCode; + $this->Vals[$Code] = $this->FirstCode; + $this->MaxCode++; + + if (($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS))) + { + $this->MaxCodeSize *= 2; + $this->CodeSize++; + } + } + + $this->OldCode = $InCode; + if ($this->sp > 0) + { + $this->sp--; + return $this->Stack[$this->sp]; + } + } + + return $Code; + } + + public function GetCode(&$data, $bInit) + { + if ($bInit) + { + $this->CurBit = 0; + $this->LastBit = 0; + $this->Done = 0; + $this->LastByte = 2; + + return 1; + } + + if (($this->CurBit + $this->CodeSize) >= $this->LastBit) + { + if ($this->Done) + { + // Ran off the end of my bits... + if ($this->CurBit >= $this->LastBit) + return 0; + + return -1; + } + + $this->Buf[0] = $this->Buf[$this->LastByte - 2]; + $this->Buf[1] = $this->Buf[$this->LastByte - 1]; + + $count = ord($data[0]); + $data = substr($data, 1); + + if ($count) + { + for ($i = 0; $i < $count; $i++) + $this->Buf[2 + $i] = ord($data{$i}); + + $data = substr($data, $count); + } + else + $this->Done = 1; + + $this->LastByte = 2 + $count; + $this->CurBit = ($this->CurBit - $this->LastBit) + 16; + $this->LastBit = (2 + $count) << 3; + } + + $iRet = 0; + for ($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++) + $iRet |= (($this->Buf[intval($i / 8)] & (1 << ($i % 8))) != 0) << $j; + + $this->CurBit += $this->CodeSize; + return $iRet; + } +} + +class gif_color_table +{ + public $m_nColors; + public $m_arColors; + + public function __construct() + { + unset($this->m_nColors, $this->m_arColors); + } + + public function load($lpData, $num) + { + $this->m_nColors = 0; + $this->m_arColors = array(); + + for ($i = 0; $i < $num; $i++) + { + $rgb = substr($lpData, $i * 3, 3); + if (strlen($rgb) < 3) + return false; + + $this->m_arColors[] = (ord($rgb[2]) << 16) + (ord($rgb[1]) << 8) + ord($rgb[0]); + $this->m_nColors++; + } + + return true; + } + + public function toString() + { + $ret = ''; + + for ($i = 0; $i < $this->m_nColors; $i++) + { + $ret .= + chr(($this->m_arColors[$i] & 0x000000FF)) . // R + chr(($this->m_arColors[$i] & 0x0000FF00) >> 8) . // G + chr(($this->m_arColors[$i] & 0x00FF0000) >> 16); // B + } + + return $ret; + } + + public function colorIndex($rgb) + { + $rgb = intval($rgb) & 0xFFFFFF; + $r1 = ($rgb & 0x0000FF); + $g1 = ($rgb & 0x00FF00) >> 8; + $b1 = ($rgb & 0xFF0000) >> 16; + $idx = -1; + + for ($i = 0; $i < $this->m_nColors; $i++) + { + $r2 = ($this->m_arColors[$i] & 0x000000FF); + $g2 = ($this->m_arColors[$i] & 0x0000FF00) >> 8; + $b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16; + $d = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1); + + if (($idx == -1) || ($d < $dif)) + { + $idx = $i; + $dif = $d; + } + } + + return $idx; + } +} + +class gif_file_header +{ + public $m_lpVer, $m_nWidth, $m_nHeight, $m_bGlobalClr, $m_nColorRes; + public $m_bSorted, $m_nTableSize, $m_nBgColor, $m_nPixelRatio; + public $m_colorTable; + + public function __construct() + { + unset($this->m_lpVer, $this->m_nWidth, $this->m_nHeight, $this->m_bGlobalClr, $this->m_nColorRes); + unset($this->m_bSorted, $this->m_nTableSize, $this->m_nBgColor, $this->m_nPixelRatio, $this->m_colorTable); + } + + public function load($lpData, &$hdrLen) + { + $hdrLen = 0; + + $this->m_lpVer = substr($lpData, 0, 6); + if (($this->m_lpVer != 'GIF87a') && ($this->m_lpVer != 'GIF89a')) + return false; + + list ($this->m_nWidth, $this->m_nHeight) = array_values(unpack('v2', substr($lpData, 6, 4))); + + if (!$this->m_nWidth || !$this->m_nHeight) + return false; + + $b = ord(substr($lpData, 10, 1)); + $this->m_bGlobalClr = ($b & 0x80) ? true : false; + $this->m_nColorRes = ($b & 0x70) >> 4; + $this->m_bSorted = ($b & 0x08) ? true : false; + $this->m_nTableSize = 2 << ($b & 0x07); + $this->m_nBgColor = ord(substr($lpData, 11, 1)); + $this->m_nPixelRatio = ord(substr($lpData, 12, 1)); + $hdrLen = 13; + + if ($this->m_bGlobalClr) + { + $this->m_colorTable = new gif_color_table(); + if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) + return false; + + $hdrLen += 3 * $this->m_nTableSize; + } + + return true; + } +} + +class gif_image_header +{ + public $m_nLeft, $m_nTop, $m_nWidth, $m_nHeight, $m_bLocalClr; + public $m_bInterlace, $m_bSorted, $m_nTableSize, $m_colorTable; + + public function __construct() + { + unset($this->m_nLeft, $this->m_nTop, $this->m_nWidth, $this->m_nHeight, $this->m_bLocalClr); + unset($this->m_bInterlace, $this->m_bSorted, $this->m_nTableSize, $this->m_colorTable); + } + + public function load($lpData, &$hdrLen) + { + $hdrLen = 0; + + // Get the width/height/etc. from the header. + list ($this->m_nLeft, $this->m_nTop, $this->m_nWidth, $this->m_nHeight) = array_values(unpack('v4', substr($lpData, 0, 8))); + + if (!$this->m_nWidth || !$this->m_nHeight) + return false; + + $b = ord($lpData[8]); + $this->m_bLocalClr = ($b & 0x80) ? true : false; + $this->m_bInterlace = ($b & 0x40) ? true : false; + $this->m_bSorted = ($b & 0x20) ? true : false; + $this->m_nTableSize = 2 << ($b & 0x07); + $hdrLen = 9; + + if ($this->m_bLocalClr) + { + $this->m_colorTable = new gif_color_table(); + if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) + return false; + + $hdrLen += 3 * $this->m_nTableSize; + } + + return true; + } +} + +class gif_image +{ + public $m_disp, $m_bUser, $m_bTrans, $m_nDelay, $m_nTrans, $m_lpComm; + public $m_gih, $m_data, $m_lzw; + + public function __construct() + { + unset($this->m_disp, $this->m_bUser, $this->m_nDelay, $this->m_nTrans, $this->m_lpComm, $this->m_data); + $this->m_gih = new gif_image_header(); + $this->m_lzw = new gif_lzw_compression(); + } + + public function load($data, &$datLen) + { + $datLen = 0; + + while (true) + { + $b = ord($data[0]); + $data = substr($data, 1); + $datLen++; + + switch ($b) + { + // Extension... + case 0x21: + $len = 0; + if (!$this->skipExt($data, $len)) + return false; + + $datLen += $len; + break; + + // Image... + case 0x2C: + // Load the header and color table. + $len = 0; + if (!$this->m_gih->load($data, $len)) + return false; + + $data = substr($data, $len); + $datLen += $len; + + // Decompress the data, and ride on home ;). + $len = 0; + if (!($this->m_data = $this->m_lzw->decompress($data, $len))) + return false; + + $data = substr($data, $len); + $datLen += $len; + + if ($this->m_gih->m_bInterlace) + $this->deInterlace(); + + return true; + + case 0x3B: // EOF + default: + return false; + } + } + return false; + } + + public function skipExt(&$data, &$extLen) + { + $extLen = 0; + + $b = ord($data[0]); + $data = substr($data, 1); + $extLen++; + + switch ($b) + { + // Graphic Control... + case 0xF9: + $b = ord($data[1]); + $this->m_disp = ($b & 0x1C) >> 2; + $this->m_bUser = ($b & 0x02) ? true : false; + $this->m_bTrans = ($b & 0x01) ? true : false; + list ($this->m_nDelay) = array_values(unpack('v', substr($data, 2, 2))); + $this->m_nTrans = ord($data[4]); + break; + + // Comment... + case 0xFE: + $this->m_lpComm = substr($data, 1, ord($data[0])); + break; + + // Plain text... + case 0x01: + break; + + // Application... + case 0xFF: + break; + } + + // Skip default as defs may change. + $b = ord($data[0]); + $data = substr($data, 1); + $extLen++; + while ($b > 0) + { + $data = substr($data, $b); + $extLen += $b; + $b = ord($data[0]); + $data = substr($data, 1); + $extLen++; + } + return true; + } + + public function deInterlace() + { + $data = $this->m_data; + + for ($i = 0; $i < 4; $i++) + { + switch ($i) + { + case 0: + $s = 8; + $y = 0; + break; + + case 1: + $s = 8; + $y = 4; + break; + + case 2: + $s = 4; + $y = 2; + break; + + case 3: + $s = 2; + $y = 1; + break; + } + + for (; $y < $this->m_gih->m_nHeight; $y += $s) + { + $lne = substr($this->m_data, 0, $this->m_gih->m_nWidth); + $this->m_data = substr($this->m_data, $this->m_gih->m_nWidth); + + $data = + substr($data, 0, $y * $this->m_gih->m_nWidth) . + $lne . + substr($data, ($y + 1) * $this->m_gih->m_nWidth); + } + } + + $this->m_data = $data; + } +} + +class gif_file +{ + public $header, $image, $data, $loaded; + + public function __construct() + { + $this->data = ''; + $this->loaded = false; + $this->header = new gif_file_header(); + $this->image = new gif_image(); + } + + public function loadFile($filename, $iIndex) + { + if ($iIndex < 0) + return false; + + $this->data = @file_get_contents($filename); + if ($this->data === false) + return false; + + // Tell the header to load up.... + $len = 0; + if (!$this->header->load($this->data, $len)) + return false; + + $this->data = substr($this->data, $len); + + // Keep reading (at least once) so we get to the actual image we're looking for. + for ($j = 0; $j <= $iIndex; $j++) + { + $imgLen = 0; + if (!$this->image->load($this->data, $imgLen)) + return false; + + $this->data = substr($this->data, $imgLen); + } + + $this->loaded = true; + return true; + } + + public function get_png_data($background_color) + { + if (!$this->loaded) + return false; + + // Prepare the color table. + if ($this->image->m_gih->m_bLocalClr) + { + $colors = $this->image->m_gih->m_nTableSize; + $pal = $this->image->m_gih->m_colorTable->toString(); + + if ($background_color != -1) + $background_color = $this->image->m_gih->m_colorTable->colorIndex($background_color); + } + elseif ($this->header->m_bGlobalClr) + { + $colors = $this->header->m_nTableSize; + $pal = $this->header->m_colorTable->toString(); + + if ($background_color != -1) + $background_color = $this->header->m_colorTable->colorIndex($background_color); + } + else + { + $colors = 0; + $background_color = -1; + } + + if ($background_color == -1) + $background_color = $this->header->m_nBgColor; + + $data = &$this->image->m_data; + $header = &$this->image->m_gih; + + $i = 0; + $bmp = ''; + + // Prepare the bitmap itself. + for ($y = 0; $y < $this->header->m_nHeight; $y++) + { + $bmp .= "\x00"; + + for ($x = 0; $x < $this->header->m_nWidth; $x++, $i++) + { + // Is this in the proper range? If so, get the specific pixel data... + if ($x >= $header->m_nLeft && $y >= $header->m_nTop && $x < ($header->m_nLeft + $header->m_nWidth) && $y < ($header->m_nTop + $header->m_nHeight)) + $bmp .= $data{$i}; + // Otherwise, this is background... + else + $bmp .= chr($background_color); + } + } + + $bmp = gzcompress($bmp, 9); + + // Output the basic signature first of all. + $out = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"; + + // Now, we want the header... + $out .= "\x00\x00\x00\x0D"; + $tmp = 'IHDR' . pack('N', (int) $this->header->m_nWidth) . pack('N', (int) $this->header->m_nHeight) . "\x08\x03\x00\x00\x00"; + $out .= $tmp . pack('N', smf_crc32($tmp)); + + // The palette, assuming we have one to speak of... + if ($colors > 0) + { + $out .= pack('N', (int) $colors * 3); + $tmp = 'PLTE' . $pal; + $out .= $tmp . pack('N', smf_crc32($tmp)); + } + + // Do we have any transparency we want to make available? + if ($this->image->m_bTrans && $colors > 0) + { + $out .= pack('N', (int) $colors); + $tmp = 'tRNS'; + + // Stick each color on - full transparency or none. + for ($i = 0; $i < $colors; $i++) + $tmp .= $i == $this->image->m_nTrans ? "\x00" : "\xFF"; + + $out .= $tmp . pack('N', smf_crc32($tmp)); + } + + // Here's the data itself! + $out .= pack('N', strlen($bmp)); + $tmp = 'IDAT' . $bmp; + $out .= $tmp . pack('N', smf_crc32($tmp)); + + // EOF marker... + $out .= "\x00\x00\x00\x00" . 'IEND' . "\xAE\x42\x60\x82"; + + return $out; + } +} + +// crc32 doesn't work as expected on 64-bit functions - make our own. +// http://www.php.net/crc32#79567 +if (!function_exists('smf_crc32')) +{ + function smf_crc32($number) + { + $crc = crc32($number); + + if ($crc & 0x80000000) + { + $crc ^= 0xffffffff; + $crc += 1; + $crc = -$crc; + } + + return $crc; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Class-Package.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Class-Package.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1019 @@ +path(path, true) in that instead of an xmlArray + of elements, an array of xmlArray's is returned for use with foreach. + + string xmlArray::create_xml(string path = '.') + - returns the specified path as an xml file. +*/ + +// An xml array. Reads in xml, allows you to access it simply. Version 1.1. +class xmlArray +{ + // The array and debugging output level. + public $array, $debug_level, $trim; + + // Create an xml array. + // the xml data, trim elements?, debugging output level, reserved. + //ie. $xml = new xmlArray(file('data.xml')); + public function __construct($data, $auto_trim = false, $level = null, $is_clone = false) + { + // If we're using this try to get some more memory. + @ini_set('memory_limit', '32M'); + + // Set the debug level. + $this->debug_level = $level !== null ? $level : error_reporting(); + $this->trim = $auto_trim; + + // Is the data already parsed? + if ($is_clone) + { + $this->array = $data; + return; + } + + // Is the input an array? (ie. passed from file()?) + if (is_array($data)) + $data = implode('', $data); + + // Remove any xml declaration or doctype, and parse out comments and CDATA. + $data = preg_replace('//s', '', $this->_to_cdata(preg_replace(array('/^<\?xml.+?\?' . '>/is', '/]+?' . '>/s'), '', $data))); + + // Now parse the xml! + $this->array = $this->_parse($data); + } + + // Get the root element's name. + //ie. echo $element->name(); + public function name() + { + return isset($this->array['name']) ? $this->array['name'] : ''; + } + + // Get a specified element's value or attribute by path. + // the path to the element to fetch, whether to include elements? + //ie. $data = $xml->fetch('html/head/title'); + public function fetch($path, $get_elements = false) + { + // Get the element, in array form. + $array = $this->path($path); + + if ($array === false) + return false; + + // Getting elements into this is a bit complicated... + if ($get_elements && !is_string($array)) + { + $temp = ''; + + // Use the _xml() function to get the xml data. + foreach ($array->array as $val) + { + // Skip the name and any attributes. + if (is_array($val)) + $temp .= $this->_xml($val, null); + } + + // Just get the XML data and then take out the CDATAs. + return $this->_to_cdata($temp); + } + + // Return the value - taking care to pick out all the text values. + return is_string($array) ? $array : $this->_fetch($array->array); + } + + // Get an element, returns a new xmlArray. + // the path to the element to get, always return full result set? (ie. don't contract a single item.) + //ie. $element = $xml->path('html/body'); + public function path($path, $return_full = false) + { + // Split up the path. + $path = explode('/', $path); + + // Start with a base array. + $array = $this->array; + + // For each element in the path. + foreach ($path as $el) + { + // Deal with sets.... + if (strpos($el, '[') !== false) + { + $lvl = (int) substr($el, strpos($el, '[') + 1); + $el = substr($el, 0, strpos($el, '[')); + } + // Find an attribute. + elseif (substr($el, 0, 1) == '@') + { + // It simplifies things if the attribute is already there ;). + if (isset($array[$el])) + return $array[$el]; + else + { + if (function_exists('debug_backtrace')) + { + $trace = debug_backtrace(); + $i = 0; + while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this)) + $i++; + $debug = ' from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line']; + } + else + $debug = ''; + + // Cause an error. + if ($this->debug_level & E_NOTICE) + trigger_error('Undefined XML attribute: ' . substr($el, 1) . $debug, E_USER_NOTICE); + return false; + } + } + else + $lvl = null; + + // Find this element. + $array = $this->_path($array, $el, $lvl); + } + + // Clean up after $lvl, for $return_full. + if ($return_full && (!isset($array['name']) || substr($array['name'], -1) != ']')) + $array = array('name' => $el . '[]', $array); + + // Create the right type of class... + $newClass = get_class($this); + + // Return a new xmlArray for the result. + return $array === false ? false : new $newClass($array, $this->trim, $this->debug_level, true); + } + + // Check if an element exists. + // the path to the element to get. + //ie. echo $xml->exists('html/body') ? 'y' : 'n'; + public function exists($path) + { + // Split up the path. + $path = explode('/', $path); + + // Start with a base array. + $array = $this->array; + + // For each element in the path. + foreach ($path as $el) + { + // Deal with sets.... + if (strpos($el, '[') !== false) + { + $lvl = (int) substr($el, strpos($el, '[') + 1); + $el = substr($el, 0, strpos($el, '[')); + } + // Find an attribute. + elseif (substr($el, 0, 1) == '@') + return isset($array[$el]); + else + $lvl = null; + + // Find this element. + $array = $this->_path($array, $el, $lvl, true); + } + + return $array !== false; + } + + // Count the number of occurances of a path. + // the path to search for. + //ie. echo $xml->count('html/head/meta'); + public function count($path) + { + // Get the element, always returning a full set. + $temp = $this->path($path, true); + + // Start at zero, then count up all the numeric keys. + $i = 0; + foreach ($temp->array as $item) + { + if (is_array($item)) + $i++; + } + + return $i; + } + + // Get an array of xmlArray's for use with foreach. + // the path to search for. + //ie. foreach ($xml->set('html/body/p') as $p) + public function set($path) + { + // None as yet, just get the path. + $array = array(); + $xml = $this->path($path, true); + + foreach ($xml->array as $val) + { + // Skip these, they aren't elements. + if (!is_array($val) || $val['name'] == '!') + continue; + + // Create the right type of class... + $newClass = get_class($this); + + // Create a new xmlArray and stick it in the array. + $array[] = new $newClass($val, $this->trim, $this->debug_level, true); + } + + return $array; + } + + // Create an xml file from an xml array. + // the path to the element. (optional) + //ie. echo $this->create_xml() + public function create_xml($path = null) + { + // Was a path specified? If so, use that array. + if ($path !== null) + { + $path = $this->path($path); + + // The path was not found + if ($path === false) + return false; + + $path = $path->array; + } + // Just use the current array. + else + $path = $this->array; + + // Add the xml declaration to the front. + return '' . $this->_xml($path, 0); + } + + // Output the xml in an array form. + // the path to output. + //ie. print_r($xml->to_array()); + public function to_array($path = null) + { + // Are we doing a specific path? + if ($path !== null) + { + $path = $this->path($path); + + // The path was not found + if ($path === false) + return false; + + $path = $path->array; + } + // No, so just use the current array. + else + $path = $this->array; + + return $this->_array($path); + } + + // Parse data into an array. (privately used...) + protected function _parse($data) + { + // Start with an 'empty' array with no data. + $current = array( + ); + + // Loop until we're out of data. + while ($data != '') + { + // Find and remove the next tag. + preg_match('/\A<([\w\-:]+)((?:\s+.+?)?)([\s]?\/)?' . '>/', $data, $match); + if (isset($match[0])) + $data = preg_replace('/' . preg_quote($match[0], '/') . '/s', '', $data, 1); + + // Didn't find a tag? Keep looping.... + if (!isset($match[1]) || $match[1] == '') + { + // If there's no <, the rest is data. + if (strpos($data, '<') === false) + { + $text_value = $this->_from_cdata($data); + $data = ''; + + if ($text_value != '') + $current[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + // If the < isn't immediately next to the current position... more data. + elseif (strpos($data, '<') > 0) + { + $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<'))); + $data = substr($data, strpos($data, '<')); + + if ($text_value != '') + $current[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + // If we're looking at a with no start, kill it. + elseif (strpos($data, '<') !== false && strpos($data, '<') == 0) + { + if (strpos($data, '<', 1) !== false) + { + $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<', 1))); + $data = substr($data, strpos($data, '<', 1)); + + if ($text_value != '') + $current[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + else + { + $text_value = $this->_from_cdata($data); + $data = ''; + + if ($text_value != '') + $current[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + } + + // Wait for an actual occurance of an element. + continue; + } + + // Create a new element in the array. + $el = &$current[]; + $el['name'] = $match[1]; + + // If this ISN'T empty, remove the close tag and parse the inner data. + if ((!isset($match[3]) || trim($match[3]) != '/') && (!isset($match[2]) || trim($match[2]) != '/')) + { + // Because PHP 5.2.0+ seems to croak using regex, we'll have to do this the less fun way. + $last_tag_end = strpos($data, ''); + if ($last_tag_end === false) + continue; + + $offset = 0; + while (1 == 1) + { + // Where is the next start tag? + $next_tag_start = strpos($data, '<' . $match[1], $offset); + // If the next start tag is after the last end tag then we've found the right close. + if ($next_tag_start === false || $next_tag_start > $last_tag_end) + break; + + // If not then find the next ending tag. + $next_tag_end = strpos($data, '', $offset); + + // Didn't find one? Then just use the last and sod it. + if ($next_tag_end === false) + break; + else + { + $last_tag_end = $next_tag_end; + $offset = $next_tag_start + 1; + } + } + // Parse the insides. + $inner_match = substr($data, 0, $last_tag_end); + // Data now starts from where this section ends. + $data = substr($data, $last_tag_end + strlen('')); + + if (!empty($inner_match)) + { + // Parse the inner data. + if (strpos($inner_match, '<') !== false) + $el += $this->_parse($inner_match); + elseif (trim($inner_match) != '') + { + $text_value = $this->_from_cdata($inner_match); + if ($text_value != '') + $el[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + } + } + + // If we're dealing with attributes as well, parse them out. + if (isset($match[2]) && $match[2] != '') + { + // Find all the attribute pairs in the string. + preg_match_all('/([\w:]+)="(.+?)"/', $match[2], $attr, PREG_SET_ORDER); + + // Set them as @attribute-name. + foreach ($attr as $match_attr) + $el['@' . $match_attr[1]] = $match_attr[2]; + } + } + + // Return the parsed array. + return $current; + } + + // Get a specific element's xml. (privately used...) + protected function _xml($array, $indent) + { + $indentation = $indent !== null ? ' +' . str_repeat(' ', $indent) : ''; + + // This is a set of elements, with no name... + if (is_array($array) && !isset($array['name'])) + { + $temp = ''; + foreach ($array as $val) + $temp .= $this->_xml($val, $indent); + return $temp; + } + + // This is just text! + if ($array['name'] == '!') + return $indentation . ''; + elseif (substr($array['name'], -2) == '[]') + $array['name'] = substr($array['name'], 0, -2); + + // Start the element. + $output = $indentation . '<' . $array['name']; + + $inside_elements = false; + $output_el = ''; + + // Run through and recurively output all the elements or attrbutes inside this. + foreach ($array as $k => $v) + { + if (substr($k, 0, 1) == '@') + $output .= ' ' . substr($k, 1) . '="' . $v . '"'; + elseif (is_array($v)) + { + $output_el .= $this->_xml($v, $indent === null ? null : $indent + 1); + $inside_elements = true; + } + } + + // Indent, if necessary.... then close the tag. + if ($inside_elements) + $output .= '>' . $output_el . $indentation . ''; + else + $output .= ' />'; + + return $output; + } + + // Return an element as an array... + protected function _array($array) + { + $return = array(); + $text = ''; + foreach ($array as $value) + { + if (!is_array($value) || !isset($value['name'])) + continue; + + if ($value['name'] == '!') + $text .= $value['value']; + else + $return[$value['name']] = $this->_array($value); + } + + if (empty($return)) + return $text; + else + return $return; + } + + // Parse out CDATA tags. (htmlspecialchars them...) + function _to_cdata($data) + { + $inCdata = $inComment = false; + $output = ''; + + $parts = preg_split('~(|)~', $data, -1, PREG_SPLIT_DELIM_CAPTURE); + foreach ($parts as $part) + { + // Handle XML comments. + if (!$inCdata && $part === '') + $inComment = false; + elseif ($inComment) + continue; + + // Handle Cdata blocks. + elseif (!$inComment && $part === '') + $inCdata = false; + elseif ($inCdata) + $output .= htmlentities($part, ENT_QUOTES); + + // Everything else is kept as is. + else + $output .= $part; + } + + return $output; + } + + // Turn the CDATAs back to normal text. + protected function _from_cdata($data) + { + // Get the HTML translation table and reverse it. + $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES, ENT_QUOTES)); + + // Translate all the entities out. + $data = strtr(preg_replace('~&#(\d{1,4});~e', "chr('\$1')", $data), $trans_tbl); + + return $this->trim ? trim($data) : $data; + } + + // Given an array, return the text from that array. (recursive and privately used.) + protected function _fetch($array) + { + // Don't return anything if this is just a string. + if (is_string($array)) + return ''; + + $temp = ''; + foreach ($array as $text) + { + // This means it's most likely an attribute or the name itself. + if (!isset($text['name'])) + continue; + + // This is text! + if ($text['name'] == '!') + $temp .= $text['value']; + // Another element - dive in ;). + else + $temp .= $this->_fetch($text); + } + + // Return all the bits and pieces we've put together. + return $temp; + } + + // Get a specific array by path, one level down. (privately used...) + protected function _path($array, $path, $level, $no_error = false) + { + // Is $array even an array? It might be false! + if (!is_array($array)) + return false; + + // Asking for *no* path? + if ($path == '' || $path == '.') + return $array; + $paths = explode('|', $path); + + // A * means all elements of any name. + $show_all = in_array('*', $paths); + + $results = array(); + + // Check each element. + foreach ($array as $value) + { + if (!is_array($value) || $value['name'] === '!') + continue; + + if ($show_all || in_array($value['name'], $paths)) + { + // Skip elements before "the one". + if ($level !== null && $level > 0) + $level--; + else + $results[] = $value; + } + } + + // No results found... + if (empty($results)) + { + if (function_exists('debug_backtrace')) + { + $trace = debug_backtrace(); + $i = 0; + while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this)) + $i++; + $debug = ' from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line']; + } + else + $debug = ''; + + // Cause an error. + if ($this->debug_level & E_NOTICE && !$no_error) + trigger_error('Undefined XML element: ' . $path . $debug, E_USER_NOTICE); + return false; + } + // Only one result. + elseif (count($results) == 1 || $level !== null) + return $results[0]; + // Return the result set. + else + return $results + array('name' => $path . '[]'); + } +} + +// http://www.faqs.org/rfcs/rfc959.html +if (!class_exists('ftp_connection')) +{ + class ftp_connection + { + public $connection, $error, $last_message, $pasv; + + // Create a new FTP connection... + public function __construct($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@simplemachines.org') + { + // Initialize variables. + $this->connection = 'no_connection'; + $this->error = false; + $this->pasv = array(); + + if ($ftp_server !== null) + $this->connect($ftp_server, $ftp_port, $ftp_user, $ftp_pass); + } + + public function connect($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@simplemachines.org') + { + if (substr($ftp_server, 0, 6) == 'ftp://') + $ftp_server = substr($ftp_server, 6); + elseif (substr($ftp_server, 0, 7) == 'ftps://') + $ftp_server = 'ssl://' . substr($ftp_server, 7); + if (substr($ftp_server, 0, 7) == 'http://') + $ftp_server = substr($ftp_server, 7); + $ftp_server = strtr($ftp_server, array('/' => '', ':' => '', '@' => '')); + + // Connect to the FTP server. + $this->connection = @fsockopen($ftp_server, $ftp_port, $err, $err, 5); + if (!$this->connection) + { + $this->error = 'bad_server'; + return; + } + + // Get the welcome message... + if (!$this->check_response(220)) + { + $this->error = 'bad_response'; + return; + } + + // Send the username, it should ask for a password. + fwrite($this->connection, 'USER ' . $ftp_user . "\r\n"); + if (!$this->check_response(331)) + { + $this->error = 'bad_username'; + return; + } + + // Now send the password... and hope it goes okay. + fwrite($this->connection, 'PASS ' . $ftp_pass . "\r\n"); + if (!$this->check_response(230)) + { + $this->error = 'bad_password'; + return; + } + } + + public function chdir($ftp_path) + { + if (!is_resource($this->connection)) + return false; + + // No slash on the end, please... + if ($ftp_path !== '/' && substr($ftp_path, -1) === '/') + $ftp_path = substr($ftp_path, 0, -1); + + fwrite($this->connection, 'CWD ' . $ftp_path . "\r\n"); + if (!$this->check_response(250)) + { + $this->error = 'bad_path'; + return false; + } + + return true; + } + + public function chmod($ftp_file, $chmod) + { + if (!is_resource($this->connection)) + return false; + + if ($ftp_file == '') + $ftp_file = '.'; + + // Convert the chmod value from octal (0777) to text ("777"). + fwrite($this->connection, 'SITE CHMOD ' . decoct($chmod) . ' ' . $ftp_file . "\r\n"); + if (!$this->check_response(200)) + { + $this->error = 'bad_file'; + return false; + } + + return true; + } + + public function unlink($ftp_file) + { + // We are actually connected, right? + if (!is_resource($this->connection)) + return false; + + // Delete file X. + fwrite($this->connection, 'DELE ' . $ftp_file . "\r\n"); + if (!$this->check_response(250)) + { + fwrite($this->connection, 'RMD ' . $ftp_file . "\r\n"); + + // Still no love? + if (!$this->check_response(250)) + { + $this->error = 'bad_file'; + return false; + } + } + + return true; + } + + public function check_response($desired) + { + // Wait for a response that isn't continued with -, but don't wait too long. + $time = time(); + do + $this->last_message = fgets($this->connection, 1024); + while ((strlen($this->last_message) < 4 || substr($this->last_message, 0, 1) == ' ' || substr($this->last_message, 3, 1) != ' ') && time() - $time < 5); + + // Was the desired response returned? + return is_array($desired) ? in_array(substr($this->last_message, 0, 3), $desired) : substr($this->last_message, 0, 3) == $desired; + } + + public function passive() + { + // We can't create a passive data connection without a primary one first being there. + if (!is_resource($this->connection)) + return false; + + // Request a passive connection - this means, we'll talk to you, you don't talk to us. + @fwrite($this->connection, 'PASV' . "\r\n"); + $time = time(); + do + $response = fgets($this->connection, 1024); + while (substr($response, 3, 1) != ' ' && time() - $time < 5); + + // If it's not 227, we weren't given an IP and port, which means it failed. + if (substr($response, 0, 4) != '227 ') + { + $this->error = 'bad_response'; + return false; + } + + // Snatch the IP and port information, or die horribly trying... + if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $response, $match) == 0) + { + $this->error = 'bad_response'; + return false; + } + + // This is pretty simple - store it for later use ;). + $this->pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]); + + return true; + } + + public function create_file($ftp_file) + { + // First, we have to be connected... very important. + if (!is_resource($this->connection)) + return false; + + // I'd like one passive mode, please! + if (!$this->passive()) + return false; + + // Seems logical enough, so far... + fwrite($this->connection, 'STOR ' . $ftp_file . "\r\n"); + + // Okay, now we connect to the data port. If it doesn't work out, it's probably "file already exists", etc. + $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5); + if (!$fp || !$this->check_response(150)) + { + $this->error = 'bad_file'; + @fclose($fp); + return false; + } + + // This may look strange, but we're just closing it to indicate a zero-byte upload. + fclose($fp); + if (!$this->check_response(226)) + { + $this->error = 'bad_response'; + return false; + } + + return true; + } + + public function list_dir($ftp_path = '', $search = false) + { + // Are we even connected...? + if (!is_resource($this->connection)) + return false; + + // Passive... non-agressive... + if (!$this->passive()) + return false; + + // Get the listing! + fwrite($this->connection, 'LIST -1' . ($search ? 'R' : '') . ($ftp_path == '' ? '' : ' ' . $ftp_path) . "\r\n"); + + // Connect, assuming we've got a connection. + $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5); + if (!$fp || !$this->check_response(array(150, 125))) + { + $this->error = 'bad_response'; + @fclose($fp); + return false; + } + + // Read in the file listing. + $data = ''; + while (!feof($fp)) + $data .= fread($fp, 4096); + fclose($fp); + + // Everything go okay? + if (!$this->check_response(226)) + { + $this->error = 'bad_response'; + return false; + } + + return $data; + } + + public function locate($file, $listing = null) + { + if ($listing === null) + $listing = $this->list_dir('', true); + $listing = explode("\n", $listing); + + @fwrite($this->connection, 'PWD' . "\r\n"); + $time = time(); + do + $response = fgets($this->connection, 1024); + while ($response[3] != ' ' && time() - $time < 5); + + // Check for 257! + if (preg_match('~^257 "(.+?)" ~', $response, $match) != 0) + $current_dir = strtr($match[1], array('""' => '"')); + else + $current_dir = ''; + + for ($i = 0, $n = count($listing); $i < $n; $i++) + { + if (trim($listing[$i]) == '' && isset($listing[$i + 1])) + { + $current_dir = substr(trim($listing[++$i]), 0, -1); + $i++; + } + + // Okay, this file's name is: + $listing[$i] = $current_dir . '/' . trim(strlen($listing[$i]) > 30 ? strrchr($listing[$i], ' ') : $listing[$i]); + + if ($file[0] == '*' && substr($listing[$i], -(strlen($file) - 1)) == substr($file, 1)) + return $listing[$i]; + if (substr($file, -1) == '*' && substr($listing[$i], 0, strlen($file) - 1) == substr($file, 0, -1)) + return $listing[$i]; + if (basename($listing[$i]) == $file || $listing[$i] == $file) + return $listing[$i]; + } + + return false; + } + + public function create_dir($ftp_dir) + { + // We must be connected to the server to do something. + if (!is_resource($this->connection)) + return false; + + // Make this new beautiful directory! + fwrite($this->connection, 'MKD ' . $ftp_dir . "\r\n"); + if (!$this->check_response(257)) + { + $this->error = 'bad_file'; + return false; + } + + return true; + } + + public function detect_path($filesystem_path, $lookup_file = null) + { + $username = ''; + + if (isset($_SERVER['DOCUMENT_ROOT'])) + { + if (preg_match('~^/home[2]?/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match)) + { + $username = $match[1]; + + $path = strtr($_SERVER['DOCUMENT_ROOT'], array('/home/' . $match[1] . '/' => '', '/home2/' . $match[1] . '/' => '')); + + if (substr($path, -1) == '/') + $path = substr($path, 0, -1); + + if (strlen(dirname($_SERVER['PHP_SELF'])) > 1) + $path .= dirname($_SERVER['PHP_SELF']); + } + elseif (substr($filesystem_path, 0, 9) == '/var/www/') + $path = substr($filesystem_path, 8); + else + $path = strtr(strtr($filesystem_path, array('\\' => '/')), array($_SERVER['DOCUMENT_ROOT'] => '')); + } + else + $path = ''; + + if (is_resource($this->connection) && $this->list_dir($path) == '') + { + $data = $this->list_dir('', true); + + if ($lookup_file === null) + $lookup_file = $_SERVER['PHP_SELF']; + + $found_path = dirname($this->locate('*' . basename(dirname($lookup_file)) . '/' . basename($lookup_file), $data)); + if ($found_path == false) + $found_path = dirname($this->locate(basename($lookup_file))); + if ($found_path != false) + $path = $found_path; + } + elseif (is_resource($this->connection)) + $found_path = true; + + return array($username, $path, isset($found_path)); + } + + public function close() + { + // Goodbye! + fwrite($this->connection, 'QUIT' . "\r\n"); + fclose($this->connection); + + return true; + } + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbExtra-mysql.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DbExtra-mysql.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,454 @@ + 'smf_db_backup_table', + 'db_optimize_table' => 'smf_db_optimize_table', + 'db_insert_sql' => 'smf_db_insert_sql', + 'db_table_sql' => 'smf_db_table_sql', + 'db_list_tables' => 'smf_db_list_tables', + 'db_get_version' => 'smf_db_get_version', + ); +} + +// Backup $table to $backup_table. +function smf_db_backup_table($table, $backup_table) +{ + global $smcFunc, $db_prefix; + + $table = str_replace('{db_prefix}', $db_prefix, $table); + + // First, get rid of the old table. + $smcFunc['db_query']('', ' + DROP TABLE IF EXISTS {raw:backup_table}', + array( + 'backup_table' => $backup_table, + ) + ); + + // Can we do this the quick way? + $result = $smcFunc['db_query']('', ' + CREATE TABLE {raw:backup_table} LIKE {raw:table}', + array( + 'backup_table' => $backup_table, + 'table' => $table + )); + // If this failed, we go old school. + if ($result) + { + $request = $smcFunc['db_query']('', ' + INSERT INTO {raw:backup_table} + SELECT * + FROM {raw:table}', + array( + 'backup_table' => $backup_table, + 'table' => $table + )); + + // Old school or no school? + if ($request) + return $request; + } + + // At this point, the quick method failed. + $result = $smcFunc['db_query']('', ' + SHOW CREATE TABLE {raw:table}', + array( + 'table' => $table, + ) + ); + list (, $create) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + $create = preg_split('/[\n\r]/', $create); + + $auto_inc = ''; + // Default engine type. + $engine = 'MyISAM'; + $charset = ''; + $collate = ''; + + foreach ($create as $k => $l) + { + // Get the name of the auto_increment column. + if (strpos($l, 'auto_increment')) + $auto_inc = trim($l); + + // For the engine type, see if we can work out what it is. + if (strpos($l, 'ENGINE') !== false || strpos($l, 'TYPE') !== false) + { + // Extract the engine type. + preg_match('~(ENGINE|TYPE)=(\w+)(\sDEFAULT)?(\sCHARSET=(\w+))?(\sCOLLATE=(\w+))?~', $l, $match); + + if (!empty($match[1])) + $engine = $match[1]; + + if (!empty($match[2])) + $engine = $match[2]; + + if (!empty($match[5])) + $charset = $match[5]; + + if (!empty($match[7])) + $collate = $match[7]; + } + + // Skip everything but keys... + if (strpos($l, 'KEY') === false) + unset($create[$k]); + } + + if (!empty($create)) + $create = '( + ' . implode(' + ', $create) . ')'; + else + $create = ''; + + $request = $smcFunc['db_query']('', ' + CREATE TABLE {raw:backup_table} {raw:create} + ENGINE={raw:engine}' . (empty($charset) ? '' : ' CHARACTER SET {raw:charset}' . (empty($collate) ? '' : ' COLLATE {raw:collate}')) . ' + SELECT * + FROM {raw:table}', + array( + 'backup_table' => $backup_table, + 'table' => $table, + 'create' => $create, + 'engine' => $engine, + 'charset' => empty($charset) ? '' : $charset, + 'collate' => empty($collate) ? '' : $collate, + ) + ); + + if ($auto_inc != '') + { + if (preg_match('~\`(.+?)\`\s~', $auto_inc, $match) != 0 && substr($auto_inc, -1, 1) == ',') + $auto_inc = substr($auto_inc, 0, -1); + + $smcFunc['db_query']('', ' + ALTER TABLE {raw:backup_table} + CHANGE COLUMN {raw:column_detail} {raw:auto_inc}', + array( + 'backup_table' => $backup_table, + 'column_detail' => $match[1], + 'auto_inc' => $auto_inc, + ) + ); + } + + return $request; +} + +// Optimize a table - return data freed! +function smf_db_optimize_table($table) +{ + global $smcFunc, $db_name, $db_prefix; + + $table = str_replace('{db_prefix}', $db_prefix, $table); + + // Get how much overhead there is. + $request = $smcFunc['db_query']('', ' + SHOW TABLE STATUS LIKE {string:table_name}', + array( + 'table_name' => str_replace('_', '\_', $table), + ) + ); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $data_before = isset($row['Data_free']) ? $row['Data_free'] : 0; + $request = $smcFunc['db_query']('', ' + OPTIMIZE TABLE `{raw:table}`', + array( + 'table' => $table, + ) + ); + if (!$request) + return -1; + + // How much left? + $request = $smcFunc['db_query']('', ' + SHOW TABLE STATUS LIKE {string:table}', + array( + 'table' => str_replace('_', '\_', $table), + ) + ); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $total_change = isset($row['Data_free']) && $data_before > $row['Data_free'] ? $data_before / 1024 : 0; + + return $total_change; +} + +// List all the tables in the database. +function smf_db_list_tables($db = false, $filter = false) +{ + global $db_name, $smcFunc; + + $db = $db == false ? $db_name : $db; + $db = trim($db); + $filter = $filter == false ? '' : ' LIKE \'' . $filter . '\''; + + $request = $smcFunc['db_query']('', ' + SHOW TABLES + FROM `{raw:db}` + {raw:filter}', + array( + 'db' => $db[0] == '`' ? strtr($db, array('`' => '')) : $db, + 'filter' => $filter, + ) + ); + $tables = array(); + while ($row = $smcFunc['db_fetch_row']($request)) + $tables[] = $row[0]; + $smcFunc['db_free_result']($request); + + return $tables; +} + +// Get the content (INSERTs) for a table. +function smf_db_insert_sql($tableName) +{ + global $smcFunc, $db_prefix; + + $tableName = str_replace('{db_prefix}', $db_prefix, $tableName); + + // This will be handy... + $crlf = "\r\n"; + + // Get everything from the table. + $result = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ * + FROM `{raw:table}`', + array( + 'table' => $tableName, + ) + ); + + // The number of rows, just for record keeping and breaking INSERTs up. + $num_rows = $smcFunc['db_num_rows']($result); + $current_row = 0; + + if ($num_rows == 0) + return ''; + + $fields = array_keys($smcFunc['db_fetch_assoc']($result)); + $smcFunc['db_data_seek']($result, 0); + + // Start it off with the basic INSERT INTO. + $data = 'INSERT INTO `' . $tableName . '`' . $crlf . "\t" . '(`' . implode('`, `', $fields) . '`)' . $crlf . 'VALUES '; + + // Loop through each row. + while ($row = $smcFunc['db_fetch_row']($result)) + { + $current_row++; + + // Get the fields in this row... + $field_list = array(); + for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++) + { + // Try to figure out the type of each field. (NULL, number, or 'string'.) + if (!isset($row[$j])) + $field_list[] = 'NULL'; + elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j]) + $field_list[] = $row[$j]; + else + $field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\''; + } + + // 'Insert' the data. + $data .= '(' . implode(', ', $field_list) . ')'; + + // All done! + if ($current_row == $num_rows) + $data .= ';' . $crlf; + // Start a new INSERT statement after every 250.... + elseif ($current_row > 249 && $current_row % 250 == 0) + $data .= ';' . $crlf . 'INSERT INTO `' . $tableName . '`' . $crlf . "\t" . '(`' . implode('`, `', $fields) . '`)' . $crlf . 'VALUES '; + // Otherwise, go to the next line. + else + $data .= ',' . $crlf . "\t"; + } + $smcFunc['db_free_result']($result); + + // Return an empty string if there were no rows. + return $num_rows == 0 ? '' : $data; +} + +// Get the schema (CREATE) for a table. +function smf_db_table_sql($tableName) +{ + global $smcFunc, $db_prefix; + + $tableName = str_replace('{db_prefix}', $db_prefix, $tableName); + + // This will be needed... + $crlf = "\r\n"; + + // Drop it if it exists. + $schema_create = 'DROP TABLE IF EXISTS `' . $tableName . '`;' . $crlf . $crlf; + + // Start the create table... + $schema_create .= 'CREATE TABLE `' . $tableName . '` (' . $crlf; + + // Find all the fields. + $result = $smcFunc['db_query']('', ' + SHOW FIELDS + FROM `{raw:table}`', + array( + 'table' => $tableName, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Make the CREATE for this column. + $schema_create .= ' `' . $row['Field'] . '` ' . $row['Type'] . ($row['Null'] != 'YES' ? ' NOT NULL' : ''); + + // Add a default...? + if (!empty($row['Default']) || $row['Null'] !== 'YES') + { + // Make a special case of auto-timestamp. + if ($row['Default'] == 'CURRENT_TIMESTAMP') + $schema_create .= ' /*!40102 NOT NULL default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP */'; + // Text shouldn't have a default. + elseif ($row['Default'] !== null) + { + // If this field is numeric the default needs no escaping. + $type = strtolower($row['Type']); + $isNumericColumn = strpos($type, 'int') !== false || strpos($type, 'bool') !== false || strpos($type, 'bit') !== false || strpos($type, 'float') !== false || strpos($type, 'double') !== false || strpos($type, 'decimal') !== false; + + $schema_create .= ' default ' . ($isNumericColumn ? $row['Default'] : '\'' . $smcFunc['db_escape_string']($row['Default']) . '\''); + } + } + + // And now any extra information. (such as auto_increment.) + $schema_create .= ($row['Extra'] != '' ? ' ' . $row['Extra'] : '') . ',' . $crlf; + } + $smcFunc['db_free_result']($result); + + // Take off the last comma. + $schema_create = substr($schema_create, 0, -strlen($crlf) - 1); + + // Find the keys. + $result = $smcFunc['db_query']('', ' + SHOW KEYS + FROM `{raw:table}`', + array( + 'table' => $tableName, + ) + ); + $indexes = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // IS this a primary key, unique index, or regular index? + $row['Key_name'] = $row['Key_name'] == 'PRIMARY' ? 'PRIMARY KEY' : (empty($row['Non_unique']) ? 'UNIQUE ' : ($row['Comment'] == 'FULLTEXT' || (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT') ? 'FULLTEXT ' : 'KEY ')) . '`' . $row['Key_name'] . '`'; + + // Is this the first column in the index? + if (empty($indexes[$row['Key_name']])) + $indexes[$row['Key_name']] = array(); + + // A sub part, like only indexing 15 characters of a varchar. + if (!empty($row['Sub_part'])) + $indexes[$row['Key_name']][$row['Seq_in_index']] = '`' . $row['Column_name'] . '`(' . $row['Sub_part'] . ')'; + else + $indexes[$row['Key_name']][$row['Seq_in_index']] = '`' . $row['Column_name'] . '`'; + } + $smcFunc['db_free_result']($result); + + // Build the CREATEs for the keys. + foreach ($indexes as $keyname => $columns) + { + // Ensure the columns are in proper order. + ksort($columns); + + $schema_create .= ',' . $crlf . ' ' . $keyname . ' (' . implode($columns, ', ') . ')'; + } + + // Now just get the comment and type... (MyISAM, etc.) + $result = $smcFunc['db_query']('', ' + SHOW TABLE STATUS + LIKE {string:table}', + array( + 'table' => strtr($tableName, array('_' => '\\_', '%' => '\\%')), + ) + ); + $row = $smcFunc['db_fetch_assoc']($result); + $smcFunc['db_free_result']($result); + + // Probably MyISAM.... and it might have a comment. + $schema_create .= $crlf . ') ENGINE=' . (isset($row['Type']) ? $row['Type'] : $row['Engine']) . ($row['Comment'] != '' ? ' COMMENT="' . $row['Comment'] . '"' : ''); + + return $schema_create; +} + +// Get the version number. +function smf_db_get_version() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT VERSION()', + array( + ) + ); + list ($ver) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $ver; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbExtra-postgresql.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DbExtra-postgresql.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,328 @@ + 'smf_db_backup_table', + 'db_optimize_table' => 'smf_db_optimize_table', + 'db_insert_sql' => 'smf_db_insert_sql', + 'db_table_sql' => 'smf_db_table_sql', + 'db_list_tables' => 'smf_db_list_tables', + 'db_get_version' => 'smf_db_get_version', + ); +} + +// Backup $table to $backup_table. +function smf_db_backup_table($table, $backup_table) +{ + global $smcFunc, $db_prefix; + + $table = str_replace('{db_prefix}', $db_prefix, $table); + + // Do we need to drop it first? + $tables = smf_db_list_tables(false, $backup_table); + if (!empty($tables)) + $smcFunc['db_query']('', ' + DROP TABLE {raw:backup_table}', + array( + 'backup_table' => $backup_table, + ) + ); + + //!!! Should we create backups of sequences as well? + $smcFunc['db_query']('', ' + CREATE TABLE {raw:backup_table} + ( + LIKE {raw:table} + INCLUDING DEFAULTS + )', + array( + 'backup_table' => $backup_table, + 'table' => $table, + ) + ); + $smcFunc['db_query']('', ' + INSERT INTO {raw:backup_table} + SELECT * FROM {raw:table}', + array( + 'backup_table' => $backup_table, + 'table' => $table, + ) + ); +} + +// Optimize a table - return data freed! +function smf_db_optimize_table($table) +{ + global $smcFunc, $db_prefix; + + $table = str_replace('{db_prefix}', $db_prefix, $table); + + $request = $smcFunc['db_query']('', ' + VACUUM ANALYZE {raw:table}', + array( + 'table' => $table, + ) + ); + if (!$request) + return -1; + + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + if (isset($row['Data_free'])) + return $row['Data_free'] / 1024; + else + return 0; +} + +// List all the tables in the database. +function smf_db_list_tables($db = false, $filter = false) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT tablename + FROM pg_tables + WHERE schemaname = {string:schema_public}' . ($filter == false ? '' : ' + AND tablename LIKE {string:filter}') . ' + ORDER BY tablename', + array( + 'schema_public' => 'public', + 'filter' => $filter, + ) + ); + + $tables = array(); + while ($row = $smcFunc['db_fetch_row']($request)) + $tables[] = $row[0]; + $smcFunc['db_free_result']($request); + + return $tables; +} + +// Get the content (INSERTs) for a table. +function smf_db_insert_sql($tableName) +{ + global $smcFunc, $db_prefix; + + $tableName = str_replace('{db_prefix}', $db_prefix, $tableName); + + // This will be handy... + $crlf = "\r\n"; + + // Get everything from the table. + $result = $smcFunc['db_query']('', ' + SELECT * + FROM {raw:table}', + array( + 'table' => $tableName, + ) + ); + + // The number of rows, just for record keeping and breaking INSERTs up. + $num_rows = $smcFunc['db_num_rows']($result); + + if ($num_rows == 0) + return ''; + + $fields = array_keys($smcFunc['db_fetch_assoc']($result)); + $smcFunc['db_data_seek']($result, 0); + + // Start it off with the basic INSERT INTO. + $data = ''; + $insert_msg = $crlf . 'INSERT INTO ' . $tableName . $crlf . "\t" . '(' . implode(', ', $fields) . ')' . $crlf . 'VALUES ' . $crlf . "\t"; + + // Loop through each row. + while ($row = $smcFunc['db_fetch_row']($result)) + { + // Get the fields in this row... + $field_list = array(); + for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++) + { + // Try to figure out the type of each field. (NULL, number, or 'string'.) + if (!isset($row[$j])) + $field_list[] = 'NULL'; + elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j]) + $field_list[] = $row[$j]; + else + $field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\''; + } + + // 'Insert' the data. + $data .= $insert_msg . '(' . implode(', ', $field_list) . ');'; + } + $smcFunc['db_free_result']($result); + + // Return an empty string if there were no rows. + return $num_rows == 0 ? '' : $data; +} + +// Get the schema (CREATE) for a table. +function smf_db_table_sql($tableName) +{ + global $smcFunc, $db_prefix; + + $tableName = str_replace('{db_prefix}', $db_prefix, $tableName); + + // This will be needed... + $crlf = "\r\n"; + + // Start the create table... + $schema_create = 'CREATE TABLE ' . $tableName . ' (' . $crlf; + $index_create = ''; + $seq_create = ''; + + // Find all the fields. + $result = $smcFunc['db_query']('', ' + SELECT column_name, column_default, is_nullable, data_type, character_maximum_length + FROM information_schema.columns + WHERE table_name = {string:table} + ORDER BY ordinal_position', + array( + 'table' => $tableName, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if ($row['data_type'] == 'character varying') + $row['data_type'] = 'varchar'; + elseif ($row['data_type'] == 'character') + $row['data_type'] = 'char'; + if ($row['character_maximum_length']) + $row['data_type'] .= '(' . $row['character_maximum_length'] . ')'; + + // Make the CREATE for this column. + $schema_create .= ' "' . $row['column_name'] . '" ' . $row['data_type'] . ($row['is_nullable'] != 'YES' ? ' NOT NULL' : ''); + + // Add a default...? + if (trim($row['column_default']) != '') + { + $schema_create .= ' default ' . $row['column_default'] . ''; + + // Auto increment? + if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0) + { + // Get to find the next variable first! + $count_req = $smcFunc['db_query']('', ' + SELECT MAX("{raw:column}") + FROM {raw:table}', + array( + 'column' => $row['column_name'], + 'table' => $tableName, + ) + ); + list ($max_ind) = $smcFunc['db_fetch_row']($count_req); + $smcFunc['db_free_result']($count_req); + // Get the right bloody start! + $seq_create .= 'CREATE SEQUENCE ' . $matches[1] . ' START WITH ' . ($max_ind + 1) . ';' . $crlf . $crlf; + } + } + + $schema_create .= ',' . $crlf; + } + $smcFunc['db_free_result']($result); + + // Take off the last comma. + $schema_create = substr($schema_create, 0, -strlen($crlf) - 1); + + $result = $smcFunc['db_query']('', ' + SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary, pg_get_indexdef(i.indexrelid) AS inddef + FROM pg_class AS c + INNER JOIN pg_index AS i ON (i.indrelid = c.oid) + INNER JOIN pg_class AS c2 ON (c2.oid = i.indexrelid) + WHERE c.relname = {string:table}', + array( + 'table' => $tableName, + ) + ); + $indexes = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if ($row['is_primary']) + { + if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0) + continue; + + $index_create .= $crlf . 'ALTER TABLE ' . $tableName . ' ADD PRIMARY KEY ("' . $matches[1] . '");'; + } + else + $index_create .= $crlf . $row['inddef'] . ';'; + } + $smcFunc['db_free_result']($result); + + // Finish it off! + $schema_create .= $crlf . ');'; + + return $seq_create . $schema_create . $index_create; +} + +// Get the version number. +function smf_db_get_version() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SHOW server_version', + array( + ) + ); + list ($ver) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $ver; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbExtra-sqlite.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DbExtra-sqlite.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,346 @@ + 'smf_db_backup_table', + 'db_optimize_table' => 'smf_db_optimize_table', + 'db_insert_sql' => 'smf_db_insert_sql', + 'db_table_sql' => 'smf_db_table_sql', + 'db_list_tables' => 'smf_db_list_tables', + 'db_get_backup' => 'smf_db_get_backup', + 'db_get_version' => 'smf_db_get_version', + ); +} + +// Backup $table to $backup_table. +function smf_db_backup_table($table, $backup_table) +{ + global $smcFunc, $db_prefix; + + $table = str_replace('{db_prefix}', $db_prefix, $table); + + $result = $smcFunc['db_query']('', ' + SELECT sql + FROM sqlite_master + WHERE type = {string:txttable} + AND name = {string:table}', + array( + 'table' => $table, + 'txttable' => 'table' + ) + ); + list ($create) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + $create = preg_split('/[\n\r]/', $create); + $auto_inc = ''; + + // Remove the first line and check to see if the second one contain useless info. + unset($create[0]); + if (trim($create[1]) == '(') + unset($create[1]); + if (trim($create[count($create)]) == ')') + unset($create[count($create)]); + + foreach ($create as $k => $l) + { + // Get the name of the auto_increment column. + if (strpos($l, 'primary') || strpos($l, 'PRIMARY')) + $auto_inc = trim($l); + + // Skip everything but keys... + if ((strpos($l, 'KEY') !== false && strpos($l, 'PRIMARY KEY') === false) || strpos($l, $table) !== false || strpos(trim($l), 'PRIMARY KEY') === 0) + unset($create[$k]); + } + + if (!empty($create)) + $create = '( + ' . implode(' + ', $create) . ')'; + else + $create = ''; + + // Is there an extra junk at the end? + if (substr($create, -2, 1) == ',') + $create = substr($create, 0, -2) . ')'; + if (substr($create, -2) == '))') + $create = substr($create, 0, -1); + + $smcFunc['db_query']('', ' + DROP TABLE {raw:backup_table}', + array( + 'backup_table' => $backup_table, + 'db_error_skip' => true, + ) + ); + + $request = $smcFunc['db_quote'](' + CREATE TABLE {raw:backup_table} {raw:create}', + array( + 'backup_table' => $backup_table, + 'create' => $create, + )); + + $smcFunc['db_query']('', ' + CREATE TABLE {raw:backup_table} {raw:create}', + array( + 'backup_table' => $backup_table, + 'create' => $create, + )); + + $request = $smcFunc['db_query']('', ' + INSERT INTO {raw:backup_table} + SELECT * + FROM {raw:table}', + array( + 'backup_table' => $backup_table, + 'table' => $table, + )); + + return $request; +} + +// Optimize a table - return data freed! +function smf_db_optimize_table($table) +{ + global $smcFunc, $db_prefix; + + $table = str_replace('{db_prefix}', $db_prefix, $table); + + $request = $smcFunc['db_query']('', ' + VACUUM {raw:table}', + array( + 'table' => $table, + ) + ); + if (!$request) + return -1; + + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // The function returns nothing. + return 0; +} + +// List all the tables in the database. +function smf_db_list_tables($db = false, $filter = false) +{ + global $smcFunc; + + $filter = $filter == false ? '' : ' AND name LIKE \'' . str_replace("\_", "_", $filter) . '\''; + + $request = $smcFunc['db_query']('', ' + SELECT name + FROM sqlite_master + WHERE type = {string:type} + {raw:filter} + ORDER BY name', + array( + 'type' => 'table', + 'filter' => $filter, + ) + ); + $tables = array(); + while ($row = $smcFunc['db_fetch_row']($request)) + $tables[] = $row[0]; + $smcFunc['db_free_result']($request); + + return $tables; +} + +// Get the content (INSERTs) for a table. +function smf_db_insert_sql($tableName) +{ + global $smcFunc, $db_prefix; + + $tableName = str_replace('{db_prefix}', $db_prefix, $tableName); + + // This will be handy... + $crlf = "\r\n"; + + // Get everything from the table. + $result = $smcFunc['db_query']('', ' + SELECT * + FROM {raw:table}', + array( + 'table' => $tableName, + ) + ); + + // The number of rows, just for record keeping and breaking INSERTs up. + $num_rows = $smcFunc['db_num_rows']($result); + + if ($num_rows == 0) + return ''; + + $fields = array_keys($smcFunc['db_fetch_assoc']($result)); + + // SQLite fetches an array so we need to filter out the numberic index for the columns. + foreach ($fields as $key => $name) + if (is_numeric($name)) + unset($fields[$key]); + + $smcFunc['db_data_seek']($result, 0); + + // Start it off with the basic INSERT INTO. + $data = 'BEGIN TRANSACTION;' . $crlf; + + // Loop through each row. + while ($row = $smcFunc['db_fetch_row']($result)) + { + // Get the fields in this row... + $field_list = array(); + for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++) + { + // Try to figure out the type of each field. (NULL, number, or 'string'.) + if (!isset($row[$j])) + $field_list[] = 'NULL'; + elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j]) + $field_list[] = $row[$j]; + else + $field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\''; + } + $data .= 'INSERT INTO ' . $tableName . ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $field_list) . ');' . $crlf; + } + $smcFunc['db_free_result']($result); + + // Return an empty string if there were no rows. + return $num_rows == 0 ? '' : $data . 'COMMIT;' . $crlf; +} + +// Get the schema (CREATE) for a table. +function smf_db_table_sql($tableName) +{ + global $smcFunc, $db_prefix; + + $tableName = str_replace('{db_prefix}', $db_prefix, $tableName); + + // This will be needed... + $crlf = "\r\n"; + + // Start the create table... + $schema_create = ''; + $index_create = ''; + + // Let's get the create statement directly from SQLite. + $result = $smcFunc['db_query']('', ' + SELECT sql + FROM sqlite_master + WHERE type = {string:type} + AND name = {string:table_name}', + array( + 'type' => 'table', + 'table_name' => $tableName, + ) + ); + list ($schema_create) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Now the indexes. + $result = $smcFunc['db_query']('', ' + SELECT sql + FROM sqlite_master + WHERE type = {string:type} + AND tbl_name = {string:table_name}', + array( + 'type' => 'index', + 'table_name' => $tableName, + ) + ); + $indexes = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + if (trim($row['sql']) != '') + $indexes[] = $row['sql']; + $smcFunc['db_free_result']($result); + + $index_create .= implode(';' . $crlf, $indexes); + $schema_create = empty($indexes) ? rtrim($schema_create) : $schema_create . ';' . $crlf . $crlf; + + return $schema_create . $index_create; +} + +// Get the version number. +function smf_db_get_version() +{ + return sqlite_libversion(); +} + +// Simple return the database - and die! +function smf_db_get_backup() +{ + global $db_name; + + $db_file = substr($db_name, -3) === '.db' ? $db_name : $db_name . '.db'; + + // Add more info if zipped... + $ext = ''; + if (isset($_REQUEST['compress']) && function_exists('gzencode')) + $ext = '.gz'; + + // Do the remaining headers. + header('Content-Disposition: attachment; filename="' . $db_file . $ext . '"'); + header('Cache-Control: private'); + header('Connection: close'); + + // Literally dump the contents. Try reading the file first. + if (@readfile($db_file) == null) + echo file_get_contents($db_file); + + obExit(false); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbPackages-mysql.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DbPackages-mysql.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,582 @@ + Size of column (If applicable) - for example 255 for a large varchar, 10 for an int etc. If not + set SMF will pick a size. + + 'default' = Default value - do not set if no default required. + + 'null' => Can it be null (true or false) - if not set default will be false. + + 'auto' => Set to true to make it an auto incrementing column. Set to a numerical value to set + from what it should begin counting. + - Adds indexes as specified within indexes parameter. Each index should be a member of $indexes. Values are: + + 'name' => Index name (If left empty SMF will generate). + + 'type' => Type of index. Choose from 'primary', 'unique' or 'index'. If not set will default to 'index'. + + 'columns' => Array containing columns that form part of key - in the order the index is to be created. + - parameters: (None yet) + - if_exists values: + + 'ignore' will do nothing if the table exists. (And will return true) + + 'overwrite' will drop any existing table of the same name. + + 'error' will return false if the table already exists. + +*/ + +// Add the file functions to the $smcFunc array. +function db_packages_init() +{ + global $smcFunc, $reservedTables, $db_package_log, $db_prefix; + + if (!isset($smcFunc['db_create_table']) || $smcFunc['db_create_table'] != 'smf_db_create_table') + { + $smcFunc += array( + 'db_add_column' => 'smf_db_add_column', + 'db_add_index' => 'smf_db_add_index', + 'db_calculate_type' => 'smf_db_calculate_type', + 'db_change_column' => 'smf_db_change_column', + 'db_create_table' => 'smf_db_create_table', + 'db_drop_table' => 'smf_db_drop_table', + 'db_table_structure' => 'smf_db_table_structure', + 'db_list_columns' => 'smf_db_list_columns', + 'db_list_indexes' => 'smf_db_list_indexes', + 'db_remove_column' => 'smf_db_remove_column', + 'db_remove_index' => 'smf_db_remove_index', + ); + $db_package_log = array(); + } + + // We setup an array of SMF tables we can't do auto-remove on - in case a mod writer cocks it up! + $reservedTables = array('admin_info_files', 'approval_queue', 'attachments', 'ban_groups', 'ban_items', + 'board_permissions', 'boards', 'calendar', 'calendar_holidays', 'categories', 'collapsed_categories', + 'custom_fields', 'group_moderators', 'log_actions', 'log_activity', 'log_banned', 'log_boards', + 'log_digest', 'log_errors', 'log_floodcontrol', 'log_group_requests', 'log_karma', 'log_mark_read', + 'log_notify', 'log_online', 'log_packages', 'log_polls', 'log_reported', 'log_reported_comments', + 'log_scheduled_tasks', 'log_search_messages', 'log_search_results', 'log_search_subjects', + 'log_search_topics', 'log_topics', 'mail_queue', 'membergroups', 'members', 'message_icons', + 'messages', 'moderators', 'package_servers', 'permission_profiles', 'permissions', 'personal_messages', + 'pm_recipients', 'poll_choices', 'polls', 'scheduled_tasks', 'sessions', 'settings', 'smileys', + 'themes', 'topics'); + foreach ($reservedTables as $k => $table_name) + $reservedTables[$k] = strtolower($db_prefix . $table_name); + + // We in turn may need the extra stuff. + db_extend('extra'); +} + +// Create a table. +function smf_db_create_table($table_name, $columns, $indexes = array(), $parameters = array(), $if_exists = 'ignore', $error = 'fatal') +{ + global $reservedTables, $smcFunc, $db_package_log, $db_prefix, $db_character_set; + + // Strip out the table name, we might not need it in some cases + $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; + + // With or without the database name, the fullname looks like this. + $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // First - no way do we touch SMF tables. + if (in_array(strtolower($table_name), $reservedTables)) + return false; + + // Log that we'll want to remove this on uninstall. + $db_package_log[] = array('remove_table', $table_name); + + // Slightly easier on MySQL than the others... + $tables = $smcFunc['db_list_tables'](); + if (in_array($full_table_name, $tables)) + { + // This is a sad day... drop the table? If not, return false (error) by default. + if ($if_exists == 'overwrite') + $smcFunc['db_drop_table']($table_name); + else + return $if_exists == 'ignore'; + } + + // Righty - let's do the damn thing! + $table_query = 'CREATE TABLE ' . $table_name . "\n" . '('; + foreach ($columns as $column) + { + // Auto increment is easy here! + if (!empty($column['auto'])) + { + $default = 'auto_increment'; + } + elseif (isset($column['default']) && $column['default'] !== null) + $default = 'default \'' . $smcFunc['db_escape_string']($column['default']) . '\''; + else + $default = ''; + + // Sort out the size... and stuff... + $column['size'] = isset($column['size']) && is_numeric($column['size']) ? $column['size'] : null; + list ($type, $size) = $smcFunc['db_calculate_type']($column['type'], $column['size']); + + // Allow unsigned integers (mysql only) + $unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint')) && !empty($column['unsigned']) ? 'unsigned ' : ''; + + if ($size !== null) + $type = $type . '(' . $size . ')'; + + // Now just put it together! + $table_query .= "\n\t`" .$column['name'] . '` ' . $type . ' ' . (!empty($unsigned) ? $unsigned : '') . (!empty($column['null']) ? '' : 'NOT NULL') . ' ' . $default . ','; + } + + // Loop through the indexes next... + foreach ($indexes as $index) + { + $columns = implode(',', $index['columns']); + + // Is it the primary? + if (isset($index['type']) && $index['type'] == 'primary') + $table_query .= "\n\t" . 'PRIMARY KEY (' . implode(',', $index['columns']) . '),'; + else + { + if (empty($index['name'])) + $index['name'] = implode('_', $index['columns']); + $table_query .= "\n\t" . (isset($index['type']) && $index['type'] == 'unique' ? 'UNIQUE' : 'KEY') . ' ' . $index['name'] . ' (' . $columns . '),'; + } + } + + // No trailing commas! + if (substr($table_query, -1) == ',') + $table_query = substr($table_query, 0, -1); + + $table_query .= ') ENGINE=MyISAM'; + if (!empty($db_character_set) && $db_character_set == 'utf8') + $table_query .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci'; + + // Create the table! + $smcFunc['db_query']('', $table_query, + array( + 'security_override' => true, + ) + ); +} + +// Drop a table. +function smf_db_drop_table($table_name, $parameters = array(), $error = 'fatal') +{ + global $reservedTables, $smcFunc, $db_prefix; + + // After stripping away the database name, this is what's left. + $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; + + // Get some aliases. + $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // God no - dropping one of these = bad. + if (in_array(strtolower($table_name), $reservedTables)) + return false; + + // Does it exist? + if (in_array($full_table_name, $smcFunc['db_list_tables']())) + { + $query = 'DROP TABLE ' . $table_name; + $smcFunc['db_query']('', + $query, + array( + 'security_override' => true, + ) + ); + + return true; + } + + // Otherwise do 'nout. + return false; +} + +// Add a column. +function smf_db_add_column($table_name, $column_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') +{ + global $smcFunc, $db_package_log, $txt, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Log that we will want to uninstall this! + $db_package_log[] = array('remove_column', $table_name, $column_info['name']); + + // Does it exist - if so don't add it again! + $columns = $smcFunc['db_list_columns']($table_name, false); + foreach ($columns as $column) + if ($column == $column_info['name']) + { + // If we're going to overwrite then use change column. + if ($if_exists == 'update') + return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info); + else + return false; + } + + // Get the specifics... + $column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null; + list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']); + + // Allow unsigned integers (mysql only) + $unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint')) && !empty($column_info['unsigned']) ? 'unsigned ' : ''; + + if ($size !== null) + $type = $type . '(' . $size . ')'; + + // Now add the thing! + $query = ' + ALTER TABLE ' . $table_name . ' + ADD `' . $column_info['name'] . '` ' . $type . ' ' . (!empty($unsigned) ? $unsigned : '') . (empty($column_info['null']) ? 'NOT NULL' : '') . ' ' . + (!isset($column_info['default']) ? '' : 'default \'' . $smcFunc['db_escape_string']($column_info['default']) . '\'') . ' ' . + (empty($column_info['auto']) ? '' : 'auto_increment primary key') . ' '; + $smcFunc['db_query']('', $query, + array( + 'security_override' => true, + ) + ); + + return true; +} + +// Remove a column. +function smf_db_remove_column($table_name, $column_name, $parameters = array(), $error = 'fatal') +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Does it exist? + $columns = $smcFunc['db_list_columns']($table_name, true); + foreach ($columns as $column) + if ($column['name'] == $column_name) + { + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + DROP COLUMN ' . $column_name, + array( + 'security_override' => true, + ) + ); + + return true; + } + + // If here we didn't have to work - joy! + return false; +} + +// Change a column. +function smf_db_change_column($table_name, $old_column, $column_info, $parameters = array(), $error = 'fatal') +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Check it does exist! + $columns = $smcFunc['db_list_columns']($table_name, true); + $old_info = null; + foreach ($columns as $column) + if ($column['name'] == $old_column) + $old_info = $column; + + // Nothing? + if ($old_info == null) + return false; + + // Get the right bits. + if (!isset($column_info['name'])) + $column_info['name'] = $old_column; + if (!isset($column_info['default'])) + $column_info['default'] = $old_info['default']; + if (!isset($column_info['null'])) + $column_info['null'] = $old_info['null']; + if (!isset($column_info['auto'])) + $column_info['auto'] = $old_info['auto']; + if (!isset($column_info['type'])) + $column_info['type'] = $old_info['type']; + if (!isset($column_info['size']) || !is_numeric($column_info['size'])) + $column_info['size'] = $old_info['size']; + if (!isset($column_info['unsigned']) || !in_array($column_info['type'], array('int', 'tinyint', 'smallint', 'mediumint', 'bigint'))) + $column_info['unsigned'] = ''; + + list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']); + + // Allow for unsigned integers (mysql only) + $unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint')) && !empty($column_info['unsigned']) ? 'unsigned ' : ''; + + if ($size !== null) + $type = $type . '(' . $size . ')'; + + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + CHANGE COLUMN `' . $old_column . '` `' . $column_info['name'] . '` ' . $type . ' ' . (!empty($unsigned) ? $unsigned : '') . (empty($column_info['null']) ? 'NOT NULL' : '') . ' ' . + (!isset($column_info['default']) ? '' : 'default \'' . $smcFunc['db_escape_string']($column_info['default']) . '\'') . ' ' . + (empty($column_info['auto']) ? '' : 'auto_increment') . ' ', + array( + 'security_override' => true, + ) + ); +} + +// Add an index. +function smf_db_add_index($table_name, $index_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') +{ + global $smcFunc, $db_package_log, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // No columns = no index. + if (empty($index_info['columns'])) + return false; + $columns = implode(',', $index_info['columns']); + + // No name - make it up! + if (empty($index_info['name'])) + { + // No need for primary. + if (isset($index_info['type']) && $index_info['type'] == 'primary') + $index_info['name'] = ''; + else + $index_info['name'] = implode('_', $index_info['columns']); + } + else + $index_info['name'] = $index_info['name']; + + // Log that we are going to want to remove this! + $db_package_log[] = array('remove_index', $table_name, $index_info['name']); + + // Let's get all our indexes. + $indexes = $smcFunc['db_list_indexes']($table_name, true); + // Do we already have it? + foreach ($indexes as $index) + { + if ($index['name'] == $index_info['name'] || ($index['type'] == 'primary' && isset($index_info['type']) && $index_info['type'] == 'primary')) + { + // If we want to overwrite simply remove the current one then continue. + if ($if_exists != 'update' || $index['type'] == 'primary') + return false; + else + $smcFunc['db_remove_index']($table_name, $index_info['name']); + } + } + + // If we're here we know we don't have the index - so just add it. + if (!empty($index_info['type']) && $index_info['type'] == 'primary') + { + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + ADD PRIMARY KEY (' . $columns . ')', + array( + 'security_override' => true, + ) + ); + } + else + { + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + ADD ' . (isset($index_info['type']) && $index_info['type'] == 'unique' ? 'UNIQUE' : 'INDEX') . ' ' . $index_info['name'] . ' (' . $columns . ')', + array( + 'security_override' => true, + ) + ); + } +} + +// Remove an index. +function smf_db_remove_index($table_name, $index_name, $parameters = array(), $error = 'fatal') +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Better exist! + $indexes = $smcFunc['db_list_indexes']($table_name, true); + + foreach ($indexes as $index) + { + // If the name is primary we want the primary key! + if ($index['type'] == 'primary' && $index_name == 'primary') + { + // Dropping primary key? + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + DROP PRIMARY KEY', + array( + 'security_override' => true, + ) + ); + + return true; + } + if ($index['name'] == $index_name) + { + // Drop the bugger... + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + DROP INDEX ' . $index_name, + array( + 'security_override' => true, + ) + ); + + return true; + } + } + + // Not to be found ;( + return false; +} + +// Get the schema formatted name for a type. +function smf_db_calculate_type($type_name, $type_size = null, $reverse = false) +{ + // MySQL is actually the generic baseline. + return array($type_name, $type_size); +} + +// Get table structure. +function smf_db_table_structure($table_name, $parameters = array()) +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + return array( + 'name' => $table_name, + 'columns' => $smcFunc['db_list_columns']($table_name, true), + 'indexes' => $smcFunc['db_list_indexes']($table_name, true), + ); +} + +// Return column information for a table. +function smf_db_list_columns($table_name, $detail = false, $parameters = array()) +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + $result = $smcFunc['db_query']('', ' + SHOW FIELDS + FROM {raw:table_name}', + array( + 'table_name' => substr($table_name, 0, 1) == '`' ? $table_name : '`' . $table_name . '`', + ) + ); + $columns = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (!$detail) + { + $columns[] = $row['Field']; + } + else + { + // Is there an auto_increment? + $auto = strpos($row['Extra'], 'auto_increment') !== false ? true : false; + + // Can we split out the size? + if (preg_match('~(.+?)\s*\((\d+)\)(?:(?:\s*)?(unsigned))?~i', $row['Type'], $matches) === 1) + { + $type = $matches[1]; + $size = $matches[2]; + if (!empty($matches[3]) && $matches[3] == 'unsigned') + $unsigned = true; + } + else + { + $type = $row['Type']; + $size = null; + } + + $columns[$row['Field']] = array( + 'name' => $row['Field'], + 'null' => $row['Null'] != 'YES' ? false : true, + 'default' => isset($row['Default']) ? $row['Default'] : null, + 'type' => $type, + 'size' => $size, + 'auto' => $auto, + ); + + if (isset($unsigned)) + { + $columns[$row['Field']]['unsigned'] = $unsigned; + unset($unsigned); + } + } + } + $smcFunc['db_free_result']($result); + + return $columns; +} + +// What about some index information? +function smf_db_list_indexes($table_name, $detail = false, $parameters = array()) +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + $result = $smcFunc['db_query']('', ' + SHOW KEYS + FROM {raw:table_name}', + array( + 'table_name' => substr($table_name, 0, 1) == '`' ? $table_name : '`' . $table_name . '`', + ) + ); + $indexes = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (!$detail) + $indexes[] = $row['Key_name']; + else + { + // What is the type? + if ($row['Key_name'] == 'PRIMARY') + $type = 'primary'; + elseif (empty($row['Non_unique'])) + $type = 'unique'; + elseif (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT') + $type = 'fulltext'; + else + $type = 'index'; + + // This is the first column we've seen? + if (empty($indexes[$row['Key_name']])) + { + $indexes[$row['Key_name']] = array( + 'name' => $row['Key_name'], + 'type' => $type, + 'columns' => array(), + ); + } + + // Is it a partial index? + if (!empty($row['Sub_part'])) + $indexes[$row['Key_name']]['columns'][] = $row['Column_name'] . '(' . $row['Sub_part'] . ')'; + else + $indexes[$row['Key_name']]['columns'][] = $row['Column_name']; + } + } + $smcFunc['db_free_result']($result); + + return $indexes; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbPackages-postgresql.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DbPackages-postgresql.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,746 @@ + Size of column (If applicable) - for example 255 for a large varchar, 10 for an int etc. If not + set SMF will pick a size. + + 'default' = Default value - do not set if no default required. + + 'null' => Can it be null (true or false) - if not set default will be false. + + 'auto' => Set to true to make it an auto incrementing column. Set to a numerical value to set + from what it should begin counting. + - Adds indexes as specified within indexes parameter. Each index should be a member of $indexes. Values are: + + 'name' => Index name (If left empty SMF will generate). + + 'type' => Type of index. Choose from 'primary', 'unique' or 'index'. If not set will default to 'index'. + + 'columns' => Array containing columns that form part of key - in the order the index is to be created. + - parameters: (None yet) + - if_exists values: + + 'ignore' will do nothing if the table exists. (And will return true) + + 'overwrite' will drop any existing table of the same name. + + 'error' will return false if the table already exists. + +*/ + +// Add the file functions to the $smcFunc array. +function db_packages_init() +{ + global $smcFunc, $reservedTables, $db_package_log, $db_prefix; + + if (!isset($smcFunc['db_create_table']) || $smcFunc['db_create_table'] != 'smf_db_create_table') + { + $smcFunc += array( + 'db_add_column' => 'smf_db_add_column', + 'db_add_index' => 'smf_db_add_index', + 'db_calculate_type' => 'smf_db_calculate_type', + 'db_change_column' => 'smf_db_change_column', + 'db_create_table' => 'smf_db_create_table', + 'db_drop_table' => 'smf_db_drop_table', + 'db_table_structure' => 'smf_db_table_structure', + 'db_list_columns' => 'smf_db_list_columns', + 'db_list_indexes' => 'smf_db_list_indexes', + 'db_remove_column' => 'smf_db_remove_column', + 'db_remove_index' => 'smf_db_remove_index', + ); + $db_package_log = array(); + } + + // We setup an array of SMF tables we can't do auto-remove on - in case a mod writer cocks it up! + $reservedTables = array('admin_info_files', 'approval_queue', 'attachments', 'ban_groups', 'ban_items', + 'board_permissions', 'boards', 'calendar', 'calendar_holidays', 'categories', 'collapsed_categories', + 'custom_fields', 'group_moderators', 'log_actions', 'log_activity', 'log_banned', 'log_boards', + 'log_digest', 'log_errors', 'log_floodcontrol', 'log_group_requests', 'log_karma', 'log_mark_read', + 'log_notify', 'log_online', 'log_packages', 'log_polls', 'log_reported', 'log_reported_comments', + 'log_scheduled_tasks', 'log_search_messages', 'log_search_results', 'log_search_subjects', + 'log_search_topics', 'log_topics', 'mail_queue', 'membergroups', 'members', 'message_icons', + 'messages', 'moderators', 'package_servers', 'permission_profiles', 'permissions', 'personal_messages', + 'pm_recipients', 'poll_choices', 'polls', 'scheduled_tasks', 'sessions', 'settings', 'smileys', + 'themes', 'topics'); + foreach ($reservedTables as $k => $table_name) + $reservedTables[$k] = strtolower($db_prefix . $table_name); + + // We in turn may need the extra stuff. + db_extend('extra'); +} + +// Create a table. +function smf_db_create_table($table_name, $columns, $indexes = array(), $parameters = array(), $if_exists = 'ignore', $error = 'fatal') +{ + global $reservedTables, $smcFunc, $db_package_log, $db_prefix; + + // Strip out the table name, we might not need it in some cases + $real_prefix = preg_match('~^("?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; + + // With or without the database name, the fullname looks like this. + $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // First - no way do we touch SMF tables. + if (in_array(strtolower($table_name), $reservedTables)) + return false; + + // Log that we'll want to remove this on uninstall. + $db_package_log[] = array('remove_table', $table_name); + + // This... my friends... is a function in a half - let's start by checking if the table exists! + $tables = $smcFunc['db_list_tables'](); + if (in_array($full_table_name, $tables)) + { + // This is a sad day... drop the table? If not, return false (error) by default. + if ($if_exists == 'overwrite') + $smcFunc['db_drop_table']($table_name); + else + return $if_exists == 'ignore'; + } + + // If we've got this far - good news - no table exists. We can build our own! + $smcFunc['db_transaction']('begin'); + $table_query = 'CREATE TABLE ' . $table_name . "\n" . '('; + foreach ($columns as $column) + { + // If we have an auto increment do it! + if (!empty($column['auto'])) + { + $smcFunc['db_query']('', ' + CREATE SEQUENCE ' . $table_name . '_seq', + array( + 'security_override' => true, + ) + ); + $default = 'default nextval(\'' . $table_name . '_seq\')'; + } + elseif (isset($column['default']) && $column['default'] !== null) + $default = 'default \'' . $smcFunc['db_escape_string']($column['default']) . '\''; + else + $default = ''; + + // Sort out the size... + $column['size'] = isset($column['size']) && is_numeric($column['size']) ? $column['size'] : null; + list ($type, $size) = $smcFunc['db_calculate_type']($column['type'], $column['size']); + if ($size !== null) + $type = $type . '(' . $size . ')'; + + // Now just put it together! + $table_query .= "\n\t\"" . $column['name'] . '" ' . $type . ' ' . (!empty($column['null']) ? '' : 'NOT NULL') . ' ' . $default . ','; + } + + // Loop through the indexes a sec... + $index_queries = array(); + foreach ($indexes as $index) + { + $columns = implode(',', $index['columns']); + + // Primary goes in the table... + if (isset($index['type']) && $index['type'] == 'primary') + $table_query .= "\n\t" . 'PRIMARY KEY (' . implode(',', $index['columns']) . '),'; + else + { + if (empty($index['name'])) + $index['name'] = implode('_', $index['columns']); + $index_queries[] = 'CREATE ' . (isset($index['type']) && $index['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $table_name . '_' . $index['name'] . ' ON ' . $table_name . ' (' . $columns . ')'; + } + } + + // No trailing commas! + if (substr($table_query, -1) == ',') + $table_query = substr($table_query, 0, -1); + + $table_query .= ')'; + + // Create the table! + $smcFunc['db_query']('', $table_query, + array( + 'security_override' => true, + ) + ); + // And the indexes... + foreach ($index_queries as $query) + $smcFunc['db_query']('', $query, + array( + 'security_override' => true, + ) + ); + + // Go, go power rangers! + $smcFunc['db_transaction']('commit'); +} + +// Drop a table. +function smf_db_drop_table($table_name, $parameters = array(), $error = 'fatal') +{ + global $reservedTables, $smcFunc, $db_prefix; + + // After stripping away the database name, this is what's left. + $real_prefix = preg_match('~^("?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; + + // Get some aliases. + $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // God no - dropping one of these = bad. + if (in_array(strtolower($table_name), $reservedTables)) + return false; + + // Does it exist? + if (in_array($full_table_name, $smcFunc['db_list_tables']())) + { + // We can then drop the table. + $smcFunc['db_transaction']('begin'); + + // the table + $table_query = 'DROP TABLE ' . $table_name; + + // and the assosciated sequence, if any + $sequence_query = 'DROP SEQUENCE IF EXISTS ' . $table_name . '_seq'; + + // drop them + $smcFunc['db_query']('', + $table_query, + array( + 'security_override' => true, + ) + ); + $smcFunc['db_query']('', + $sequence_query, + array( + 'security_override' => true, + ) + ); + + $smcFunc['db_transaction']('commit'); + + return true; + } + + // Otherwise do 'nout. + return false; +} + +// Add a column. +function smf_db_add_column($table_name, $column_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') +{ + global $smcFunc, $db_package_log, $txt, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Log that we will want to uninstall this! + $db_package_log[] = array('remove_column', $table_name, $column_info['name']); + + // Does it exist - if so don't add it again! + $columns = $smcFunc['db_list_columns']($table_name, false); + foreach ($columns as $column) + if ($column == $column_info['name']) + { + // If we're going to overwrite then use change column. + if ($if_exists == 'update') + return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info); + else + return false; + } + + // Get the specifics... + $column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null; + list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']); + if ($size !== null) + $type = $type . '(' . $size . ')'; + + // Now add the thing! + $query = ' + ALTER TABLE ' . $table_name . ' + ADD COLUMN ' . $column_info['name'] . ' ' . $type; + $smcFunc['db_query']('', $query, + array( + 'security_override' => true, + ) + ); + + // If there's more attributes they need to be done via a change on PostgreSQL. + unset($column_info['type'], $column_info['size']); + + if (count($column_info) != 1) + return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info); + else + return true; +} + +// Remove a column. +function smf_db_remove_column($table_name, $column_name, $parameters = array(), $error = 'fatal') +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Does it exist? + $columns = $smcFunc['db_list_columns']($table_name, true); + foreach ($columns as $column) + if ($column['name'] == $column_name) + { + // If there is an auto we need remove it! + if ($column['auto']) + $smcFunc['db_query']('', + 'DROP SEQUENCE ' . $table_name . '_seq', + array( + 'security_override' => true, + ) + ); + + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + DROP COLUMN ' . $column_name, + array( + 'security_override' => true, + ) + ); + + return true; + } + + // If here we didn't have to work - joy! + return false; +} + +// Change a column. +function smf_db_change_column($table_name, $old_column, $column_info, $parameters = array(), $error = 'fatal') +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Check it does exist! + $columns = $smcFunc['db_list_columns']($table_name, true); + $old_info = null; + foreach ($columns as $column) + if ($column['name'] == $old_column) + $old_info = $column; + + // Nothing? + if ($old_info == null) + return false; + + // Now we check each bit individually and ALTER as required. + if (isset($column_info['name']) && $column_info['name'] != $old_column) + { + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + RENAME COLUMN ' . $old_column . ' TO ' . $column_info['name'], + array( + 'security_override' => true, + ) + ); + } + // Different default? + if (isset($column_info['default']) && $column_info['default'] != $old_info['default']) + { + $action = $column_info['default'] !== null ? 'SET DEFAULT \'' . $smcFunc['db_escape_string']($column_info['default']) . '\'' : 'DROP DEFAULT'; + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + ALTER COLUMN ' . $column_info['name'] . ' ' . $action, + array( + 'security_override' => true, + ) + ); + } + // Is it null - or otherwise? + if (isset($column_info['null']) && $column_info['null'] != $old_info['null']) + { + $action = $column_info['null'] ? 'DROP' : 'SET'; + $smcFunc['db_transaction']('begin'); + if (!$column_info['null']) + { + // We have to set it to something if we are making it NOT NULL. + $setTo = isset($column_info['default']) ? $column_info['default'] : ''; + $smcFunc['db_query']('', ' + UPDATE ' . $table_name . ' + SET ' . $column_info['name'] . ' = \'' . $setTo . '\' + WHERE ' . $column_info['name'] . ' = NULL', + array( + 'security_override' => true, + ) + ); + } + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + ALTER COLUMN ' . $column_info['name'] . ' ' . $action . ' NOT NULL', + array( + 'security_override' => true, + ) + ); + $smcFunc['db_transaction']('commit'); + } + // What about a change in type? + if (isset($column_info['type']) && ($column_info['type'] != $old_info['type'] || (isset($column_info['size']) && $column_info['size'] != $old_info['size']))) + { + $column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null; + list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']); + if ($size !== null) + $type = $type . '(' . $size . ')'; + + // The alter is a pain. + $smcFunc['db_transaction']('begin'); + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + ADD COLUMN ' . $column_info['name'] . '_tempxx ' . $type, + array( + 'security_override' => true, + ) + ); + $smcFunc['db_query']('', ' + UPDATE ' . $table_name . ' + SET ' . $column_info['name'] . '_tempxx = CAST(' . $column_info['name'] . ' AS ' . $type . ')', + array( + 'security_override' => true, + ) + ); + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + DROP COLUMN ' . $column_info['name'], + array( + 'security_override' => true, + ) + ); + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + RENAME COLUMN ' . $column_info['name'] . '_tempxx TO ' . $column_info['name'], + array( + 'security_override' => true, + ) + ); + $smcFunc['db_transaction']('commit'); + } + // Finally - auto increment?! + if (isset($column_info['auto']) && $column_info['auto'] != $old_info['auto']) + { + // Are we removing an old one? + if ($old_info['auto']) + { + // Alter the table first - then drop the sequence. + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + ALTER COLUMN ' . $column_info['name'] . ' SET DEFAULT \'0\'', + array( + 'security_override' => true, + ) + ); + $smcFunc['db_query']('', ' + DROP SEQUENCE ' . $table_name . '_seq', + array( + 'security_override' => true, + ) + ); + } + // Otherwise add it! + else + { + $smcFunc['db_query']('', ' + CREATE SEQUENCE ' . $table_name . '_seq', + array( + 'security_override' => true, + ) + ); + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + ALTER COLUMN ' . $column_info['name'] . ' SET DEFAULT nextval(\'' . $table_name . '_seq\')', + array( + 'security_override' => true, + ) + ); + } + } +} + +// Add an index. +function smf_db_add_index($table_name, $index_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') +{ + global $smcFunc, $db_package_log, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // No columns = no index. + if (empty($index_info['columns'])) + return false; + $columns = implode(',', $index_info['columns']); + + // No name - make it up! + if (empty($index_info['name'])) + { + // No need for primary. + if (isset($index_info['type']) && $index_info['type'] == 'primary') + $index_info['name'] = ''; + else + $index_info['name'] = $table_name . implode('_', $index_info['columns']); + } + else + $index_info['name'] = $table_name . $index_info['name']; + + // Log that we are going to want to remove this! + $db_package_log[] = array('remove_index', $table_name, $index_info['name']); + + // Let's get all our indexes. + $indexes = $smcFunc['db_list_indexes']($table_name, true); + // Do we already have it? + foreach ($indexes as $index) + { + if ($index['name'] == $index_info['name'] || ($index['type'] == 'primary' && isset($index_info['type']) && $index_info['type'] == 'primary')) + { + // If we want to overwrite simply remove the current one then continue. + if ($if_exists != 'update' || $index['type'] == 'primary') + return false; + else + $smcFunc['db_remove_index']($table_name, $index_info['name']); + } + } + + // If we're here we know we don't have the index - so just add it. + if (!empty($index_info['type']) && $index_info['type'] == 'primary') + { + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + ADD PRIMARY KEY (' . $columns . ')', + array( + 'security_override' => true, + ) + ); + } + else + { + $smcFunc['db_query']('', ' + CREATE ' . (isset($index_info['type']) && $index_info['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $index_info['name'] . ' ON ' . $table_name . ' (' . $columns . ')', + array( + 'security_override' => true, + ) + ); + } +} + +// Remove an index. +function smf_db_remove_index($table_name, $index_name, $parameters = array(), $error = 'fatal') +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Better exist! + $indexes = $smcFunc['db_list_indexes']($table_name, true); + if ($index_name != 'primary') + $index_name = $table_name . '_' . $index_name; + + foreach ($indexes as $index) + { + // If the name is primary we want the primary key! + if ($index['type'] == 'primary' && $index_name == 'primary') + { + // Dropping primary key is odd... + $smcFunc['db_query']('', ' + ALTER TABLE ' . $table_name . ' + DROP CONSTRAINT ' . $index['name'], + array( + 'security_override' => true, + ) + ); + + return true; + } + if ($index['name'] == $index_name) + { + // Drop the bugger... + $smcFunc['db_query']('', ' + DROP INDEX ' . $index_name, + array( + 'security_override' => true, + ) + ); + + return true; + } + } + + // Not to be found ;( + return false; +} + +// Get the schema formatted name for a type. +function smf_db_calculate_type($type_name, $type_size = null, $reverse = false) +{ + // Generic => Specific. + if (!$reverse) + { + $types = array( + 'varchar' => 'character varying', + 'char' => 'character', + 'mediumint' => 'int', + 'tinyint' => 'smallint', + 'tinytext' => 'character varying', + 'mediumtext' => 'text', + 'largetext' => 'text', + ); + } + else + { + $types = array( + 'character varying' => 'varchar', + 'character' => 'char', + 'integer' => 'int', + ); + } + + // Got it? Change it! + if (isset($types[$type_name])) + { + if ($type_name == 'tinytext') + $type_size = 255; + $type_name = $types[$type_name]; + } + // Numbers don't have a size. + if (strpos($type_name, 'int') !== false) + $type_size = null; + + return array($type_name, $type_size); +} + +// Get table structure. +function smf_db_table_structure($table_name, $parameters = array()) +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + return array( + 'name' => $table_name, + 'columns' => $smcFunc['db_list_columns']($table_name, true), + 'indexes' => $smcFunc['db_list_indexes']($table_name, true), + ); +} + +// Return column information for a table. +function smf_db_list_columns($table_name, $detail = false, $parameters = array()) +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + $result = $smcFunc['db_query']('', ' + SELECT column_name, column_default, is_nullable, data_type, character_maximum_length + FROM information_schema.columns + WHERE table_name = \'' . $table_name . '\' + ORDER BY ordinal_position', + array( + 'security_override' => true, + ) + ); + $columns = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (!$detail) + { + $columns[] = $row['column_name']; + } + else + { + $auto = false; + // What is the default? + if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0) + { + $default = null; + $auto = true; + } + elseif (trim($row['column_default']) != '') + $default = strpos($row['column_default'], '::') === false ? $row['column_default'] : substr($row['column_default'], 0, strpos($row['column_default'], '::')); + else + $default = null; + + // Make the type generic. + list ($type, $size) = $smcFunc['db_calculate_type']($row['data_type'], $row['character_maximum_length'], true); + + $columns[$row['column_name']] = array( + 'name' => $row['column_name'], + 'null' => $row['is_nullable'] ? true : false, + 'default' => $default, + 'type' => $type, + 'size' => $size, + 'auto' => $auto, + ); + } + } + $smcFunc['db_free_result']($result); + + return $columns; +} + +// What about some index information? +function smf_db_list_indexes($table_name, $detail = false, $parameters = array()) +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + $result = $smcFunc['db_query']('', ' + SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary, + CASE WHEN i.indisunique THEN 1 ELSE 0 END AS is_unique, + c2.relname AS name, + pg_get_indexdef(i.indexrelid) AS inddef + FROM pg_class AS c, pg_class AS c2, pg_index AS i + WHERE c.relname = \'' . $table_name . '\' + AND c.oid = i.indrelid + AND i.indexrelid = c2.oid', + array( + 'security_override' => true, + ) + ); + $indexes = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Try get the columns that make it up. + if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0) + continue; + + $columns = explode(',', $matches[1]); + + if (empty($columns)) + continue; + + foreach ($columns as $k => $v) + $columns[$k] = trim($v); + + // Fix up the name to be consistent cross databases + if (substr($row['name'], -5) == '_pkey' && $row['is_primary'] == 1) + $row['name'] = 'PRIMARY'; + else + $row['name'] = str_replace($table_name . '_', '', $row['name']); + + if (!$detail) + $indexes[] = $row['name']; + else + { + $indexes[$row['name']] = array( + 'name' => $row['name'], + 'type' => $row['is_primary'] ? 'primary' : ($row['is_unique'] ? 'unique' : 'index'), + 'columns' => $columns, + ); + } + } + $smcFunc['db_free_result']($result); + + return $indexes; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbPackages-sqlite.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DbPackages-sqlite.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,736 @@ + Size of column (If applicable) - for example 255 for a large varchar, 10 for an int etc. If not + set SMF will pick a size. + + 'default' = Default value - do not set if no default required. + + 'null' => Can it be null (true or false) - if not set default will be false. + + 'auto' => Set to true to make it an auto incrementing column. Set to a numerical value to set + from what it should begin counting. + - Adds indexes as specified within indexes parameter. Each index should be a member of $indexes. Values are: + + 'name' => Index name (If left empty SMF will generate). + + 'type' => Type of index. Choose from 'primary', 'unique' or 'index'. If not set will default to 'index'. + + 'columns' => Array containing columns that form part of key - in the order the index is to be created. + - parameters: (None yet) + - if_exists values: + + 'ignore' will do nothing if the table exists. (And will return true) + + 'overwrite' will drop any existing table of the same name. + + 'error' will return false if the table already exists. + +*/ + +// Add the file functions to the $smcFunc array. +function db_packages_init() +{ + global $smcFunc, $reservedTables, $db_package_log, $db_prefix; + + if (!isset($smcFunc['db_create_table']) || $smcFunc['db_create_table'] != 'smf_db_create_table') + { + $smcFunc += array( + 'db_add_column' => 'smf_db_add_column', + 'db_add_index' => 'smf_db_add_index', + 'db_alter_table' => 'smf_db_alter_table', + 'db_calculate_type' => 'smf_db_calculate_type', + 'db_change_column' => 'smf_db_change_column', + 'db_create_table' => 'smf_db_create_table', + 'db_drop_table' => 'smf_db_drop_table', + 'db_table_structure' => 'smf_db_table_structure', + 'db_list_columns' => 'smf_db_list_columns', + 'db_list_indexes' => 'smf_db_list_indexes', + 'db_remove_column' => 'smf_db_remove_column', + 'db_remove_index' => 'smf_db_remove_index', + ); + $db_package_log = array(); + } + + // We setup an array of SMF tables we can't do auto-remove on - in case a mod writer cocks it up! + $reservedTables = array('admin_info_files', 'approval_queue', 'attachments', 'ban_groups', 'ban_items', + 'board_permissions', 'boards', 'calendar', 'calendar_holidays', 'categories', 'collapsed_categories', + 'custom_fields', 'group_moderators', 'log_actions', 'log_activity', 'log_banned', 'log_boards', + 'log_digest', 'log_errors', 'log_floodcontrol', 'log_group_requests', 'log_karma', 'log_mark_read', + 'log_notify', 'log_online', 'log_packages', 'log_polls', 'log_reported', 'log_reported_comments', + 'log_scheduled_tasks', 'log_search_messages', 'log_search_results', 'log_search_subjects', + 'log_search_topics', 'log_topics', 'mail_queue', 'membergroups', 'members', 'message_icons', + 'messages', 'moderators', 'package_servers', 'permission_profiles', 'permissions', 'personal_messages', + 'pm_recipients', 'poll_choices', 'polls', 'scheduled_tasks', 'sessions', 'settings', 'smileys', + 'themes', 'topics'); + foreach ($reservedTables as $k => $table_name) + $reservedTables[$k] = strtolower($db_prefix . $table_name); + + // We in turn may need the extra stuff. + db_extend('extra'); +} + +// Create a table. +function smf_db_create_table($table_name, $columns, $indexes = array(), $parameters = array(), $if_exists = 'ignore', $error = 'fatal') +{ + global $reservedTables, $smcFunc, $db_package_log, $db_prefix; + + // With or without the database name, the full name looks like this. + $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; + $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // First - no way do we touch SMF tables. + // Commented out for now. We need to alter SMF tables in order to use this in the upgrade. +/* + if (in_array(strtolower($table_name), $reservedTables)) + return false; +*/ + + // Log that we'll want to remove this on uninstall. + $db_package_log[] = array('remove_table', $table_name); + + // Does this table exist or not? + $tables = $smcFunc['db_list_tables'](); + if (in_array($full_table_name, $tables)) + { + // This is a sad day... drop the table? If not, return false (error) by default. + if ($if_exists == 'overwrite') + $smcFunc['db_drop_table']($table_name); + else + return $if_exists == 'ignore'; + } + + // Righty - let's do the damn thing! + $table_query = 'CREATE TABLE ' . $table_name . "\n" . '('; + $done_primary = false; + foreach ($columns as $column) + { + // Auto increment is special + if (!empty($column['auto'])) + { + $table_query .= "\n" . $column['name'] . ' integer PRIMARY KEY,'; + $done_primary = true; + continue; + } + elseif (isset($column['default']) && $column['default'] !== null) + $default = 'default \'' . $smcFunc['db_escape_string']($column['default']) . '\''; + else + $default = ''; + + // Sort out the size... and stuff... + $column['size'] = isset($column['size']) && is_numeric($column['size']) ? $column['size'] : null; + list ($type, $size) = $smcFunc['db_calculate_type']($column['type'], $column['size']); + if ($size !== null) + $type = $type . '(' . $size . ')'; + + // Now just put it together! + $table_query .= "\n\t" . $column['name'] . ' ' . $type . ' ' . (!empty($column['null']) ? '' : 'NOT NULL') . ' ' . $default . ','; + } + + // Loop through the indexes next... + $index_queries = array(); + foreach ($indexes as $index) + { + $columns = implode(',', $index['columns']); + + // Is it the primary? + if (isset($index['type']) && $index['type'] == 'primary') + { + // If we've done the primary via auto_inc, don't do it again! + if (!$done_primary) + $table_query .= "\n\t" . 'PRIMARY KEY (' . implode(',', $index['columns']) . '),'; + } + else + { + if (empty($index['name'])) + $index['name'] = implode('_', $index['columns']); + $index_queries[] = 'CREATE ' . (isset($index['type']) && $index['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $table_name . '_' . $index['name'] . ' ON ' . $table_name . ' (' . $columns . ')'; + } + } + + // No trailing commas! + if (substr($table_query, -1) == ',') + $table_query = substr($table_query, 0, -1); + + $table_query .= ')'; + + if (empty($parameters['skip_transaction'])) + $smcFunc['db_transaction']('begin'); + + // Do the table and indexes... + $smcFunc['db_query']('', $table_query, + array( + 'security_override' => true, + ) + ); + foreach ($index_queries as $query) + $smcFunc['db_query']('', $query, + array( + 'security_override' => true, + ) + ); + + if (empty($parameters['skip_transaction'])) + $smcFunc['db_transaction']('commit'); +} + +// Drop a table. +function smf_db_drop_table($table_name, $parameters = array(), $error = 'fatal') +{ + global $reservedTables, $smcFunc, $db_prefix; + + // Strip out the table name, we might not need it in some cases + $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; + $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // God no - dropping one of these = bad. + if (in_array(strtolower($table_name), $reservedTables)) + return false; + + // Does it exist? + if (in_array($full_table_name, $smcFunc['db_list_tables']())) + { + $query = 'DROP TABLE ' . $table_name; + $smcFunc['db_query']('', $query, + array( + 'security_override' => true, + ) + ); + + return true; + } + + // Otherwise do 'nout. + return false; +} + +// Add a column. +function smf_db_add_column($table_name, $column_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') +{ + global $smcFunc, $db_package_log, $txt, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Log that we will want to uninstall this! + $db_package_log[] = array('remove_column', $table_name, $column_info['name']); + + // Does it exist - if so don't add it again! + $columns = $smcFunc['db_list_columns']($table_name, false); + foreach ($columns as $column) + if ($column == $column_info['name']) + { + // If we're going to overwrite then use change column. + if ($if_exists == 'update') + return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info); + else + return false; + } + + // Alter the table to add the column. + if ($smcFunc['db_alter_table']($table_name, array('add' => array($column_info))) === false) + return false; + + return true; +} + +// We can't reliably do this on SQLite - damn! +function smf_db_remove_column($table_name, $column_name, $parameters = array(), $error = 'fatal') +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + if ($smcFunc['db_alter_table']($table_name, array('remove' => array(array('name' => $column_name))))) + return true; + else + return false; +} + +// Change a column. +function smf_db_change_column($table_name, $old_column, $column_info, $parameters = array(), $error = 'fatal') +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + if ($smcFunc['db_alter_table']($table_name, array('change' => array(array('name' => $old_column) + $column_info)))) + return true; + else + return false; +} + +// Add an index. +function smf_db_add_index($table_name, $index_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') +{ + global $smcFunc, $db_package_log, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // No columns = no index. + if (empty($index_info['columns'])) + return false; + $columns = implode(',', $index_info['columns']); + + // No name - make it up! + if (empty($index_info['name'])) + { + // No need for primary. + if (isset($index_info['type']) && $index_info['type'] == 'primary') + $index_info['name'] = ''; + else + $index_info['name'] = implode('_', $index_info['columns']); + } + else + $index_info['name'] = $index_info['name']; + + // Log that we are going to want to remove this! + $db_package_log[] = array('remove_index', $table_name, $index_info['name']); + + // Let's get all our indexes. + $indexes = $smcFunc['db_list_indexes']($table_name, true); + // Do we already have it? + foreach ($indexes as $index) + { + if ($index['name'] == $index_info['name'] || ($index['type'] == 'primary' && isset($index_info['type']) && $index_info['type'] == 'primary')) + { + // If we want to overwrite simply remove the current one then continue. + if ($if_exists != 'update' || $index['type'] == 'primary') + return false; + else + $smcFunc['db_remove_index']($table_name, $index_info['name']); + } + } + + // If we're here we know we don't have the index - so just add it. + if (!empty($index_info['type']) && $index_info['type'] == 'primary') + { + //!!! Doesn't work with PRIMARY KEY yet. + } + else + { + $smcFunc['db_query']('', ' + CREATE ' . (isset($index_info['type']) && $index_info['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $index_info['name'] . ' ON ' . $table_name . ' (' . $columns . ')', + array( + 'security_override' => true, + ) + ); + } +} + +// Remove an index. +function smf_db_remove_index($table_name, $index_name, $parameters = array(), $error = 'fatal') +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Better exist! + $indexes = $smcFunc['db_list_indexes']($table_name, true); + + foreach ($indexes as $index) + { + //!!! Doesn't do primary key at the moment! + if ($index['type'] != 'primary' && $index['name'] == $index_name) + { + // Drop the bugger... + $smcFunc['db_query']('', ' + DROP INDEX ' . $index_name, + array( + 'security_override' => true, + ) + ); + + return true; + } + } + + // Not to be found ;( + return false; +} + +// Get the schema formatted name for a type. +function smf_db_calculate_type($type_name, $type_size = null, $reverse = false) +{ + // Generic => Specific. + if (!$reverse) + { + $types = array( + 'mediumint' => 'int', + 'tinyint' => 'smallint', + 'mediumtext' => 'text', + 'largetext' => 'text', + ); + } + else + { + $types = array( + 'integer' => 'int', + ); + } + + // Got it? Change it! + if (isset($types[$type_name])) + { + if ($type_name == 'tinytext') + $type_size = 255; + $type_name = $types[$type_name]; + } + // Numbers don't have a size. + if (strpos($type_name, 'int') !== false) + $type_size = null; + + return array($type_name, $type_size); +} + +// Get table structure. +function smf_db_table_structure($table_name, $parameters = array()) +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + return array( + 'name' => $table_name, + 'columns' => $smcFunc['db_list_columns']($table_name, true), + 'indexes' => $smcFunc['db_list_indexes']($table_name, true), + ); +} + +// Harder than it should be on sqlite! +function smf_db_list_columns($table_name, $detail = false, $parameters = array()) +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + $result = $smcFunc['db_query']('', ' + PRAGMA table_info(' . $table_name . ')', + array( + 'security_override' => true, + ) + ); + $columns = array(); + + $primaries = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (!$detail) + { + $columns[] = $row['name']; + } + else + { + // Auto increment is hard to tell really... if there's only one primary it probably is. + if ($row['pk']) + $primaries[] = $row['name']; + + // Can we split out the size? + if (preg_match('~(.+?)\s*\((\d+)\)~i', $row['type'], $matches)) + { + $type = $matches[1]; + $size = $matches[2]; + } + else + { + $type = $row['type']; + $size = null; + } + + $columns[$row['name']] = array( + 'name' => $row['name'], + 'null' => $row['notnull'] ? false : true, + 'default' => $row['dflt_value'], + 'type' => $type, + 'size' => $size, + 'auto' => false, + ); + } + } + $smcFunc['db_free_result']($result); + + // Put in our guess at auto_inc. + if (count($primaries) == 1) + $columns[$primaries[0]]['auto'] = true; + + return $columns; +} + +// What about some index information? +function smf_db_list_indexes($table_name, $detail = false, $parameters = array()) +{ + global $smcFunc, $db_prefix; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + $result = $smcFunc['db_query']('', ' + PRAGMA index_list(' . $table_name . ')', + array( + 'security_override' => true, + ) + ); + $indexes = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (!$detail) + $indexes[] = $row['name']; + else + { + $result2 = $smcFunc['db_query']('', ' + PRAGMA index_info(' . $row['name'] . ')', + array( + 'security_override' => true, + ) + ); + while ($row2 = $smcFunc['db_fetch_assoc']($result2)) + { + // What is the type? + if ($row['unique']) + $type = 'unique'; + else + $type = 'index'; + + // This is the first column we've seen? + if (empty($indexes[$row['name']])) + { + $indexes[$row['name']] = array( + 'name' => $row['name'], + 'type' => $type, + 'columns' => array(), + ); + } + + // Add the column... + $indexes[$row['name']]['columns'][] = $row2['name']; + } + $smcFunc['db_free_result']($result2); + } + } + $smcFunc['db_free_result']($result); + + return $indexes; +} + +function smf_db_alter_table($table_name, $columns) +{ + global $smcFunc, $db_prefix, $db_name, $boarddir; + + $db_file = substr($db_name, -3) === '.db' ? $db_name : $db_name . '.db'; + + $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); + + // Let's get the current columns for the table. + $current_columns = $smcFunc['db_list_columns']($table_name, true); + + // Let's get a list of columns for the temp table. + $temp_table_columns = array(); + + // Let's see if we have columns to remove or columns that are being added that already exist. + foreach ($current_columns as $key => $column) + { + $exists = false; + if (isset($columns['remove'])) + foreach ($columns['remove'] as $drop) + if ($drop['name'] == $column['name']) + { + $exists = true; + break; + } + + if (isset($columns['add'])) + foreach ($columns['add'] as $key2 => $add) + if ($add['name'] == $column['name']) + { + unset($columns['add'][$key2]); + break; + } + + // Doesn't exist then we 'remove'. + if (!$exists) + $temp_table_columns[] = $column['name']; + } + + // If they are equal then that means that the column that we are adding exists or it doesn't exist and we are not looking to change any one of them. + if (count($temp_table_columns) == count($current_columns) && empty($columns['change']) && empty($columns['add'])) + return true; + + // Drop the temp table. + $smcFunc['db_query']('', ' + DROP TABLE {raw:temp_table_name}', + array( + 'temp_table_name' => $table_name . '_tmp', + 'db_error_skip' => true, + ) + ); + + // Let's make a backup of the current database. + // We only want the first backup of a table modification. So if there is a backup file and older than an hour just delete and back up again + $db_backup_file = $boarddir . '/Packages/backups/backup_' . $table_name . '_' . basename($db_file) . md5($table_name . $db_file); + if (file_exists($db_backup_file) && time() - filemtime($db_backup_file) > 3600) + { + @unlink($db_backup_file); + @copy($db_file, $db_backup_file); + } + elseif (!file_exists($db_backup_file)) + @copy($db_file, $db_backup_file); + + // If we don't have temp tables then everything crapped out. Just exit. + if (empty($temp_table_columns)) + return false; + + // Start + $smcFunc['db_transaction']('begin'); + + // Let's create the temporary table. + $createTempTable = $smcFunc['db_query']('', ' + CREATE TEMPORARY TABLE {raw:temp_table_name} + ( + {raw:columns} + );', + array( + 'temp_table_name' => $table_name . '_tmp', + 'columns' => implode(', ', $temp_table_columns), + 'db_error_skip' => true, + ) + ) !== false; + + if (!$createTempTable) + return false; + + // Insert into temp table. + $smcFunc['db_query']('', ' + INSERT INTO {raw:temp_table_name} + ({raw:columns}) + SELECT {raw:columns} + FROM {raw:table_name}', + array( + 'table_name' => $table_name, + 'columns' => implode(', ', $temp_table_columns), + 'temp_table_name' => $table_name . '_tmp', + ) + ); + + // Drop the current table. + $dropTable = $smcFunc['db_query']('', ' + DROP TABLE {raw:table_name}', + array( + 'table_name' => $table_name, + 'db_error_skip' => true, + ) + ) !== false; + + // If you can't drop the main table then there is no where to go from here. Just return. + if (!$dropTable) + return false; + + // We need to keep track of the structure for the current columns and the new columns. + $new_columns = array(); + $column_names = array(); + + // Let's get the ones that we already have first. + foreach ($current_columns as $name => $column) + { + if (in_array($name, $temp_table_columns)) + { + $new_columns[$name] = array( + 'name' => $name, + 'type' => $column['type'], + 'size' => isset($column['size']) ? (int) $column['size'] : null, + 'null' => !empty($column['null']), + 'auto' => isset($column['auto']) ? $column['auto'] : false, + 'default' => isset($column['default']) ? $column['default'] : '', + ); + + // Lets keep track of the name for the column. + $column_names[$name] = $name; + } + } + + // Now the new. + if (!empty($columns['add'])) + foreach ($columns['add'] as $add) + { + $new_columns[$add['name']] = array( + 'name' => $add['name'], + 'type' => $add['type'], + 'size' => isset($add['size']) ? (int) $add['size'] : null, + 'null' => !empty($add['null']), + 'auto' => isset($add['auto']) ? $add['auto'] : false, + 'default' => isset($add['default']) ? $add['default'] : '', + ); + + // Let's keep track of the name for the column. + $column_names[$add['name']] = strstr('int', $add['type']) ? ' 0 AS ' . $add['name'] : ' {string:empty_string} AS ' . $add['name']; + } + + // Now to change a column. Not drop but change it. + if (isset($columns['change'])) + foreach ($columns['change'] as $change) + if (isset($new_columns[$change['name']])) + $new_columns[$change['name']] = array( + 'name' => $change['name'], + 'type' => $change['type'], + 'size' => isset($change['size']) ? (int) $change['size'] : null, + 'null' => !empty($change['null']), + 'auto' => isset($change['auto']) ? $change['auto'] : false, + 'default' => isset($change['default']) ? $change['default'] : '', + ); + + // Now let's create the table. + $createTable = $smcFunc['db_create_table']($table_name, $new_columns, array(), array('skip_transaction' => true)); + + // Did it create correctly? + if ($createTable === false) + return false; + + // Back to it's original table. + $insertData = $smcFunc['db_query']('', ' + INSERT INTO {raw:table_name} + ({raw:columns}) + SELECT ' . implode(', ', $column_names) . ' + FROM {raw:temp_table_name}', + array( + 'table_name' => $table_name, + 'columns' => implode(', ', array_keys($new_columns)), + 'columns_select' => implode(', ', $column_names), + 'temp_table_name' => $table_name . '_tmp', + 'empty_string' => '', + ) + ); + + // Did everything insert correctly? + if (!$insertData) + return false; + + // Drop the temp table. + $smcFunc['db_query']('', ' + DROP TABLE {raw:temp_table_name}', + array( + 'temp_table_name' => $table_name . '_tmp', + 'db_error_skip' => true, + ) + ); + + // Commit or else there is no point in doing the previous steps. + $smcFunc['db_transaction']('commit'); + + // We got here so we're good. The temp table should be deleted, if not it will be gone later on >:D. + return true; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbSearch-mysql.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DbSearch-mysql.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,77 @@ + 'smf_db_query', + 'db_search_support' => 'smf_db_search_support', + 'db_create_word_search' => 'smf_db_create_word_search', + 'db_support_ignore' => true, + ); +} + +// Does this database type support this search type? +function smf_db_search_support($search_type) +{ + $supported_types = array('fulltext'); + + return in_array($search_type, $supported_types); +} + +// Highly specific - create the custom word index table! +function smf_db_create_word_search($size) +{ + global $smcFunc; + + if ($size == 'small') + $size = 'smallint(5)'; + elseif ($size == 'medium') + $size = 'mediumint(8)'; + else + $size = 'int(10)'; + + $smcFunc['db_query']('', ' + CREATE TABLE {db_prefix}log_search_words ( + id_word {raw:size} unsigned NOT NULL default {string:string_zero}, + id_msg int(10) unsigned NOT NULL default {string:string_zero}, + PRIMARY KEY (id_word, id_msg) + ) ENGINE=InnoDB', + array( + 'string_zero' => '0', + 'size' => $size, + ) + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbSearch-postgresql.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DbSearch-postgresql.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,122 @@ + 'smf_db_search_query', + 'db_search_support' => 'smf_db_search_support', + 'db_create_word_search' => 'smf_db_create_word_search', + 'db_support_ignore' => false, + ); +} + +// Does this database type support this search type? +function smf_db_search_support($search_type) +{ + $supported_types = array('custom'); + + return in_array($search_type, $supported_types); +} + +// Returns the correct query for this search type. +function smf_db_search_query($identifier, $db_string, $db_values = array(), $connection = null) +{ + global $smcFunc; + + $replacements = array( + 'create_tmp_log_search_topics' => array( + '~mediumint\(\d\)~i' => 'int', + '~unsigned~i' => '', + '~TYPE=HEAP~i' => '', + ), + 'create_tmp_log_search_messages' => array( + '~mediumint\(\d\)' => 'int', + '~unsigned~i' => '', + '~TYPE=HEAP~i' => '', + ), + 'drop_tmp_log_search_topics' => array( + '~IF\sEXISTS~i' => '', + ), + 'drop_tmp_log_search_messages' => array( + '~IF\sEXISTS~i' => '', + ), + 'insert_into_log_messages_fulltext' => array( + '~NOT\sRLIKE~i' => '!~*', + '~RLIKE~i' => '~*', + ), + 'insert_log_search_results_subject' => array( + '~NOT\sRLIKE~i' => '!~*', + '~RLIKE~i' => '~*', + ), + ); + + if (isset($replacements[$identifier])) + $db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string); + elseif (preg_match('~^\s*INSERT\sIGNORE~i', $db_string) != 0) + { + $db_string = preg_replace('~^\s*INSERT\sIGNORE~i', 'INSERT', $db_string); + // Don't error on multi-insert. + $db_values['db_error_skip'] = true; + } + + $return = $smcFunc['db_query']('', $db_string, + $db_values, $connection + ); + + return $return; +} + +// Highly specific - create the custom word index table! +function smf_db_create_word_search($size) +{ + global $smcFunc; + + $size = 'int'; + + $smcFunc['db_query']('', ' + CREATE TABLE {db_prefix}log_search_words ( + id_word {raw:size} NOT NULL default {string:string_zero}, + id_msg int NOT NULL default {string:string_zero}, + PRIMARY KEY (id_word, id_msg) + )', + array( + 'size' => $size, + 'string_zero' => '0', + ) + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbSearch-sqlite.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DbSearch-sqlite.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,106 @@ + 'smf_db_search_query', + 'db_search_support' => 'smf_db_search_support', + 'db_create_word_search' => 'smf_db_create_word_search', + 'db_support_ignore' => false, + ); +} + +// Does this database type support this search type? +function smf_db_search_support($search_type) +{ + $supported_types = array('custom'); + + return in_array($search_type, $supported_types); +} + +// Returns the correct query for this search type. +function smf_db_search_query($identifier, $db_string, $db_values = array(), $connection = null) +{ + global $smcFunc; + + $replacements = array( + 'create_tmp_log_search_topics' => array( + '~mediumint\(\d\)~i' => 'int', + '~TYPE=HEAP~i' => '', + ), + 'create_tmp_log_search_messages' => array( + '~mediumint\(\d\)~i' => 'int', + '~TYPE=HEAP~i' => '', + ), + ); + + if (isset($replacements[$identifier])) + $db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string); + elseif (preg_match('~^\s*INSERT\sIGNORE~i', $db_string) != 0) + { + $db_string = preg_replace('~^\s*INSERT\sIGNORE~i', 'INSERT', $db_string); + // Don't error on multi-insert. + $db_values['db_error_skip'] = true; + } + + $return = $smcFunc['db_query']('', $db_string, + $db_values, $connection + ); + + return $return; +} + +// Highly specific - create the custom word index table! +function smf_db_create_word_search($size) +{ + global $smcFunc; + + $size = 'int'; + + $smcFunc['db_query']('', ' + CREATE TABLE {db_prefix}log_search_words ( + id_word {raw:size} NOT NULL default {string:string_zero}, + id_msg int(10) NOT NULL default {string:string_zero}, + PRIMARY KEY (id_word, id_msg) + )', + array( + 'size' => $size, + 'string_zero' => '0', + ) + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Display.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Display.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1727 @@ + 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) + { + // Just prepare some variables that are used in the query. + $gt_lt = $_REQUEST['prev_next'] == 'prev' ? '>' : '<'; + $order = $_REQUEST['prev_next'] == 'prev' ? '' : ' DESC'; + + $request = $smcFunc['db_query']('', ' + SELECT t2.id_topic + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}topics AS t2 ON (' . (empty($modSettings['enableStickyTopics']) ? ' + t2.id_last_msg ' . $gt_lt . ' t.id_last_msg' : ' + (t2.id_last_msg ' . $gt_lt . ' t.id_last_msg AND t2.is_sticky ' . $gt_lt . '= t.is_sticky) OR t2.is_sticky ' . $gt_lt . ' t.is_sticky') . ') + WHERE t.id_topic = {int:current_topic} + AND t2.id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND (t2.approved = {int:is_approved} OR (t2.id_member_started != {int:id_member_started} AND t2.id_member_started = {int:current_member}))') . ' + ORDER BY' . (empty($modSettings['enableStickyTopics']) ? '' : ' t2.is_sticky' . $order . ',') . ' t2.id_last_msg' . $order . ' + LIMIT 1', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + 'is_approved' => 1, + 'id_member_started' => 0, + ) + ); + + // No more left. + if ($smcFunc['db_num_rows']($request) == 0) + { + $smcFunc['db_free_result']($request); + + // Roll over - if we're going prev, get the last - otherwise the first. + $request = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}topics + WHERE id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND (approved = {int:is_approved} OR (id_member_started != {int:id_member_started} AND id_member_started = {int:current_member}))') . ' + ORDER BY' . (empty($modSettings['enableStickyTopics']) ? '' : ' is_sticky' . $order . ',') . ' id_last_msg' . $order . ' + LIMIT 1', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'is_approved' => 1, + 'id_member_started' => 0, + ) + ); + } + + // Now you can be sure $topic is the id_topic to view. + list ($topic) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $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. + if (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET num_views = num_views + 1 + WHERE id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + ) + ); + + $_SESSION['last_read_topic'] = $topic; + } + + // Get all the important topic info. + $request = $smcFunc['db_query']('', ' + SELECT + t.num_replies, t.num_views, t.locked, ms.subject, t.is_sticky, t.id_poll, + t.id_member_started, t.id_first_msg, t.id_last_msg, t.approved, t.unapproved_posts, + ' . ($user_info['is_guest'] ? 't.id_last_msg + 1' : 'IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1') . ' AS new_from + ' . (!empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board ? ', id_previous_board, id_previous_topic' : '') . ' + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)' . ($user_info['is_guest'] ? '' : ' + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})') . ' + WHERE t.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + 'current_board' => $board, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('not_a_topic', false); + $topicinfo = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $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']; + + // 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 has unapproved posts, we need to work out how many posts the user can see, for page indexing. + if ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !$user_info['is_guest'] && !allowedTo('approve_posts')) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(id_member) AS my_unapproved_posts + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic} + AND id_member = {int:current_member} + AND approved = 0', + array( + 'current_topic' => $topic, + 'current_member' => $user_info['id'], + ) + ); + list ($myUnapprovedPosts) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['total_visible_posts'] = $context['num_replies'] + $myUnapprovedPosts + ($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? + $request = $smcFunc['db_query']('', ' + SELECT poster_time + FROM {db_prefix}messages + WHERE id_msg = {int:id_last_msg} + LIMIT 1', + array( + 'id_last_msg' => $topicinfo['id_last_msg'], + ) + ); + + list ($lastPostTime) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['oldTopicError'] = !empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky); + + // 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'] = empty($options['view_newest_first']) ? $context['start_from'] : 0; + } + else + { + // Find the earliest unread message in the topic. (the use of topics here is just for both tables.) + $request = $smcFunc['db_query']('', ' + SELECT IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member}) + WHERE t.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + ) + ); + list ($new_from) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Fall through to the next if statement. + $_REQUEST['start'] = 'msg' . $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... + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}messages + WHERE poster_time < {int:timestamp} + AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts') ? ' + AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''), + array( + 'current_topic' => $topic, + 'current_member' => $user_info['id'], + 'is_approved' => 1, + 'timestamp' => $timestamp, + ) + ); + list ($context['start_from']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Handle view_newest_first options, and get the correct start value. + $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $context['total_visible_posts'] - $context['start_from'] - 1; + } + } + + // Link to a message... + 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 + { + // Find the start value for that message...... + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}messages + WHERE id_msg < {int:virtual_msg} + AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts') ? ' + AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''), + array( + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + 'virtual_msg' => $virtual_msg, + 'is_approved' => 1, + 'no_member' => 0, + ) + ); + list ($context['start_from']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // We need to reverse the start as well in this case. + $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $context['total_visible_posts'] - $context['start_from'] - 1; + } + } + + // Create a previous next string if the selected theme has it as a selected option. + $context['previous_next'] = $modSettings['enablePreviousNext'] ? '' . $txt['previous_next_back'] . ' ' . $txt['previous_next_forward'] . '' : ''; + + // Check if spellchecking is both enabled and actually working. (for quick reply.) + $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new'); + + // 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($sourcedir . '/Subs-Editor.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']; + + // 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']); + + // Let's get nosey, who is viewing this topic? + if (!empty($settings['display_who_viewing'])) + { + // Start out with no one at all viewing it. + $context['view_members'] = array(); + $context['view_members_list'] = array(); + $context['view_num_hidden'] = 0; + + // Search for members who have this topic set in their GET data. + $request = $smcFunc['db_query']('', ' + SELECT + lo.id_member, lo.log_time, mem.real_name, mem.member_name, mem.show_online, + mg.online_color, mg.id_group, mg.group_name + FROM {db_prefix}log_online AS lo + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lo.id_member) + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_id_group} THEN mem.id_post_group ELSE mem.id_group END) + WHERE INSTR(lo.url, {string:in_url_string}) > 0 OR lo.session = {string:session}', + array( + 'reg_id_group' => 0, + 'in_url_string' => 's:5:"topic";i:' . $topic . ';', + 'session' => $user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($row['id_member'])) + continue; + + if (!empty($row['online_color'])) + $link = '' . $row['real_name'] . ''; + else + $link = '' . $row['real_name'] . ''; + + $is_buddy = in_array($row['id_member'], $user_info['buddies']); + if ($is_buddy) + $link = '' . $link . ''; + + // Add them both to the list and to the more detailed list. + if (!empty($row['show_online']) || allowedTo('moderate_forum')) + $context['view_members_list'][$row['log_time'] . $row['member_name']] = empty($row['show_online']) ? '' . $link . '' : $link; + $context['view_members'][$row['log_time'] . $row['member_name']] = array( + 'id' => $row['id_member'], + 'username' => $row['member_name'], + 'name' => $row['real_name'], + 'group' => $row['id_group'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => $link, + 'is_buddy' => $is_buddy, + 'hidden' => empty($row['show_online']), + ); + + if (empty($row['show_online'])) + $context['view_num_hidden']++; + } + + // The number of guests is equal to the rows minus the ones we actually used ;). + $context['view_num_guests'] = $smcFunc['db_num_rows']($request) - count($context['view_members']); + $smcFunc['db_free_result']($request); + + // Sort the list. + krsort($context['view_members']); + krsort($context['view_members_list']); + } + + // 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']); + // Otherwise, it must be allowed... so pretend start was -1. + 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); + $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/first/last/etc. for wireless mainly. + $context['links'] = array( + 'first' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.0' : '', + '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']) : '', + 'last' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic. '.' . (floor($context['total_visible_posts'] / $context['messages_per_page']) * $context['messages_per_page']) : '', + 'up' => $scripturl . '?board=' . $board . '.0' + ); + + // If they are viewing all the posts, show all the posts, otherwise limit the number. + if ($can_show_all) + { + if (isset($_REQUEST['all'])) + { + // No limit! (actually, there is a limit, but...) + $context['messages_per_page'] = -1; + $context['page_index'] .= empty($modSettings['compactTopicPagesEnable']) ? '' . $txt['all'] . ' ' : '[' . $txt['all'] . '] '; + + // Set start back to 0... + $_REQUEST['start'] = 0; + } + // They aren't using it, but the *option* is there, at least. + else + $context['page_index'] .= ' ' . $txt['all'] . ' '; + } + + // Build the link tree. + $context['linktree'][] = array( + 'url' => $scripturl . '?topic=' . $topic . '.0', + 'name' => $topicinfo['subject'], + 'extra_before' => $settings['linktree_inline'] ? $txt['topic'] . ': ' : '' + ); + + // Build a list of this board's moderators. + $context['moderators'] = &$board_info['moderators']; + $context['link_moderators'] = array(); + if (!empty($board_info['moderators'])) + { + // Add a link for each moderator... + foreach ($board_info['moderators'] as $mod) + $context['link_moderators'][] = '' . $mod['name'] . ''; + + // And show it after the board's name. + $context['linktree'][count($context['linktree']) - 2]['extra_after'] = ' (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')'; + } + + // 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']; + + // We don't want to show the poll icon in the topic class here, so pretend it's not one. + $context['is_poll'] = false; + determineTopicClass($context); + + $context['is_poll'] = $topicinfo['id_poll'] > 0 && $modSettings['pollMode'] == '1' && allowedTo('poll_view'); + + // 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['mark_unread_time'] = $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. + if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix', 600))) + { + if ($language === $user_info['language']) + $context['response_prefix'] = $txt['response_prefix']; + else + { + loadLanguage('index', $language, false); + $context['response_prefix'] = $txt['response_prefix']; + loadLanguage('index'); + } + cache_put_data('response_prefix', $context['response_prefix'], 600); + } + + // 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'])) + { + // 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]; + + // Any calendar information for this topic? + $request = $smcFunc['db_query']('', ' + SELECT cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, mem.real_name + FROM {db_prefix}calendar AS cal + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = cal.id_member) + WHERE cal.id_topic = {int:current_topic} + ORDER BY start_date', + array( + 'current_topic' => $topic, + ) + ); + $context['linked_calendar_events'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Prepare the dates for being formatted. + $start_date = sscanf($row['start_date'], '%04d-%02d-%02d'); + $start_date = mktime(12, 0, 0, $start_date[1], $start_date[2], $start_date[0]); + $end_date = sscanf($row['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' => $row['id_event'], + 'title' => $row['title'], + 'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')), + 'modify_href' => $scripturl . '?action=post;msg=' . $topicinfo['id_first_msg'] . ';topic=' . $topic . '.0;calendar;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], + 'start_date' => timeformat($start_date, $date_string, 'none'), + 'start_timestamp' => $start_date, + 'end_date' => timeformat($end_date, $date_string, 'none'), + 'end_timestamp' => $end_date, + 'is_last' => false + ); + } + $smcFunc['db_free_result']($request); + + if (!empty($context['linked_calendar_events'])) + $context['linked_calendar_events'][count($context['linked_calendar_events']) - 1]['is_last'] = true; + } + + // Create the poll info if it exists. + if ($context['is_poll']) + { + // Get the question and if it's locked. + $request = $smcFunc['db_query']('', ' + SELECT + p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.change_vote, + p.guest_vote, p.id_member, IFNULL(mem.real_name, p.poster_name) AS poster_name, p.num_guest_voters, p.reset_poll + FROM {db_prefix}polls AS p + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = p.id_member) + WHERE p.id_poll = {int:id_poll} + LIMIT 1', + array( + 'id_poll' => $topicinfo['id_poll'], + ) + ); + $pollinfo = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(DISTINCT id_member) AS total + FROM {db_prefix}log_polls + WHERE id_poll = {int:id_poll} + AND id_member != {int:not_guest}', + array( + 'id_poll' => $topicinfo['id_poll'], + 'not_guest' => 0, + ) + ); + list ($pollinfo['total']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Total voters needs to include guest voters + $pollinfo['total'] += $pollinfo['num_guest_voters']; + + // Get all the options, and calculate the total votes. + $request = $smcFunc['db_query']('', ' + SELECT pc.id_choice, pc.label, pc.votes, IFNULL(lp.id_choice, -1) AS voted_this + FROM {db_prefix}poll_choices AS pc + LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_choice = pc.id_choice AND lp.id_poll = {int:id_poll} AND lp.id_member = {int:current_member} AND lp.id_member != {int:not_guest}) + WHERE pc.id_poll = {int:id_poll}', + array( + 'current_member' => $user_info['id'], + 'id_poll' => $topicinfo['id_poll'], + 'not_guest' => 0, + ) + ); + $pollOptions = array(); + $realtotal = 0; + $pollinfo['has_voted'] = false; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + censorText($row['label']); + $pollOptions[$row['id_choice']] = $row; + $realtotal += $row['votes']; + $pollinfo['has_voted'] |= $row['voted_this'] != -1; + } + $smcFunc['db_free_result']($request); + + // If this is a guest we need to do our best to work out if they have voted, and what they voted for. + if ($user_info['is_guest'] && $pollinfo['guest_vote'] && allowedTo('poll_vote')) + { + if (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $topicinfo['id_poll'] . ',') !== false) + { + // ;id,timestamp,[vote,vote...]; etc + $guestinfo = explode(';', $_COOKIE['guest_poll_vote']); + // Find the poll we're after. + foreach ($guestinfo as $i => $guestvoted) + { + $guestvoted = explode(',', $guestvoted); + if ($guestvoted[0] == $topicinfo['id_poll']) + break; + } + // Has the poll been reset since guest voted? + if ($pollinfo['reset_poll'] > $guestvoted[1]) + { + // Remove the poll info from the cookie to allow guest to vote again + unset($guestinfo[$i]); + if (!empty($guestinfo)) + $_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo); + else + unset($_COOKIE['guest_poll_vote']); + } + else + { + // What did they vote for? + unset($guestvoted[0], $guestvoted[1]); + foreach ($pollOptions as $choice => $details) + { + $pollOptions[$choice]['voted_this'] = in_array($choice, $guestvoted) ? 1 : -1; + $pollinfo['has_voted'] |= $pollOptions[$choice]['voted_this'] != -1; + } + unset($choice, $details, $guestvoted); + } + unset($guestinfo, $guestvoted, $i); + } + } + + // Set up the basic poll information. + $context['poll'] = array( + 'id' => $topicinfo['id_poll'], + 'image' => 'normal_' . (empty($pollinfo['voting_locked']) ? 'poll' : 'locked_poll'), + 'question' => parse_bbc($pollinfo['question']), + 'total_votes' => $pollinfo['total'], + 'change_vote' => !empty($pollinfo['change_vote']), + 'is_locked' => !empty($pollinfo['voting_locked']), + 'options' => array(), + 'lock' => allowedTo('poll_lock_any') || ($context['user']['started'] && allowedTo('poll_lock_own')), + 'edit' => allowedTo('poll_edit_any') || ($context['user']['started'] && allowedTo('poll_edit_own')), + 'allowed_warning' => $pollinfo['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($pollOptions), $pollinfo['max_votes'])) : '', + 'is_expired' => !empty($pollinfo['expire_time']) && $pollinfo['expire_time'] < time(), + 'expire_time' => !empty($pollinfo['expire_time']) ? timeformat($pollinfo['expire_time']) : 0, + 'has_voted' => !empty($pollinfo['has_voted']), + 'starter' => array( + 'id' => $pollinfo['id_member'], + 'name' => $row['poster_name'], + 'href' => $pollinfo['id_member'] == 0 ? '' : $scripturl . '?action=profile;u=' . $pollinfo['id_member'], + 'link' => $pollinfo['id_member'] == 0 ? $row['poster_name'] : '' . $row['poster_name'] . '' + ) + ); + + // Make the lock and edit permissions defined above more directly accessible. + $context['allow_lock_poll'] = $context['poll']['lock']; + $context['allow_edit_poll'] = $context['poll']['edit']; + + // You're allowed to vote if: + // 1. the poll did not expire, and + // 2. you're either not a guest OR guest voting is enabled... and + // 3. you're not trying to view the results, and + // 4. the poll is not locked, and + // 5. you have the proper permissions, and + // 6. you haven't already voted before. + $context['allow_vote'] = !$context['poll']['is_expired'] && (!$user_info['is_guest'] || ($pollinfo['guest_vote'] && allowedTo('poll_vote'))) && empty($pollinfo['voting_locked']) && allowedTo('poll_vote') && !$context['poll']['has_voted']; + + // You're allowed to view the results if: + // 1. you're just a super-nice-guy, or + // 2. anyone can see them (hide_results == 0), or + // 3. you can see them after you voted (hide_results == 1), or + // 4. you've waited long enough for the poll to expire. (whether hide_results is 1 or 2.) + $context['allow_poll_view'] = allowedTo('moderate_board') || $pollinfo['hide_results'] == 0 || ($pollinfo['hide_results'] == 1 && $context['poll']['has_voted']) || $context['poll']['is_expired']; + $context['poll']['show_results'] = $context['allow_poll_view'] && (isset($_REQUEST['viewresults']) || isset($_REQUEST['viewResults'])); + $context['show_view_results_button'] = $context['allow_vote'] && (!$context['allow_poll_view'] || !$context['poll']['show_results'] || !$context['poll']['has_voted']); + + // You're allowed to change your vote if: + // 1. the poll did not expire, and + // 2. you're not a guest... and + // 3. the poll is not locked, and + // 4. you have the proper permissions, and + // 5. you have already voted, and + // 6. the poll creator has said you can! + $context['allow_change_vote'] = !$context['poll']['is_expired'] && !$user_info['is_guest'] && empty($pollinfo['voting_locked']) && allowedTo('poll_vote') && $context['poll']['has_voted'] && $context['poll']['change_vote']; + + // You're allowed to return to voting options if: + // 1. you are (still) allowed to vote. + // 2. you are currently seeing the results. + $context['allow_return_vote'] = $context['allow_vote'] && $context['poll']['show_results']; + + // Calculate the percentages and bar lengths... + $divisor = $realtotal == 0 ? 1 : $realtotal; + + // Determine if a decimal point is needed in order for the options to add to 100%. + $precision = $realtotal == 100 ? 0 : 1; + + // Now look through each option, and... + foreach ($pollOptions as $i => $option) + { + // First calculate the percentage, and then the width of the bar... + $bar = round(($option['votes'] * 100) / $divisor, $precision); + $barWide = $bar == 0 ? 1 : floor(($bar * 8) / 3); + + // Now add it to the poll's contextual theme data. + $context['poll']['options'][$i] = array( + 'id' => 'options-' . $i, + 'percent' => $bar, + 'votes' => $option['votes'], + 'voted_this' => $option['voted_this'] != -1, + 'bar' => '-', + // Note: IE < 8 requires us to set a width on the container, too. + 'bar_ndt' => $bar > 0 ? '
' : '', + 'bar_width' => $barWide, + 'option' => parse_bbc($option['label']), + 'vote_button' => '' + ); + } + } + + // Calculate the fastest way to get the messages! + $ascending = empty($options['view_newest_first']); + $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; + } + + // Get each post and poster in this topic. + $request = $smcFunc['db_query']('display_get_post_poster', ' + SELECT id_msg, id_member, approved + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : (!empty($modSettings['db_mysql_group_by_fix']) ? '' : ' + GROUP BY id_msg') . ' + HAVING (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')') . ' + ORDER BY id_msg ' . ($ascending ? '' : 'DESC') . ($context['messages_per_page'] == -1 ? '' : ' + LIMIT ' . $start . ', ' . $limit), + array( + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + 'is_approved' => 1, + 'blank_id_member' => 0, + ) + ); + + $messages = array(); + $all_posters = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!empty($row['id_member'])) + $all_posters[$row['id_msg']] = $row['id_member']; + $messages[] = $row['id_msg']; + } + $smcFunc['db_free_result']($request); + $posters = array_unique($all_posters); + + // Guests can't mark topics read or for notifications, just can't sorry. + if (!$user_info['is_guest']) + { + $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']) + { + $smcFunc['db_insert']($topicinfo['new_from'] == 0 ? 'ignore' : 'replace', + '{db_prefix}log_topics', + array( + 'id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', + ), + array( + $user_info['id'], $topic, $mark_at_msg, + ), + array('id_member', 'id_topic') + ); + } + + // Check for notifications on this topic OR board. + $request = $smcFunc['db_query']('', ' + SELECT sent, id_topic + FROM {db_prefix}log_notify + WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board}) + AND id_member = {int:current_member} + LIMIT 2', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + ) + ); + $do_once = true; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Find if this topic is marked for notification... + if (!empty($row['id_topic'])) + $context['is_marked_notify'] = true; + + // Only do this once, but mark the notifications as "not sent yet" for next time. + if (!empty($row['sent']) && $do_once) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_notify + SET sent = {int:is_not_sent} + WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board}) + AND id_member = {int:current_member}', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + 'is_not_sent' => 0, + ) + ); + $do_once = false; + } + } + + // 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]--; + // Mark board as seen if this is the only new topic. + elseif (isset($_REQUEST['topicseen'])) + { + // Use the mark read tables... and the last visit to figure out if this should be read or not. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = {int:current_board} AND lb.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member}) + WHERE t.id_board = {int:current_board} + AND t.id_last_msg > IFNULL(lb.id_msg, 0) + AND t.id_last_msg > IFNULL(lt.id_msg, 0)' . (empty($_SESSION['id_msg_last_visit']) ? '' : ' + AND t.id_last_msg > {int:id_msg_last_visit}'), + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'id_msg_last_visit' => (int) $_SESSION['id_msg_last_visit'], + ) + ); + list ($numNewTopics) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // 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; + } + // Probably one less topic - maybe not, but even if we decrease this too fast it will only make us look more often. + 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'])) + { + $smcFunc['db_insert']('replace', + '{db_prefix}log_boards', + array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'), + array($modSettings['maxMsgID'], $user_info['id'], $board), + array('id_member', 'id_board') + ); + } + } + + $attachments = array(); + + // If there _are_ messages here... (probably an error otherwise :!) + if (!empty($messages)) + { + // Fetch attachments. + if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments')) + { + $request = $smcFunc['db_query']('', ' + SELECT + a.id_attach, a.id_folder, a.id_msg, a.filename, a.file_hash, IFNULL(a.size, 0) AS filesize, a.downloads, a.approved, + a.width, a.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ', + IFNULL(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . ' + FROM {db_prefix}attachments AS a' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ' + LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb)') . ' + WHERE a.id_msg IN ({array_int:message_list}) + AND a.attachment_type = {int:attachment_type}', + array( + 'message_list' => $messages, + 'attachment_type' => 0, + 'is_approved' => 1, + ) + ); + $temp = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!$row['approved'] && $modSettings['postmod_active'] && !allowedTo('approve_posts') && (!isset($all_posters[$row['id_msg']]) || $all_posters[$row['id_msg']] != $user_info['id'])) + continue; + + $temp[$row['id_attach']] = $row; + + if (!isset($attachments[$row['id_msg']])) + $attachments[$row['id_msg']] = array(); + } + $smcFunc['db_free_result']($request); + + // This is better than sorting it with the query... + ksort($temp); + + foreach ($temp as $row) + $attachments[$row['id_msg']][] = $row; + } + + // What? It's not like it *couldn't* be only guests in this topic... + if (!empty($posters)) + loadMemberData($posters); + $messages_request = $smcFunc['db_query']('', ' + SELECT + id_msg, icon, subject, poster_time, poster_ip, id_member, modified_time, modified_name, body, + smileys_enabled, poster_name, poster_email, approved, + id_msg_modified < {int:new_from} AS is_read + FROM {db_prefix}messages + WHERE id_msg IN ({array_int:message_list}) + ORDER BY id_msg' . (empty($options['view_newest_first']) ? '' : ' DESC'), + array( + 'message_list' => $messages, + 'new_from' => $topicinfo['new_from'], + ) + ); + + // 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]; + if (empty($options['view_newest_first'])) + $context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['start_from']; + else + $context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $topicinfo['num_replies'] - $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('&' => '&'))), + 'child_level' => $board_info['child_level'], + ); + + // Set the callback. (do you REALIZE how much memory all the messages would take?!?) + $context['get_message'] = 'prepareDisplayContext'; + + // 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_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']); + $context['can_add_poll'] &= $modSettings['pollMode'] == '1' && $topicinfo['id_poll'] <= 0; + $context['can_remove_poll'] &= $modSettings['pollMode'] == '1' && $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']) && $modSettings['warning_settings'][0] == 1; + // 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_send_topic'] = (!$modSettings['postmod_active'] || $topicinfo['approved']) && allowedTo('send_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']); + + // Wireless shows a "more" if you can do anything special. + if (WIRELESS && WIRELESS_PROTOCOL != 'wap') + { + $context['wireless_more'] = $context['can_sticky'] || $context['can_lock'] || allowedTo('modify_any'); + $context['wireless_moderate'] = isset($_GET['moderate']) ? ';moderate' : ''; + } + + // 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'] : ''; + } +} + +// Callback for the message display. +function prepareDisplayContext($reset = false) +{ + global $settings, $txt, $modSettings, $scripturl, $options, $user_info, $smcFunc; + global $memberContext, $context, $messages_request, $topic, $attachments, $topicinfo; + + static $counter = null; + + // If the query returned false, bail. + if ($messages_request == false) + return false; + + // Remember which message this is. (ie. reply #83) + if ($counter === null || $reset) + $counter = empty($options['view_newest_first']) ? $context['start'] : $context['total_visible_posts'] - $context['start']; + + // Start from the beginning... + if ($reset) + return @$smcFunc['db_data_seek']($messages_request, 0); + + // Attempt to get the next message. + $message = $smcFunc['db_fetch_assoc']($messages_request); + if (!$message) + { + $smcFunc['db_free_result']($messages_request); + return false; + } + + // $context['icon_sources'] says where each icon should come from - here we set up the ones which will always exist! + if (empty($context['icon_sources'])) + { + $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless', 'clip'); + $context['icon_sources'] = array(); + foreach ($stable_icons as $icon) + $context['icon_sources'][$icon] = 'images_url'; + } + + // Message Icon Management... check the images exist. + if (empty($modSettings['messageIconChecks_disable'])) + { + // If the current icon isn't known, then we need to do something... + if (!isset($context['icon_sources'][$message['icon']])) + $context['icon_sources'][$message['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $message['icon'] . '.gif') ? 'images_url' : 'default_images_url'; + } + elseif (!isset($context['icon_sources'][$message['icon']])) + $context['icon_sources'][$message['icon']] = 'images_url'; + + // If you're a lazy bum, you probably didn't give a subject... + $message['subject'] = $message['subject'] != '' ? $message['subject'] : $txt['no_subject']; + + // Are you allowed to remove at least a single reply? + $context['can_remove_post'] |= allowedTo('delete_own') && (empty($modSettings['edit_disable_time']) || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 >= time()) && $message['id_member'] == $user_info['id']; + + // If it couldn't load, or the user was a guest.... someday may be done with a guest table. + if (!loadMemberContext($message['id_member'], true)) + { + // Notice this information isn't used anywhere else.... + $memberContext[$message['id_member']]['name'] = $message['poster_name']; + $memberContext[$message['id_member']]['id'] = 0; + $memberContext[$message['id_member']]['group'] = $txt['guest_title']; + $memberContext[$message['id_member']]['link'] = $message['poster_name']; + $memberContext[$message['id_member']]['email'] = $message['poster_email']; + $memberContext[$message['id_member']]['show_email'] = showEmailAddress(true, 0); + $memberContext[$message['id_member']]['is_guest'] = true; + } + else + { + $memberContext[$message['id_member']]['can_view_profile'] = allowedTo('profile_view_any') || ($message['id_member'] == $user_info['id'] && allowedTo('profile_view_own')); + $memberContext[$message['id_member']]['is_topic_starter'] = $message['id_member'] == $context['topic_starter_id']; + $memberContext[$message['id_member']]['can_see_warning'] = !isset($context['disabled_fields']['warning_status']) && $memberContext[$message['id_member']]['warning_status'] && ($context['user']['can_mod'] || (!$user_info['is_guest'] && !empty($modSettings['warning_show']) && ($modSettings['warning_show'] > 1 || $message['id_member'] == $user_info['id']))); + } + + $memberContext[$message['id_member']]['ip'] = $message['poster_ip']; + + // Do the censor thang. + censorText($message['body']); + censorText($message['subject']); + + // Run BBC interpreter on the message. + $message['body'] = parse_bbc($message['body'], $message['smileys_enabled'], $message['id_msg']); + + // Compose the memory eat- I mean message array. + $output = array( + 'attachment' => loadAttachmentContext($message['id_msg']), + 'alternate' => $counter % 2, + 'id' => $message['id_msg'], + 'href' => $scripturl . '?topic=' . $topic . '.msg' . $message['id_msg'] . '#msg' . $message['id_msg'], + 'link' => '' . $message['subject'] . '', + 'member' => &$memberContext[$message['id_member']], + 'icon' => $message['icon'], + 'icon_url' => $settings[$context['icon_sources'][$message['icon']]] . '/post/' . $message['icon'] . '.gif', + 'subject' => $message['subject'], + 'time' => timeformat($message['poster_time']), + 'timestamp' => forum_time(true, $message['poster_time']), + 'counter' => $counter, + 'modified' => array( + 'time' => timeformat($message['modified_time']), + 'timestamp' => forum_time(true, $message['modified_time']), + 'name' => $message['modified_name'] + ), + 'body' => $message['body'], + 'new' => empty($message['is_read']), + 'approved' => $message['approved'], + 'first_new' => isset($context['start_from']) && $context['start_from'] == $counter, + 'is_ignored' => !empty($modSettings['enable_buddylist']) && !empty($options['posts_apply_ignore_list']) && in_array($message['id_member'], $context['user']['ignoreusers']), + 'can_approve' => !$message['approved'] && $context['can_approve'], + 'can_unapprove' => $message['approved'] && $context['can_approve'], + 'can_modify' => (!$context['is_locked'] || allowedTo('moderate_board')) && (allowedTo('modify_any') || (allowedTo('modify_replies') && $context['user']['started']) || (allowedTo('modify_own') && $message['id_member'] == $user_info['id'] && (empty($modSettings['edit_disable_time']) || !$message['approved'] || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 > time()))), + 'can_remove' => allowedTo('delete_any') || (allowedTo('delete_replies') && $context['user']['started']) || (allowedTo('delete_own') && $message['id_member'] == $user_info['id'] && (empty($modSettings['edit_disable_time']) || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 > time())), + 'can_see_ip' => allowedTo('moderate_forum') || ($message['id_member'] == $user_info['id'] && !empty($user_info['id'])), + ); + + // Is this user the message author? + $output['is_message_author'] = $message['id_member'] == $user_info['id']; + + if (empty($options['view_newest_first'])) + $counter++; + else + $counter--; + + return $output; +} + +// Download an attachment. +function Download() +{ + global $txt, $modSettings, $user_info, $scripturl, $context, $sourcedir, $topic, $smcFunc; + + // Some defaults that we need. + $context['character_set'] = empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'ISO-8859-1' : $txt['lang_character_set']) : $modSettings['global_character_set']; + $context['utf8'] = $context['character_set'] === 'UTF-8' && (strpos(strtolower(PHP_OS), 'win') === false || @version_compare(PHP_VERSION, '4.2.3') != -1); + $context['no_last_modified'] = true; + + // Make sure some attachment was requested! + if (!isset($_REQUEST['attach']) && !isset($_REQUEST['id'])) + fatal_lang_error('no_access', false); + + $_REQUEST['attach'] = isset($_REQUEST['attach']) ? (int) $_REQUEST['attach'] : (int) $_REQUEST['id']; + + if (isset($_REQUEST['type']) && $_REQUEST['type'] == 'avatar') + { + $request = $smcFunc['db_query']('', ' + SELECT id_folder, filename, file_hash, fileext, id_attach, attachment_type, mime_type, approved, id_member + FROM {db_prefix}attachments + WHERE id_attach = {int:id_attach} + AND id_member > {int:blank_id_member} + LIMIT 1', + array( + 'id_attach' => $_REQUEST['attach'], + 'blank_id_member' => 0, + ) + ); + $_REQUEST['image'] = true; + } + // This is just a regular attachment... + else + { + // This checks only the current board for $board/$topic's permissions. + isAllowedTo('view_attachments'); + + // Make sure this attachment is on this board. + // NOTE: We must verify that $topic is the attachment's topic, or else the permission check above is broken. + $request = $smcFunc['db_query']('', ' + SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, a.approved, m.id_member + FROM {db_prefix}attachments AS a + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic}) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board}) + WHERE a.id_attach = {int:attach} + LIMIT 1', + array( + 'attach' => $_REQUEST['attach'], + 'current_topic' => $topic, + ) + ); + } + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + list ($id_folder, $real_filename, $file_hash, $file_ext, $id_attach, $attachment_type, $mime_type, $is_approved, $id_member) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // If it isn't yet approved, do they have permission to view it? + if (!$is_approved && ($id_member == 0 || $user_info['id'] != $id_member) && ($attachment_type == 0 || $attachment_type == 3)) + isAllowedTo('approve_posts'); + + // Update the download counter (unless it's a thumbnail). + if ($attachment_type != 3) + $smcFunc['db_query']('attach_download_increase', ' + UPDATE LOW_PRIORITY {db_prefix}attachments + SET downloads = downloads + 1 + WHERE id_attach = {int:id_attach}', + array( + 'id_attach' => $id_attach, + ) + ); + + $filename = getAttachmentFilename($real_filename, $_REQUEST['attach'], $id_folder, false, $file_hash); + + // This is done to clear any output that was made before now. (would use ob_clean(), but that's PHP 4.2.0+...) + ob_end_clean(); + if (!empty($modSettings['enableCompressedOutput']) && @version_compare(PHP_VERSION, '4.2.0') >= 0 && @filesize($filename) <= 4194304 && in_array($file_ext, array('txt', 'html', 'htm', 'js', 'doc', 'pdf', 'docx', 'rtf', 'css', 'php', 'log', 'xml', 'sql', 'c', 'java'))) + @ob_start('ob_gzhandler'); + else + { + ob_start(); + header('Content-Encoding: none'); + } + + // No point in a nicer message, because this is supposed to be an attachment anyway... + if (!file_exists($filename)) + { + loadLanguage('Errors'); + + header('HTTP/1.0 404 ' . $txt['attachment_not_found']); + header('Content-Type: text/plain; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); + + // We need to die like this *before* we send any anti-caching headers as below. + die('404 - ' . $txt['attachment_not_found']); + } + + // If it hasn't been modified since the last time this attachement was retrieved, there's no need to display it again. + if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) + { + list($modified_since) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']); + if (strtotime($modified_since) >= filemtime($filename)) + { + ob_end_clean(); + + // Answer the question - no, it hasn't been modified ;). + header('HTTP/1.1 304 Not Modified'); + exit; + } + } + + // Check whether the ETag was sent back, and cache based on that... + $eTag = '"' . substr($_REQUEST['attach'] . $real_filename . filemtime($filename), 0, 64) . '"'; + if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'], $eTag) !== false) + { + ob_end_clean(); + + header('HTTP/1.1 304 Not Modified'); + exit; + } + + // Send the attachment headers. + header('Pragma: '); + if (!$context['browser']['is_gecko']) + header('Content-Transfer-Encoding: binary'); + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($filename)) . ' GMT'); + header('Accept-Ranges: bytes'); + header('Connection: close'); + header('ETag: ' . $eTag); + + // IE 6 just doesn't play nice. As dirty as this seems, it works. + if ($context['browser']['is_ie6'] && isset($_REQUEST['image'])) + unset($_REQUEST['image']); + + // Make sure the mime type warrants an inline display. + elseif (isset($_REQUEST['image']) && !empty($mime_type) && strpos($mime_type, 'image/') !== 0) + unset($_REQUEST['image']); + + // Does this have a mime type? + elseif (!empty($mime_type) && (isset($_REQUEST['image']) || !in_array($file_ext, array('jpg', 'gif', 'jpeg', 'x-ms-bmp', 'png', 'psd', 'tiff', 'iff')))) + header('Content-Type: ' . strtr($mime_type, array('image/bmp' => 'image/x-ms-bmp'))); + + else + { + header('Content-Type: ' . ($context['browser']['is_ie'] || $context['browser']['is_opera'] ? 'application/octetstream' : 'application/octet-stream')); + if (isset($_REQUEST['image'])) + unset($_REQUEST['image']); + } + + // Convert the file to UTF-8, cuz most browsers dig that. + $utf8name = !$context['utf8'] && function_exists('iconv') ? iconv($context['character_set'], 'UTF-8', $real_filename) : (!$context['utf8'] && function_exists('mb_convert_encoding') ? mb_convert_encoding($real_filename, 'UTF-8', $context['character_set']) : $real_filename); + $fixchar = create_function('$n', ' + if ($n < 32) + return \'\'; + elseif ($n < 128) + return chr($n); + elseif ($n < 2048) + return chr(192 | $n >> 6) . chr(128 | $n & 63); + elseif ($n < 65536) + return chr(224 | $n >> 12) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63); + else + return chr(240 | $n >> 18) . chr(128 | $n >> 12 & 63) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63);'); + + $disposition = !isset($_REQUEST['image']) ? 'attachment' : 'inline'; + + // Different browsers like different standards... + if ($context['browser']['is_firefox']) + header('Content-Disposition: ' . $disposition . '; filename*="UTF-8\'\'' . preg_replace('~&#(\d{3,8});~e', '$fixchar(\'$1\')', $utf8name) . '"'); + + elseif ($context['browser']['is_opera']) + header('Content-Disposition: ' . $disposition . '; filename="' . preg_replace('~&#(\d{3,8});~e', '$fixchar(\'$1\')', $utf8name) . '"'); + + elseif ($context['browser']['is_ie']) + header('Content-Disposition: ' . $disposition . '; filename="' . urlencode(preg_replace('~&#(\d{3,8});~e', '$fixchar(\'$1\')', $utf8name)) . '"'); + + else + header('Content-Disposition: ' . $disposition . '; filename="' . $utf8name . '"'); + + // If this has an "image extension" - but isn't actually an image - then ensure it isn't cached cause of silly IE. + if (!isset($_REQUEST['image']) && in_array($file_ext, array('gif', 'jpg', 'bmp', 'png', 'jpeg', 'tiff'))) + header('Cache-Control: no-cache'); + else + header('Cache-Control: max-age=' . (525600 * 60) . ', private'); + + if (empty($modSettings['enableCompressedOutput']) || filesize($filename) > 4194304) + header('Content-Length: ' . filesize($filename)); + + // Try to buy some time... + @set_time_limit(600); + + // Recode line endings for text files, if enabled. + if (!empty($modSettings['attachmentRecodeLineEndings']) && !isset($_REQUEST['image']) && in_array($file_ext, array('txt', 'css', 'htm', 'html', 'php', 'xml'))) + { + if (strpos($_SERVER['HTTP_USER_AGENT'], 'Windows') !== false) + $callback = create_function('$buffer', 'return preg_replace(\'~[\r]?\n~\', "\r\n", $buffer);'); + elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') !== false) + $callback = create_function('$buffer', 'return preg_replace(\'~[\r]?\n~\', "\r", $buffer);'); + else + $callback = create_function('$buffer', 'return preg_replace(\'~[\r]?\n~\', "\n", $buffer);'); + } + + // Since we don't do output compression for files this large... + if (filesize($filename) > 4194304) + { + // Forcibly end any output buffering going on. + if (function_exists('ob_get_level')) + { + while (@ob_get_level() > 0) + @ob_end_clean(); + } + else + { + @ob_end_clean(); + @ob_end_clean(); + @ob_end_clean(); + } + + $fp = fopen($filename, 'rb'); + while (!feof($fp)) + { + if (isset($callback)) + echo $callback(fread($fp, 8192)); + else + echo fread($fp, 8192); + flush(); + } + fclose($fp); + } + // On some of the less-bright hosts, readfile() is disabled. It's just a faster, more byte safe, version of what's in the if. + elseif (isset($callback) || @readfile($filename) == null) + echo isset($callback) ? $callback(file_get_contents($filename)) : file_get_contents($filename); + + obExit(false); +} + +function loadAttachmentContext($id_msg) +{ + global $attachments, $modSettings, $txt, $scripturl, $topic, $sourcedir, $smcFunc; + + // Set up the attachment info - based on code by Meriadoc. + $attachmentData = array(); + $have_unapproved = false; + if (isset($attachments[$id_msg]) && !empty($modSettings['attachmentEnable'])) + { + foreach ($attachments[$id_msg] as $i => $attachment) + { + $attachmentData[$i] = array( + 'id' => $attachment['id_attach'], + 'name' => preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($attachment['filename'])), + 'downloads' => $attachment['downloads'], + 'size' => round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'], + 'byte_size' => $attachment['filesize'], + 'href' => $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attachment['id_attach'], + 'link' => '' . htmlspecialchars($attachment['filename']) . '', + 'is_image' => !empty($attachment['width']) && !empty($attachment['height']) && !empty($modSettings['attachmentShowImages']), + 'is_approved' => $attachment['approved'], + ); + + // If something is unapproved we'll note it so we can sort them. + if (!$attachment['approved']) + $have_unapproved = true; + + if (!$attachmentData[$i]['is_image']) + continue; + + $attachmentData[$i]['real_width'] = $attachment['width']; + $attachmentData[$i]['width'] = $attachment['width']; + $attachmentData[$i]['real_height'] = $attachment['height']; + $attachmentData[$i]['height'] = $attachment['height']; + + // Let's see, do we want thumbs? + if (!empty($modSettings['attachmentThumbnails']) && !empty($modSettings['attachmentThumbWidth']) && !empty($modSettings['attachmentThumbHeight']) && ($attachment['width'] > $modSettings['attachmentThumbWidth'] || $attachment['height'] > $modSettings['attachmentThumbHeight']) && strlen($attachment['filename']) < 249) + { + // A proper thumb doesn't exist yet? Create one! + if (empty($attachment['id_thumb']) || $attachment['thumb_width'] > $modSettings['attachmentThumbWidth'] || $attachment['thumb_height'] > $modSettings['attachmentThumbHeight'] || ($attachment['thumb_width'] < $modSettings['attachmentThumbWidth'] && $attachment['thumb_height'] < $modSettings['attachmentThumbHeight'])) + { + $filename = getAttachmentFilename($attachment['filename'], $attachment['id_attach'], $attachment['id_folder']); + + require_once($sourcedir . '/Subs-Graphics.php'); + if (createThumbnail($filename, $modSettings['attachmentThumbWidth'], $modSettings['attachmentThumbHeight'])) + { + // So what folder are we putting this image in? + if (!empty($modSettings['currentAttachmentUploadDir'])) + { + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = @unserialize($modSettings['attachmentUploadDir']); + $path = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']]; + $id_folder_thumb = $modSettings['currentAttachmentUploadDir']; + } + else + { + $path = $modSettings['attachmentUploadDir']; + $id_folder_thumb = 1; + } + + // Calculate the size of the created thumbnail. + $size = @getimagesize($filename . '_thumb'); + list ($attachment['thumb_width'], $attachment['thumb_height']) = $size; + $thumb_size = filesize($filename . '_thumb'); + + // These are the only valid image types for SMF. + $validImageTypes = array(1 => 'gif', 2 => 'jpeg', 3 => 'png', 5 => 'psd', 6 => 'bmp', 7 => 'tiff', 8 => 'tiff', 9 => 'jpeg', 14 => 'iff'); + + // What about the extension? + $thumb_ext = isset($validImageTypes[$size[2]]) ? $validImageTypes[$size[2]] : ''; + + // Figure out the mime type. + if (!empty($size['mime'])) + $thumb_mime = $size['mime']; + else + $thumb_mime = 'image/' . $thumb_ext; + + $thumb_filename = $attachment['filename'] . '_thumb'; + $thumb_hash = getAttachmentFilename($thumb_filename, false, null, true); + + // Add this beauty to the database. + $smcFunc['db_insert']('', + '{db_prefix}attachments', + array('id_folder' => 'int', 'id_msg' => 'int', 'attachment_type' => 'int', 'filename' => 'string', 'file_hash' => 'string', 'size' => 'int', 'width' => 'int', 'height' => 'int', 'fileext' => 'string', 'mime_type' => 'string'), + array($id_folder_thumb, $id_msg, 3, $thumb_filename, $thumb_hash, (int) $thumb_size, (int) $attachment['thumb_width'], (int) $attachment['thumb_height'], $thumb_ext, $thumb_mime), + array('id_attach') + ); + $old_id_thumb = $attachment['id_thumb']; + $attachment['id_thumb'] = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach'); + if (!empty($attachment['id_thumb'])) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET id_thumb = {int:id_thumb} + WHERE id_attach = {int:id_attach}', + array( + 'id_thumb' => $attachment['id_thumb'], + 'id_attach' => $attachment['id_attach'], + ) + ); + + $thumb_realname = getAttachmentFilename($thumb_filename, $attachment['id_thumb'], $id_folder_thumb, false, $thumb_hash); + rename($filename . '_thumb', $thumb_realname); + + // Do we need to remove an old thumbnail? + if (!empty($old_id_thumb)) + { + require_once($sourcedir . '/ManageAttachments.php'); + removeAttachments(array('id_attach' => $old_id_thumb), '', false, false); + } + } + } + } + + // Only adjust dimensions on successful thumbnail creation. + if (!empty($attachment['thumb_width']) && !empty($attachment['thumb_height'])) + { + $attachmentData[$i]['width'] = $attachment['thumb_width']; + $attachmentData[$i]['height'] = $attachment['thumb_height']; + } + } + + if (!empty($attachment['id_thumb'])) + $attachmentData[$i]['thumbnail'] = array( + 'id' => $attachment['id_thumb'], + 'href' => $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attachment['id_thumb'] . ';image', + ); + $attachmentData[$i]['thumbnail']['has_thumb'] = !empty($attachment['id_thumb']); + + // If thumbnails are disabled, check the maximum size of the image. + if (!$attachmentData[$i]['thumbnail']['has_thumb'] && ((!empty($modSettings['max_image_width']) && $attachment['width'] > $modSettings['max_image_width']) || (!empty($modSettings['max_image_height']) && $attachment['height'] > $modSettings['max_image_height']))) + { + if (!empty($modSettings['max_image_width']) && (empty($modSettings['max_image_height']) || $attachment['height'] * $modSettings['max_image_width'] / $attachment['width'] <= $modSettings['max_image_height'])) + { + $attachmentData[$i]['width'] = $modSettings['max_image_width']; + $attachmentData[$i]['height'] = floor($attachment['height'] * $modSettings['max_image_width'] / $attachment['width']); + } + elseif (!empty($modSettings['max_image_width'])) + { + $attachmentData[$i]['width'] = floor($attachment['width'] * $modSettings['max_image_height'] / $attachment['height']); + $attachmentData[$i]['height'] = $modSettings['max_image_height']; + } + } + elseif ($attachmentData[$i]['thumbnail']['has_thumb']) + { + // If the image is too large to show inline, make it a popup. + if (((!empty($modSettings['max_image_width']) && $attachmentData[$i]['real_width'] > $modSettings['max_image_width']) || (!empty($modSettings['max_image_height']) && $attachmentData[$i]['real_height'] > $modSettings['max_image_height']))) + $attachmentData[$i]['thumbnail']['javascript'] = 'return reqWin(\'' . $attachmentData[$i]['href'] . ';image\', ' . ($attachment['width'] + 20) . ', ' . ($attachment['height'] + 20) . ', true);'; + else + $attachmentData[$i]['thumbnail']['javascript'] = 'return expandThumb(' . $attachment['id_attach'] . ');'; + } + + if (!$attachmentData[$i]['thumbnail']['has_thumb']) + $attachmentData[$i]['downloads']++; + } + } + + // Do we need to instigate a sort? + if ($have_unapproved) + usort($attachmentData, 'approved_attach_sort'); + + return $attachmentData; +} + +// A sort function for putting unapproved attachments first. +function approved_attach_sort($a, $b) +{ + if ($a['is_approved'] == $b['is_approved']) + return 0; + + return $a['is_approved'] > $b['is_approved'] ? -1 : 1; +} + +// In-topic quick moderation. +function QuickInTopicModeration() +{ + global $sourcedir, $topic, $board, $user_info, $smcFunc, $modSettings, $context; + + // Check the session = get or post. + checkSession('request'); + + require_once($sourcedir . '/RemoveTopic.php'); + + if (empty($_REQUEST['msgs'])) + redirectexit('topic=' . $topic . '.' . $_REQUEST['start']); + + $messages = array(); + foreach ($_REQUEST['msgs'] as $dummy) + $messages[] = (int) $dummy; + + // We are restoring messages. We handle this in another place. + if (isset($_REQUEST['restore_selected'])) + redirectexit('action=restoretopic;msgs=' . implode(',', $messages) . ';' . $context['session_var'] . '=' . $context['session_id']); + + // Allowed to delete any message? + if (allowedTo('delete_any')) + $allowed_all = true; + // Allowed to delete replies to their messages? + elseif (allowedTo('delete_replies')) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member_started + FROM {db_prefix}topics + WHERE id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + list ($starter) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $allowed_all = $starter == $user_info['id']; + } + else + $allowed_all = false; + + // Make sure they're allowed to delete their own messages, if not any. + if (!$allowed_all) + isAllowedTo('delete_own'); + + // Allowed to remove which messages? + $request = $smcFunc['db_query']('', ' + SELECT id_msg, subject, id_member, poster_time + FROM {db_prefix}messages + WHERE id_msg IN ({array_int:message_list}) + AND id_topic = {int:current_topic}' . (!$allowed_all ? ' + AND id_member = {int:current_member}' : '') . ' + LIMIT ' . count($messages), + array( + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + 'message_list' => $messages, + ) + ); + $messages = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!$allowed_all && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time()) + continue; + + $messages[$row['id_msg']] = array($row['subject'], $row['id_member']); + } + $smcFunc['db_free_result']($request); + + // Get the first message in the topic - because you can't delete that! + $request = $smcFunc['db_query']('', ' + SELECT id_first_msg, id_last_msg + FROM {db_prefix}topics + WHERE id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + list ($first_message, $last_message) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Delete all the messages we know they can delete. ($messages) + foreach ($messages as $message => $info) + { + // Just skip the first message - if it's not the last. + if ($message == $first_message && $message != $last_message) + continue; + // If the first message is going then don't bother going back to the topic as we're effectively deleting it. + elseif ($message == $first_message) + $topicGone = true; + + removeMessage($message); + + // Log this moderation action ;). + if (allowedTo('delete_any') && (!allowedTo('delete_own') || $info[1] != $user_info['id'])) + logAction('delete', array('topic' => $topic, 'subject' => $info[0], 'member' => $info[1], 'board' => $board)); + } + + redirectexit(!empty($topicGone) ? 'board=' . $board : 'topic=' . $topic . '.' . $_REQUEST['start']); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DumpDatabase.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/DumpDatabase.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,182 @@ + '')); + $dbp = str_replace('_', '\_', $match[2]); + } + else + { + $db = false; + $dbp = $db_prefix; + } + + // Dump each table. + $tables = $smcFunc['db_list_tables'](false, $db_prefix . '%'); + foreach ($tables as $tableName) + { + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + // Are we dumping the structures? + if (isset($_REQUEST['struct'])) + { + echo + $crlf, + '--', $crlf, + '-- Table structure for table `', $tableName, '`', $crlf, + '--', $crlf, + $crlf, + $smcFunc['db_table_sql']($tableName), ';', $crlf; + } + + // How about the data? + if (!isset($_REQUEST['data']) || substr($tableName, -10) == 'log_errors') + continue; + + // Are there any rows in this table? + $get_rows = $smcFunc['db_insert_sql']($tableName); + + // No rows to get - skip it. + if (empty($get_rows)) + continue; + + echo + $crlf, + '--', $crlf, + '-- Dumping data in `', $tableName, '`', $crlf, + '--', $crlf, + $crlf, + $get_rows, + '-- --------------------------------------------------------', $crlf; + } + + echo + $crlf, + '-- Done', $crlf; + + exit; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Errors.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Errors.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,420 @@ + '<', '>' => '>', '"' => '"')); + $error_message = strtr($error_message, array('<br />' => '
', '<b>' => '', '</b>' => '', "\n" => '
')); + + // Add a file and line to the error message? + // Don't use the actual txt entries for file and line but instead use %1$s for file and %2$s for line + if ($file == null) + $file = ''; + else + // Window style slashes don't play well, lets convert them to the unix style. + $file = str_replace('\\', '/', $file); + + if ($line == null) + $line = 0; + else + $line = (int) $line; + + // Just in case there's no id_member or IP set yet. + if (empty($user_info['id'])) + $user_info['id'] = 0; + if (empty($user_info['ip'])) + $user_info['ip'] = ''; + + // Find the best query string we can... + $query_string = empty($_SERVER['QUERY_STRING']) ? (empty($_SERVER['REQUEST_URL']) ? '' : str_replace($scripturl, '', $_SERVER['REQUEST_URL'])) : $_SERVER['QUERY_STRING']; + + // Don't log the session hash in the url twice, it's a waste. + $query_string = htmlspecialchars((SMF == 'SSI' ? '' : '?') . preg_replace(array('~;sesc=[^&;]+~', '~' . session_name() . '=' . session_id() . '[&;]~'), array(';sesc', ''), $query_string)); + + // Just so we know what board error messages are from. + if (isset($_POST['board']) && !isset($_GET['board'])) + $query_string .= ($query_string == '' ? 'board=' : ';board=') . $_POST['board']; + + // What types of categories do we have? + $known_error_types = array( + 'general', + 'critical', + 'database', + 'undefined_vars', + 'user', + 'template', + 'debug', + ); + + // Make sure the category that was specified is a valid one + $error_type = in_array($error_type, $known_error_types) && $error_type !== true ? $error_type : 'general'; + + // Don't log the same error countless times, as we can get in a cycle of depression... + $error_info = array($user_info['id'], time(), $user_info['ip'], $query_string, $error_message, (string) $sc, $error_type, $file, $line); + if (empty($last_error) || $last_error != $error_info) + { + // Insert the error into the database. + $smcFunc['db_insert']('', + '{db_prefix}log_errors', + array('id_member' => 'int', 'log_time' => 'int', 'ip' => 'string-16', 'url' => 'string-65534', 'message' => 'string-65534', 'session' => 'string', 'error_type' => 'string', 'file' => 'string-255', 'line' => 'int'), + $error_info, + array('id_error') + ); + $last_error = $error_info; + } + + // Return the message to make things simpler. + return $error_message; +} + +// An irrecoverable error. +function fatal_error($error, $log = 'general') +{ + global $txt, $context, $modSettings; + + // We don't have $txt yet, but that's okay... + if (empty($txt)) + die($error); + + setup_fatal_error_context($log || (!empty($modSettings['enableErrorLogging']) && $modSettings['enableErrorLogging'] == 2) ? log_error($error, $log) : $error); +} + +// A fatal error with a message stored in the language file. +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); + + $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); +} + +// Handler for standard error messages. +function error_handler($error_level, $error_string, $file, $line) +{ + global $settings, $modSettings, $db_show_debug; + + // Ignore errors if we're ignoring them or they are strict notices from PHP 5 (which cannot be solved without breaking PHP 4.) + if (error_reporting() == 0 || (defined('E_STRICT') && $error_level == E_STRICT && (empty($modSettings['enableErrorLogging']) || $modSettings['enableErrorLogging'] != 2))) + return; + + if (strpos($file, 'eval()') !== false && !empty($settings['current_include_filename'])) + { + if (function_exists('debug_backtrace')) + { + $array = debug_backtrace(); + for ($i = 0; $i < count($array); $i++) + { + if ($array[$i]['function'] != 'loadSubTemplate') + continue; + + // This is a bug in PHP, with eval, it seems! + if (empty($array[$i]['args'])) + $i++; + break; + } + + if (isset($array[$i]) && !empty($array[$i]['args'])) + $file = realpath($settings['current_include_filename']) . ' (' . $array[$i]['args'][0] . ' sub template - eval?)'; + else + $file = realpath($settings['current_include_filename']) . ' (eval?)'; + } + else + $file = realpath($settings['current_include_filename']) . ' (eval?)'; + } + + if (isset($db_show_debug) && $db_show_debug === true) + { + // Commonly, undefined indexes will occur inside attributes; try to show them anyway! + if ($error_level % 255 != E_ERROR) + { + $temporary = ob_get_contents(); + if (substr($temporary, -2) == '="') + echo '"'; + } + + // Debugging! This should look like a PHP error message. + echo '
+', $error_level % 255 == E_ERROR ? 'Error' : ($error_level % 255 == E_WARNING ? 'Warning' : 'Notice'), ': ', $error_string, ' in ', $file, ' on line ', $line, '
'; + } + + $error_type = strpos(strtolower($error_string), 'undefined') !== false ? 'undefined_vars' : 'general'; + + $message = log_error($error_level . ': ' . $error_string, $error_type, $file, $line); + + // Let's give integrations a chance to ouput a bit differently + call_integration_hook('integrate_output_error', array($message, $error_type, $error_level, $file, $line)); + + // Dying on these errors only causes MORE problems (blank pages!) + if ($file == 'Unknown') + return; + + // If this is an E_ERROR or E_USER_ERROR.... die. Violently so. + if ($error_level % 255 == E_ERROR) + obExit(false); + else + return; + + // If this is an E_ERROR, E_USER_ERROR, E_WARNING, or E_USER_WARNING.... die. Violently so. + if ($error_level % 255 == E_ERROR || $error_level % 255 == E_WARNING) + fatal_error(allowedTo('admin_forum') ? $message : $error_string, false); + + // We should NEVER get to this point. Any fatal error MUST quit, or very bad things can happen. + if ($error_level % 255 == E_ERROR) + die('Hacking attempt...'); +} + +function setup_fatal_error_context($error_message) +{ + global $context, $txt, $ssi_on_error_method; + static $level = 0; + + // Attempt to prevent a recursive loop. + ++$level; + if ($level > 1) + return false; + + // Maybe they came from dlattach or similar? + if (SMF != 'SSI' && empty($context['theme_loaded'])) + loadTheme(); + + // Don't bother indexing errors mate... + $context['robot_no_index'] = true; + + if (!isset($context['error_title'])) + $context['error_title'] = $txt['error_occured']; + $context['error_message'] = isset($context['error_message']) ? $context['error_message'] : $error_message; + + if (empty($context['page_title'])) + $context['page_title'] = $context['error_title']; + + // Display the error message - wireless? + if (defined('WIRELESS') && WIRELESS) + $context['sub_template'] = WIRELESS_PROTOCOL . '_error'; + // Load the template and set the sub template. + else + { + loadTemplate('Errors'); + $context['sub_template'] = 'fatal_error'; + } + + // If this is SSI, what do they want us to do? + if (SMF == 'SSI') + { + if (!empty($ssi_on_error_method) && $ssi_on_error_method !== true && is_callable($ssi_on_error_method)) + $ssi_on_error_method(); + elseif (empty($ssi_on_error_method) || $ssi_on_error_method !== true) + loadSubTemplate('fatal_error'); + + // No layers? + if (empty($ssi_on_error_method) || $ssi_on_error_method !== true) + exit; + } + + // We want whatever for the header, and a footer. (footer includes sub template!) + obExit(null, true, false, true); + + /* DO NOT IGNORE: + If you are creating a bridge to SMF or modifying this function, you MUST + make ABSOLUTELY SURE that this function quits and DOES NOT RETURN TO NORMAL + PROGRAM FLOW. Otherwise, security error messages will not be shown, and + your forum will be in a very easily hackable state. + */ + trigger_error('Hacking attempt...', E_USER_ERROR); +} + +// Show an error message for the connection problems. +function show_db_error($loadavg = false) +{ + global $sourcedir, $mbname, $maintenance, $mtitle, $mmessage, $modSettings; + global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc; + + // Just check we're not in any buffers, just in case. + for ($i = ob_get_level(); $i > 0; $i--) + @ob_end_clean(); + + // Don't cache this page! + header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Cache-Control: no-cache'); + + // Send the right error codes. + header('HTTP/1.1 503 Service Temporarily Unavailable'); + header('Status: 503 Service Temporarily Unavailable'); + header('Retry-After: 3600'); + + if ($loadavg == false) + { + // For our purposes, we're gonna want this on if at all possible. + $modSettings['cache_enable'] = '1'; + + if (($temp = cache_get_data('db_last_error', 600)) !== null) + $db_last_error = max($db_last_error, $temp); + + if ($db_last_error < time() - 3600 * 24 * 3 && empty($maintenance) && !empty($db_error_send)) + { + require_once($sourcedir . '/Subs-Admin.php'); + + // Avoid writing to the Settings.php file if at all possible; use shared memory instead. + cache_put_data('db_last_error', time(), 600); + if (($temp = cache_get_data('db_last_error', 600)) == null) + updateLastDatabaseError(); + + // Language files aren't loaded yet :(. + $db_error = @$smcFunc['db_error']($db_connection); + @mail($webmaster_email, $mbname . ': SMF Database Error!', 'There has been a problem with the database!' . ($db_error == '' ? '' : "\n" . $smcFunc['db_title'] . ' reported:' . "\n" . $db_error) . "\n\n" . 'This is a notice email to let you know that SMF could not connect to the database, contact your host if this continues.'); + } + } + + if (!empty($maintenance)) + echo ' + + + + ', $mtitle, ' + + +

', $mtitle, '

+ ', $mmessage, ' + +'; + // If this is a load average problem, display an appropriate message (but we still don't have language files!) + elseif ($loadavg) + echo ' + + + + Temporarily Unavailable + + +

Temporarily Unavailable

+ Due to high stress on the server the forum is temporarily unavailable. Please try again later. + +'; + // What to do? Language files haven't and can't be loaded yet... + else + echo ' + + + + Connection Problems + + +

Connection Problems

+ Sorry, SMF was unable to connect to the database. This may be caused by the server being busy. Please try again later. + +'; + + die; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Groups.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Groups.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,967 @@ + array('GroupList', 'view_groups'), + 'members' => array('MembergroupMembers', 'view_groups'), + 'requests' => array('GroupRequests', 'group_requests'), + ); + + // Default to sub action 'index' or 'settings' depending on permissions. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'index'; + + // Get the template stuff up and running. + loadLanguage('ManageMembers'); + loadLanguage('ModerationCenter'); + loadTemplate('ManageMembergroups'); + + // If we can see the moderation center, and this has a mod bar entry, add the mod center bar. + if (allowedTo('access_mod_center') || $user_info['mod_cache']['bq'] != '0=1' || $user_info['mod_cache']['gq'] != '0=1' || allowedTo('manage_membergroups')) + { + require_once($sourcedir . '/ModerationCenter.php'); + $_GET['area'] = $_REQUEST['sa'] == 'requests' ? 'groups' : 'viewgroups'; + ModerationMain(true); + } + // Otherwise add something to the link tree, for normal people. + else + { + isAllowedTo('view_mlist'); + + $context['linktree'][] = array( + 'url' => $scripturl . '?action=groups', + 'name' => $txt['groups'], + ); + } + + // Call the actual function. + $subActions[$_REQUEST['sa']][0](); +} + +// This very simply lists the groups, nothing snazy. +function GroupList() +{ + global $txt, $scripturl, $user_profile, $user_info, $context, $settings, $modSettings, $smcFunc, $sourcedir; + + // Yep, find the groups... + $request = $smcFunc['db_query']('', ' + SELECT mg.id_group, mg.group_name, mg.description, mg.group_type, mg.online_color, mg.hidden, + mg.stars, IFNULL(gm.id_member, 0) AS can_moderate + FROM {db_prefix}membergroups AS mg + LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member}) + WHERE mg.min_posts = {int:min_posts} + AND mg.id_group != {int:mod_group}' . (allowedTo('admin_forum') ? '' : ' + AND mg.group_type != {int:is_protected}') . ' + ORDER BY group_name', + array( + 'current_member' => $user_info['id'], + 'min_posts' => -1, + 'mod_group' => 3, + 'is_protected' => 1, + ) + ); + // This is where we store our groups. + $context['groups'] = array(); + $group_ids = array(); + $context['can_moderate'] = allowedTo('manage_membergroups'); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // We only list the groups they can see. + if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups')) + continue; + + $row['stars'] = explode('#', $row['stars']); + + $context['groups'][$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'desc' => $row['description'], + 'color' => $row['online_color'], + 'type' => $row['group_type'], + 'num_members' => 0, + 'stars' => !empty($row['stars'][0]) && !empty($row['stars'][1]) ? str_repeat('*', $row['stars'][0]) : '', + ); + + $context['can_moderate'] |= $row['can_moderate']; + $group_ids[] = $row['id_group']; + } + $smcFunc['db_free_result']($request); + + // Count up the members separately... + if (!empty($group_ids)) + { + $query = $smcFunc['db_query']('', ' + SELECT id_group, COUNT(*) AS num_members + FROM {db_prefix}members + WHERE id_group IN ({array_int:group_list}) + GROUP BY id_group', + array( + 'group_list' => $group_ids, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $context['groups'][$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + + // Only do additional groups if we can moderate... + if ($context['can_moderate']) + { + $query = $smcFunc['db_query']('', ' + SELECT mg.id_group, COUNT(*) AS num_members + FROM {db_prefix}membergroups AS mg + INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_screen} + AND mem.id_group != mg.id_group + AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0) + WHERE mg.id_group IN ({array_int:group_list}) + GROUP BY mg.id_group', + array( + 'group_list' => $group_ids, + 'blank_screen' => '', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $context['groups'][$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + } + } + + $context['sub_template'] = 'group_index'; + $context['page_title'] = $txt['viewing_groups']; + + // Making a list is not hard with this beauty. + require_once($sourcedir . '/Subs-List.php'); + + // Use the standard templates for showing this. + $listOptions = array( + 'id' => 'group_lists', + 'title' => $context['page_title'], + 'get_items' => array( + 'function' => 'list_getGroups', + ), + 'columns' => array( + 'group' => array( + 'header' => array( + 'value' => $txt['name'], + ), + 'data' => array( + 'function' => create_function('$group', ' + global $scripturl, $context; + + $output = \'\' . $group[\'name\'] . \'\'; + + if ($group[\'desc\']) + $output .= \'
\' . $group[\'desc\'] . \'
\'; + + return $output; + '), + 'style' => 'width: 50%;', + ), + ), + 'stars' => array( + 'header' => array( + 'value' => $txt['membergroups_stars'], + ), + 'data' => array( + 'db' => 'stars', + ), + ), + 'moderators' => array( + 'header' => array( + 'value' => $txt['moderators'], + ), + 'data' => array( + 'function' => create_function('$group', ' + global $txt; + + return empty($group[\'moderators\']) ? \'\' . $txt[\'membergroups_new_copy_none\'] . \'\' : implode(\', \', $group[\'moderators\']); + '), + ), + ), + 'members' => array( + 'header' => array( + 'value' => $txt['membergroups_members_top'], + ), + 'data' => array( + 'comma_format' => true, + 'db' => 'num_members', + ), + ), + ), + ); + + // Create the request list. + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'group_lists'; +} + +// Get the group information for the list. +function list_getGroups($start, $items_per_page, $sort) +{ + global $smcFunc, $txt, $scripturl, $user_info, $settings; + + // Yep, find the groups... + $request = $smcFunc['db_query']('', ' + SELECT mg.id_group, mg.group_name, mg.description, mg.group_type, mg.online_color, mg.hidden, + mg.stars, IFNULL(gm.id_member, 0) AS can_moderate + FROM {db_prefix}membergroups AS mg + LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member}) + WHERE mg.min_posts = {int:min_posts} + AND mg.id_group != {int:mod_group}' . (allowedTo('admin_forum') ? '' : ' + AND mg.group_type != {int:is_protected}') . ' + ORDER BY group_name', + array( + 'current_member' => $user_info['id'], + 'min_posts' => -1, + 'mod_group' => 3, + 'is_protected' => 1, + ) + ); + // Start collecting the data. + $groups = array(); + $group_ids = array(); + $context['can_moderate'] = allowedTo('manage_membergroups'); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // We only list the groups they can see. + if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups')) + continue; + + $row['stars'] = explode('#', $row['stars']); + + $groups[$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'desc' => $row['description'], + 'color' => $row['online_color'], + 'type' => $row['group_type'], + 'num_members' => 0, + 'moderators' => array(), + 'stars' => !empty($row['stars'][0]) && !empty($row['stars'][1]) ? str_repeat('*', $row['stars'][0]) : '', + ); + + $context['can_moderate'] |= $row['can_moderate']; + $group_ids[] = $row['id_group']; + } + $smcFunc['db_free_result']($request); + + // Count up the members separately... + if (!empty($group_ids)) + { + $query = $smcFunc['db_query']('', ' + SELECT id_group, COUNT(*) AS num_members + FROM {db_prefix}members + WHERE id_group IN ({array_int:group_list}) + GROUP BY id_group', + array( + 'group_list' => $group_ids, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $groups[$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + + // Only do additional groups if we can moderate... + if ($context['can_moderate']) + { + $query = $smcFunc['db_query']('', ' + SELECT mg.id_group, COUNT(*) AS num_members + FROM {db_prefix}membergroups AS mg + INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_screen} + AND mem.id_group != mg.id_group + AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0) + WHERE mg.id_group IN ({array_int:group_list}) + GROUP BY mg.id_group', + array( + 'group_list' => $group_ids, + 'blank_screen' => '', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $groups[$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + } + } + + // Get any group moderators. + // Count up the members separately... + if (!empty($group_ids)) + { + $query = $smcFunc['db_query']('', ' + SELECT mods.id_group, mods.id_member, mem.member_name, mem.real_name + FROM {db_prefix}group_moderators AS mods + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member) + WHERE mods.id_group IN ({array_int:group_list})', + array( + 'group_list' => $group_ids, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $groups[$row['id_group']]['moderators'][] = '' . $row['real_name'] . ''; + $smcFunc['db_free_result']($query); + } + + return $groups; +} + +// How many groups are there that are visible? +function list_getGroupCount() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(id_group) AS group_count + FROM {db_prefix}membergroups + WHERE mg.min_posts = {int:min_posts} + AND mg.id_group != {int:mod_group}' . (allowedTo('admin_forum') ? '' : ' + AND mg.group_type != {int:is_protected}'), + array( + 'min_posts' => -1, + 'mod_group' => 3, + 'is_protected' => 1, + ) + ); + list ($group_count) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $group_count; +} + +// Display members of a group, and allow adding of members to a group. Silly function name though ;) +function MembergroupMembers() +{ + global $txt, $scripturl, $context, $modSettings, $sourcedir, $user_info, $settings, $smcFunc; + + $_REQUEST['group'] = isset($_REQUEST['group']) ? (int) $_REQUEST['group'] : 0; + + // No browsing of guests, membergroup 0 or moderators. + if (in_array($_REQUEST['group'], array(-1, 0, 3))) + fatal_lang_error('membergroup_does_not_exist', false); + + // Load up the group details. + $request = $smcFunc['db_query']('', ' + SELECT id_group AS id, group_name AS name, CASE WHEN min_posts = {int:min_posts} THEN 1 ELSE 0 END AS assignable, hidden, online_color, + stars, description, CASE WHEN min_posts != {int:min_posts} THEN 1 ELSE 0 END AS is_post_group, group_type + FROM {db_prefix}membergroups + WHERE id_group = {int:id_group} + LIMIT 1', + array( + 'min_posts' => -1, + 'id_group' => $_REQUEST['group'], + ) + ); + // Doesn't exist? + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('membergroup_does_not_exist', false); + $context['group'] = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Fix the stars. + $context['group']['stars'] = explode('#', $context['group']['stars']); + $context['group']['stars'] = !empty($context['group']['stars'][0]) && !empty($context['group']['stars'][1]) ? str_repeat('*', $context['group']['stars'][0]) : ''; + $context['group']['can_moderate'] = allowedTo('manage_membergroups') && (allowedTo('admin_forum') || $context['group']['group_type'] != 1); + + $context['linktree'][] = array( + 'url' => $scripturl . '?action=groups;sa=members;group=' . $context['group']['id'], + 'name' => $context['group']['name'], + ); + + // Load all the group moderators, for fun. + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member, mem.real_name + FROM {db_prefix}group_moderators AS mods + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member) + WHERE mods.id_group = {int:id_group}', + array( + 'id_group' => $_REQUEST['group'], + ) + ); + $context['group']['moderators'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['group']['moderators'][] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'] + ); + + if ($user_info['id'] == $row['id_member'] && $context['group']['group_type'] != 1) + $context['group']['can_moderate'] = true; + } + $smcFunc['db_free_result']($request); + + // If this group is hidden then it can only "exists" if the user can moderate it! + if ($context['group']['hidden'] && !$context['group']['can_moderate']) + fatal_lang_error('membergroup_does_not_exist', false); + + // You can only assign membership if you are the moderator and/or can manage groups! + if (!$context['group']['can_moderate']) + $context['group']['assignable'] = 0; + // Non-admins cannot assign admins. + elseif ($context['group']['id'] == 1 && !allowedTo('admin_forum')) + $context['group']['assignable'] = 0; + + // Removing member from group? + if (isset($_POST['remove']) && !empty($_REQUEST['rem']) && is_array($_REQUEST['rem']) && $context['group']['assignable']) + { + checkSession(); + + // Make sure we're dealing with integers only. + foreach ($_REQUEST['rem'] as $key => $group) + $_REQUEST['rem'][$key] = (int) $group; + + require_once($sourcedir . '/Subs-Membergroups.php'); + removeMembersFromGroups($_REQUEST['rem'], $_REQUEST['group'], true); + } + // Must be adding new members to the group... + elseif (isset($_REQUEST['add']) && (!empty($_REQUEST['toAdd']) || !empty($_REQUEST['member_add'])) && $context['group']['assignable']) + { + checkSession(); + + $member_query = array(); + $member_parameters = array(); + + // Get all the members to be added... taking into account names can be quoted ;) + $_REQUEST['toAdd'] = strtr($smcFunc['htmlspecialchars']($_REQUEST['toAdd'], ENT_QUOTES), array('"' => '"')); + preg_match_all('~"([^"]+)"~', $_REQUEST['toAdd'], $matches); + $member_names = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_REQUEST['toAdd'])))); + + foreach ($member_names as $index => $member_name) + { + $member_names[$index] = trim($smcFunc['strtolower']($member_names[$index])); + + if (strlen($member_names[$index]) == 0) + unset($member_names[$index]); + } + + // Any passed by ID? + $member_ids = array(); + if (!empty($_REQUEST['member_add'])) + foreach ($_REQUEST['member_add'] as $id) + if ($id > 0) + $member_ids[] = (int) $id; + + // Construct the query pelements. + if (!empty($member_ids)) + { + $member_query[] = 'id_member IN ({array_int:member_ids})'; + $member_parameters['member_ids'] = $member_ids; + } + if (!empty($member_names)) + { + $member_query[] = 'LOWER(member_name) IN ({array_string:member_names})'; + $member_query[] = 'LOWER(real_name) IN ({array_string:member_names})'; + $member_parameters['member_names'] = $member_names; + } + + $members = array(); + if (!empty($member_query)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE (' . implode(' OR ', $member_query) . ') + AND id_group != {int:id_group} + AND FIND_IN_SET({int:id_group}, additional_groups) = 0', + array_merge($member_parameters, array( + 'id_group' => $_REQUEST['group'], + )) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[] = $row['id_member']; + $smcFunc['db_free_result']($request); + } + + // !!! Add $_POST['additional'] to templates! + + // Do the updates... + if (!empty($members)) + { + require_once($sourcedir . '/Subs-Membergroups.php'); + addMembersToGroup($members, $_REQUEST['group'], isset($_POST['additional']) || $context['group']['hidden'] ? 'only_additional' : 'auto', true); + } + } + + // Sort out the sorting! + $sort_methods = array( + 'name' => 'real_name', + 'email' => allowedTo('moderate_forum') ? 'email_address' : 'hide_email ' . (isset($_REQUEST['desc']) ? 'DESC' : 'ASC') . ', email_address', + 'active' => 'last_login', + 'registered' => 'date_registered', + 'posts' => 'posts', + ); + + // They didn't pick one, default to by name.. + if (!isset($_REQUEST['sort']) || !isset($sort_methods[$_REQUEST['sort']])) + { + $context['sort_by'] = 'name'; + $querySort = 'real_name'; + } + // Otherwise default to ascending. + else + { + $context['sort_by'] = $_REQUEST['sort']; + $querySort = $sort_methods[$_REQUEST['sort']]; + } + + $context['sort_direction'] = isset($_REQUEST['desc']) ? 'down' : 'up'; + + // The where on the query is interesting. Non-moderators should only see people who are in this group as primary. + if ($context['group']['can_moderate']) + $where = $context['group']['is_post_group'] ? 'id_post_group = {int:group}' : 'id_group = {int:group} OR FIND_IN_SET({int:group}, additional_groups) != 0'; + else + $where = $context['group']['is_post_group'] ? 'id_post_group = {int:group}' : 'id_group = {int:group}'; + + // Count members of the group. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}members + WHERE ' . $where, + array( + 'group' => $_REQUEST['group'], + ) + ); + list ($context['total_members']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + $context['total_members'] = comma_format($context['total_members']); + + // Create the page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=' . ($context['group']['can_moderate'] ? 'moderate;area=viewgroups' : 'groups') . ';sa=members;group=' . $_REQUEST['group'] . ';sort=' . $context['sort_by'] . (isset($_REQUEST['desc']) ? ';desc' : ''), $_REQUEST['start'], $context['total_members'], $modSettings['defaultMaxMembers']); + $context['start'] = $_REQUEST['start']; + $context['can_moderate_forum'] = allowedTo('moderate_forum'); + + // Load up all members of this group. + $request = $smcFunc['db_query']('', ' + SELECT id_member, member_name, real_name, email_address, member_ip, date_registered, last_login, + hide_email, posts, is_activated, real_name + FROM {db_prefix}members + WHERE ' . $where . ' + ORDER BY ' . $querySort . ' ' . ($context['sort_direction'] == 'down' ? 'DESC' : 'ASC') . ' + LIMIT ' . $context['start'] . ', ' . $modSettings['defaultMaxMembers'], + array( + 'group' => $_REQUEST['group'], + ) + ); + $context['members'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $last_online = empty($row['last_login']) ? $txt['never'] : timeformat($row['last_login']); + + // Italicize the online note if they aren't activated. + if ($row['is_activated'] % 10 != 1) + $last_online = '' . $last_online . ''; + + $context['members'][] = array( + 'id' => $row['id_member'], + 'name' => '' . $row['real_name'] . '', + 'email' => $row['email_address'], + 'show_email' => showEmailAddress(!empty($row['hide_email']), $row['id_member']), + 'ip' => '' . $row['member_ip'] . '', + 'registered' => timeformat($row['date_registered']), + 'last_online' => $last_online, + 'posts' => comma_format($row['posts']), + 'is_activated' => $row['is_activated'] % 10 == 1, + ); + } + $smcFunc['db_free_result']($request); + + // Select the template. + $context['sub_template'] = 'group_members'; + $context['page_title'] = $txt['membergroups_members_title'] . ': ' . $context['group']['name']; +} + +// Show and manage all group requests. +function GroupRequests() +{ + global $txt, $context, $scripturl, $user_info, $sourcedir, $smcFunc, $modSettings, $language; + + // Set up the template stuff... + $context['page_title'] = $txt['mc_group_requests']; + $context['sub_template'] = 'show_list'; + + // Verify we can be here. + if ($user_info['mod_cache']['gq'] == '0=1') + isAllowedTo('manage_membergroups'); + + // Normally, we act normally... + $where = $user_info['mod_cache']['gq'] == '1=1' || $user_info['mod_cache']['gq'] == '0=1' ? $user_info['mod_cache']['gq'] : 'lgr.' . $user_info['mod_cache']['gq']; + $where_parameters = array(); + + // We've submitted? + if (isset($_POST[$context['session_var']]) && !empty($_POST['groupr']) && !empty($_POST['req_action'])) + { + checkSession('post'); + + // Clean the values. + foreach ($_POST['groupr'] as $k => $request) + $_POST['groupr'][$k] = (int) $request; + + // If we are giving a reason (And why shouldn't we?), then we don't actually do much. + if ($_POST['req_action'] == 'reason') + { + // Different sub template... + $context['sub_template'] = 'group_request_reason'; + // And a limitation. We don't care that the page number bit makes no sense, as we don't need it! + $where .= ' AND lgr.id_request IN ({array_int:request_ids})'; + $where_parameters['request_ids'] = $_POST['groupr']; + + $context['group_requests'] = list_getGroupRequests(0, $modSettings['defaultMaxMessages'], 'lgr.id_request', $where, $where_parameters); + + // Let obExit etc sort things out. + obExit(); + } + // Otherwise we do something! + else + { + // Get the details of all the members concerned... + $request = $smcFunc['db_query']('', ' + SELECT lgr.id_request, lgr.id_member, lgr.id_group, mem.email_address, mem.id_group AS primary_group, + mem.additional_groups AS additional_groups, mem.lngfile, mem.member_name, mem.notify_types, + mg.hidden, mg.group_name + FROM {db_prefix}log_group_requests AS lgr + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member) + INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group) + WHERE ' . $where . ' + AND lgr.id_request IN ({array_int:request_list}) + ORDER BY mem.lngfile', + array( + 'request_list' => $_POST['groupr'], + ) + ); + $email_details = array(); + $group_changes = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $row['lngfile'] = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']; + + // If we are approving work out what their new group is. + if ($_POST['req_action'] == 'approve') + { + // For people with more than one request at once. + if (isset($group_changes[$row['id_member']])) + { + $row['additional_groups'] = $group_changes[$row['id_member']]['add']; + $row['primary_group'] = $group_changes[$row['id_member']]['primary']; + } + else + $row['additional_groups'] = explode(',', $row['additional_groups']); + + // Don't have it already? + if ($row['primary_group'] == $row['id_group'] || in_array($row['id_group'], $row['additional_groups'])) + continue; + + // Should it become their primary? + if ($row['primary_group'] == 0 && $row['hidden'] == 0) + $row['primary_group'] = $row['id_group']; + else + $row['additional_groups'][] = $row['id_group']; + + // Add them to the group master list. + $group_changes[$row['id_member']] = array( + 'primary' => $row['primary_group'], + 'add' => $row['additional_groups'], + ); + } + + // Add required information to email them. + if ($row['notify_types'] != 4) + $email_details[] = array( + 'rid' => $row['id_request'], + 'member_id' => $row['id_member'], + 'member_name' => $row['member_name'], + 'group_id' => $row['id_group'], + 'group_name' => $row['group_name'], + 'email' => $row['email_address'], + 'language' => $row['lngfile'], + ); + } + $smcFunc['db_free_result']($request); + + // Remove the evidence... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_group_requests + WHERE id_request IN ({array_int:request_list})', + array( + 'request_list' => $_POST['groupr'], + ) + ); + + // Ensure everyone who is online gets their changes right away. + updateSettings(array('settings_updated' => time())); + + if (!empty($email_details)) + { + require_once($sourcedir . '/Subs-Post.php'); + + // They are being approved? + if ($_POST['req_action'] == 'approve') + { + // Make the group changes. + foreach ($group_changes as $id => $groups) + { + // Sanity check! + foreach ($groups['add'] as $key => $value) + if ($value == 0 || trim($value) == '') + unset($groups['add'][$key]); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_group = {int:primary_group}, additional_groups = {string:additional_groups} + WHERE id_member = {int:selected_member}', + array( + 'primary_group' => $groups['primary'], + 'selected_member' => $id, + 'additional_groups' => implode(',', $groups['add']), + ) + ); + } + + $lastLng = $user_info['language']; + foreach ($email_details as $email) + { + $replacements = array( + 'USERNAME' => $email['member_name'], + 'GROUPNAME' => $email['group_name'], + ); + + $emaildata = loadEmailTemplate('mc_group_approve', $replacements, $email['language']); + + sendmail($email['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 2); + } + } + // Otherwise, they are getting rejected (With or without a reason). + else + { + // Same as for approving, kind of. + $lastLng = $user_info['language']; + foreach ($email_details as $email) + { + $custom_reason = isset($_POST['groupreason']) && isset($_POST['groupreason'][$email['rid']]) ? $_POST['groupreason'][$email['rid']] : ''; + + $replacements = array( + 'USERNAME' => $email['member_name'], + 'GROUPNAME' => $email['group_name'], + ); + + if (!empty($custom_reason)) + $replacements['REASON'] = $custom_reason; + + $emaildata = loadEmailTemplate(empty($custom_reason) ? 'mc_group_reject' : 'mc_group_reject_reason', $replacements, $email['language']); + + sendmail($email['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 2); + } + } + } + + // Restore the current language. + loadLanguage('ModerationCenter'); + } + } + + // We're going to want this for making our list. + require_once($sourcedir . '/Subs-List.php'); + + // This is all the information required for a group listing. + $listOptions = array( + 'id' => 'group_request_list', + 'title' => $txt['mc_group_requests'], + 'width' => '100%', + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $txt['mc_groupr_none_found'], + 'base_href' => $scripturl . '?action=groups;sa=requests', + 'default_sort_col' => 'member', + 'get_items' => array( + 'function' => 'list_getGroupRequests', + 'params' => array( + $where, + $where_parameters, + ), + ), + 'get_count' => array( + 'function' => 'list_getGroupRequestCount', + 'params' => array( + $where, + $where_parameters, + ), + ), + 'columns' => array( + 'member' => array( + 'header' => array( + 'value' => $txt['mc_groupr_member'], + ), + 'data' => array( + 'db' => 'member_link', + ), + 'sort' => array( + 'default' => 'mem.member_name', + 'reverse' => 'mem.member_name DESC', + ), + ), + 'group' => array( + 'header' => array( + 'value' => $txt['mc_groupr_group'], + ), + 'data' => array( + 'db' => 'group_link', + ), + 'sort' => array( + 'default' => 'mg.group_name', + 'reverse' => 'mg.group_name DESC', + ), + ), + 'reason' => array( + 'header' => array( + 'value' => $txt['mc_groupr_reason'], + ), + 'data' => array( + 'db' => 'reason', + ), + ), + 'action' => array( + 'header' => array( + 'value' => '', + 'style' => 'width: 4%;', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id' => false, + ), + ), + 'style' => 'text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=groups;sa=requests', + 'include_sort' => true, + 'include_start' => true, + 'hidden_fields' => array( + $context['session_var'] => $context['session_id'], + ), + ), + 'additional_rows' => array( + array( + 'position' => 'bottom_of_list', + 'value' => ' + + ', + 'align' => 'right', + ), + ), + ); + + // Create the request list. + createList($listOptions); + + $context['default_list'] = 'group_request_list'; +} + +function list_getGroupRequestCount($where, $where_parameters) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_group_requests AS lgr + WHERE ' . $where, + array_merge($where_parameters, array( + )) + ); + list ($totalRequests) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $totalRequests; +} + +function list_getGroupRequests($start, $items_per_page, $sort, $where, $where_parameters) +{ + global $smcFunc, $txt, $scripturl; + + $request = $smcFunc['db_query']('', ' + SELECT lgr.id_request, lgr.id_member, lgr.id_group, lgr.time_applied, lgr.reason, + mem.member_name, mg.group_name, mg.online_color, mem.real_name + FROM {db_prefix}log_group_requests AS lgr + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member) + INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group) + WHERE ' . $where . ' + ORDER BY {raw:sort} + LIMIT ' . $start . ', ' . $items_per_page, + array_merge($where_parameters, array( + 'sort' => $sort, + )) + ); + $group_requests = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $group_requests[] = array( + 'id' => $row['id_request'], + 'member_link' => '' . $row['real_name'] . '', + 'group_link' => '' . $row['group_name'] . '', + 'reason' => censorText($row['reason']), + 'time_submitted' => timeformat($row['time_applied']), + ); + } + $smcFunc['db_free_result']($request); + + return $group_requests; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Help.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Help.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,111 @@ + 'Registering', + 'logging_in' => 'Logging_In', + 'profile' => 'Profile', + 'search' => 'Search', + 'posting' => 'Posting', + 'bbc' => 'Bulletin_board_code', + 'personal_messages' => 'Personal_messages', + 'memberlist' => 'Memberlist', + 'calendar' => 'Calendar', + 'features' => 'Features', + ); + + // Build the link tree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=help', + 'name' => $txt['help'], + ); + + // Lastly, some minor template stuff. + $context['page_title'] = $txt['manual_smf_user_help']; + $context['sub_template'] = 'manual'; +} + +// Show some of the more detailed help to give the admin an idea... +function ShowAdminHelp() +{ + global $txt, $helptxt, $context, $scripturl; + + if (!isset($_GET['help']) || !is_string($_GET['help'])) + fatal_lang_error('no_access', false); + + if (!isset($helptxt)) + $helptxt = array(); + + // Load the admin help language file and template. + loadLanguage('Help'); + + // Permission specific help? + if (isset($_GET['help']) && substr($_GET['help'], 0, 14) == 'permissionhelp') + loadLanguage('ManagePermissions'); + + loadTemplate('Help'); + + // Set the page title to something relevant. + $context['page_title'] = $context['forum_name'] . ' - ' . $txt['help']; + + // Don't show any template layers, just the popup sub template. + $context['template_layers'] = array(); + $context['sub_template'] = 'popup'; + + // What help string should be used? + if (isset($helptxt[$_GET['help']])) + $context['help_text'] = $helptxt[$_GET['help']]; + elseif (isset($txt[$_GET['help']])) + $context['help_text'] = $txt[$_GET['help']]; + else + $context['help_text'] = $_GET['help']; + + // Does this text contain a link that we should fill in? + if (preg_match('~%([0-9]+\$)?s\?~', $context['help_text'], $match)) + $context['help_text'] = sprintf($context['help_text'], $scripturl, $context['session_id'], $context['session_var']); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Karma.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Karma.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,204 @@ + {int:wait_time}', + array( + 'wait_time' => (int) ($modSettings['karmaWaitTime'] * 3600), + 'current_time' => time(), + ) + ); + + // Start off with no change in karma. + $action = 0; + + // Not an administrator... or one who is restricted as well. + if (!empty($modSettings['karmaTimeRestrictAdmins']) || !allowedTo('moderate_forum')) + { + // Find out if this user has done this recently... + $request = $smcFunc['db_query']('', ' + SELECT action + FROM {db_prefix}log_karma + WHERE id_target = {int:id_target} + AND id_executor = {int:current_member} + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'id_target' => $_REQUEST['uid'], + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + list ($action) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // They haven't, not before now, anyhow. + if (empty($action) || empty($modSettings['karmaWaitTime'])) + { + // Put it in the log. + $smcFunc['db_insert']('replace', + '{db_prefix}log_karma', + array('action' => 'int', 'id_target' => 'int', 'id_executor' => 'int', 'log_time' => 'int'), + array($dir, $_REQUEST['uid'], $user_info['id'], time()), + array('id_target', 'id_executor') + ); + + // Change by one. + updateMemberData($_REQUEST['uid'], array($dir == 1 ? 'karma_good' : 'karma_bad' => '+')); + } + else + { + // If you are gonna try to repeat.... don't allow it. + if ($action == $dir) + fatal_lang_error('karma_wait_time', false, array($modSettings['karmaWaitTime'], $txt['hours'])); + + // You decided to go back on your previous choice? + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_karma + SET action = {int:action}, log_time = {int:current_time} + WHERE id_target = {int:id_target} + AND id_executor = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'action' => $dir, + 'current_time' => time(), + 'id_target' => $_REQUEST['uid'], + ) + ); + + // It was recently changed the OTHER way... so... reverse it! + if ($dir == 1) + updateMemberData($_REQUEST['uid'], array('karma_good' => '+', 'karma_bad' => '-')); + else + updateMemberData($_REQUEST['uid'], array('karma_bad' => '+', 'karma_good' => '-')); + } + + // Figure out where to go back to.... the topic? + if (!empty($topic)) + redirectexit('topic=' . $topic . '.' . $_REQUEST['start'] . '#msg' . (int) $_REQUEST['m']); + // Hrm... maybe a personal message? + elseif (isset($_REQUEST['f'])) + redirectexit('action=pm;f=' . $_REQUEST['f'] . ';start=' . $_REQUEST['start'] . (isset($_REQUEST['l']) ? ';l=' . (int) $_REQUEST['l'] : '') . (isset($_REQUEST['pm']) ? '#' . (int) $_REQUEST['pm'] : '')); + // JavaScript as a last resort. + else + { + echo ' + + + ... + + + « +'; + + obExit(false); + } +} + +// What's this? I dunno, what are you talking about? Never seen this before, nope. No siree. +function BookOfUnknown() +{ + global $context; + + if (strpos($_GET['action'], 'mozilla') !== false && !$context['browser']['is_gecko']) + redirectexit('http://www.getfirefox.com/'); + elseif (strpos($_GET['action'], 'mozilla') !== false) + redirectexit('about:mozilla'); + + echo ' + + + The Book of Unknown, ', @$_GET['verse'] == '2:18' ? '2:18' : '4:16', ' + + + +
'; + if (@$_GET['verse'] == '2:18') + echo ' + Woe, it was that his name wasn\'t known, that he came in mystery, and was recognized by none. And it became to be in those days something.  Something not yet unknown to mankind.  And thus what was to be known the secret project began into its existence.  Henceforth the opposition was only weary and fearful, for now their match was at arms against them.'; + else + echo ' + And it came to pass that the unbelievers dwindled in number and saw rise of many proselytizers, and the opposition found fear in the face of the x and the j while those who stood with the something grew stronger and came together.  Still, this was only the beginning, and what lay in the future was unknown to all, even those on the right side.'; + echo ' +
+
'; + if (@$_GET['verse'] == '2:18') + echo ' + from The Book of Unknown, 2:18'; + else + echo ' + from The Book of Unknown, 4:16'; + echo ' +
+ +'; + + obExit(false); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Load.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Load.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2740 @@ + 999) + $modSettings['defaultMaxTopics'] = 20; + if (empty($modSettings['defaultMaxMessages']) || $modSettings['defaultMaxMessages'] <= 0 || $modSettings['defaultMaxMessages'] > 999) + $modSettings['defaultMaxMessages'] = 15; + if (empty($modSettings['defaultMaxMembers']) || $modSettings['defaultMaxMembers'] <= 0 || $modSettings['defaultMaxMembers'] > 999) + $modSettings['defaultMaxMembers'] = 30; + + if (!empty($modSettings['cache_enable'])) + cache_put_data('modSettings', $modSettings, 90); + } + + // UTF-8 in regular expressions is unsupported on PHP(win) versions < 4.2.3. + $utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8' && (strpos(strtolower(PHP_OS), 'win') === false || @version_compare(PHP_VERSION, '4.2.3') != -1); + + // Set a list of common functions. + $ent_list = empty($modSettings['disableEntityCheck']) ? '&(#\d{1,7}|quot|amp|lt|gt|nbsp);' : '&(#021|quot|amp|lt|gt|nbsp);'; + $ent_check = empty($modSettings['disableEntityCheck']) ? array('preg_replace(\'~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~e\', \'$smcFunc[\\\'entity_fix\\\'](\\\'\\2\\\')\', ', ')') : array('', ''); + + // Preg_replace can handle complex characters only for higher PHP versions. + $space_chars = $utf8 ? (@version_compare(PHP_VERSION, '4.3.3') != -1 ? '\x{A0}\x{AD}\x{2000}-\x{200F}\x{201F}\x{202F}\x{3000}\x{FEFF}' : "\xC2\xA0\xC2\xAD\xE2\x80\x80-\xE2\x80\x8F\xE2\x80\x9F\xE2\x80\xAF\xE2\x80\x9F\xE3\x80\x80\xEF\xBB\xBF") : '\x00-\x08\x0B\x0C\x0E-\x19\xA0'; + + $smcFunc += array( + 'entity_fix' => create_function('$string', ' + $num = substr($string, 0, 1) === \'x\' ? hexdec(substr($string, 1)) : (int) $string; + return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) || $num === 0x202E || $num === 0x202D ? \'\' : \'&#\' . $num . \';\';'), + 'htmlspecialchars' => create_function('$string, $quote_style = ENT_COMPAT, $charset = \'ISO-8859-1\'', ' + global $smcFunc; + return ' . strtr($ent_check[0], array('&' => '&')) . 'htmlspecialchars($string, $quote_style, ' . ($utf8 ? '\'UTF-8\'' : '$charset') . ')' . $ent_check[1] . ';'), + 'htmltrim' => create_function('$string', ' + global $smcFunc; + return preg_replace(\'~^(?:[ \t\n\r\x0B\x00' . $space_chars . ']| )+|(?:[ \t\n\r\x0B\x00' . $space_chars . ']| )+$~' . ($utf8 ? 'u' : '') . '\', \'\', ' . implode('$string', $ent_check) . ');'), + 'strlen' => create_function('$string', ' + global $smcFunc; + return strlen(preg_replace(\'~' . $ent_list . ($utf8 ? '|.~u' : '~') . '\', \'_\', ' . implode('$string', $ent_check) . '));'), + 'strpos' => create_function('$haystack, $needle, $offset = 0', ' + global $smcFunc; + $haystack_arr = preg_split(\'~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|"|&|<|>| |.)~' . ($utf8 ? 'u' : '') . '\', ' . implode('$haystack', $ent_check) . ', -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $haystack_size = count($haystack_arr); + if (strlen($needle) === 1) + { + $result = array_search($needle, array_slice($haystack_arr, $offset)); + return is_int($result) ? $result + $offset : false; + } + else + { + $needle_arr = preg_split(\'~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|"|&|<|>| |.)~' . ($utf8 ? 'u' : '') . '\', ' . implode('$needle', $ent_check) . ', -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $needle_size = count($needle_arr); + + $result = array_search($needle_arr[0], array_slice($haystack_arr, $offset)); + while (is_int($result)) + { + $offset += $result; + if (array_slice($haystack_arr, $offset, $needle_size) === $needle_arr) + return $offset; + $result = array_search($needle_arr[0], array_slice($haystack_arr, ++$offset)); + } + return false; + }'), + 'substr' => create_function('$string, $start, $length = null', ' + global $smcFunc; + $ent_arr = preg_split(\'~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|"|&|<|>| |.)~' . ($utf8 ? 'u' : '') . '\', ' . implode('$string', $ent_check) . ', -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + return $length === null ? implode(\'\', array_slice($ent_arr, $start)) : implode(\'\', array_slice($ent_arr, $start, $length));'), + 'strtolower' => $utf8 ? (function_exists('mb_strtolower') ? create_function('$string', ' + return mb_strtolower($string, \'UTF-8\');') : create_function('$string', ' + global $sourcedir; + require_once($sourcedir . \'/Subs-Charset.php\'); + return utf8_strtolower($string);')) : 'strtolower', + 'strtoupper' => $utf8 ? (function_exists('mb_strtoupper') ? create_function('$string', ' + return mb_strtoupper($string, \'UTF-8\');') : create_function('$string', ' + global $sourcedir; + require_once($sourcedir . \'/Subs-Charset.php\'); + return utf8_strtoupper($string);')) : 'strtoupper', + 'truncate' => create_function('$string, $length', (empty($modSettings['disableEntityCheck']) ? ' + global $smcFunc; + $string = ' . implode('$string', $ent_check) . ';' : '') . ' + preg_match(\'~^(' . $ent_list . '|.){\' . $smcFunc[\'strlen\'](substr($string, 0, $length)) . \'}~'. ($utf8 ? 'u' : '') . '\', $string, $matches); + $string = $matches[0]; + while (strlen($string) > $length) + $string = preg_replace(\'~(?:' . $ent_list . '|.)$~'. ($utf8 ? 'u' : '') . '\', \'\', $string); + return $string;'), + 'ucfirst' => $utf8 ? create_function('$string', ' + global $smcFunc; + return $smcFunc[\'strtoupper\']($smcFunc[\'substr\']($string, 0, 1)) . $smcFunc[\'substr\']($string, 1);') : 'ucfirst', + 'ucwords' => $utf8 ? create_function('$string', ' + global $smcFunc; + $words = preg_split(\'~([\s\r\n\t]+)~\', $string, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $n = count($words); $i < $n; $i += 2) + $words[$i] = $smcFunc[\'ucfirst\']($words[$i]); + return implode(\'\', $words);') : 'ucwords', + ); + + // Setting the timezone is a requirement for some functions in PHP >= 5.1. + if (isset($modSettings['default_timezone']) && function_exists('date_default_timezone_set')) + date_default_timezone_set($modSettings['default_timezone']); + + // Check the load averages? + if (!empty($modSettings['loadavg_enable'])) + { + if (($modSettings['load_average'] = cache_get_data('loadavg', 90)) == null) + { + $modSettings['load_average'] = @file_get_contents('/proc/loadavg'); + if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) != 0) + $modSettings['load_average'] = (float) $matches[1]; + elseif (($modSettings['load_average'] = @`uptime`) != null && preg_match('~load average[s]?: (\d+\.\d+), (\d+\.\d+), (\d+\.\d+)~i', $modSettings['load_average'], $matches) != 0) + $modSettings['load_average'] = (float) $matches[1]; + else + unset($modSettings['load_average']); + + if (!empty($modSettings['load_average'])) + cache_put_data('loadavg', $modSettings['load_average'], 90); + } + + if (!empty($modSettings['loadavg_forum']) && !empty($modSettings['load_average']) && $modSettings['load_average'] >= $modSettings['loadavg_forum']) + db_fatal_error(true); + } + + // Is post moderation alive and well? + $modSettings['postmod_active'] = isset($modSettings['admin_features']) ? in_array('pm', explode(',', $modSettings['admin_features'])) : true; + + // Integration is cool. + if (defined('SMF_INTEGRATION_SETTINGS')) + { + $integration_settings = unserialize(SMF_INTEGRATION_SETTINGS); + foreach ($integration_settings as $hook => $function) + add_integration_function($hook, $function, false); + } + + // Any files to pre include? + if (!empty($modSettings['integrate_pre_include'])) + { + $pre_includes = explode(',', $modSettings['integrate_pre_include']); + foreach ($pre_includes as $include) + { + $include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir)); + if (file_exists($include)) + require_once($include); + } + } + + // Call pre load integration functions. + call_integration_hook('integrate_pre_load'); +} + +// Load all the important user information... +function loadUserSettings() +{ + global $modSettings, $user_settings, $sourcedir, $smcFunc; + global $cookiename, $user_info, $language; + + // Check first the integration, then the cookie, and last the session. + if (count($integration_ids = call_integration_hook('integrate_verify_user')) > 0) + { + $id_member = 0; + foreach ($integration_ids as $integration_id) + { + $integration_id = (int) $integration_id; + if ($integration_id > 0) + { + $id_member = $integration_id; + $already_verified = true; + break; + } + } + } + else + $id_member = 0; + + if (empty($id_member) && isset($_COOKIE[$cookiename])) + { + // Fix a security hole in PHP 4.3.9 and below... + if (preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}$~i', $_COOKIE[$cookiename]) == 1) + { + list ($id_member, $password) = @unserialize($_COOKIE[$cookiename]); + $id_member = !empty($id_member) && strlen($password) > 0 ? (int) $id_member : 0; + } + else + $id_member = 0; + } + elseif (empty($id_member) && isset($_SESSION['login_' . $cookiename]) && ($_SESSION['USER_AGENT'] == $_SERVER['HTTP_USER_AGENT'] || !empty($modSettings['disableCheckUA']))) + { + // !!! Perhaps we can do some more checking on this, such as on the first octet of the IP? + list ($id_member, $password, $login_span) = @unserialize($_SESSION['login_' . $cookiename]); + $id_member = !empty($id_member) && strlen($password) == 40 && $login_span > time() ? (int) $id_member : 0; + } + + // Only load this stuff if the user isn't a guest. + if ($id_member != 0) + { + // Is the member data cached? + if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < 2 || ($user_settings = cache_get_data('user_settings-' . $id_member, 60)) == null) + { + $request = $smcFunc['db_query']('', ' + SELECT mem.*, IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type + FROM {db_prefix}members AS mem + LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = {int:id_member}) + WHERE mem.id_member = {int:id_member} + LIMIT 1', + array( + 'id_member' => $id_member, + ) + ); + $user_settings = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2) + cache_put_data('user_settings-' . $id_member, $user_settings, 60); + } + + // Did we find 'im? If not, junk it. + if (!empty($user_settings)) + { + // As much as the password should be right, we can assume the integration set things up. + if (!empty($already_verified) && $already_verified === true) + $check = true; + // SHA-1 passwords should be 40 characters long. + elseif (strlen($password) == 40) + $check = sha1($user_settings['passwd'] . $user_settings['password_salt']) == $password; + else + $check = false; + + // Wrong password or not activated - either way, you're going nowhere. + $id_member = $check && ($user_settings['is_activated'] == 1 || $user_settings['is_activated'] == 11) ? $user_settings['id_member'] : 0; + } + else + $id_member = 0; + + // If we no longer have the member maybe they're being all hackey, stop brute force! + if (!$id_member) + { + require_once($sourcedir . '/LogInOut.php'); + validatePasswordFlood(!empty($user_settings['id_member']) ? $user_settings['id_member'] : $id_member, !empty($user_settings['passwd_flood']) ? $user_settings['passwd_flood'] : false, $id_member != 0); + } + } + + // Found 'im, let's set up the variables. + if ($id_member != 0) + { + // Let's not update the last visit time in these cases... + // 1. SSI doesn't count as visiting the forum. + // 2. RSS feeds and XMLHTTP requests don't count either. + // 3. If it was set within this session, no need to set it again. + // 4. New session, yet updated < five hours ago? Maybe cache can help. + if (SMF != 'SSI' && !isset($_REQUEST['xml']) && (!isset($_REQUEST['action']) || $_REQUEST['action'] != '.xml') && empty($_SESSION['id_msg_last_visit']) && (empty($modSettings['cache_enable']) || ($_SESSION['id_msg_last_visit'] = cache_get_data('user_last_visit-' . $id_member, 5 * 3600)) === null)) + { + // Do a quick query to make sure this isn't a mistake. + $result = $smcFunc['db_query']('', ' + SELECT poster_time + FROM {db_prefix}messages + WHERE id_msg = {int:id_msg} + LIMIT 1', + array( + 'id_msg' => $user_settings['id_msg_last_visit'], + ) + ); + list ($visitTime) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + $_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit']; + + // If it was *at least* five hours ago... + if ($visitTime < time() - 5 * 3600) + { + updateMemberData($id_member, array('id_msg_last_visit' => (int) $modSettings['maxMsgID'], 'last_login' => time(), 'member_ip' => $_SERVER['REMOTE_ADDR'], 'member_ip2' => $_SERVER['BAN_CHECK_IP'])); + $user_settings['last_login'] = time(); + + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2) + cache_put_data('user_settings-' . $id_member, $user_settings, 60); + + if (!empty($modSettings['cache_enable'])) + cache_put_data('user_last_visit-' . $id_member, $_SESSION['id_msg_last_visit'], 5 * 3600); + } + } + elseif (empty($_SESSION['id_msg_last_visit'])) + $_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit']; + + $username = $user_settings['member_name']; + + if (empty($user_settings['additional_groups'])) + $user_info = array( + 'groups' => array($user_settings['id_group'], $user_settings['id_post_group']) + ); + else + $user_info = array( + 'groups' => array_merge( + array($user_settings['id_group'], $user_settings['id_post_group']), + explode(',', $user_settings['additional_groups']) + ) + ); + + // Because history has proven that it is possible for groups to go bad - clean up in case. + foreach ($user_info['groups'] as $k => $v) + $user_info['groups'][$k] = (int) $v; + + // This is a logged in user, so definitely not a spider. + $user_info['possibly_robot'] = false; + } + // If the user is a guest, initialize all the critical user settings. + else + { + // This is what a guest's variables should be. + $username = ''; + $user_info = array('groups' => array(-1)); + $user_settings = array(); + + if (isset($_COOKIE[$cookiename])) + $_COOKIE[$cookiename] = ''; + + // Do we perhaps think this is a search robot? Check every five minutes just in case... + if ((!empty($modSettings['spider_mode']) || !empty($modSettings['spider_group'])) && (!isset($_SESSION['robot_check']) || $_SESSION['robot_check'] < time() - 300)) + { + require_once($sourcedir . '/ManageSearchEngines.php'); + $user_info['possibly_robot'] = SpiderCheck(); + } + elseif (!empty($modSettings['spider_mode'])) + $user_info['possibly_robot'] = isset($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0; + // If we haven't turned on proper spider hunts then have a guess! + else + { + $ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']); + $user_info['possibly_robot'] = (strpos($_SERVER['HTTP_USER_AGENT'], 'Mozilla') === false && strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') === false) || strpos($ci_user_agent, 'googlebot') !== false || strpos($ci_user_agent, 'slurp') !== false || strpos($ci_user_agent, 'crawl') !== false; + } + } + + // Set up the $user_info array. + $user_info += array( + 'id' => $id_member, + 'username' => $username, + 'name' => isset($user_settings['real_name']) ? $user_settings['real_name'] : '', + 'email' => isset($user_settings['email_address']) ? $user_settings['email_address'] : '', + 'passwd' => isset($user_settings['passwd']) ? $user_settings['passwd'] : '', + 'language' => empty($user_settings['lngfile']) || empty($modSettings['userLanguage']) ? $language : $user_settings['lngfile'], + 'is_guest' => $id_member == 0, + 'is_admin' => in_array(1, $user_info['groups']), + 'theme' => empty($user_settings['id_theme']) ? 0 : $user_settings['id_theme'], + 'last_login' => empty($user_settings['last_login']) ? 0 : $user_settings['last_login'], + 'ip' => $_SERVER['REMOTE_ADDR'], + 'ip2' => $_SERVER['BAN_CHECK_IP'], + 'posts' => empty($user_settings['posts']) ? 0 : $user_settings['posts'], + 'time_format' => empty($user_settings['time_format']) ? $modSettings['time_format'] : $user_settings['time_format'], + 'time_offset' => empty($user_settings['time_offset']) ? 0 : $user_settings['time_offset'], + 'avatar' => array( + 'url' => isset($user_settings['avatar']) ? $user_settings['avatar'] : '', + 'filename' => empty($user_settings['filename']) ? '' : $user_settings['filename'], + 'custom_dir' => !empty($user_settings['attachment_type']) && $user_settings['attachment_type'] == 1, + 'id_attach' => isset($user_settings['id_attach']) ? $user_settings['id_attach'] : 0 + ), + 'smiley_set' => isset($user_settings['smiley_set']) ? $user_settings['smiley_set'] : '', + 'messages' => empty($user_settings['instant_messages']) ? 0 : $user_settings['instant_messages'], + 'unread_messages' => empty($user_settings['unread_messages']) ? 0 : $user_settings['unread_messages'], + 'total_time_logged_in' => empty($user_settings['total_time_logged_in']) ? 0 : $user_settings['total_time_logged_in'], + 'buddies' => !empty($modSettings['enable_buddylist']) && !empty($user_settings['buddy_list']) ? explode(',', $user_settings['buddy_list']) : array(), + 'ignoreboards' => !empty($user_settings['ignore_boards']) && !empty($modSettings['allow_ignore_boards']) ? explode(',', $user_settings['ignore_boards']) : array(), + 'ignoreusers' => !empty($user_settings['pm_ignore_list']) ? explode(',', $user_settings['pm_ignore_list']) : array(), + 'warning' => isset($user_settings['warning']) ? $user_settings['warning'] : 0, + 'permissions' => array(), + ); + $user_info['groups'] = array_unique($user_info['groups']); + // Make sure that the last item in the ignore boards array is valid. If the list was too long it could have an ending comma that could cause problems. + if (!empty($user_info['ignoreboards']) && empty($user_info['ignoreboards'][$tmp = count($user_info['ignoreboards']) - 1])) + unset($user_info['ignoreboards'][$tmp]); + + // Do we have any languages to validate this? + if (!empty($modSettings['userLanguage']) && (!empty($_GET['language']) || !empty($_SESSION['language']))) + $languages = getLanguages(); + + // Allow the user to change their language if its valid. + if (!empty($modSettings['userLanguage']) && !empty($_GET['language']) && isset($languages[strtr($_GET['language'], './\\:', '____')])) + { + $user_info['language'] = strtr($_GET['language'], './\\:', '____'); + $_SESSION['language'] = $user_info['language']; + } + elseif (!empty($modSettings['userLanguage']) && !empty($_SESSION['language']) && isset($languages[strtr($_SESSION['language'], './\\:', '____')])) + $user_info['language'] = strtr($_SESSION['language'], './\\:', '____'); + + // Just build this here, it makes it easier to change/use - administrators can see all boards. + if ($user_info['is_admin']) + $user_info['query_see_board'] = '1=1'; + // Otherwise just the groups in $user_info['groups']. + else + $user_info['query_see_board'] = '(FIND_IN_SET(' . implode(', b.member_groups) != 0 OR FIND_IN_SET(', $user_info['groups']) . ', b.member_groups) != 0' . (isset($user_info['mod_cache']) ? ' OR ' . $user_info['mod_cache']['mq'] : '') . ')'; + + // Build the list of boards they WANT to see. + // This will take the place of query_see_boards in certain spots, so it better include the boards they can see also + + // If they aren't ignoring any boards then they want to see all the boards they can see + if (empty($user_info['ignoreboards'])) + $user_info['query_wanna_see_board'] = $user_info['query_see_board']; + // Ok I guess they don't want to see all the boards + else + $user_info['query_wanna_see_board'] = '(' . $user_info['query_see_board'] . ' AND b.id_board NOT IN (' . implode(',', $user_info['ignoreboards']) . '))'; +} + +// Check for moderators and see if they have access to the board. +function loadBoard() +{ + global $txt, $scripturl, $context, $modSettings; + global $board_info, $board, $topic, $user_info, $smcFunc; + + // Assume they are not a moderator. + $user_info['is_mod'] = false; + $context['user']['is_mod'] = &$user_info['is_mod']; + + // Start the linktree off empty.. + $context['linktree'] = array(); + + // Have they by chance specified a message id but nothing else? + if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg'])) + { + // Make sure the message id is really an int. + $_REQUEST['msg'] = (int) $_REQUEST['msg']; + + // Looking through the message table can be slow, so try using the cache first. + if (($topic = cache_get_data('msg_topic-' . $_REQUEST['msg'], 120)) === NULL) + { + $request = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}messages + WHERE id_msg = {int:id_msg} + LIMIT 1', + array( + 'id_msg' => $_REQUEST['msg'], + ) + ); + + // So did it find anything? + if ($smcFunc['db_num_rows']($request)) + { + list ($topic) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + // Save save save. + cache_put_data('msg_topic-' . $_REQUEST['msg'], $topic, 120); + } + } + + // Remember redirection is the key to avoiding fallout from your bosses. + if (!empty($topic)) + redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']); + else + { + loadPermissions(); + loadTheme(); + fatal_lang_error('topic_gone', false); + } + } + + // Load this board only if it is specified. + if (empty($board) && empty($topic)) + { + $board_info = array('moderators' => array()); + return; + } + + if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3)) + { + // !!! SLOW? + if (!empty($topic)) + $temp = cache_get_data('topic_board-' . $topic, 120); + else + $temp = cache_get_data('board-' . $board, 120); + + if (!empty($temp)) + { + $board_info = $temp; + $board = $board_info['id']; + } + } + + if (empty($temp)) + { + $request = $smcFunc['db_query']('', ' + SELECT + c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, + b.id_parent, c.name AS cname, IFNULL(mem.id_member, 0) AS id_moderator, + mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level, + b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect, + b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . ' + FROM {db_prefix}boards AS b' . (!empty($topic) ? ' + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . ' + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link}) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member) + WHERE b.id_board = {raw:board_link}', + array( + 'current_topic' => $topic, + 'board_link' => empty($topic) ? $smcFunc['db_quote']('{int:current_board}', array('current_board' => $board)) : 't.id_board', + ) + ); + // If there aren't any, skip. + if ($smcFunc['db_num_rows']($request) > 0) + { + $row = $smcFunc['db_fetch_assoc']($request); + + // Set the current board. + if (!empty($row['id_board'])) + $board = $row['id_board']; + + // Basic operating information. (globals... :/) + $board_info = array( + 'id' => $board, + 'moderators' => array(), + 'cat' => array( + 'id' => $row['id_cat'], + 'name' => $row['cname'] + ), + 'name' => $row['bname'], + 'description' => $row['description'], + 'num_topics' => $row['num_topics'], + 'unapproved_topics' => $row['unapproved_topics'], + 'unapproved_posts' => $row['unapproved_posts'], + 'unapproved_user_topics' => 0, + 'parent_boards' => getBoardParents($row['id_parent']), + 'parent' => $row['id_parent'], + 'child_level' => $row['child_level'], + 'theme' => $row['id_theme'], + 'override_theme' => !empty($row['override_theme']), + 'profile' => $row['id_profile'], + 'redirect' => $row['redirect'], + 'posts_count' => empty($row['count_posts']), + 'cur_topic_approved' => empty($topic) || $row['approved'], + 'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'], + ); + + // Load the membergroups allowed, and check permissions. + $board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']); + + do + { + if (!empty($row['id_moderator'])) + $board_info['moderators'][$row['id_moderator']] = array( + 'id' => $row['id_moderator'], + 'name' => $row['real_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'], + 'link' => '' . $row['real_name'] . '' + ); + } + while ($row = $smcFunc['db_fetch_assoc']($request)); + + // If the board only contains unapproved posts and the user isn't an approver then they can't see any topics. + // If that is the case do an additional check to see if they have any topics waiting to be approved. + if ($board_info['num_topics'] == 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts')) + { + $smcFunc['db_free_result']($request); // Free the previous result + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(id_topic) + FROM {db_prefix}topics + WHERE id_member_started={int:id_member} + AND approved = {int:unapproved} + AND id_board = {int:board}', + array( + 'id_member' => $user_info['id'], + 'unapproved' => 0, + 'board' => $board, + ) + ); + + list ($board_info['unapproved_user_topics']) = $smcFunc['db_fetch_row']($request); + } + + if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3)) + { + // !!! SLOW? + if (!empty($topic)) + cache_put_data('topic_board-' . $topic, $board_info, 120); + cache_put_data('board-' . $board, $board_info, 120); + } + } + else + { + // Otherwise the topic is invalid, there are no moderators, etc. + $board_info = array( + 'moderators' => array(), + 'error' => 'exist' + ); + $topic = null; + $board = 0; + } + $smcFunc['db_free_result']($request); + } + + if (!empty($topic)) + $_GET['board'] = (int) $board; + + if (!empty($board)) + { + // Now check if the user is a moderator. + $user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]); + + if (count(array_intersect($user_info['groups'], $board_info['groups'])) == 0 && !$user_info['is_admin']) + $board_info['error'] = 'access'; + + // Build up the linktree. + $context['linktree'] = array_merge( + $context['linktree'], + array(array( + 'url' => $scripturl . '#c' . $board_info['cat']['id'], + 'name' => $board_info['cat']['name'] + )), + array_reverse($board_info['parent_boards']), + array(array( + 'url' => $scripturl . '?board=' . $board . '.0', + 'name' => $board_info['name'] + )) + ); + } + + // Set the template contextual information. + $context['user']['is_mod'] = &$user_info['is_mod']; + $context['current_topic'] = $topic; + $context['current_board'] = $board; + + // Hacker... you can't see this topic, I'll tell you that. (but moderators can!) + if (!empty($board_info['error']) && ($board_info['error'] != 'access' || !$user_info['is_mod'])) + { + // The permissions and theme need loading, just to make sure everything goes smoothly. + loadPermissions(); + loadTheme(); + + $_GET['board'] = ''; + $_GET['topic'] = ''; + + // The linktree should not give the game away mate! + $context['linktree'] = array( + array( + 'url' => $scripturl, + 'name' => $context['forum_name_html_safe'] + ) + ); + + // If it's a prefetching agent or we're requesting an attachment. + if ((isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') || (!empty($_REQUEST['action']) && $_REQUEST['action'] === 'dlattach')) + { + ob_end_clean(); + header('HTTP/1.1 403 Forbidden'); + die; + } + elseif ($user_info['is_guest']) + { + loadLanguage('Errors'); + is_not_guest($txt['topic_gone']); + } + else + fatal_lang_error('topic_gone', false); + } + + if ($user_info['is_mod']) + $user_info['groups'][] = 3; +} + +// Load this user's permissions. +function loadPermissions() +{ + global $user_info, $board, $board_info, $modSettings, $smcFunc, $sourcedir; + + if ($user_info['is_admin']) + { + banPermissions(); + return; + } + + if (!empty($modSettings['cache_enable'])) + { + $cache_groups = $user_info['groups']; + asort($cache_groups); + $cache_groups = implode(',', $cache_groups); + // If it's a spider then cache it different. + if ($user_info['possibly_robot']) + $cache_groups .= '-spider'; + + if ($modSettings['cache_enable'] >= 2 && !empty($board) && ($temp = cache_get_data('permissions:' . $cache_groups . ':' . $board, 240)) != null && time() - 240 > $modSettings['settings_updated']) + { + list ($user_info['permissions']) = $temp; + banPermissions(); + + return; + } + elseif (($temp = cache_get_data('permissions:' . $cache_groups, 240)) != null && time() - 240 > $modSettings['settings_updated']) + list ($user_info['permissions'], $removals) = $temp; + } + + // If it is detected as a robot, and we are restricting permissions as a special group - then implement this. + $spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : ''; + + if (empty($user_info['permissions'])) + { + // Get the general permissions. + $request = $smcFunc['db_query']('', ' + SELECT permission, add_deny + FROM {db_prefix}permissions + WHERE id_group IN ({array_int:member_groups}) + ' . $spider_restrict, + array( + 'member_groups' => $user_info['groups'], + 'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0, + ) + ); + $removals = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($row['add_deny'])) + $removals[] = $row['permission']; + else + $user_info['permissions'][] = $row['permission']; + } + $smcFunc['db_free_result']($request); + + if (isset($cache_groups)) + cache_put_data('permissions:' . $cache_groups, array($user_info['permissions'], $removals), 240); + } + + // Get the board permissions. + if (!empty($board)) + { + // Make sure the board (if any) has been loaded by loadBoard(). + if (!isset($board_info['profile'])) + fatal_lang_error('no_board'); + + $request = $smcFunc['db_query']('', ' + SELECT permission, add_deny + FROM {db_prefix}board_permissions + WHERE (id_group IN ({array_int:member_groups}) + ' . $spider_restrict . ') + AND id_profile = {int:id_profile}', + array( + 'member_groups' => $user_info['groups'], + 'id_profile' => $board_info['profile'], + 'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($row['add_deny'])) + $removals[] = $row['permission']; + else + $user_info['permissions'][] = $row['permission']; + } + $smcFunc['db_free_result']($request); + } + + // Remove all the permissions they shouldn't have ;). + if (!empty($modSettings['permission_enable_deny'])) + $user_info['permissions'] = array_diff($user_info['permissions'], $removals); + + if (isset($cache_groups) && !empty($board) && $modSettings['cache_enable'] >= 2) + cache_put_data('permissions:' . $cache_groups . ':' . $board, array($user_info['permissions'], null), 240); + + // Banned? Watch, don't touch.. + banPermissions(); + + // Load the mod cache so we can know what additional boards they should see, but no sense in doing it for guests + if (!$user_info['is_guest']) + { + if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated']) + { + require_once($sourcedir . '/Subs-Auth.php'); + rebuildModCache(); + } + else + $user_info['mod_cache'] = $_SESSION['mc']; + } +} + +// Loads an array of users' data by ID or member_name. +function loadMemberData($users, $is_name = false, $set = 'normal') +{ + global $user_profile, $modSettings, $board_info, $smcFunc; + + // Can't just look for no users :P. + if (empty($users)) + return false; + + // Make sure it's an array. + $users = !is_array($users) ? array($users) : array_unique($users); + $loaded_ids = array(); + + if (!$is_name && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3) + { + $users = array_values($users); + for ($i = 0, $n = count($users); $i < $n; $i++) + { + $data = cache_get_data('member_data-' . $set . '-' . $users[$i], 240); + if ($data == null) + continue; + + $loaded_ids[] = $data['id_member']; + $user_profile[$data['id_member']] = $data; + unset($users[$i]); + } + } + + if ($set == 'normal') + { + $select_columns = ' + IFNULL(lo.log_time, 0) AS is_online, IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type, + mem.signature, mem.personal_text, mem.location, mem.gender, mem.avatar, mem.id_member, mem.member_name, + mem.real_name, mem.email_address, mem.hide_email, mem.date_registered, mem.website_title, mem.website_url, + mem.birthdate, mem.member_ip, mem.member_ip2, mem.icq, mem.aim, mem.yim, mem.msn, mem.posts, mem.last_login, + mem.karma_good, mem.id_post_group, mem.karma_bad, mem.lngfile, mem.id_group, mem.time_offset, mem.show_online, + mem.buddy_list, mg.online_color AS member_group_color, IFNULL(mg.group_name, {string:blank_string}) AS member_group, + pg.online_color AS post_group_color, IFNULL(pg.group_name, {string:blank_string}) AS post_group, mem.is_activated, mem.warning, + CASE WHEN mem.id_group = 0 OR mg.stars = {string:blank_string} THEN pg.stars ELSE mg.stars END AS stars' . (!empty($modSettings['titlesEnable']) ? ', + mem.usertitle' : ''); + $select_tables = ' + LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member) + LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member) + LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group) + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)'; + } + elseif ($set == 'profile') + { + $select_columns = ' + IFNULL(lo.log_time, 0) AS is_online, IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type, + mem.signature, mem.personal_text, mem.location, mem.gender, mem.avatar, mem.id_member, mem.member_name, + mem.real_name, mem.email_address, mem.hide_email, mem.date_registered, mem.website_title, mem.website_url, + mem.openid_uri, mem.birthdate, mem.icq, mem.aim, mem.yim, mem.msn, mem.posts, mem.last_login, mem.karma_good, + mem.karma_bad, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group, mem.id_theme, mem.buddy_list, + mem.pm_ignore_list, mem.pm_email_notify, mem.pm_receive_from, mem.time_offset' . (!empty($modSettings['titlesEnable']) ? ', mem.usertitle' : '') . ', + mem.time_format, mem.secret_question, mem.is_activated, mem.additional_groups, mem.smiley_set, mem.show_online, + mem.total_time_logged_in, mem.id_post_group, mem.notify_announcements, mem.notify_regularity, mem.notify_send_body, + mem.notify_types, lo.url, mg.online_color AS member_group_color, IFNULL(mg.group_name, {string:blank_string}) AS member_group, + pg.online_color AS post_group_color, IFNULL(pg.group_name, {string:blank_string}) AS post_group, mem.ignore_boards, mem.warning, + CASE WHEN mem.id_group = 0 OR mg.stars = {string:blank_string} THEN pg.stars ELSE mg.stars END AS stars, mem.password_salt, mem.pm_prefs'; + $select_tables = ' + LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member) + LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member) + LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group) + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)'; + } + elseif ($set == 'minimal') + { + $select_columns = ' + mem.id_member, mem.member_name, mem.real_name, mem.email_address, mem.hide_email, mem.date_registered, + mem.posts, mem.last_login, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group'; + $select_tables = ''; + } + else + trigger_error('loadMemberData(): Invalid member data set \'' . $set . '\'', E_USER_WARNING); + + if (!empty($users)) + { + // Load the member's data. + $request = $smcFunc['db_query']('', ' + SELECT' . $select_columns . ' + FROM {db_prefix}members AS mem' . $select_tables . ' + WHERE mem.' . ($is_name ? 'member_name' : 'id_member') . (count($users) == 1 ? ' = {' . ($is_name ? 'string' : 'int') . ':users}' : ' IN ({' . ($is_name ? 'array_string' : 'array_int') . ':users})'), + array( + 'blank_string' => '', + 'users' => count($users) == 1 ? current($users) : $users, + ) + ); + $new_loaded_ids = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $new_loaded_ids[] = $row['id_member']; + $loaded_ids[] = $row['id_member']; + $row['options'] = array(); + $user_profile[$row['id_member']] = $row; + } + $smcFunc['db_free_result']($request); + } + + if (!empty($new_loaded_ids) && $set !== 'minimal') + { + $request = $smcFunc['db_query']('', ' + SELECT * + FROM {db_prefix}themes + WHERE id_member' . (count($new_loaded_ids) == 1 ? ' = {int:loaded_ids}' : ' IN ({array_int:loaded_ids})'), + array( + 'loaded_ids' => count($new_loaded_ids) == 1 ? $new_loaded_ids[0] : $new_loaded_ids, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $user_profile[$row['id_member']]['options'][$row['variable']] = $row['value']; + $smcFunc['db_free_result']($request); + } + + if (!empty($new_loaded_ids) && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3) + { + for ($i = 0, $n = count($new_loaded_ids); $i < $n; $i++) + cache_put_data('member_data-' . $set . '-' . $new_loaded_ids[$i], $user_profile[$new_loaded_ids[$i]], 240); + } + + // Are we loading any moderators? If so, fix their group data... + if (!empty($loaded_ids) && !empty($board_info['moderators']) && $set === 'normal' && count($temp_mods = array_intersect($loaded_ids, array_keys($board_info['moderators']))) !== 0) + { + if (($row = cache_get_data('moderator_group_info', 480)) == null) + { + $request = $smcFunc['db_query']('', ' + SELECT group_name AS member_group, online_color AS member_group_color, stars + FROM {db_prefix}membergroups + WHERE id_group = {int:moderator_group} + LIMIT 1', + array( + 'moderator_group' => 3, + ) + ); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + cache_put_data('moderator_group_info', $row, 480); + } + + foreach ($temp_mods as $id) + { + // By popular demand, don't show admins or global moderators as moderators. + if ($user_profile[$id]['id_group'] != 1 && $user_profile[$id]['id_group'] != 2) + $user_profile[$id]['member_group'] = $row['member_group']; + + // If the Moderator group has no color or stars, but their group does... don't overwrite. + if (!empty($row['stars'])) + $user_profile[$id]['stars'] = $row['stars']; + if (!empty($row['member_group_color'])) + $user_profile[$id]['member_group_color'] = $row['member_group_color']; + } + } + + return empty($loaded_ids) ? false : $loaded_ids; +} + +// Loads the user's basic values... meant for template/theme usage. +function loadMemberContext($user, $display_custom_fields = false) +{ + global $memberContext, $user_profile, $txt, $scripturl, $user_info; + global $context, $modSettings, $board_info, $settings; + global $smcFunc; + static $dataLoaded = array(); + + // If this person's data is already loaded, skip it. + if (isset($dataLoaded[$user])) + return true; + + // We can't load guests or members not loaded by loadMemberData()! + if ($user == 0) + return false; + if (!isset($user_profile[$user])) + { + trigger_error('loadMemberContext(): member id ' . $user . ' not previously loaded by loadMemberData()', E_USER_WARNING); + return false; + } + + // Well, it's loaded now anyhow. + $dataLoaded[$user] = true; + $profile = $user_profile[$user]; + + // Censor everything. + censorText($profile['signature']); + censorText($profile['personal_text']); + censorText($profile['location']); + + // Set things up to be used before hand. + $gendertxt = $profile['gender'] == 2 ? $txt['female'] : ($profile['gender'] == 1 ? $txt['male'] : ''); + $profile['signature'] = str_replace(array("\n", "\r"), array('
', ''), $profile['signature']); + $profile['signature'] = parse_bbc($profile['signature'], true, 'sig' . $profile['id_member']); + + $profile['is_online'] = (!empty($profile['show_online']) || allowedTo('moderate_forum')) && $profile['is_online'] > 0; + $profile['stars'] = empty($profile['stars']) ? array('', '') : explode('#', $profile['stars']); + // Setup the buddy status here (One whole in_array call saved :P) + $profile['buddy'] = in_array($profile['id_member'], $user_info['buddies']); + $buddy_list = !empty($profile['buddy_list']) ? explode(',', $profile['buddy_list']) : array(); + + // If we're always html resizing, assume it's too large. + if ($modSettings['avatar_action_too_large'] == 'option_html_resize' || $modSettings['avatar_action_too_large'] == 'option_js_resize') + { + $avatar_width = !empty($modSettings['avatar_max_width_external']) ? ' width="' . $modSettings['avatar_max_width_external'] . '"' : ''; + $avatar_height = !empty($modSettings['avatar_max_height_external']) ? ' height="' . $modSettings['avatar_max_height_external'] . '"' : ''; + } + else + { + $avatar_width = ''; + $avatar_height = ''; + } + + // What a monstrous array... + $memberContext[$user] = array( + 'username' => $profile['member_name'], + 'name' => $profile['real_name'], + 'id' => $profile['id_member'], + 'is_buddy' => $profile['buddy'], + 'is_reverse_buddy' => in_array($user_info['id'], $buddy_list), + 'buddies' => $buddy_list, + 'title' => !empty($modSettings['titlesEnable']) ? $profile['usertitle'] : '', + 'href' => $scripturl . '?action=profile;u=' . $profile['id_member'], + 'link' => '' . $profile['real_name'] . '', + 'email' => $profile['email_address'], + 'show_email' => showEmailAddress(!empty($profile['hide_email']), $profile['id_member']), + 'registered' => empty($profile['date_registered']) ? $txt['not_applicable'] : timeformat($profile['date_registered']), + 'registered_timestamp' => empty($profile['date_registered']) ? 0 : forum_time(true, $profile['date_registered']), + 'blurb' => $profile['personal_text'], + 'gender' => array( + 'name' => $gendertxt, + 'image' => !empty($profile['gender']) ? '' . $gendertxt . '' : '' + ), + 'website' => array( + 'title' => $profile['website_title'], + 'url' => $profile['website_url'], + ), + 'birth_date' => empty($profile['birthdate']) || $profile['birthdate'] === '0001-01-01' ? '0000-00-00' : (substr($profile['birthdate'], 0, 4) === '0004' ? '0000' . substr($profile['birthdate'], 4) : $profile['birthdate']), + 'signature' => $profile['signature'], + 'location' => $profile['location'], + 'icq' => $profile['icq'] != '' && (empty($modSettings['guest_hideContacts']) || !$user_info['is_guest']) ? array( + 'name' => $profile['icq'], + 'href' => 'http://www.icq.com/whitepages/about_me.php?uin=' . $profile['icq'], + 'link' => '' . $txt['icq_title'] . ' - ' . $profile['icq'] . '', + 'link_text' => '' . $profile['icq'] . '', + ) : array('name' => '', 'add' => '', 'href' => '', 'link' => '', 'link_text' => ''), + 'aim' => $profile['aim'] != '' && (empty($modSettings['guest_hideContacts']) || !$user_info['is_guest']) ? array( + 'name' => $profile['aim'], + 'href' => 'aim:goim?screenname=' . urlencode(strtr($profile['aim'], array(' ' => '%20'))) . '&message=' . $txt['aim_default_message'], + 'link' => '' . $txt['aim_title'] . ' - ' . $profile['aim'] . '', + 'link_text' => '' . $profile['aim'] . '' + ) : array('name' => '', 'href' => '', 'link' => '', 'link_text' => ''), + 'yim' => $profile['yim'] != '' && (empty($modSettings['guest_hideContacts']) || !$user_info['is_guest']) ? array( + 'name' => $profile['yim'], + 'href' => 'http://edit.yahoo.com/config/send_webmesg?.target=' . urlencode($profile['yim']), + 'link' => '' . $txt['yim_title'] . ' - ' . $profile['yim'] . '', + 'link_text' => '' . $profile['yim'] . '' + ) : array('name' => '', 'href' => '', 'link' => '', 'link_text' => ''), + 'msn' => $profile['msn'] !='' && (empty($modSettings['guest_hideContacts']) || !$user_info['is_guest']) ? array( + 'name' => $profile['msn'], + 'href' => 'http://members.msn.com/' . $profile['msn'], + 'link' => '' . $txt['msn_title'] . ' - ' . $profile['msn'] . '', + 'link_text' => '' . $profile['msn'] . '' + ) : array('name' => '', 'href' => '', 'link' => '', 'link_text' => ''), + 'real_posts' => $profile['posts'], + 'posts' => $profile['posts'] > 500000 ? $txt['geek'] : comma_format($profile['posts']), + 'avatar' => array( + 'name' => $profile['avatar'], + 'image' => $profile['avatar'] == '' ? ($profile['id_attach'] > 0 ? '' : '') : (stristr($profile['avatar'], 'http://') ? '' : ''), + 'href' => $profile['avatar'] == '' ? ($profile['id_attach'] > 0 ? (empty($profile['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $profile['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $profile['filename']) : '') : (stristr($profile['avatar'], 'http://') ? $profile['avatar'] : $modSettings['avatar_url'] . '/' . $profile['avatar']), + 'url' => $profile['avatar'] == '' ? '' : (stristr($profile['avatar'], 'http://') ? $profile['avatar'] : $modSettings['avatar_url'] . '/' . $profile['avatar']) + ), + 'last_login' => empty($profile['last_login']) ? $txt['never'] : timeformat($profile['last_login']), + 'last_login_timestamp' => empty($profile['last_login']) ? 0 : forum_time(0, $profile['last_login']), + 'karma' => array( + 'good' => $profile['karma_good'], + 'bad' => $profile['karma_bad'], + 'allow' => !$user_info['is_guest'] && !empty($modSettings['karmaMode']) && $user_info['id'] != $user && allowedTo('karma_edit') && + ($user_info['posts'] >= $modSettings['karmaMinPosts'] || $user_info['is_admin']), + ), + 'ip' => htmlspecialchars($profile['member_ip']), + 'ip2' => htmlspecialchars($profile['member_ip2']), + 'online' => array( + 'is_online' => $profile['is_online'], + 'text' => $txt[$profile['is_online'] ? 'online' : 'offline'], + 'href' => $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'], + 'link' => '' . $txt[$profile['is_online'] ? 'online' : 'offline'] . '', + 'image_href' => $settings['images_url'] . '/' . ($profile['buddy'] ? 'buddy_' : '') . ($profile['is_online'] ? 'useron' : 'useroff') . '.gif', + 'label' => $txt[$profile['is_online'] ? 'online' : 'offline'] + ), + 'language' => $smcFunc['ucwords'](strtr($profile['lngfile'], array('_' => ' ', '-utf8' => ''))), + 'is_activated' => isset($profile['is_activated']) ? $profile['is_activated'] : 1, + 'is_banned' => isset($profile['is_activated']) ? $profile['is_activated'] >= 10 : 0, + 'options' => $profile['options'], + 'is_guest' => false, + 'group' => $profile['member_group'], + 'group_color' => $profile['member_group_color'], + 'group_id' => $profile['id_group'], + 'post_group' => $profile['post_group'], + 'post_group_color' => $profile['post_group_color'], + 'group_stars' => str_repeat('*', empty($profile['stars'][0]) || empty($profile['stars'][1]) ? 0 : $profile['stars'][0]), + 'warning' => $profile['warning'], + 'warning_status' => !empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $profile['warning'] ? 'mute' : (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $profile['warning'] ? 'moderate' : (!empty($modSettings['warning_watch']) && $modSettings['warning_watch'] <= $profile['warning'] ? 'watch' : (''))), + 'local_time' => timeformat(time() + ($profile['time_offset'] - $user_info['time_offset']) * 3600, false), + ); + + // First do a quick run through to make sure there is something to be shown. + $memberContext[$user]['has_messenger'] = false; + foreach (array('icq', 'msn', 'aim', 'yim') as $messenger) + { + if (!isset($context['disabled_fields'][$messenger]) && !empty($memberContext[$user][$messenger]['link'])) + { + $memberContext[$user]['has_messenger'] = true; + break; + } + } + + // Are we also loading the members custom fields into context? + if ($display_custom_fields && !empty($modSettings['displayFields'])) + { + $memberContext[$user]['custom_fields'] = array(); + if (!isset($context['display_fields'])) + $context['display_fields'] = unserialize($modSettings['displayFields']); + + foreach ($context['display_fields'] as $custom) + { + if (empty($custom['title']) || empty($profile['options'][$custom['colname']])) + continue; + + $value = $profile['options'][$custom['colname']]; + + // BBC? + if ($custom['bbc']) + $value = parse_bbc($value); + // ... or checkbox? + elseif (isset($custom['type']) && $custom['type'] == 'check') + $value = $value ? $txt['yes'] : $txt['no']; + + // Enclosing the user input within some other text? + if (!empty($custom['enclose'])) + $value = strtr($custom['enclose'], array( + '{SCRIPTURL}' => $scripturl, + '{IMAGES_URL}' => $settings['images_url'], + '{DEFAULT_IMAGES_URL}' => $settings['default_images_url'], + '{INPUT}' => $value, + )); + + $memberContext[$user]['custom_fields'][] = array( + 'title' => $custom['title'], + 'colname' => $custom['colname'], + 'value' => $value, + 'placement' => !empty($custom['placement']) ? $custom['placement'] : 0, + ); + } + } + + return true; +} + +function detectBrowser() +{ + global $context, $user_info; + + // The following determines the user agent (browser) as best it can. + $context['browser'] = array( + 'is_opera' => strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') !== false, + 'is_opera6' => strpos($_SERVER['HTTP_USER_AGENT'], 'Opera 6') !== false, + 'is_opera7' => strpos($_SERVER['HTTP_USER_AGENT'], 'Opera 7') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera/7') !== false, + 'is_opera8' => strpos($_SERVER['HTTP_USER_AGENT'], 'Opera 8') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera/8') !== false, + 'is_opera9' => preg_match('~Opera[ /]9(?!\\.[89])~', $_SERVER['HTTP_USER_AGENT']) === 1, + 'is_opera10' => preg_match('~Opera[ /]10\\.~', $_SERVER['HTTP_USER_AGENT']) === 1 || (preg_match('~Opera[ /]9\\.[89]~', $_SERVER['HTTP_USER_AGENT']) === 1 && preg_match('~Version/1[0-9]\\.~', $_SERVER['HTTP_USER_AGENT']) === 1), + 'is_ie4' => strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 4') !== false && strpos($_SERVER['HTTP_USER_AGENT'], 'WebTV') === false, + 'is_webkit' => strpos($_SERVER['HTTP_USER_AGENT'], 'AppleWebKit') !== false, + 'is_mac_ie' => strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.') !== false && strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') !== false, + 'is_web_tv' => strpos($_SERVER['HTTP_USER_AGENT'], 'WebTV') !== false, + 'is_konqueror' => strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror') !== false, + 'is_firefox' => preg_match('~(?:Firefox|Ice[wW]easel|IceCat)/~', $_SERVER['HTTP_USER_AGENT']) === 1, + 'is_firefox1' => preg_match('~(?:Firefox|Ice[wW]easel|IceCat)/1\\.~', $_SERVER['HTTP_USER_AGENT']) === 1, + 'is_firefox2' => preg_match('~(?:Firefox|Ice[wW]easel|IceCat)/2\\.~', $_SERVER['HTTP_USER_AGENT']) === 1, + 'is_firefox3' => preg_match('~(?:Firefox|Ice[wW]easel|IceCat|Shiretoko|Minefield)/3\\.~', $_SERVER['HTTP_USER_AGENT']) === 1, + 'is_iphone' => strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'iPod') !== false, + 'is_android' => strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false, + ); + + $context['browser']['is_chrome'] = $context['browser']['is_webkit'] && strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== false; + $context['browser']['is_safari'] = !$context['browser']['is_chrome'] && strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== false; + $context['browser']['is_gecko'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko') !== false && !$context['browser']['is_webkit'] && !$context['browser']['is_konqueror']; + + // Internet Explorer 5 and 6 are often "emulated". + $context['browser']['is_ie8'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 8') !== false; + $context['browser']['is_ie7'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 7') !== false && !$context['browser']['is_ie8']; + $context['browser']['is_ie6'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6') !== false && !$context['browser']['is_ie8'] && !$context['browser']['is_ie7']; + $context['browser']['is_ie5.5'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.5') !== false; + $context['browser']['is_ie5'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.0') !== false; + + $context['browser']['is_ie'] = $context['browser']['is_ie4'] || $context['browser']['is_ie5'] || $context['browser']['is_ie5.5'] || $context['browser']['is_ie6'] || $context['browser']['is_ie7'] || $context['browser']['is_ie8']; + // Before IE8 we need to fix IE... lots! + $context['browser']['ie_standards_fix'] = !$context['browser']['is_ie8']; + + $context['browser']['needs_size_fix'] = ($context['browser']['is_ie5'] || $context['browser']['is_ie5.5'] || $context['browser']['is_ie4'] || $context['browser']['is_opera6']) && strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') === false; + + // This isn't meant to be reliable, it's just meant to catch most bots to prevent PHPSESSID from showing up. + $context['browser']['possibly_robot'] = !empty($user_info['possibly_robot']); + + // Robots shouldn't be logging in or registering. So, they aren't a bot. Better to be wrong than sorry (or people won't be able to log in!), anyway. + if ((isset($_REQUEST['action']) && in_array($_REQUEST['action'], array('login', 'login2', 'register'))) || !$user_info['is_guest']) + $context['browser']['possibly_robot'] = false; +} + +// Load a theme, by ID. +function loadTheme($id_theme = 0, $initialize = true) +{ + global $user_info, $user_settings, $board_info, $sc, $boarddir; + global $txt, $boardurl, $scripturl, $mbname, $modSettings, $language; + global $context, $settings, $options, $sourcedir, $ssi_theme, $smcFunc; + + // The theme was specified by parameter. + if (!empty($id_theme)) + $id_theme = (int) $id_theme; + // The theme was specified by REQUEST. + elseif (!empty($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum'))) + { + $id_theme = (int) $_REQUEST['theme']; + $_SESSION['id_theme'] = $id_theme; + } + // The theme was specified by REQUEST... previously. + elseif (!empty($_SESSION['id_theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum'))) + $id_theme = (int) $_SESSION['id_theme']; + // The theme is just the user's choice. (might use ?board=1;theme=0 to force board theme.) + elseif (!empty($user_info['theme']) && !isset($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum'))) + $id_theme = $user_info['theme']; + // The theme was specified by the board. + elseif (!empty($board_info['theme'])) + $id_theme = $board_info['theme']; + // The theme is the forum's default. + else + $id_theme = $modSettings['theme_guests']; + + // Verify the id_theme... no foul play. + // Always allow the board specific theme, if they are overriding. + if (!empty($board_info['theme']) && $board_info['override_theme']) + $id_theme = $board_info['theme']; + // If they have specified a particular theme to use with SSI allow it to be used. + elseif (!empty($ssi_theme) && $id_theme == $ssi_theme) + $id_theme = (int) $id_theme; + elseif (!empty($modSettings['knownThemes']) && !allowedTo('admin_forum')) + { + $themes = explode(',', $modSettings['knownThemes']); + if (!in_array($id_theme, $themes)) + $id_theme = $modSettings['theme_guests']; + else + $id_theme = (int) $id_theme; + } + else + $id_theme = (int) $id_theme; + + $member = empty($user_info['id']) ? -1 : $user_info['id']; + + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2 && ($temp = cache_get_data('theme_settings-' . $id_theme . ':' . $member, 60)) != null && time() - 60 > $modSettings['settings_updated']) + { + $themeData = $temp; + $flag = true; + } + elseif (($temp = cache_get_data('theme_settings-' . $id_theme, 90)) != null && time() - 60 > $modSettings['settings_updated']) + $themeData = $temp + array($member => array()); + else + $themeData = array(-1 => array(), 0 => array(), $member => array()); + + if (empty($flag)) + { + // Load variables from the current or default theme, global or this user's. + $result = $smcFunc['db_query']('', ' + SELECT variable, value, id_member, id_theme + FROM {db_prefix}themes + WHERE id_member' . (empty($themeData[0]) ? ' IN (-1, 0, {int:id_member})' : ' = {int:id_member}') . ' + AND id_theme' . ($id_theme == 1 ? ' = {int:id_theme}' : ' IN ({int:id_theme}, 1)'), + array( + 'id_theme' => $id_theme, + 'id_member' => $member, + ) + ); + // Pick between $settings and $options depending on whose data it is. + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // There are just things we shouldn't be able to change as members. + if ($row['id_member'] != 0 && in_array($row['variable'], array('actual_theme_url', 'actual_images_url', 'base_theme_dir', 'base_theme_url', 'default_images_url', 'default_theme_dir', 'default_theme_url', 'default_template', 'images_url', 'number_recent_posts', 'smiley_sets_default', 'theme_dir', 'theme_id', 'theme_layers', 'theme_templates', 'theme_url'))) + continue; + + // If this is the theme_dir of the default theme, store it. + if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1' && empty($row['id_member'])) + $themeData[0]['default_' . $row['variable']] = $row['value']; + + // If this isn't set yet, is a theme option, or is not the default theme.. + if (!isset($themeData[$row['id_member']][$row['variable']]) || $row['id_theme'] != '1') + $themeData[$row['id_member']][$row['variable']] = substr($row['variable'], 0, 5) == 'show_' ? $row['value'] == '1' : $row['value']; + } + $smcFunc['db_free_result']($result); + + if (!empty($themeData[-1])) + foreach ($themeData[-1] as $k => $v) + { + if (!isset($themeData[$member][$k])) + $themeData[$member][$k] = $v; + } + + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2) + cache_put_data('theme_settings-' . $id_theme . ':' . $member, $themeData, 60); + // Only if we didn't already load that part of the cache... + elseif (!isset($temp)) + cache_put_data('theme_settings-' . $id_theme, array(-1 => $themeData[-1], 0 => $themeData[0]), 90); + } + + $settings = $themeData[0]; + $options = $themeData[$member]; + + $settings['theme_id'] = $id_theme; + + $settings['actual_theme_url'] = $settings['theme_url']; + $settings['actual_images_url'] = $settings['images_url']; + $settings['actual_theme_dir'] = $settings['theme_dir']; + + $settings['template_dirs'] = array(); + // This theme first. + $settings['template_dirs'][] = $settings['theme_dir']; + + // Based on theme (if there is one). + if (!empty($settings['base_theme_dir'])) + $settings['template_dirs'][] = $settings['base_theme_dir']; + + // Lastly the default theme. + if ($settings['theme_dir'] != $settings['default_theme_dir']) + $settings['template_dirs'][] = $settings['default_theme_dir']; + + if (!$initialize) + return; + + // Check to see if they're accessing it from the wrong place. + if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME'])) + { + $detected_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https://' : 'http://'; + $detected_url .= empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST']; + $temp = preg_replace('~/' . basename($scripturl) . '(/.+)?$~', '', strtr(dirname($_SERVER['PHP_SELF']), '\\', '/')); + if ($temp != '/') + $detected_url .= $temp; + } + if (isset($detected_url) && $detected_url != $boardurl) + { + // Try #1 - check if it's in a list of alias addresses. + if (!empty($modSettings['forum_alias_urls'])) + { + $aliases = explode(',', $modSettings['forum_alias_urls']); + + foreach ($aliases as $alias) + { + // Rip off all the boring parts, spaces, etc. + if ($detected_url == trim($alias) || strtr($detected_url, array('http://' => '', 'https://' => '')) == trim($alias)) + $do_fix = true; + } + } + + // Hmm... check #2 - is it just different by a www? Send them to the correct place!! + if (empty($do_fix) && strtr($detected_url, array('://' => '://www.')) == $boardurl && (empty($_GET) || count($_GET) == 1) && SMF != 'SSI') + { + // Okay, this seems weird, but we don't want an endless loop - this will make $_GET not empty ;). + if (empty($_GET)) + redirectexit('wwwRedirect'); + else + { + list ($k, $v) = each($_GET); + + if ($k != 'wwwRedirect') + redirectexit('wwwRedirect;' . $k . '=' . $v); + } + } + + // #3 is just a check for SSL... + if (strtr($detected_url, array('https://' => 'http://')) == $boardurl) + $do_fix = true; + + // Okay, #4 - perhaps it's an IP address? We're gonna want to use that one, then. (assuming it's the IP or something...) + if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1) + { + // Caching is good ;). + $oldurl = $boardurl; + + // Fix $boardurl and $scripturl. + $boardurl = $detected_url; + $scripturl = strtr($scripturl, array($oldurl => $boardurl)); + $_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], array($oldurl => $boardurl)); + + // Fix the theme urls... + $settings['theme_url'] = strtr($settings['theme_url'], array($oldurl => $boardurl)); + $settings['default_theme_url'] = strtr($settings['default_theme_url'], array($oldurl => $boardurl)); + $settings['actual_theme_url'] = strtr($settings['actual_theme_url'], array($oldurl => $boardurl)); + $settings['images_url'] = strtr($settings['images_url'], array($oldurl => $boardurl)); + $settings['default_images_url'] = strtr($settings['default_images_url'], array($oldurl => $boardurl)); + $settings['actual_images_url'] = strtr($settings['actual_images_url'], array($oldurl => $boardurl)); + + // And just a few mod settings :). + $modSettings['smileys_url'] = strtr($modSettings['smileys_url'], array($oldurl => $boardurl)); + $modSettings['avatar_url'] = strtr($modSettings['avatar_url'], array($oldurl => $boardurl)); + + // Clean up after loadBoard(). + if (isset($board_info['moderators'])) + { + foreach ($board_info['moderators'] as $k => $dummy) + { + $board_info['moderators'][$k]['href'] = strtr($dummy['href'], array($oldurl => $boardurl)); + $board_info['moderators'][$k]['link'] = strtr($dummy['link'], array('"' . $oldurl => '"' . $boardurl)); + } + } + foreach ($context['linktree'] as $k => $dummy) + $context['linktree'][$k]['url'] = strtr($dummy['url'], array($oldurl => $boardurl)); + } + } + // Set up the contextual user array. + $context['user'] = array( + 'id' => $user_info['id'], + 'is_logged' => !$user_info['is_guest'], + 'is_guest' => &$user_info['is_guest'], + 'is_admin' => &$user_info['is_admin'], + 'is_mod' => &$user_info['is_mod'], + // A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator. + 'can_mod' => allowedTo('access_mod_center') || (!$user_info['is_guest'] && ($user_info['mod_cache']['gq'] != '0=1' || $user_info['mod_cache']['bq'] != '0=1' || ($modSettings['postmod_active'] && !empty($user_info['mod_cache']['ap'])))), + 'username' => $user_info['username'], + 'language' => $user_info['language'], + 'email' => $user_info['email'], + 'ignoreusers' => $user_info['ignoreusers'], + ); + if (!$context['user']['is_guest']) + $context['user']['name'] = $user_info['name']; + elseif ($context['user']['is_guest'] && !empty($txt['guest_title'])) + $context['user']['name'] = $txt['guest_title']; + + // Determine the current smiley set. + $user_info['smiley_set'] = (!in_array($user_info['smiley_set'], explode(',', $modSettings['smiley_sets_known'])) && $user_info['smiley_set'] != 'none') || empty($modSettings['smiley_sets_enable']) ? (!empty($settings['smiley_sets_default']) ? $settings['smiley_sets_default'] : $modSettings['smiley_sets_default']) : $user_info['smiley_set']; + $context['user']['smiley_set'] = $user_info['smiley_set']; + + // Some basic information... + if (!isset($context['html_headers'])) + $context['html_headers'] = ''; + + $context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | '; + $context['session_var'] = $_SESSION['session_var']; + $context['session_id'] = $_SESSION['session_value']; + $context['forum_name'] = $mbname; + $context['forum_name_html_safe'] = $smcFunc['htmlspecialchars']($context['forum_name']); + $context['header_logo_url_html_safe'] = empty($settings['header_logo_url']) ? '' : $smcFunc['htmlspecialchars']($settings['header_logo_url']); + $context['current_action'] = isset($_REQUEST['action']) ? $_REQUEST['action'] : null; + $context['current_subaction'] = isset($_REQUEST['sa']) ? $_REQUEST['sa'] : null; + if (isset($modSettings['load_average'])) + $context['load_average'] = $modSettings['load_average']; + + // Set some permission related settings. + $context['show_login_bar'] = $user_info['is_guest'] && !empty($modSettings['enableVBStyleLogin']); + + // This determines the server... not used in many places, except for login fixing. + $context['server'] = array( + 'is_iis' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false, + 'is_apache' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false, + 'is_lighttpd' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false, + 'is_nginx' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false, + 'is_cgi' => isset($_SERVER['SERVER_SOFTWARE']) && strpos(php_sapi_name(), 'cgi') !== false, + 'is_windows' => strpos(PHP_OS, 'WIN') === 0, + 'iso_case_folding' => ord(strtolower(chr(138))) === 154, + 'complex_preg_chars' => @version_compare(PHP_VERSION, '4.3.3') != -1, + ); + // A bug in some versions of IIS under CGI (older ones) makes cookie setting not work with Location: headers. + $context['server']['needs_login_fix'] = $context['server']['is_cgi'] && $context['server']['is_iis']; + + // Detect the browser. This is separated out because it's also used in attachment downloads + detectBrowser(); + + // Set the top level linktree up. + array_unshift($context['linktree'], array( + 'url' => $scripturl, + 'name' => $context['forum_name_html_safe'] + )); + + // This allows sticking some HTML on the page output - useful for controls. + $context['insert_after_template'] = ''; + + if (!isset($txt)) + $txt = array(); + $simpleActions = array( + 'findmember', + 'helpadmin', + 'printpage', + 'quotefast', + 'spellcheck', + ); + + // Wireless mode? Load up the wireless stuff. + if (WIRELESS) + { + $context['template_layers'] = array(WIRELESS_PROTOCOL); + loadTemplate('Wireless'); + loadLanguage('Wireless+index+Modifications'); + } + // Output is fully XML, so no need for the index template. + elseif (isset($_REQUEST['xml'])) + { + loadLanguage('index+Modifications'); + loadTemplate('Xml'); + $context['template_layers'] = array(); + } + // These actions don't require the index template at all. + elseif (!empty($_REQUEST['action']) && in_array($_REQUEST['action'], $simpleActions)) + { + loadLanguage('index+Modifications'); + $context['template_layers'] = array(); + } + else + { + // Custom templates to load, or just default? + if (isset($settings['theme_templates'])) + $templates = explode(',', $settings['theme_templates']); + else + $templates = array('index'); + + // Load each template... + foreach ($templates as $template) + loadTemplate($template); + + // ...and attempt to load their associated language files. + $required_files = implode('+', array_merge($templates, array('Modifications'))); + loadLanguage($required_files, '', false); + + // Custom template layers? + if (isset($settings['theme_layers'])) + $context['template_layers'] = explode(',', $settings['theme_layers']); + else + $context['template_layers'] = array('html', 'body'); + } + + // Initialize the theme. + loadSubTemplate('init', 'ignore'); + + // Load the compatibility stylesheet if the theme hasn't been updated for 2.0 RC2 (yet). + if (isset($settings['theme_version']) && (version_compare($settings['theme_version'], '2.0 RC2', '<') || strpos($settings['theme_version'], '2.0 Beta') !== false)) + loadTemplate(false, 'compat'); + + // Guests may still need a name. + if ($context['user']['is_guest'] && empty($context['user']['name'])) + $context['user']['name'] = $txt['guest_title']; + + // Any theme-related strings that need to be loaded? + if (!empty($settings['require_theme_strings'])) + loadLanguage('ThemeStrings', '', false); + + // We allow theme variants, because we're cool. + $context['theme_variant'] = ''; + $context['theme_variant_url'] = ''; + if (!empty($settings['theme_variants'])) + { + // Overriding - for previews and that ilk. + if (!empty($_REQUEST['variant'])) + $_SESSION['id_variant'] = $_REQUEST['variant']; + // User selection? + if (empty($settings['disable_user_variant']) || allowedTo('admin_forum')) + $context['theme_variant'] = !empty($_SESSION['id_variant']) ? $_SESSION['id_variant'] : (!empty($options['theme_variant']) ? $options['theme_variant'] : ''); + // If not a user variant, select the default. + if ($context['theme_variant'] == '' || !in_array($context['theme_variant'], $settings['theme_variants'])) + $context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0]; + + // Do this to keep things easier in the templates. + $context['theme_variant'] = '_' . $context['theme_variant']; + $context['theme_variant_url'] = $context['theme_variant'] . '/'; + } + + // Let's be compatible with old themes! + if (!function_exists('template_html_above') && in_array('html', $context['template_layers'])) + $context['template_layers'] = array('main'); + + // Allow overriding the board wide time/number formats. + if (empty($user_settings['time_format']) && !empty($txt['time_format'])) + $user_info['time_format'] = $txt['time_format']; + $txt['number_format'] = empty($txt['number_format']) ? empty($modSettings['number_format']) ? '' : $modSettings['number_format'] : $txt['number_format']; + + if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'always') + { + $settings['theme_url'] = $settings['default_theme_url']; + $settings['images_url'] = $settings['default_images_url']; + $settings['theme_dir'] = $settings['default_theme_dir']; + } + // Make a special URL for the language. + $settings['lang_images_url'] = $settings['images_url'] . '/' . (!empty($txt['image_lang']) ? $txt['image_lang'] : $user_info['language']); + + // Set the character set from the template. + $context['character_set'] = empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']; + $context['utf8'] = $context['character_set'] === 'UTF-8' && (strpos(strtolower(PHP_OS), 'win') === false || @version_compare(PHP_VERSION, '4.2.3') != -1); + $context['right_to_left'] = !empty($txt['lang_rtl']); + + $context['tabindex'] = 1; + + // Fix font size with HTML 4.01, etc. + if (isset($settings['doctype'])) + $context['browser']['needs_size_fix'] |= $settings['doctype'] == 'html' && $context['browser']['is_ie6']; + + // Compatibility. + if (!isset($settings['theme_version'])) + $modSettings['memberCount'] = $modSettings['totalMembers']; + + // This allows us to change the way things look for the admin. + $context['admin_features'] = isset($modSettings['admin_features']) ? explode(',', $modSettings['admin_features']) : array('cd,cp,k,w,rg,ml,pm'); + + // If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!) + if ((!empty($modSettings['mail_next_send']) && $modSettings['mail_next_send'] < time() && empty($modSettings['mail_queue_use_cron'])) || empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time()) + { + if ($context['browser']['possibly_robot']) + { + //!!! Maybe move this somewhere better?! + require_once($sourcedir . '/ScheduledTasks.php'); + + // What to do, what to do?! + if (empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time()) + AutoTask(); + else + ReduceMailQueue(); + } + else + { + $type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq'; + $ts = $type == 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time']; + + $context['html_headers'] .= ' + '; + } + } + + // Any files to include at this point? + if (!empty($modSettings['integrate_theme_include'])) + { + $theme_includes = explode(',', $modSettings['integrate_theme_include']); + foreach ($theme_includes as $include) + { + $include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir'])); + if (file_exists($include)) + require_once($include); + } + } + + // Call load theme integration functions. + call_integration_hook('integrate_load_theme'); + + // We are ready to go. + $context['theme_loaded'] = true; +} + +// Load a template - if the theme doesn't include it, use the default. +function loadTemplate($template_name, $style_sheets = array(), $fatal = true) +{ + global $context, $settings, $txt, $scripturl, $boarddir, $db_show_debug; + + // Do any style sheets first, cause we're easy with those. + if (!empty($style_sheets)) + { + if (!is_array($style_sheets)) + $style_sheets = array($style_sheets); + + foreach ($style_sheets as $sheet) + { + // Prevent the style sheet from being included twice. + if (strpos($context['html_headers'], 'id="' . $sheet . '_css"') !== false) + continue; + + $sheet_path = file_exists($settings['theme_dir']. '/css/' . $sheet . '.css') ? 'theme_url' : (file_exists($settings['default_theme_dir']. '/css/' . $sheet . '.css') ? 'default_theme_url' : ''); + if ($sheet_path) + { + $context['html_headers'] .= "\n\t" . ''; + if ($db_show_debug === true) + $context['debug']['sheets'][] = $sheet . ' (' . basename($settings[$sheet_path]) . ')'; + } + } + } + + // No template to load? + if ($template_name === false) + return true; + + $loaded = false; + foreach ($settings['template_dirs'] as $template_dir) + { + if (file_exists($template_dir . '/' . $template_name . '.template.php')) + { + $loaded = true; + template_include($template_dir . '/' . $template_name . '.template.php', true); + break; + } + } + + if ($loaded) + { + // For compatibility reasons, if this is the index template without new functions, include compatible stuff. + if (substr($template_name, 0, 5) == 'index' && !function_exists('template_button_strip')) + loadTemplate('Compat'); + + if ($db_show_debug === true) + $context['debug']['templates'][] = $template_name . ' (' . basename($template_dir) . ')'; + + // If they have specified an initialization function for this template, go ahead and call it now. + if (function_exists('template_' . $template_name . '_init')) + call_user_func('template_' . $template_name . '_init'); + } + // Hmmm... doesn't exist?! I don't suppose the directory is wrong, is it? + elseif (!file_exists($settings['default_theme_dir']) && file_exists($boarddir . '/Themes/default')) + { + $settings['default_theme_dir'] = $boarddir . '/Themes/default'; + $settings['template_dirs'][] = $settings['default_theme_dir']; + + if (!empty($context['user']['is_admin']) && !isset($_GET['th'])) + { + loadLanguage('Errors'); + echo ' +
+ ', $txt['theme_dir_wrong'], ' +
'; + } + + loadTemplate($template_name); + } + // Cause an error otherwise. + elseif ($template_name != 'Errors' && $template_name != 'index' && $fatal) + fatal_lang_error('theme_template_error', 'template', array((string) $template_name)); + elseif ($fatal) + die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load Themes/default/%s.template.php!', (string) $template_name), 'template')); + else + return false; +} + +// Load a sub template... fatal is for templates that shouldn't get a 'pretty' error screen. +function loadSubTemplate($sub_template_name, $fatal = false) +{ + global $context, $settings, $options, $txt, $db_show_debug; + + if ($db_show_debug === true) + $context['debug']['sub_templates'][] = $sub_template_name; + + // Figure out what the template function is named. + $theme_function = 'template_' . $sub_template_name; + if (function_exists($theme_function)) + $theme_function(); + elseif ($fatal === false) + fatal_lang_error('theme_template_error', 'template', array((string) $sub_template_name)); + elseif ($fatal !== 'ignore') + die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load the %s sub template!', (string) $sub_template_name), 'template')); + + // Are we showing debugging for templates? Just make sure not to do it before the doctype... + if (allowedTo('admin_forum') && isset($_REQUEST['debug']) && !in_array($sub_template_name, array('init', 'main_below')) && ob_get_length() > 0 && !isset($_REQUEST['xml'])) + { + echo ' +
---- ', $sub_template_name, ' ends ----
'; + } +} + +// Load a language file. Tries the current and default themes as well as the user and global languages. +function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false) +{ + global $user_info, $language, $settings, $context, $modSettings; + global $cachedir, $db_show_debug, $sourcedir, $txt; + static $already_loaded = array(); + + // Default to the user's language. + if ($lang == '') + $lang = isset($user_info['language']) ? $user_info['language'] : $language; + + // Do we want the English version of language file as fallback? + if (empty($modSettings['disable_language_fallback']) && $lang != 'english') + loadLanguage($template_name, 'english', false); + + if (!$force_reload && isset($already_loaded[$template_name]) && $already_loaded[$template_name] == $lang) + return $lang; + + // Make sure we have $settings - if not we're in trouble and need to find it! + if (empty($settings['default_theme_dir'])) + { + require_once($sourcedir . '/ScheduledTasks.php'); + loadEssentialThemeData(); + } + + // What theme are we in? + $theme_name = basename($settings['theme_url']); + if (empty($theme_name)) + $theme_name = 'unknown'; + + // For each file open it up and write it out! + foreach (explode('+', $template_name) as $template) + { + // Obviously, the current theme is most important to check. + $attempts = array( + array($settings['theme_dir'], $template, $lang, $settings['theme_url']), + array($settings['theme_dir'], $template, $language, $settings['theme_url']), + ); + + // Do we have a base theme to worry about? + if (isset($settings['base_theme_dir'])) + { + $attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']); + $attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']); + } + + // Fall back on the default theme if necessary. + $attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']); + $attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']); + + // Fall back on the English language if none of the preferred languages can be found. + if (!in_array('english', array($lang, $language))) + { + $attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']); + $attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']); + } + + // Try to find the language file. + $found = false; + foreach ($attempts as $k => $file) + { + if (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php')) + { + // Include it! + template_include($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'); + + // Note that we found it. + $found = true; + + break; + } + } + + // That couldn't be found! Log the error, but *try* to continue normally. + if (!$found && $fatal) + { + log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template')); + break; + } + } + + // Keep track of what we're up to soldier. + if ($db_show_debug === true) + $context['debug']['language_files'][] = $template_name . '.' . $lang . ' (' . $theme_name . ')'; + + // Remember what we have loaded, and in which language. + $already_loaded[$template_name] = $lang; + + // Return the language actually loaded. + return $lang; +} + +// Get all parent boards (requires first parent as parameter) +function getBoardParents($id_parent) +{ + global $scripturl, $smcFunc; + + // First check if we have this cached already. + if (($boards = cache_get_data('board_parents-' . $id_parent, 480)) === null) + { + $boards = array(); + $original_parent = $id_parent; + + // Loop while the parent is non-zero. + while ($id_parent != 0) + { + $result = $smcFunc['db_query']('', ' + SELECT + b.id_parent, b.name, {int:board_parent} AS id_board, IFNULL(mem.id_member, 0) AS id_moderator, + mem.real_name, b.child_level + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member) + WHERE b.id_board = {int:board_parent}', + array( + 'board_parent' => $id_parent, + ) + ); + // In the EXTREMELY unlikely event this happens, give an error message. + if ($smcFunc['db_num_rows']($result) == 0) + fatal_lang_error('parent_not_found', 'critical'); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (!isset($boards[$row['id_board']])) + { + $id_parent = $row['id_parent']; + $boards[$row['id_board']] = array( + 'url' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'name' => $row['name'], + 'level' => $row['child_level'], + 'moderators' => array() + ); + } + // If a moderator exists for this board, add that moderator for all children too. + if (!empty($row['id_moderator'])) + foreach ($boards as $id => $dummy) + { + $boards[$id]['moderators'][$row['id_moderator']] = array( + 'id' => $row['id_moderator'], + 'name' => $row['real_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'], + 'link' => '' . $row['real_name'] . '' + ); + } + } + $smcFunc['db_free_result']($result); + } + + cache_put_data('board_parents-' . $original_parent, $boards, 480); + } + + return $boards; +} + +// Attempt to reload our languages. +function getLanguages($use_cache = true, $favor_utf8 = true) +{ + global $context, $smcFunc, $settings, $modSettings; + + // Either we don't use the cache, or its expired. + if (!$use_cache || ($context['languages'] = cache_get_data('known_languages' . ($favor_utf8 ? '' : '_all'), !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600)) == null) + { + // If we don't have our theme information yet, lets get it. + if (empty($settings['default_theme_dir'])) + loadTheme(0, false); + + // Default language directories to try. + $language_directories = array( + $settings['default_theme_dir'] . '/languages', + $settings['actual_theme_dir'] . '/languages', + ); + + // We possibly have a base theme directory. + if (!empty($settings['base_theme_dir'])) + $language_directories[] = $settings['base_theme_dir'] . '/languages'; + + // Remove any duplicates. + $language_directories = array_unique($language_directories); + + foreach ($language_directories as $language_dir) + { + // Can't look in here... doesn't exist! + if (!file_exists($language_dir)) + continue; + + $dir = dir($language_dir); + while ($entry = $dir->read()) + { + // Look for the index language file.... + if (!preg_match('~^index\.(.+)\.php$~', $entry, $matches)) + continue; + + $context['languages'][$matches[1]] = array( + 'name' => $smcFunc['ucwords'](strtr($matches[1], array('_' => ' '))), + 'selected' => false, + 'filename' => $matches[1], + 'location' => $language_dir . '/index.' . $matches[1] . '.php', + ); + + } + $dir->close(); + } + + // Favoring UTF8? Then prevent us from selecting non-UTF8 versions. + if ($favor_utf8) + { + foreach ($context['languages'] as $lang) + if (substr($lang['filename'], strlen($lang['filename']) - 5, 5) != '-utf8' && isset($context['languages'][$lang['filename'] . '-utf8'])) + unset($context['languages'][$lang['filename']]); + } + + // Lets cash in on this deal. + if (!empty($modSettings['cache_enable'])) + cache_put_data('known_languages' . ($favor_utf8 ? '' : '_all'), $context['languages'], !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600); + } + + return $context['languages']; +} + +// Replace all vulgar words with respective proper words. (substring or whole words..) +function &censorText(&$text, $force = false) +{ + global $modSettings, $options, $settings, $txt; + static $censor_vulgar = null, $censor_proper; + + if ((!empty($options['show_no_censored']) && $settings['allow_no_censored'] && !$force) || empty($modSettings['censor_vulgar'])) + return $text; + + // If they haven't yet been loaded, load them. + if ($censor_vulgar == null) + { + $censor_vulgar = explode("\n", $modSettings['censor_vulgar']); + $censor_proper = explode("\n", $modSettings['censor_proper']); + + // Quote them for use in regular expressions. + for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++) + { + $censor_vulgar[$i] = strtr(preg_quote($censor_vulgar[$i], '/'), array('\\\\\\*' => '[*]', '\\*' => '[^\s]*?', '&' => '&')); + $censor_vulgar[$i] = (empty($modSettings['censorWholeWord']) ? '/' . $censor_vulgar[$i] . '/' : '/(?<=^|\W)' . $censor_vulgar[$i] . '(?=$|\W)/') . (empty($modSettings['censorIgnoreCase']) ? '' : 'i') . ((empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8' ? 'u' : ''); + + if (strpos($censor_vulgar[$i], '\'') !== false) + { + $censor_proper[count($censor_vulgar)] = $censor_proper[$i]; + $censor_vulgar[count($censor_vulgar)] = strtr($censor_vulgar[$i], array('\'' => ''')); + } + } + } + + // Censoring isn't so very complicated :P. + $text = preg_replace($censor_vulgar, $censor_proper, $text); + return $text; +} + +// Load the template/language file using eval or require? (with eval we can show an error message!) +function template_include($filename, $once = false) +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + global $user_info, $boardurl, $boarddir, $sourcedir; + global $maintenance, $mtitle, $mmessage; + static $templates = array(); + + // We want to be able to figure out any errors... + @ini_set('track_errors', '1'); + + // Don't include the file more than once, if $once is true. + if ($once && in_array($filename, $templates)) + return; + // Add this file to the include list, whether $once is true or not. + else + $templates[] = $filename; + + // Are we going to use eval? + if (empty($modSettings['disableTemplateEval'])) + { + $file_found = file_exists($filename) && eval('?' . '>' . rtrim(file_get_contents($filename))) !== false; + $settings['current_include_filename'] = $filename; + } + else + { + $file_found = file_exists($filename); + + if ($once && $file_found) + require_once($filename); + elseif ($file_found) + require($filename); + } + + if ($file_found !== true) + { + ob_end_clean(); + if (!empty($modSettings['enableCompressedOutput'])) + @ob_start('ob_gzhandler'); + else + ob_start(); + + if (isset($_GET['debug']) && !WIRELESS) + header('Content-Type: application/xhtml+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); + + // Don't cache error pages!! + header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Cache-Control: no-cache'); + + if (!isset($txt['template_parse_error'])) + { + $txt['template_parse_error'] = 'Template Parse Error!'; + $txt['template_parse_error_message'] = 'It seems something has gone sour on the forum with the template system. This problem should only be temporary, so please come back later and try again. If you continue to see this message, please contact the administrator.

You can also try refreshing this page.'; + $txt['template_parse_error_details'] = 'There was a problem loading the %1$s template or language file. Please check the syntax and try again - remember, single quotes (\') often have to be escaped with a slash (\\). To see more specific error information from PHP, try accessing the file directly.

You may want to try to refresh this page or use the default theme.'; + } + + // First, let's get the doctype and language information out of the way. + echo ' + + '; + if (isset($context['character_set'])) + echo ' + '; + + if (!empty($maintenance) && !allowedTo('admin_forum')) + echo ' + ', $mtitle, ' + + +

', $mtitle, '

+ ', $mmessage, ' + +'; + elseif (!allowedTo('admin_forum')) + echo ' + ', $txt['template_parse_error'], ' + + +

', $txt['template_parse_error'], '

+ ', $txt['template_parse_error_message'], ' + +'; + else + { + require_once($sourcedir . '/Subs-Package.php'); + + $error = fetch_web_data($boardurl . strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => ''))); + if (empty($error)) + $error = $php_errormsg; + + $error = strtr($error, array('' => '', '' => '')); + + echo ' + ', $txt['template_parse_error'], ' + + +

', $txt['template_parse_error'], '

+ ', sprintf($txt['template_parse_error_details'], strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => ''))); + + if (!empty($error)) + echo ' +
+ +
', strtr(strtr($error, array('' . $boarddir => '...', '' . strtr($boarddir, '\\', '/') => '...')), '\\', '/'), '
'; + + // I know, I know... this is VERY COMPLICATED. Still, it's good. + if (preg_match('~ (\d+)$~i', $error, $match) != 0) + { + $data = file($filename); + $data2 = highlight_php_code(implode('', $data)); + $data2 = preg_split('~\~', $data2); + + // Fix the PHP code stuff... + if ($context['browser']['is_ie4'] || $context['browser']['is_ie5'] || $context['browser']['is_ie5.5']) + $data2 = str_replace("\t", '
' . "\t" . '
', $data2); + elseif (!$context['browser']['is_gecko']) + $data2 = str_replace("\t", '' . "\t" . '', $data2); + else + $data2 = str_replace('
' . "\t" . '
', "\t", $data2); + + // Now we get to work around a bug in PHP where it doesn't escape
s! + $j = -1; + foreach ($data as $line) + { + $j++; + + if (substr_count($line, '
') == 0) + continue; + + $n = substr_count($line, '
'); + for ($i = 0; $i < $n; $i++) + { + $data2[$j] .= '<br />' . $data2[$j + $i + 1]; + unset($data2[$j + $i + 1]); + } + $j += $n; + } + $data2 = array_values($data2); + array_unshift($data2, ''); + + echo ' +
';
+
+				// Figure out what the color coding was before...
+				$line = max($match[1] - 9, 1);
+				$last_line = '';
+				for ($line2 = $line - 1; $line2 > 1; $line2--)
+					if (strpos($data2[$line2], '<') !== false)
+					{
+						if (preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line2], $color_match) != 0)
+							$last_line = $color_match[1];
+						break;
+					}
+
+				// Show the relevant lines...
+				for ($n = min($match[1] + 4, count($data2) + 1); $line <= $n; $line++)
+				{
+					if ($line == $match[1])
+						echo '
';
+
+					echo '', sprintf('%' . strlen($n) . 's', $line), ': ';
+					if (isset($data2[$line]) && $data2[$line] != '')
+						echo substr($data2[$line], 0, 2) == ']+>~', '', $data2[$line]) : $last_line . $data2[$line];
+
+					if (isset($data2[$line]) && preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line], $color_match) != 0)
+					{
+						$last_line = $color_match[1];
+						echo '';
+					}
+					elseif ($last_line != '' && strpos($data2[$line], '<') !== false)
+						$last_line = '';
+					elseif ($last_line != '' && $data2[$line] != '')
+						echo '';
+
+					if ($line == $match[1])
+						echo '
';
+					else
+						echo "\n";
+				}
+
+				echo '
'; + } + + echo ' + +'; + } + + die; + } +} + +// Attempt to start the session, unless it already has been. +function loadSession() +{ + global $HTTP_SESSION_VARS, $modSettings, $boardurl, $sc; + + // Attempt to change a few PHP settings. + @ini_set('session.use_cookies', true); + @ini_set('session.use_only_cookies', false); + @ini_set('url_rewriter.tags', ''); + @ini_set('session.use_trans_sid', false); + @ini_set('arg_separator.output', '&'); + + if (!empty($modSettings['globalCookies'])) + { + $parsed_url = parse_url($boardurl); + + if (preg_match('~^\d{1,3}(\.\d{1,3}){3}$~', $parsed_url['host']) == 0 && preg_match('~(?:[^\.]+\.)?([^\.]{2,}\..+)\z~i', $parsed_url['host'], $parts) == 1) + @ini_set('session.cookie_domain', '.' . $parts[1]); + } + // !!! Set the session cookie path? + + // If it's already been started... probably best to skip this. + if ((@ini_get('session.auto_start') == 1 && !empty($modSettings['databaseSession_enable'])) || session_id() == '') + { + // Attempt to end the already-started session. + if (@ini_get('session.auto_start') == 1) + @session_write_close(); + + // This is here to stop people from using bad junky PHPSESSIDs. + if (isset($_REQUEST[session_name()]) && preg_match('~^[A-Za-z0-9,-]{16,32}$~', $_REQUEST[session_name()]) == 0 && !isset($_COOKIE[session_name()])) + { + $session_id = md5(md5('smf_sess_' . time()) . mt_rand()); + $_REQUEST[session_name()] = $session_id; + $_GET[session_name()] = $session_id; + $_POST[session_name()] = $session_id; + } + + // Use database sessions? (they don't work in 4.1.x!) + if (!empty($modSettings['databaseSession_enable']) && @version_compare(PHP_VERSION, '4.2.0') != -1) + { + session_set_save_handler('sessionOpen', 'sessionClose', 'sessionRead', 'sessionWrite', 'sessionDestroy', 'sessionGC'); + @ini_set('session.gc_probability', '1'); + } + elseif (@ini_get('session.gc_maxlifetime') <= 1440 && !empty($modSettings['databaseSession_lifetime'])) + @ini_set('session.gc_maxlifetime', max($modSettings['databaseSession_lifetime'], 60)); + + // Use cache setting sessions? + if (empty($modSettings['databaseSession_enable']) && !empty($modSettings['cache_enable']) && php_sapi_name() != 'cli') + { + if (function_exists('mmcache_set_session_handlers')) + mmcache_set_session_handlers(); + elseif (function_exists('eaccelerator_set_session_handlers')) + eaccelerator_set_session_handlers(); + } + + session_start(); + + // Change it so the cache settings are a little looser than default. + if (!empty($modSettings['databaseSession_loose'])) + header('Cache-Control: private'); + } + + // While PHP 4.1.x should use $_SESSION, it seems to need this to do it right. + if (@version_compare(PHP_VERSION, '4.2.0') == -1) + $HTTP_SESSION_VARS['php_412_bugfix'] = true; + + // Set the randomly generated code. + if (!isset($_SESSION['session_var'])) + { + $_SESSION['session_value'] = md5(session_id() . mt_rand()); + $_SESSION['session_var'] = substr(preg_replace('~^\d+~', '', sha1(mt_rand() . session_id() . mt_rand())), 0, rand(7, 12)); + } + $sc = $_SESSION['session_value']; +} + +function sessionOpen($save_path, $session_name) +{ + return true; +} + +function sessionClose() +{ + return true; +} + +function sessionRead($session_id) +{ + global $smcFunc; + + if (preg_match('~^[A-Za-z0-9,-]{16,32}$~', $session_id) == 0) + return false; + + // Look for it in the database. + $result = $smcFunc['db_query']('', ' + SELECT data + FROM {db_prefix}sessions + WHERE session_id = {string:session_id} + LIMIT 1', + array( + 'session_id' => $session_id, + ) + ); + list ($sess_data) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + return $sess_data; +} + +function sessionWrite($session_id, $data) +{ + global $smcFunc; + + if (preg_match('~^[A-Za-z0-9,-]{16,32}$~', $session_id) == 0) + return false; + + // First try to update an existing row... + $result = $smcFunc['db_query']('', ' + UPDATE {db_prefix}sessions + SET data = {string:data}, last_update = {int:last_update} + WHERE session_id = {string:session_id}', + array( + 'last_update' => time(), + 'data' => $data, + 'session_id' => $session_id, + ) + ); + + // If that didn't work, try inserting a new one. + if ($smcFunc['db_affected_rows']() == 0) + $result = $smcFunc['db_insert']('ignore', + '{db_prefix}sessions', + array('session_id' => 'string', 'data' => 'string', 'last_update' => 'int'), + array($session_id, $data, time()), + array('session_id') + ); + + return $result; +} + +function sessionDestroy($session_id) +{ + global $smcFunc; + + if (preg_match('~^[A-Za-z0-9,-]{16,32}$~', $session_id) == 0) + return false; + + // Just delete the row... + return $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}sessions + WHERE session_id = {string:session_id}', + array( + 'session_id' => $session_id, + ) + ); +} + +function sessionGC($max_lifetime) +{ + global $modSettings, $smcFunc; + + // Just set to the default or lower? Ignore it for a higher value. (hopefully) + if (!empty($modSettings['databaseSession_lifetime']) && ($max_lifetime <= 1440 || $modSettings['databaseSession_lifetime'] > $max_lifetime)) + $max_lifetime = max($modSettings['databaseSession_lifetime'], 60); + + // Clean up ;). + return $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}sessions + WHERE last_update < {int:last_update}', + array( + 'last_update' => time() - $max_lifetime, + ) + ); +} + +// Load up a database connection. +function loadDatabase() +{ + global $db_persist, $db_connection, $db_server, $db_user, $db_passwd; + global $db_type, $db_name, $ssi_db_user, $ssi_db_passwd, $sourcedir, $db_prefix; + + // Figure out what type of database we are using. + if (empty($db_type) || !file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')) + $db_type = 'mysql'; + + // Load the file for the database. + require_once($sourcedir . '/Subs-Db-' . $db_type . '.php'); + + // If we are in SSI try them first, but don't worry if it doesn't work, we have the normal username and password we can use. + if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd)) + $db_connection = smf_db_initiate($db_server, $db_name, $ssi_db_user, $ssi_db_passwd, $db_prefix, array('persist' => $db_persist, 'non_fatal' => true, 'dont_select_db' => true)); + + // Either we aren't in SSI mode, or it failed. + if (empty($db_connection)) + $db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('persist' => $db_persist, 'dont_select_db' => SMF == 'SSI')); + + // Safe guard here, if there isn't a valid connection lets put a stop to it. + if (!$db_connection) + db_fatal_error(); + + // If in SSI mode fix up the prefix. + if (SMF == 'SSI') + db_fix_prefix($db_prefix, $db_name); +} + +// Try to retrieve a cache entry. On failure, call the appropriate function. +function cache_quick_get($key, $file, $function, $params, $level = 1) +{ + global $modSettings, $sourcedir; + + // Refresh the cache if either: + // 1. Caching is disabled. + // 2. The cache level isn't high enough. + // 3. The item has not been cached or the cached item expired. + // 4. The cached item has a custom expiration condition evaluating to true. + // 5. The expire time set in the cache item has passed (needed for Zend). + if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < $level || !is_array($cache_block = cache_get_data($key, 3600)) || (!empty($cache_block['refresh_eval']) && eval($cache_block['refresh_eval'])) || (!empty($cache_block['expires']) && $cache_block['expires'] < time())) + { + require_once($sourcedir . '/' . $file); + $cache_block = call_user_func_array($function, $params); + + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= $level) + cache_put_data($key, $cache_block, $cache_block['expires'] - time()); + } + + // Some cached data may need a freshening up after retrieval. + if (!empty($cache_block['post_retri_eval'])) + eval($cache_block['post_retri_eval']); + + return $cache_block['data']; +} + +function cache_put_data($key, $value, $ttl = 120) +{ + global $boardurl, $sourcedir, $modSettings, $memcached; + global $cache_hits, $cache_count, $db_show_debug, $cachedir; + + if (empty($modSettings['cache_enable']) && !empty($modSettings)) + return; + + $cache_count = isset($cache_count) ? $cache_count + 1 : 1; + if (isset($db_show_debug) && $db_show_debug === true) + { + $cache_hits[$cache_count] = array('k' => $key, 'd' => 'put', 's' => $value === null ? 0 : strlen(serialize($value))); + $st = microtime(); + } + + $key = md5($boardurl . filemtime($sourcedir . '/Load.php')) . '-SMF-' . strtr($key, ':', '-'); + $value = $value === null ? null : serialize($value); + + // The simple yet efficient memcached. + if (function_exists('memcache_set') && isset($modSettings['cache_memcached']) && trim($modSettings['cache_memcached']) != '') + { + // Not connected yet? + if (empty($memcached)) + get_memcached_server(); + if (!$memcached) + return; + + memcache_set($memcached, $key, $value, 0, $ttl); + } + // eAccelerator... + elseif (function_exists('eaccelerator_put')) + { + if (mt_rand(0, 10) == 1) + eaccelerator_gc(); + + if ($value === null) + @eaccelerator_rm($key); + else + eaccelerator_put($key, $value, $ttl); + } + // Turck MMCache? + elseif (function_exists('mmcache_put')) + { + if (mt_rand(0, 10) == 1) + mmcache_gc(); + + if ($value === null) + @mmcache_rm($key); + else + mmcache_put($key, $value, $ttl); + } + // Alternative PHP Cache, ahoy! + elseif (function_exists('apc_store')) + { + // An extended key is needed to counteract a bug in APC. + if ($value === null) + apc_delete($key . 'smf'); + else + apc_store($key . 'smf', $value, $ttl); + } + // Zend Platform/ZPS/etc. + elseif (function_exists('output_cache_put')) + output_cache_put($key, $value); + elseif (function_exists('xcache_set') && ini_get('xcache.var_size') > 0) + { + if ($value === null) + xcache_unset($key); + else + xcache_set($key, $value, $ttl); + } + // Otherwise custom cache? + else + { + if ($value === null) + @unlink($cachedir . '/data_' . $key . '.php'); + else + { + $cache_data = '<' . '?' . 'php if (!defined(\'SMF\')) die; if (' . (time() + $ttl) . ' < time()) $expired = true; else{$expired = false; $value = \'' . addcslashes($value, '\\\'') . '\';}' . '?' . '>'; + $fh = @fopen($cachedir . '/data_' . $key . '.php', 'w'); + if ($fh) + { + // Write the file. + set_file_buffer($fh, 0); + flock($fh, LOCK_EX); + $cache_bytes = fwrite($fh, $cache_data); + flock($fh, LOCK_UN); + fclose($fh); + + // Check that the cache write was successful; all the data should be written + // If it fails due to low diskspace, remove the cache file + if ($cache_bytes != strlen($cache_data)) + @unlink($cachedir . '/data_' . $key . '.php'); + } + } + } + + if (isset($db_show_debug) && $db_show_debug === true) + $cache_hits[$cache_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st)); +} + +function cache_get_data($key, $ttl = 120) +{ + global $boardurl, $sourcedir, $modSettings, $memcached; + global $cache_hits, $cache_count, $db_show_debug, $cachedir; + + if (empty($modSettings['cache_enable']) && !empty($modSettings)) + return; + + $cache_count = isset($cache_count) ? $cache_count + 1 : 1; + if (isset($db_show_debug) && $db_show_debug === true) + { + $cache_hits[$cache_count] = array('k' => $key, 'd' => 'get'); + $st = microtime(); + } + + $key = md5($boardurl . filemtime($sourcedir . '/Load.php')) . '-SMF-' . strtr($key, ':', '-'); + + // Okay, let's go for it memcached! + if (function_exists('memcache_get') && isset($modSettings['cache_memcached']) && trim($modSettings['cache_memcached']) != '') + { + // Not connected yet? + if (empty($memcached)) + get_memcached_server(); + if (!$memcached) + return; + + $value = memcache_get($memcached, $key); + } + // Again, eAccelerator. + elseif (function_exists('eaccelerator_get')) + $value = eaccelerator_get($key); + // The older, but ever-stable, Turck MMCache... + elseif (function_exists('mmcache_get')) + $value = mmcache_get($key); + // This is the free APC from PECL. + elseif (function_exists('apc_fetch')) + $value = apc_fetch($key . 'smf'); + // Zend's pricey stuff. + elseif (function_exists('output_cache_get')) + $value = output_cache_get($key, $ttl); + elseif (function_exists('xcache_get') && ini_get('xcache.var_size') > 0) + $value = xcache_get($key); + // Otherwise it's SMF data! + elseif (file_exists($cachedir . '/data_' . $key . '.php') && filesize($cachedir . '/data_' . $key . '.php') > 10) + { + require($cachedir . '/data_' . $key . '.php'); + if (!empty($expired) && isset($value)) + { + @unlink($cachedir . '/data_' . $key . '.php'); + unset($value); + } + } + + if (isset($db_show_debug) && $db_show_debug === true) + { + $cache_hits[$cache_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st)); + $cache_hits[$cache_count]['s'] = isset($value) ? strlen($value) : 0; + } + + if (empty($value)) + return null; + // If it's broke, it's broke... so give up on it. + else + return @unserialize($value); +} + +function get_memcached_server($level = 3) +{ + global $modSettings, $memcached, $db_persist; + + $servers = explode(',', $modSettings['cache_memcached']); + $server = explode(':', trim($servers[array_rand($servers)])); + + // Don't try more times than we have servers! + $level = min(count($servers), $level); + + // Don't wait too long: yes, we want the server, but we might be able to run the query faster! + if (empty($db_persist)) + $memcached = memcache_connect($server[0], empty($server[1]) ? 11211 : $server[1]); + else + $memcached = memcache_pconnect($server[0], empty($server[1]) ? 11211 : $server[1]); + + if (!$memcached && $level > 0) + get_memcached_server($level - 1); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/LockTopic.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/LockTopic.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,158 @@ + $topic, + ) + ); + list ($starter, $locked) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Can you lock topics here, mister? + $user_lock = !allowedTo('lock_any'); + if ($user_lock && $starter == $user_info['id']) + isAllowedTo('lock_own'); + else + isAllowedTo('lock_any'); + + // Locking with high privileges. + if ($locked == '0' && !$user_lock) + $locked = '1'; + // Locking with low privileges. + elseif ($locked == '0') + $locked = '2'; + // Unlocking - make sure you don't unlock what you can't. + elseif ($locked == '2' || ($locked == '1' && !$user_lock)) + $locked = '0'; + // You cannot unlock this! + else + fatal_lang_error('locked_by_admin', 'user'); + + // Actually lock the topic in the database with the new value. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET locked = {int:locked} + WHERE id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + 'locked' => $locked, + ) + ); + + // If they are allowed a "moderator" permission, log it in the moderator log. + if (!$user_lock) + logAction($locked ? 'lock' : 'unlock', array('topic' => $topic, 'board' => $board)); + // Notify people that this topic has been locked? + sendNotifications($topic, empty($locked) ? 'unlock' : 'lock'); + + // Back to the topic! + redirectexit('topic=' . $topic . '.' . $_REQUEST['start'] . (WIRELESS ? ';moderate' : '')); +} + +// Sticky a topic. Can't be done by topic starters - that would be annoying! +function Sticky() +{ + global $modSettings, $topic, $board, $sourcedir, $smcFunc; + + // Make sure the user can sticky it, and they are stickying *something*. + isAllowedTo('make_sticky'); + + // You shouldn't be able to (un)sticky a topic if the setting is disabled. + if (empty($modSettings['enableStickyTopics'])) + fatal_lang_error('cannot_make_sticky', false); + + // You can't sticky a board or something! + if (empty($topic)) + fatal_lang_error('not_a_topic', false); + + checkSession('get'); + + // We need Subs-Post.php for the sendNotifications() function. + require_once($sourcedir . '/Subs-Post.php'); + + // Is this topic already stickied, or no? + $request = $smcFunc['db_query']('', ' + SELECT is_sticky + FROM {db_prefix}topics + WHERE id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + list ($is_sticky) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Toggle the sticky value.... pretty simple ;). + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET is_sticky = {int:is_sticky} + WHERE id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + 'is_sticky' => empty($is_sticky) ? 1 : 0, + ) + ); + + // Log this sticky action - always a moderator thing. + logAction(empty($is_sticky) ? 'sticky' : 'unsticky', array('topic' => $topic, 'board' => $board)); + // Notify people that this topic has been stickied? + if (empty($is_sticky)) + sendNotifications($topic, 'sticky'); + + // Take them back to the now stickied topic. + redirectexit('topic=' . $topic . '.' . $_REQUEST['start'] . (WIRELESS ? ';moderate' : '')); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/LogInOut.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/LogInOut.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,721 @@ + $scripturl . '?action=login', + 'name' => $txt['login'], + ); + + // Set the login URL - will be used when the login process is done (but careful not to send us to an attachment). + if (isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0) + $_SESSION['login_url'] = $_SESSION['old_url']; + else + unset($_SESSION['login_url']); +} + +// Perform the actual logging-in. +function Login2() +{ + global $txt, $scripturl, $user_info, $user_settings, $smcFunc; + global $cookiename, $maintenance, $modSettings, $context, $sc, $sourcedir; + + // Load cookie authentication stuff. + require_once($sourcedir . '/Subs-Auth.php'); + + if (isset($_GET['sa']) && $_GET['sa'] == 'salt' && !$user_info['is_guest']) + { + if (isset($_COOKIE[$cookiename]) && preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}$~', $_COOKIE[$cookiename]) === 1) + list (, , $timeout) = @unserialize($_COOKIE[$cookiename]); + elseif (isset($_SESSION['login_' . $cookiename])) + list (, , $timeout) = @unserialize($_SESSION['login_' . $cookiename]); + else + trigger_error('Login2(): Cannot be logged in without a session or cookie', E_USER_ERROR); + + $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); + updateMemberData($user_info['id'], array('password_salt' => $user_settings['password_salt'])); + + setLoginCookie($timeout - time(), $user_info['id'], sha1($user_settings['passwd'] . $user_settings['password_salt'])); + + redirectexit('action=login2;sa=check;member=' . $user_info['id'], $context['server']['needs_login_fix']); + } + // Double check the cookie... + elseif (isset($_GET['sa']) && $_GET['sa'] == 'check') + { + // Strike! You're outta there! + if ($_GET['member'] != $user_info['id']) + fatal_lang_error('login_cookie_error', false); + + // Some whitelisting for login_url... + if (empty($_SESSION['login_url'])) + redirectexit(); + else + { + // Best not to clutter the session data too much... + $temp = $_SESSION['login_url']; + unset($_SESSION['login_url']); + + redirectexit($temp); + } + } + + // Beyond this point you are assumed to be a guest trying to login. + if (!$user_info['is_guest']) + redirectexit(); + + // Are you guessing with a script? + spamProtection('login'); + + // Set the login_url if it's not already set (but careful not to send us to an attachment). + if (empty($_SESSION['login_url']) && isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0) + $_SESSION['login_url'] = $_SESSION['old_url']; + + // Been guessing a lot, haven't we? + if (isset($_SESSION['failed_login']) && $_SESSION['failed_login'] >= $modSettings['failed_login_threshold'] * 3) + fatal_lang_error('login_threshold_fail', 'critical'); + + // Set up the cookie length. (if it's invalid, just fall through and use the default.) + if (isset($_POST['cookieneverexp']) || (!empty($_POST['cookielength']) && $_POST['cookielength'] == -1)) + $modSettings['cookieTime'] = 3153600; + elseif (!empty($_POST['cookielength']) && ($_POST['cookielength'] >= 1 || $_POST['cookielength'] <= 525600)) + $modSettings['cookieTime'] = (int) $_POST['cookielength']; + + loadLanguage('Login'); + // Load the template stuff - wireless or normal. + if (WIRELESS) + $context['sub_template'] = WIRELESS_PROTOCOL . '_login'; + else + { + loadTemplate('Login'); + $context['sub_template'] = 'login'; + } + + // Set up the default/fallback stuff. + $context['default_username'] = isset($_POST['user']) ? preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($_POST['user'])) : ''; + $context['default_password'] = ''; + $context['never_expire'] = $modSettings['cookieTime'] == 525600 || $modSettings['cookieTime'] == 3153600; + $context['login_errors'] = array($txt['error_occured']); + $context['page_title'] = $txt['login']; + + // Add the login chain to the link tree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=login', + 'name' => $txt['login'], + ); + + if (!empty($_POST['openid_identifier']) && !empty($modSettings['enableOpenID'])) + { + require_once($sourcedir . '/Subs-OpenID.php'); + if (($open_id = smf_openID_validate($_POST['openid_identifier'])) !== 'no_data') + return $open_id; + } + + // You forgot to type your username, dummy! + if (!isset($_POST['user']) || $_POST['user'] == '') + { + $context['login_errors'] = array($txt['need_username']); + return; + } + + // Hmm... maybe 'admin' will login with no password. Uhh... NO! + if ((!isset($_POST['passwrd']) || $_POST['passwrd'] == '') && (!isset($_POST['hash_passwrd']) || strlen($_POST['hash_passwrd']) != 40)) + { + $context['login_errors'] = array($txt['no_password']); + return; + } + + // No funky symbols either. + if (preg_match('~[<>&"\'=\\\]~', preg_replace('~(&#(\\d{1,7}|x[0-9a-fA-F]{1,6});)~', '', $_POST['user'])) != 0) + { + $context['login_errors'] = array($txt['error_invalid_characters_username']); + return; + } + + // Are we using any sort of integration to validate the login? + if (in_array('retry', call_integration_hook('integrate_validate_login', array($_POST['user'], isset($_POST['hash_passwrd']) && strlen($_POST['hash_passwrd']) == 40 ? $_POST['hash_passwrd'] : null, $modSettings['cookieTime'])), true)) + { + $context['login_errors'] = array($txt['login_hash_error']); + $context['disable_login_hashing'] = true; + return; + } + + // Load the data up! + $request = $smcFunc['db_query']('', ' + SELECT passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, + openid_uri, passwd_flood + FROM {db_prefix}members + WHERE ' . ($smcFunc['db_case_sensitive'] ? 'LOWER(member_name) = LOWER({string:user_name})' : 'member_name = {string:user_name}') . ' + LIMIT 1', + array( + 'user_name' => $smcFunc['db_case_sensitive'] ? strtolower($_POST['user']) : $_POST['user'], + ) + ); + // Probably mistyped or their email, try it as an email address. (member_name first, though!) + if ($smcFunc['db_num_rows']($request) == 0) + { + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, openid_uri, + passwd_flood + FROM {db_prefix}members + WHERE email_address = {string:user_name} + LIMIT 1', + array( + 'user_name' => $_POST['user'], + ) + ); + // Let them try again, it didn't match anything... + if ($smcFunc['db_num_rows']($request) == 0) + { + $context['login_errors'] = array($txt['username_no_exist']); + return; + } + } + + $user_settings = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Figure out the password using SMF's encryption - if what they typed is right. + if (isset($_POST['hash_passwrd']) && strlen($_POST['hash_passwrd']) == 40) + { + // Needs upgrading? + if (strlen($user_settings['passwd']) != 40) + { + $context['login_errors'] = array($txt['login_hash_error']); + $context['disable_login_hashing'] = true; + unset($user_settings); + return; + } + // Challenge passed. + elseif ($_POST['hash_passwrd'] == sha1($user_settings['passwd'] . $sc)) + $sha_passwd = $user_settings['passwd']; + else + { + // Don't allow this! + validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood']); + + $_SESSION['failed_login'] = @$_SESSION['failed_login'] + 1; + + if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold']) + redirectexit('action=reminder'); + else + { + log_error($txt['incorrect_password'] . ' - ' . $user_settings['member_name'] . '', 'user'); + + $context['disable_login_hashing'] = true; + $context['login_errors'] = array($txt['incorrect_password']); + unset($user_settings); + return; + } + } + } + else + $sha_passwd = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd'])); + + // Bad password! Thought you could fool the database?! + if ($user_settings['passwd'] != $sha_passwd) + { + // Let's be cautious, no hacking please. thanx. + validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood']); + + // Maybe we were too hasty... let's try some other authentication methods. + $other_passwords = array(); + + // None of the below cases will be used most of the time (because the salt is normally set.) + if ($user_settings['password_salt'] == '') + { + // YaBB SE, Discus, MD5 (used a lot), SHA-1 (used some), SMF 1.0.x, IkonBoard, and none at all. + $other_passwords[] = crypt($_POST['passwrd'], substr($_POST['passwrd'], 0, 2)); + $other_passwords[] = crypt($_POST['passwrd'], substr($user_settings['passwd'], 0, 2)); + $other_passwords[] = md5($_POST['passwrd']); + $other_passwords[] = sha1($_POST['passwrd']); + $other_passwords[] = md5_hmac($_POST['passwrd'], strtolower($user_settings['member_name'])); + $other_passwords[] = md5($_POST['passwrd'] . strtolower($user_settings['member_name'])); + $other_passwords[] = md5(md5($_POST['passwrd'])); + $other_passwords[] = $_POST['passwrd']; + + // This one is a strange one... MyPHP, crypt() on the MD5 hash. + $other_passwords[] = crypt(md5($_POST['passwrd']), md5($_POST['passwrd'])); + + // Snitz style - SHA-256. Technically, this is a downgrade, but most PHP configurations don't support sha256 anyway. + if (strlen($user_settings['passwd']) == 64 && function_exists('mhash') && defined('MHASH_SHA256')) + $other_passwords[] = bin2hex(mhash(MHASH_SHA256, $_POST['passwrd'])); + + // phpBB3 users new hashing. We now support it as well ;). + $other_passwords[] = phpBB3_password_check($_POST['passwrd'], $user_settings['passwd']); + + // APBoard 2 Login Method. + $other_passwords[] = md5(crypt($_POST['passwrd'], 'CRYPT_MD5')); + } + // The hash should be 40 if it's SHA-1, so we're safe with more here too. + elseif (strlen($user_settings['passwd']) == 32) + { + // vBulletin 3 style hashing? Let's welcome them with open arms \o/. + $other_passwords[] = md5(md5($_POST['passwrd']) . $user_settings['password_salt']); + + // Hmm.. p'raps it's Invision 2 style? + $other_passwords[] = md5(md5($user_settings['password_salt']) . md5($_POST['passwrd'])); + + // Some common md5 ones. + $other_passwords[] = md5($user_settings['password_salt'] . $_POST['passwrd']); + $other_passwords[] = md5($_POST['passwrd'] . $user_settings['password_salt']); + } + elseif (strlen($user_settings['passwd']) == 40) + { + // Maybe they are using a hash from before the password fix. + $other_passwords[] = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd'])); + + // BurningBoard3 style of hashing. + $other_passwords[] = sha1($user_settings['password_salt'] . sha1($user_settings['password_salt'] . sha1($_POST['passwrd']))); + + // Perhaps we converted to UTF-8 and have a valid password being hashed differently. + if ($context['character_set'] == 'utf8' && !empty($modSettings['previousCharacterSet']) && $modSettings['previousCharacterSet'] != 'utf8') + { + // Try iconv first, for no particular reason. + if (function_exists('iconv')) + $other_passwords['iconv'] = sha1(strtolower(iconv('UTF-8', $modSettings['previousCharacterSet'], $user_settings['member_name'])) . un_htmlspecialchars(iconv('UTF-8', $modSettings['previousCharacterSet'], $_POST['passwrd']))); + + // Say it aint so, iconv failed! + if (empty($other_passwords['iconv']) && function_exists('mb_convert_encoding')) + $other_passwords[] = sha1(strtolower(mb_convert_encoding($user_settings['member_name'], 'UTF-8', $modSettings['previousCharacterSet'])) . un_htmlspecialchars(mb_convert_encoding($_POST['passwrd'], 'UTF-8', $modSettings['previousCharacterSet']))); + } + } + + // SMF's sha1 function can give a funny result on Linux (Not our fault!). If we've now got the real one let the old one be valid! + if (strpos(strtolower(PHP_OS), 'win') !== 0) + { + require_once($sourcedir . '/Subs-Compat.php'); + $other_passwords[] = sha1_smf(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd'])); + } + + // Whichever encryption it was using, let's make it use SMF's now ;). + if (in_array($user_settings['passwd'], $other_passwords)) + { + $user_settings['passwd'] = $sha_passwd; + $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); + + // Update the password and set up the hash. + updateMemberData($user_settings['id_member'], array('passwd' => $user_settings['passwd'], 'password_salt' => $user_settings['password_salt'], 'passwd_flood' => '')); + } + // Okay, they for sure didn't enter the password! + else + { + // They've messed up again - keep a count to see if they need a hand. + $_SESSION['failed_login'] = @$_SESSION['failed_login'] + 1; + + // Hmm... don't remember it, do you? Here, try the password reminder ;). + if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold']) + redirectexit('action=reminder'); + // We'll give you another chance... + else + { + // Log an error so we know that it didn't go well in the error log. + log_error($txt['incorrect_password'] . ' - ' . $user_settings['member_name'] . '', 'user'); + + $context['login_errors'] = array($txt['incorrect_password']); + return; + } + } + } + elseif (!empty($user_settings['passwd_flood'])) + { + // Let's be sure they weren't a little hacker. + validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood'], true); + + // If we got here then we can reset the flood counter. + updateMemberData($user_settings['id_member'], array('passwd_flood' => '')); + } + + // Correct password, but they've got no salt; fix it! + if ($user_settings['password_salt'] == '') + { + $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); + updateMemberData($user_settings['id_member'], array('password_salt' => $user_settings['password_salt'])); + } + + // Check their activation status. + if (!checkActivation()) + return; + + DoLogin(); +} + +function checkActivation() +{ + global $context, $txt, $scripturl, $user_settings, $modSettings; + + if (!isset($context['login_errors'])) + $context['login_errors'] = array(); + + // What is the true activation status of this account? + $activation_status = $user_settings['is_activated'] > 10 ? $user_settings['is_activated'] - 10 : $user_settings['is_activated']; + + // Check if the account is activated - COPPA first... + if ($activation_status == 5) + { + $context['login_errors'][] = $txt['coppa_no_concent'] . ' ' . $txt['coppa_need_more_details'] . ''; + return false; + } + // Awaiting approval still? + elseif ($activation_status == 3) + fatal_lang_error('still_awaiting_approval', 'user'); + // Awaiting deletion, changed their mind? + elseif ($activation_status == 4) + { + if (isset($_REQUEST['undelete'])) + { + updateMemberData($user_settings['id_member'], array('is_activated' => 1)); + updateSettings(array('unapprovedMembers' => ($modSettings['unapprovedMembers'] > 0 ? $modSettings['unapprovedMembers'] - 1 : 0))); + } + else + { + $context['disable_login_hashing'] = true; + $context['login_errors'][] = $txt['awaiting_delete_account']; + $context['login_show_undelete'] = true; + return false; + } + } + // Standard activation? + elseif ($activation_status != 1) + { + log_error($txt['activate_not_completed1'] . ' - ' . $user_settings['member_name'] . '', false); + + $context['login_errors'][] = $txt['activate_not_completed1'] . ' ' . $txt['activate_not_completed2'] . ''; + return false; + } + return true; +} + +function DoLogin() +{ + global $txt, $scripturl, $user_info, $user_settings, $smcFunc; + global $cookiename, $maintenance, $modSettings, $context, $sourcedir; + + // Load cookie authentication stuff. + require_once($sourcedir . '/Subs-Auth.php'); + + // Call login integration functions. + call_integration_hook('integrate_login', array($user_settings['member_name'], isset($_POST['hash_passwrd']) && strlen($_POST['hash_passwrd']) == 40 ? $_POST['hash_passwrd'] : null, $modSettings['cookieTime'])); + + // Get ready to set the cookie... + $username = $user_settings['member_name']; + $user_info['id'] = $user_settings['id_member']; + + // Bam! Cookie set. A session too, just in case. + setLoginCookie(60 * $modSettings['cookieTime'], $user_settings['id_member'], sha1($user_settings['passwd'] . $user_settings['password_salt'])); + + // Reset the login threshold. + if (isset($_SESSION['failed_login'])) + unset($_SESSION['failed_login']); + + $user_info['is_guest'] = false; + $user_settings['additional_groups'] = explode(',', $user_settings['additional_groups']); + $user_info['is_admin'] = $user_settings['id_group'] == 1 || in_array(1, $user_settings['additional_groups']); + + // Are you banned? + is_not_banned(true); + + // An administrator, set up the login so they don't have to type it again. + if ($user_info['is_admin'] && isset($user_settings['openid_uri']) && empty($user_settings['openid_uri'])) + { + $_SESSION['admin_time'] = time(); + unset($_SESSION['just_registered']); + } + + // Don't stick the language or theme after this point. + unset($_SESSION['language'], $_SESSION['id_theme']); + + // First login? + $request = $smcFunc['db_query']('', ' + SELECT last_login + FROM {db_prefix}members + WHERE id_member = {int:id_member} + AND last_login = 0', + array( + 'id_member' => $user_info['id'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 1) + $_SESSION['first_login'] = true; + else + unset($_SESSION['first_login']); + $smcFunc['db_free_result']($request); + + // You've logged in, haven't you? + updateMemberData($user_info['id'], array('last_login' => time(), 'member_ip' => $user_info['ip'], 'member_ip2' => $_SERVER['BAN_CHECK_IP'])); + + // Get rid of the online entry for that old guest.... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_online + WHERE session = {string:session}', + array( + 'session' => 'ip' . $user_info['ip'], + ) + ); + $_SESSION['log_time'] = 0; + + // Just log you back out if it's in maintenance mode and you AREN'T an admin. + if (empty($maintenance) || allowedTo('admin_forum')) + redirectexit('action=login2;sa=check;member=' . $user_info['id'], $context['server']['needs_login_fix']); + else + redirectexit('action=logout;' . $context['session_var'] . '=' . $context['session_id'], $context['server']['needs_login_fix']); +} + +// Log the user out. +function Logout($internal = false, $redirect = true) +{ + global $sourcedir, $user_info, $user_settings, $context, $modSettings, $smcFunc; + + // Make sure they aren't being auto-logged out. + if (!$internal) + checkSession('get'); + + require_once($sourcedir . '/Subs-Auth.php'); + + if (isset($_SESSION['pack_ftp'])) + $_SESSION['pack_ftp'] = null; + + // They cannot be open ID verified any longer. + if (isset($_SESSION['openid'])) + unset($_SESSION['openid']); + + // It won't be first login anymore. + unset($_SESSION['first_login']); + + // Just ensure they aren't a guest! + if (!$user_info['is_guest']) + { + // Pass the logout information to integrations. + call_integration_hook('integrate_logout', array($user_settings['member_name'])); + + // If you log out, you aren't online anymore :P. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_online + WHERE id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + ) + ); + } + + $_SESSION['log_time'] = 0; + + // Empty the cookie! (set it in the past, and for id_member = 0) + setLoginCookie(-3600, 0); + + // Off to the merry board index we go! + if ($redirect) + { + if (empty($_SESSION['logout_url'])) + redirectexit('', $context['server']['needs_login_fix']); + else + { + $temp = $_SESSION['logout_url']; + unset($_SESSION['logout_url']); + + redirectexit($temp, $context['server']['needs_login_fix']); + } + } +} + +// MD5 Encryption used for older passwords. +function md5_hmac($data, $key) +{ + $key = str_pad(strlen($key) <= 64 ? $key : pack('H*', md5($key)), 64, chr(0x00)); + return md5(($key ^ str_repeat(chr(0x5c), 64)) . pack('H*', md5(($key ^ str_repeat(chr(0x36), 64)) . $data))); +} + +// Special encryption used by phpBB3. +function phpBB3_password_check($passwd, $passwd_hash) +{ + // Too long or too short? + if (strlen($passwd_hash) != 34) + return; + + // Range of characters allowed. + $range = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + // Tests + $strpos = strpos($range, $passwd_hash[3]); + $count = 1 << $strpos; + $count2 = $count; + $salt = substr($passwd_hash, 4, 8); + + // Things are done differently for PHP 5. + if (@version_compare(PHP_VERSION, '5') >= 0) + { + $hash = md5($salt . $passwd, true); + for (; $count != 0; --$count) + $hash = md5($hash . $passwd, true); + } + else + { + $hash = pack('H*', md5($salt . $passwd)); + for (; $count != 0; --$count) + $hash = pack('H*', md5($hash . $passwd)); + } + + $output = substr($passwd_hash, 0, 12); + $i = 0; + while ($i < 16) + { + $value = ord($hash[$i++]); + $output .= $range[$value & 0x3f]; + + if ($i < 16) + $value |= ord($hash[$i]) << 8; + + $output .= $range[($value >> 6) & 0x3f]; + + if ($i++ >= 16) + break; + + if ($i < 16) + $value |= ord($hash[$i]) << 16; + + $output .= $range[($value >> 12) & 0x3f]; + + if ($i++ >= 16) + break; + + $output .= $range[($value >> 18) & 0x3f]; + } + + // Return now. + return $output; +} + +// This protects against brute force attacks on a member's password. Importantly even if the password was right we DON'T TELL THEM! +function validatePasswordFlood($id_member, $password_flood_value = false, $was_correct = false) +{ + global $smcFunc, $cookiename, $sourcedir; + + // As this is only brute protection, we allow 5 attempts every 10 seconds. + + // Destroy any session or cookie data about this member, as they validated wrong. + require_once($sourcedir . '/Subs-Auth.php'); + setLoginCookie(-3600, 0); + + if (isset($_SESSION['login_' . $cookiename])) + unset($_SESSION['login_' . $cookiename]); + + // We need a member! + if (!$id_member) + { + // Redirect back! + redirectexit(); + + // Probably not needed, but still make sure... + fatal_lang_error('no_access', false); + } + + // Right, have we got a flood value? + if ($password_flood_value !== false) + @list ($time_stamp, $number_tries) = explode('|', $password_flood_value); + + // Timestamp or number of tries invalid? + if (empty($number_tries) || empty($time_stamp)) + { + $number_tries = 0; + $time_stamp = time(); + } + + // They've failed logging in already + if (!empty($number_tries)) + { + // Give them less chances if they failed before + $number_tries = $time_stamp < time() - 20 ? 2 : $number_tries; + + // They are trying too fast, make them wait longer + if ($time_stamp < time() - 10) + $time_stamp = time(); + } + + $number_tries++; + + // Broken the law? + if ($number_tries > 5) + fatal_lang_error('login_threshold_brute_fail', 'critical'); + + // Otherwise set the members data. If they correct on their first attempt then we actually clear it, otherwise we set it! + updateMemberData($id_member, array('passwd_flood' => $was_correct && $number_tries == 1 ? '' : $time_stamp . '|' . $number_tries)); + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageAttachments.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageAttachments.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1835 @@ + 'ManageAttachmentSettings', + 'attachpaths' => 'ManageAttachmentPaths', + 'avatars' => 'ManageAvatarSettings', + 'browse' => 'BrowseFiles', + 'byAge' => 'RemoveAttachmentByAge', + 'bySize' => 'RemoveAttachmentBySize', + 'maintenance' => 'MaintainFiles', + 'moveAvatars' => 'MoveAvatars', + 'repair' => 'RepairAttachments', + 'remove' => 'RemoveAttachment', + 'removeall' => 'RemoveAllAttachments' + ); + + // Pick the correct sub-action. + if (isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']])) + $context['sub_action'] = $_REQUEST['sa']; + else + $context['sub_action'] = 'browse'; + + // Default page title is good. + $context['page_title'] = $txt['attachments_avatars']; + + // This uses admin tabs - as it should! + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['attachments_avatars'], + 'help' => 'manage_files', + 'description' => $txt['attachments_desc'], + ); + + // Finally fall through to what we are doing. + $subActions[$context['sub_action']](); +} + +function ManageAttachmentSettings($return_config = false) +{ + global $txt, $modSettings, $scripturl, $context, $options, $sourcedir; + + $context['valid_upload_dir'] = is_dir($modSettings['attachmentUploadDir']) && is_writable($modSettings['attachmentUploadDir']); + + // Perform a test to see if the GD module is installed. + $testGD = get_extension_funcs('gd'); + + $config_vars = array( + array('title', 'attachment_manager_settings'), + // Are attachments enabled? + array('select', 'attachmentEnable', array($txt['attachmentEnable_deactivate'], $txt['attachmentEnable_enable_all'], $txt['attachmentEnable_disable_new'])), + '', + // Extension checks etc. + array('check', 'attachmentCheckExtensions'), + array('text', 'attachmentExtensions', 40), + array('check', 'attachmentRecodeLineEndings'), + '', + // Directory and size limits. + empty($modSettings['currentAttachmentUploadDir']) ? array('text', 'attachmentUploadDir', 40, 'invalid' => !$context['valid_upload_dir']) : array('var_message', 'attachmentUploadDir_multiple', 'message' => 'attachmentUploadDir_multiple_configure'), + array('text', 'attachmentDirSizeLimit', 6, 'postinput' => $txt['kilobyte']), + array('text', 'attachmentPostLimit', 6, 'postinput' => $txt['kilobyte']), + array('text', 'attachmentSizeLimit', 6, 'postinput' => $txt['kilobyte']), + array('text', 'attachmentNumPerPostLimit', 6), + '', + // Image settings. + array('warning', empty($testGD) ? 'attachment_gd_warning' : ''), + array('check', 'attachment_image_reencode'), + '', + array('warning', 'attachment_image_paranoid_warning'), + array('check', 'attachment_image_paranoid'), + '', + // Thumbnail settings. + array('check', 'attachmentShowImages'), + array('check', 'attachmentThumbnails'), + array('check', 'attachment_thumb_png'), + array('text', 'attachmentThumbWidth', 6), + array('text', 'attachmentThumbHeight', 6), + ); + + if ($return_config) + return $config_vars; + + // These are very likely to come in handy! (i.e. without them we're doomed!) + require_once($sourcedir . '/ManagePermissions.php'); + require_once($sourcedir . '/ManageServer.php'); + + // Saving settings? + if (isset($_GET['save'])) + { + checkSession(); + + saveDBSettings($config_vars); + redirectexit('action=admin;area=manageattachments;sa=attachments'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=manageattachments;save;sa=attachments'; + prepareDBSettingContext($config_vars); + + $context['sub_template'] = 'show_settings'; +} + +function ManageAvatarSettings($return_config = false) +{ + global $txt, $context, $modSettings, $sourcedir, $scripturl; + + // Perform a test to see if the GD module is installed. + $testGD = get_extension_funcs('gd'); + + $context['valid_avatar_dir'] = is_dir($modSettings['avatar_directory']); + $context['valid_custom_avatar_dir'] = empty($modSettings['custom_avatar_enabled']) || (!empty($modSettings['custom_avatar_dir']) && is_dir($modSettings['custom_avatar_dir']) && is_writable($modSettings['custom_avatar_dir'])); + + $config_vars = array( + // Server stored avatars! + array('title', 'avatar_server_stored'), + array('warning', empty($testGD) ? 'avatar_gd_warning' : ''), + array('permissions', 'profile_server_avatar', 0, $txt['avatar_server_stored_groups']), + array('text', 'avatar_directory', 40, 'invalid' => !$context['valid_avatar_dir']), + array('text', 'avatar_url', 40), + // External avatars? + array('title', 'avatar_external'), + array('permissions', 'profile_remote_avatar', 0, $txt['avatar_external_url_groups']), + array('check', 'avatar_download_external', 0, 'onchange' => 'fUpdateStatus();'), + array('text', 'avatar_max_width_external', 6), + array('text', 'avatar_max_height_external', 6), + array('select', 'avatar_action_too_large', + array( + 'option_refuse' => $txt['option_refuse'], + 'option_html_resize' => $txt['option_html_resize'], + 'option_js_resize' => $txt['option_js_resize'], + 'option_download_and_resize' => $txt['option_download_and_resize'], + ), + ), + // Uploadable avatars? + array('title', 'avatar_upload'), + array('permissions', 'profile_upload_avatar', 0, $txt['avatar_upload_groups']), + array('text', 'avatar_max_width_upload', 6), + array('text', 'avatar_max_height_upload', 6), + array('check', 'avatar_resize_upload', 'subtext' => $txt['avatar_resize_upload_note']), + array('check', 'avatar_reencode'), + '', + array('warning', 'avatar_paranoid_warning'), + array('check', 'avatar_paranoid'), + '', + array('check', 'avatar_download_png'), + array('select', 'custom_avatar_enabled', array($txt['option_attachment_dir'], $txt['option_specified_dir']), 'onchange' => 'fUpdateStatus();'), + array('text', 'custom_avatar_dir', 40, 'subtext' => $txt['custom_avatar_dir_desc'], 'invalid' => !$context['valid_custom_avatar_dir']), + array('text', 'custom_avatar_url', 40), + ); + + if ($return_config) + return $config_vars; + + // We need these files for the inline permission settings, and the settings template. + require_once($sourcedir . '/ManagePermissions.php'); + require_once($sourcedir . '/ManageServer.php'); + + // Saving avatar settings? + if (isset($_GET['save'])) + { + checkSession(); + + // Just incase the admin forgot to set both custom avatar values, we disable it to prevent errors. + 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; + + saveDBSettings($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;'; + + // Prepare the context. + $context['post_url'] = $scripturl . '?action=admin;area=manageattachments;save;sa=avatars'; + prepareDBSettingContext($config_vars); + + // Add a layer for the javascript. + $context['template_layers'][] = 'avatar_settings'; + $context['sub_template'] = 'show_settings'; +} + +function BrowseFiles() +{ + global $context, $txt, $scripturl, $options, $modSettings; + global $smcFunc, $sourcedir; + + $context['sub_template'] = 'browse'; + + // Attachments or avatars? + $context['browse_type'] = isset($_REQUEST['avatars']) ? 'avatars' : (isset($_REQUEST['thumbs']) ? 'thumbs' : 'attachments'); + + // Set the options for the list component. + $listOptions = array( + 'id' => 'file_list', + 'title' => $txt['attachment_manager_' . ($context['browse_type'] === 'avatars' ? 'avatars' : ( $context['browse_type'] === 'thumbs' ? 'thumbs' : 'attachments'))], + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'base_href' => $scripturl . '?action=admin;area=manageattachments;sa=browse' . ($context['browse_type'] === 'avatars' ? ';avatars' : ($context['browse_type'] === 'thumbs' ? ';thumbs' : '')), + 'default_sort_col' => 'name', + 'no_items_label' => $txt['attachment_manager_' . ($context['browse_type'] === 'avatars' ? 'avatars' : ( $context['browse_type'] === 'thumbs' ? 'thumbs' : 'attachments')) . '_no_entries'], + 'get_items' => array( + 'function' => 'list_getFiles', + 'params' => array( + $context['browse_type'], + ), + ), + 'get_count' => array( + 'function' => 'list_getNumFiles', + 'params' => array( + $context['browse_type'], + ), + ), + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['attachment_name'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $modSettings, $context, $scripturl; + + $link = \'%1$s\', preg_replace(\'~&#(\\\\d{1,7}|x[0-9a-fA-F]{1,6});~\', \'&#\\\\1;\', htmlspecialchars($rowData[\'filename\']))); + + // Show the dimensions. + if (!empty($rowData[\'width\']) && !empty($rowData[\'height\'])) + $link .= sprintf(\' %1$dx%2$d\', $rowData[\'width\'], $rowData[\'height\']); + + return $link; + '), + ), + 'sort' => array( + 'default' => 'a.filename', + 'reverse' => 'a.filename DESC', + ), + ), + 'filesize' => array( + 'header' => array( + 'value' => $txt['attachment_file_size'], + ), + 'data' => array( + 'function' => create_function('$rowData',' + global $txt; + + return sprintf(\'%1$s%2$s\', round($rowData[\'size\'] / 1024, 2), $txt[\'kilobyte\']); + '), + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'a.size', + 'reverse' => 'a.size DESC', + ), + ), + 'member' => array( + 'header' => array( + 'value' => $context['browse_type'] == 'avatars' ? $txt['attachment_manager_member'] : $txt['posted_by'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $scripturl; + + // In case of an attachment, return the poster of the attachment. + if (empty($rowData[\'id_member\'])) + return htmlspecialchars($rowData[\'poster_name\']); + + // Otherwise it must be an avatar, return the link to the owner of it. + else + return sprintf(\'%3$s\', $scripturl, $rowData[\'id_member\'], $rowData[\'poster_name\']); + '), + ), + 'sort' => array( + 'default' => 'mem.real_name', + 'reverse' => 'mem.real_name DESC', + ), + ), + 'date' => array( + 'header' => array( + 'value' => $context['browse_type'] == 'avatars' ? $txt['attachment_manager_last_active'] : $txt['date'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt, $context, $scripturl; + + // The date the message containing the attachment was posted or the owner of the avatar was active. + $date = empty($rowData[\'poster_time\']) ? $txt[\'never\'] : timeformat($rowData[\'poster_time\']); + + // Add a link to the topic in case of an attachment. + if ($context[\'browse_type\'] !== \'avatars\') + $date .= sprintf(\'
%1$s %5$s\', $txt[\'in\'], $scripturl, $rowData[\'id_topic\'], $rowData[\'id_msg\'], $rowData[\'subject\']); + + return $date; + '), + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => $context['browse_type'] === 'avatars' ? 'mem.last_login' : 'm.id_msg', + 'reverse' => $context['browse_type'] === 'avatars' ? 'mem.last_login DESC' : 'm.id_msg DESC', + ), + ), + 'downloads' => array( + 'header' => array( + 'value' => $txt['downloads'], + ), + 'data' => array( + 'function' => create_function('$rowData',' + global $txt; + + return comma_format($rowData[\'downloads\']); + '), + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'a.downloads', + 'reverse' => 'a.downloads DESC', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id_attach' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=manageattachments;sa=remove' . ($context['browse_type'] === 'avatars' ? ';avatars' : ($context['browse_type'] === 'thumbs' ? ';thumbs' : '')), + 'include_sort' => true, + 'include_start' => true, + 'hidden_fields' => array( + 'type' => $context['browse_type'], + ), + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '', + 'style' => 'text-align: right;', + ), + ), + ); + + // Create the list. + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); +} + +function list_getFiles($start, $items_per_page, $sort, $browse_type) +{ + global $smcFunc, $txt; + + // Choose a query depending on what we are viewing. + if ($browse_type === 'avatars') + $request = $smcFunc['db_query']('', ' + SELECT + {string:blank_text} AS id_msg, IFNULL(mem.real_name, {string:not_applicable_text}) AS poster_name, + mem.last_login AS poster_time, 0 AS id_topic, a.id_member, a.id_attach, a.filename, a.file_hash, a.attachment_type, + a.size, a.width, a.height, a.downloads, {string:blank_text} AS subject, 0 AS id_board + FROM {db_prefix}attachments AS a + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = a.id_member) + WHERE a.id_member != {int:guest_id} + ORDER BY {raw:sort} + LIMIT {int:start}, {int:per_page}', + array( + 'guest_id' => 0, + 'blank_text' => '', + 'not_applicable_text' => $txt['not_applicable'], + 'sort' => $sort, + 'start' => $start, + 'per_page' => $items_per_page, + ) + ); + else + $request = $smcFunc['db_query']('', ' + SELECT + m.id_msg, IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.id_topic, m.id_member, + a.id_attach, a.filename, a.file_hash, a.attachment_type, a.size, a.width, a.height, a.downloads, mf.subject, t.id_board + FROM {db_prefix}attachments AS a + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE a.attachment_type = {int:attachment_type} + ORDER BY {raw:sort} + LIMIT {int:start}, {int:per_page}', + array( + 'attachment_type' => $browse_type == 'thumbs' ? '3' : '0', + 'sort' => $sort, + 'start' => $start, + 'per_page' => $items_per_page, + ) + ); + $files = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $files[] = $row; + $smcFunc['db_free_result']($request); + + return $files; +} + +function list_getNumFiles($browse_type) +{ + global $smcFunc; + + // Depending on the type of file, different queries are used. + if ($browse_type === 'avatars') + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}attachments + WHERE id_member != {int:guest_id_member}', + array( + 'guest_id_member' => 0, + ) + ); + else + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS num_attach + FROM {db_prefix}attachments AS a + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) + WHERE a.attachment_type = {int:attachment_type} + AND a.id_member = {int:guest_id_member}', + array( + 'attachment_type' => $browse_type === 'thumbs' ? '3' : '0', + 'guest_id_member' => 0, + ) + ); + + list ($num_files) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $num_files; +} + +function MaintainFiles() +{ + global $context, $modSettings, $txt, $smcFunc; + + $context['sub_template'] = 'maintenance'; + + if (!empty($modSettings['currentAttachmentUploadDir'])) + $attach_dirs = unserialize($modSettings['attachmentUploadDir']); + else + $attach_dirs = array($modSettings['attachmentUploadDir']); + + // Get the number of attachments.... + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}attachments + WHERE attachment_type = {int:attachment_type} + AND id_member = {int:guest_id_member}', + array( + 'attachment_type' => 0, + 'guest_id_member' => 0, + ) + ); + list ($context['num_attachments']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Also get the avatar amount.... + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}attachments + WHERE id_member != {int:guest_id_member}', + array( + 'guest_id_member' => 0, + ) + ); + list ($context['num_avatars']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Find out how big the directory is. We have to loop through all our attachment paths in case there's an old temp file in one of them. + $attachmentDirSize = 0; + foreach ($attach_dirs as $id => $attach_dir) + { + $dir = @opendir($attach_dir) or fatal_lang_error('cant_access_upload_path', 'critical'); + while ($file = readdir($dir)) + { + if ($file == '.' || $file == '..') + continue; + + if (preg_match('~^post_tmp_\d+_\d+$~', $file) != 0) + { + // Temp file is more than 5 hours old! + if (filemtime($attach_dir . '/' . $file) < time() - 18000) + @unlink($attach_dir . '/' . $file); + continue; + } + + // We're only counting the size of the current attachment directory. + if (empty($modSettings['currentAttachmentUploadDir']) || $modSettings['currentAttachmentUploadDir'] == $id) + $attachmentDirSize += filesize($attach_dir . '/' . $file); + } + closedir($dir); + } + // Divide it into kilobytes. + $attachmentDirSize /= 1024; + + // If they specified a limit only.... + if (!empty($modSettings['attachmentDirSizeLimit'])) + $context['attachment_space'] = max(round($modSettings['attachmentDirSizeLimit'] - $attachmentDirSize, 2), 0); + $context['attachment_total_size'] = round($attachmentDirSize, 2); + + $context['attach_multiple_dirs'] = !empty($modSettings['currentAttachmentUploadDir']); +} + +// !!! Not implemented yet. +function MoveAvatars() +{ + global $modSettings, $smcFunc; + + // First make sure the custom avatar dir is writable. + if (!is_writable($modSettings['custom_avatar_dir'])) + { + // Try to fix it. + @chmod($modSettings['custom_avatar_dir'], 0777); + + // Guess that didn't work? + if (!is_writable($modSettings['custom_avatar_dir'])) + fatal_lang_error('attachments_no_write', 'critical'); + } + + $request = $smcFunc['db_query']('', ' + SELECT id_attach, id_folder, id_member, filename, file_hash + FROM {db_prefix}attachments + WHERE attachment_type = {int:attachment_type} + AND id_member > {int:guest_id_member}', + array( + 'attachment_type' => 0, + 'guest_id_member' => 0, + ) + ); + $updatedAvatars = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); + + if (rename($filename, $modSettings['custom_avatar_dir'] . '/' . $row['filename'])) + $updatedAvatars[] = $row['id_attach']; + } + $smcFunc['db_free_result']($request); + + if (!empty($updatedAvatars)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET attachment_type = {int:attachment_type} + WHERE id_attach IN ({array_int:updated_avatars})', + array( + 'updated_avatars' => $updatedAvatars, + 'attachment_type' => 1, + ) + ); + + redirectexit('action=admin;area=manageattachments;sa=maintenance'); +} + +function RemoveAttachmentByAge() +{ + global $modSettings, $smcFunc; + + checkSession('post', 'admin'); + + // !!! Ignore messages in topics that are stickied? + + // Deleting an attachment? + if ($_REQUEST['type'] != 'avatars') + { + // Get all the old attachments. + $messages = removeAttachments(array('attachment_type' => 0, 'poster_time' => (time() - 24 * 60 * 60 * $_POST['age'])), 'messages', true); + + // Update the messages to reflect the change. + if (!empty($messages)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET body = CONCAT(body, ' . (!empty($_POST['notice']) ? '{string:notice}' : '') . ') + WHERE id_msg IN ({array_int:messages})', + array( + 'messages' => $messages, + 'notice' => empty($_POST['notice']) ? '' : '

' . $_POST['notice'], + ) + ); + } + else + { + // Remove all the old avatars. + removeAttachments(array('not_id_member' => 0, 'last_login' => (time() - 24 * 60 * 60 * $_POST['age'])), 'members'); + } + redirectexit('action=admin;area=manageattachments' . (empty($_REQUEST['avatars']) ? ';sa=maintenance' : ';avatars')); +} + +function RemoveAttachmentBySize() +{ + global $modSettings, $smcFunc; + + checkSession('post', 'admin'); + + // Find humungous attachments. + $messages = removeAttachments(array('attachment_type' => 0, 'size' => 1024 * $_POST['size']), 'messages', true); + + // And make a note on the post. + if (!empty($messages)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET body = CONCAT(body, ' . (!empty($_POST['notice']) ? '{string:notice}' : '') . ') + WHERE id_msg IN ({array_int:messages})', + array( + 'messages' => $messages, + 'notice' => empty($_POST['notice']) ? '' : '

' . $_POST['notice'], + ) + ); + + redirectexit('action=admin;area=manageattachments;sa=maintenance'); +} + +function RemoveAttachment() +{ + global $modSettings, $txt, $smcFunc; + + checkSession('post'); + + if (!empty($_POST['remove'])) + { + $attachments = array(); + // There must be a quicker way to pass this safety test?? + foreach ($_POST['remove'] as $removeID => $dummy) + $attachments[] = (int) $removeID; + + if ($_REQUEST['type'] == 'avatars' && !empty($attachments)) + removeAttachments(array('id_attach' => $attachments)); + else if (!empty($attachments)) + { + $messages = removeAttachments(array('id_attach' => $attachments), 'messages', true); + + // And change the message to reflect this. + if (!empty($messages)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET body = CONCAT(body, {string:deleted_message}) + WHERE id_msg IN ({array_int:messages_affected})', + array( + 'messages_affected' => $messages, + 'deleted_message' => '

' . $txt['attachment_delete_admin'], + ) + ); + } + } + + $_GET['sort'] = isset($_GET['sort']) ? $_GET['sort'] : 'date'; + redirectexit('action=admin;area=manageattachments;sa=browse;' . $_REQUEST['type'] . ';sort=' . $_GET['sort'] . (isset($_GET['desc']) ? ';desc' : '') . ';start=' . $_REQUEST['start']); +} + +// !!! Not implemented (yet?) +function RemoveAllAttachments() +{ + global $txt, $smcFunc; + + checkSession('get', 'admin'); + + $messages = removeAttachments(array('attachment_type' => 0), '', true); + + if (!isset($_POST['notice'])) + $_POST['notice'] = $txt['attachment_delete_admin']; + + // Add the notice on the end of the changed messages. + if (!empty($messages)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET body = CONCAT(body, {string:deleted_message}) + WHERE id_msg IN ({array_int:messages})', + array( + 'messages' => $messages, + 'deleted_message' => '

' . $_POST['notice'], + ) + ); + + redirectexit('action=admin;area=manageattachments;sa=maintenance'); +} + +// Removes attachments - allowed query_types: '', 'messages', 'members' +function removeAttachments($condition, $query_type = '', $return_affected_messages = false, $autoThumbRemoval = true) +{ + global $modSettings, $smcFunc; + + //!!! This might need more work! + $new_condition = array(); + $query_parameter = array( + 'thumb_attachment_type' => 3, + ); + + if (is_array($condition)) + { + foreach ($condition as $real_type => $restriction) + { + // Doing a NOT? + $is_not = substr($real_type, 0, 4) == 'not_'; + $type = $is_not ? substr($real_type, 4) : $real_type; + + if (in_array($type, array('id_member', 'id_attach', 'id_msg'))) + $new_condition[] = 'a.' . $type . ($is_not ? ' NOT' : '') . ' IN (' . (is_array($restriction) ? '{array_int:' . $real_type . '}' : '{int:' . $real_type . '}') . ')'; + elseif ($type == 'attachment_type') + $new_condition[] = 'a.attachment_type = {int:' . $real_type . '}'; + elseif ($type == 'poster_time') + $new_condition[] = 'm.poster_time < {int:' . $real_type . '}'; + elseif ($type == 'last_login') + $new_condition[] = 'mem.last_login < {int:' . $real_type . '}'; + elseif ($type == 'size') + $new_condition[] = 'a.size > {int:' . $real_type . '}'; + elseif ($type == 'id_topic') + $new_condition[] = 'm.id_topic IN (' . (is_array($restriction) ? '{array_int:' . $real_type . '}' : '{int:' . $real_type . '}') . ')'; + + // Add the parameter! + $query_parameter[$real_type] = $restriction; + } + $condition = implode(' AND ', $new_condition); + } + + // Delete it only if it exists... + $msgs = array(); + $attach = array(); + $parents = array(); + + // Get all the attachment names and id_msg's. + $request = $smcFunc['db_query']('', ' + SELECT + a.id_folder, a.filename, a.file_hash, a.attachment_type, a.id_attach, a.id_member' . ($query_type == 'messages' ? ', m.id_msg' : ', a.id_msg') . ', + thumb.id_folder AS thumb_folder, IFNULL(thumb.id_attach, 0) AS id_thumb, thumb.filename AS thumb_filename, thumb.file_hash AS thumb_file_hash, thumb_parent.id_attach AS id_parent + FROM {db_prefix}attachments AS a' .($query_type == 'members' ? ' + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = a.id_member)' : ($query_type == 'messages' ? ' + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)' : '')) . ' + LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb) + LEFT JOIN {db_prefix}attachments AS thumb_parent ON (thumb.attachment_type = {int:thumb_attachment_type} AND thumb_parent.id_thumb = a.id_attach) + WHERE ' . $condition, + $query_parameter + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Figure out the "encrypted" filename and unlink it ;). + if ($row['attachment_type'] == 1) + @unlink($modSettings['custom_avatar_dir'] . '/' . $row['filename']); + else + { + $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); + @unlink($filename); + + // If this was a thumb, the parent attachment should know about it. + if (!empty($row['id_parent'])) + $parents[] = $row['id_parent']; + + // If this attachments has a thumb, remove it as well. + if (!empty($row['id_thumb']) && $autoThumbRemoval) + { + $thumb_filename = getAttachmentFilename($row['thumb_filename'], $row['id_thumb'], $row['thumb_folder'], false, $row['thumb_file_hash']); + @unlink($thumb_filename); + $attach[] = $row['id_thumb']; + } + } + + // Make a list. + if ($return_affected_messages && empty($row['attachment_type'])) + $msgs[] = $row['id_msg']; + $attach[] = $row['id_attach']; + } + $smcFunc['db_free_result']($request); + + // Removed attachments don't have to be updated anymore. + $parents = array_diff($parents, $attach); + if (!empty($parents)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET id_thumb = {int:no_thumb} + WHERE id_attach IN ({array_int:parent_attachments})', + array( + 'parent_attachments' => $parents, + 'no_thumb' => 0, + ) + ); + + if (!empty($attach)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}attachments + WHERE id_attach IN ({array_int:attachment_list})', + array( + 'attachment_list' => $attach, + ) + ); + + if ($return_affected_messages) + return array_unique($msgs); +} + +// This function should find attachments in the database that no longer exist and clear them, and fix filesize issues. +function RepairAttachments() +{ + global $modSettings, $context, $txt, $smcFunc; + + checkSession('get'); + + // If we choose cancel, redirect right back. + if (isset($_POST['cancel'])) + redirectexit('action=admin;area=manageattachments;sa=maintenance'); + + // Try give us a while to sort this out... + @set_time_limit(600); + + $_GET['step'] = empty($_GET['step']) ? 0 : (int) $_GET['step']; + $_GET['substep'] = empty($_GET['substep']) ? 0 : (int) $_GET['substep']; + + // Don't recall the session just in case. + if ($_GET['step'] == 0 && $_GET['substep'] == 0) + { + unset($_SESSION['attachments_to_fix'], $_SESSION['attachments_to_fix2']); + + // If we're actually fixing stuff - work out what. + if (isset($_GET['fixErrors'])) + { + // Nothing? + if (empty($_POST['to_fix'])) + redirectexit('action=admin;area=manageattachments;sa=maintenance'); + + $_SESSION['attachments_to_fix'] = array(); + //!!! No need to do this I think. + foreach ($_POST['to_fix'] as $key => $value) + $_SESSION['attachments_to_fix'][] = $value; + } + } + + // All the valid problems are here: + $context['repair_errors'] = array( + 'missing_thumbnail_parent' => 0, + 'parent_missing_thumbnail' => 0, + 'file_missing_on_disk' => 0, + 'file_wrong_size' => 0, + 'file_size_of_zero' => 0, + 'attachment_no_msg' => 0, + 'avatar_no_member' => 0, + 'wrong_folder' => 0, + ); + + $to_fix = !empty($_SESSION['attachments_to_fix']) ? $_SESSION['attachments_to_fix'] : array(); + $context['repair_errors'] = isset($_SESSION['attachments_to_fix2']) ? $_SESSION['attachments_to_fix2'] : $context['repair_errors']; + $fix_errors = isset($_GET['fixErrors']) ? true : false; + + // Get stranded thumbnails. + if ($_GET['step'] <= 0) + { + $result = $smcFunc['db_query']('', ' + SELECT MAX(id_attach) + FROM {db_prefix}attachments + WHERE attachment_type = {int:thumbnail}', + array( + 'thumbnail' => 3, + ) + ); + list ($thumbnails) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 500) + { + $to_remove = array(); + + $result = $smcFunc['db_query']('', ' + SELECT thumb.id_attach, thumb.id_folder, thumb.filename, thumb.file_hash + FROM {db_prefix}attachments AS thumb + LEFT JOIN {db_prefix}attachments AS tparent ON (tparent.id_thumb = thumb.id_attach) + WHERE thumb.id_attach BETWEEN {int:substep} AND {int:substep} + 499 + AND thumb.attachment_type = {int:thumbnail} + AND tparent.id_attach IS NULL', + array( + 'thumbnail' => 3, + 'substep' => $_GET['substep'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Only do anything once... just in case + if (!isset($to_remove[$row['id_attach']])) + { + $to_remove[$row['id_attach']] = $row['id_attach']; + $context['repair_errors']['missing_thumbnail_parent']++; + + // If we are repairing remove the file from disk now. + if ($fix_errors && in_array('missing_thumbnail_parent', $to_fix)) + { + $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); + @unlink($filename); + } + } + } + if ($smcFunc['db_num_rows']($result) != 0) + $to_fix[] = 'missing_thumbnail_parent'; + $smcFunc['db_free_result']($result); + + // Do we need to delete what we have? + if ($fix_errors && !empty($to_remove) && in_array('missing_thumbnail_parent', $to_fix)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}attachments + WHERE id_attach IN ({array_int:to_remove}) + AND attachment_type = {int:attachment_type}', + array( + 'to_remove' => $to_remove, + 'attachment_type' => 3, + ) + ); + + pauseAttachmentMaintenance($to_fix, $thumbnails); + } + + $_GET['step'] = 1; + $_GET['substep'] = 0; + pauseAttachmentMaintenance($to_fix); + } + + // Find parents which think they have thumbnails, but actually, don't. + if ($_GET['step'] <= 1) + { + $result = $smcFunc['db_query']('', ' + SELECT MAX(id_attach) + FROM {db_prefix}attachments + WHERE id_thumb != {int:no_thumb}', + array( + 'no_thumb' => 0, + ) + ); + list ($thumbnails) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 500) + { + $to_update = array(); + + $result = $smcFunc['db_query']('', ' + SELECT a.id_attach + FROM {db_prefix}attachments AS a + LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb) + WHERE a.id_attach BETWEEN {int:substep} AND {int:substep} + 499 + AND a.id_thumb != {int:no_thumb} + AND thumb.id_attach IS NULL', + array( + 'no_thumb' => 0, + 'substep' => $_GET['substep'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $to_update[] = $row['id_attach']; + $context['repair_errors']['parent_missing_thumbnail']++; + } + if ($smcFunc['db_num_rows']($result) != 0) + $to_fix[] = 'parent_missing_thumbnail'; + $smcFunc['db_free_result']($result); + + // Do we need to delete what we have? + if ($fix_errors && !empty($to_update) && in_array('parent_missing_thumbnail', $to_fix)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET id_thumb = {int:no_thumb} + WHERE id_attach IN ({array_int:to_update})', + array( + 'to_update' => $to_update, + 'no_thumb' => 0, + ) + ); + + pauseAttachmentMaintenance($to_fix, $thumbnails); + } + + $_GET['step'] = 2; + $_GET['substep'] = 0; + pauseAttachmentMaintenance($to_fix); + } + + // This may take forever I'm afraid, but life sucks... recount EVERY attachments! + if ($_GET['step'] <= 2) + { + $result = $smcFunc['db_query']('', ' + SELECT MAX(id_attach) + FROM {db_prefix}attachments', + array( + ) + ); + list ($thumbnails) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 250) + { + $to_remove = array(); + $errors_found = array(); + + $result = $smcFunc['db_query']('', ' + SELECT id_attach, id_folder, filename, file_hash, size, attachment_type + FROM {db_prefix}attachments + WHERE id_attach BETWEEN {int:substep} AND {int:substep} + 249', + array( + 'substep' => $_GET['substep'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Get the filename. + if ($row['attachment_type'] == 1) + $filename = $modSettings['custom_avatar_dir'] . '/' . $row['filename']; + else + $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); + + // File doesn't exist? + if (!file_exists($filename)) + { + // If we're lucky it might just be in a different folder. + if (!empty($modSettings['currentAttachmentUploadDir'])) + { + // Get the attachment name with out the folder. + $attachment_name = !empty($row['file_hash']) ? $row['id_attach'] . '_' . $row['file_hash'] : getLegacyAttachmentFilename($row['filename'], $row['id_attach'], null, true); + + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + + // Loop through the other folders. + foreach ($modSettings['attachmentUploadDir'] as $id => $dir) + if (file_exists($dir . '/' . $attachment_name)) + { + $context['repair_errors']['wrong_folder']++; + $errors_found[] = 'wrong_folder'; + + // Are we going to fix this now? + if ($fix_errors && in_array('wrong_folder', $to_fix)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET id_folder = {int:new_folder} + WHERE id_attach = {int:id_attach}', + array( + 'new_folder' => $id, + 'id_attach' => $row['id_attach'], + ) + ); + + continue 2; + } + } + + $to_remove[] = $row['id_attach']; + $context['repair_errors']['file_missing_on_disk']++; + $errors_found[] = 'file_missing_on_disk'; + } + elseif (filesize($filename) == 0) + { + $context['repair_errors']['file_size_of_zero']++; + $errors_found[] = 'file_size_of_zero'; + + // Fixing? + if ($fix_errors && in_array('file_size_of_zero', $to_fix)) + { + $to_remove[] = $row['id_attach']; + @unlink($filename); + } + } + elseif (filesize($filename) != $row['size']) + { + $context['repair_errors']['file_wrong_size']++; + $errors_found[] = 'file_wrong_size'; + + // Fix it here? + if ($fix_errors && in_array('file_wrong_size', $to_fix)) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET size = {int:filesize} + WHERE id_attach = {int:id_attach}', + array( + 'filesize' => filesize($filename), + 'id_attach' => $row['id_attach'], + ) + ); + } + } + } + + if (in_array('file_missing_on_disk', $errors_found)) + $to_fix[] = 'file_missing_on_disk'; + if (in_array('file_size_of_zero', $errors_found)) + $to_fix[] = 'file_size_of_zero'; + if (in_array('file_wrong_size', $errors_found)) + $to_fix[] = 'file_wrong_size'; + if (in_array('wrong_folder', $errors_found)) + $to_fix[] = 'wrong_folder'; + $smcFunc['db_free_result']($result); + + // Do we need to delete what we have? + if ($fix_errors && !empty($to_remove)) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}attachments + WHERE id_attach IN ({array_int:to_remove})', + array( + 'to_remove' => $to_remove, + ) + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET id_thumb = {int:no_thumb} + WHERE id_thumb IN ({array_int:to_remove})', + array( + 'to_remove' => $to_remove, + 'no_thumb' => 0, + ) + ); + } + + pauseAttachmentMaintenance($to_fix, $thumbnails); + } + + $_GET['step'] = 3; + $_GET['substep'] = 0; + pauseAttachmentMaintenance($to_fix); + } + + // Get avatars with no members associated with them. + if ($_GET['step'] <= 3) + { + $result = $smcFunc['db_query']('', ' + SELECT MAX(id_attach) + FROM {db_prefix}attachments', + array( + ) + ); + list ($thumbnails) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 500) + { + $to_remove = array(); + + $result = $smcFunc['db_query']('', ' + SELECT a.id_attach, a.id_folder, a.filename, a.file_hash, a.attachment_type + FROM {db_prefix}attachments AS a + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = a.id_member) + WHERE a.id_attach BETWEEN {int:substep} AND {int:substep} + 499 + AND a.id_member != {int:no_member} + AND a.id_msg = {int:no_msg} + AND mem.id_member IS NULL', + array( + 'no_member' => 0, + 'no_msg' => 0, + 'substep' => $_GET['substep'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $to_remove[] = $row['id_attach']; + $context['repair_errors']['avatar_no_member']++; + + // If we are repairing remove the file from disk now. + if ($fix_errors && in_array('avatar_no_member', $to_fix)) + { + if ($row['attachment_type'] == 1) + $filename = $modSettings['custom_avatar_dir'] . '/' . $row['filename']; + else + $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); + @unlink($filename); + } + } + if ($smcFunc['db_num_rows']($result) != 0) + $to_fix[] = 'avatar_no_member'; + $smcFunc['db_free_result']($result); + + // Do we need to delete what we have? + if ($fix_errors && !empty($to_remove) && in_array('avatar_no_member', $to_fix)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}attachments + WHERE id_attach IN ({array_int:to_remove}) + AND id_member != {int:no_member} + AND id_msg = {int:no_msg}', + array( + 'to_remove' => $to_remove, + 'no_member' => 0, + 'no_msg' => 0, + ) + ); + + pauseAttachmentMaintenance($to_fix, $thumbnails); + } + + $_GET['step'] = 4; + $_GET['substep'] = 0; + pauseAttachmentMaintenance($to_fix); + } + + // What about attachments, who are missing a message :'( + if ($_GET['step'] <= 4) + { + $result = $smcFunc['db_query']('', ' + SELECT MAX(id_attach) + FROM {db_prefix}attachments', + array( + ) + ); + list ($thumbnails) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 500) + { + $to_remove = array(); + + $result = $smcFunc['db_query']('', ' + SELECT a.id_attach, a.id_folder, a.filename, a.file_hash + FROM {db_prefix}attachments AS a + LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) + WHERE a.id_attach BETWEEN {int:substep} AND {int:substep} + 499 + AND a.id_member = {int:no_member} + AND a.id_msg != {int:no_msg} + AND m.id_msg IS NULL', + array( + 'no_member' => 0, + 'no_msg' => 0, + 'substep' => $_GET['substep'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $to_remove[] = $row['id_attach']; + $context['repair_errors']['attachment_no_msg']++; + + // If we are repairing remove the file from disk now. + if ($fix_errors && in_array('attachment_no_msg', $to_fix)) + { + $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); + @unlink($filename); + } + } + if ($smcFunc['db_num_rows']($result) != 0) + $to_fix[] = 'attachment_no_msg'; + $smcFunc['db_free_result']($result); + + // Do we need to delete what we have? + if ($fix_errors && !empty($to_remove) && in_array('attachment_no_msg', $to_fix)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}attachments + WHERE id_attach IN ({array_int:to_remove}) + AND id_member = {int:no_member} + AND id_msg != {int:no_msg}', + array( + 'to_remove' => $to_remove, + 'no_member' => 0, + 'no_msg' => 0, + ) + ); + + pauseAttachmentMaintenance($to_fix, $thumbnails); + } + + $_GET['step'] = 5; + $_GET['substep'] = 0; + pauseAttachmentMaintenance($to_fix); + } + + // Got here we must be doing well - just the template! :D + $context['page_title'] = $txt['repair_attachments']; + $context[$context['admin_menu_name']]['current_subsection'] = 'maintenance'; + $context['sub_template'] = 'attachment_repair'; + + // What stage are we at? + $context['completed'] = $fix_errors ? true : false; + $context['errors_found'] = !empty($to_fix) ? true : false; + +} + +function pauseAttachmentMaintenance($to_fix, $max_substep = 0) +{ + global $context, $txt, $time_start; + + // Try get more time... + @set_time_limit(600); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + // Have we already used our maximum time? + if (time() - array_sum(explode(' ', $time_start)) < 3) + return; + + $context['continue_get_data'] = '?action=admin;area=manageattachments;sa=repair' . (isset($_GET['fixErrors']) ? ';fixErrors' : '') . ';step=' . $_GET['step'] . ';substep=' . $_GET['substep'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['page_title'] = $txt['not_done_title']; + $context['continue_post_data'] = ''; + $context['continue_countdown'] = '2'; + $context['sub_template'] = 'not_done'; + + // Specific stuff to not break this template! + $context[$context['admin_menu_name']]['current_subsection'] = 'maintenance'; + + // Change these two if more steps are added! + if (empty($max_substep)) + $context['continue_percent'] = round(($_GET['step'] * 100) / 25); + else + $context['continue_percent'] = round(($_GET['step'] * 100 + ($_GET['substep'] * 100) / $max_substep) / 25); + + // Never more than 100%! + $context['continue_percent'] = min($context['continue_percent'], 100); + + $_SESSION['attachments_to_fix'] = $to_fix; + $_SESSION['attachments_to_fix2'] = $context['repair_errors']; + + obExit(); +} + +// Called from a mouse click, works out what we want to do with attachments and actions it. +function ApproveAttach() +{ + global $smcFunc; + + // Security is our primary concern... + checkSession('get'); + + // If it approve or delete? + $is_approve = !isset($_GET['sa']) || $_GET['sa'] != 'reject' ? true : false; + + $attachments = array(); + // If we are approving all ID's in a message , get the ID's. + if ($_GET['sa'] == 'all' && !empty($_GET['mid'])) + { + $id_msg = (int) $_GET['mid']; + + $request = $smcFunc['db_query']('', ' + SELECT id_attach + FROM {db_prefix}attachments + WHERE id_msg = {int:id_msg} + AND approved = {int:is_approved} + AND attachment_type = {int:attachment_type}', + array( + 'id_msg' => $id_msg, + 'is_approved' => 0, + 'attachment_type' => 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $attachments[] = $row['id_attach']; + $smcFunc['db_free_result']($request); + } + elseif (!empty($_GET['aid'])) + $attachments[] = (int) $_GET['aid']; + + if (empty($attachments)) + fatal_lang_error('no_access', false); + + // Now we have some ID's cleaned and ready to approve, but first - let's check we have permission! + $allowed_boards = boardsAllowedTo('approve_posts'); + + // Validate the attachments exist and are the right approval state. + $request = $smcFunc['db_query']('', ' + SELECT a.id_attach, m.id_board, m.id_msg, m.id_topic + FROM {db_prefix}attachments AS a + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) + WHERE a.id_attach IN ({array_int:attachments}) + AND a.attachment_type = {int:attachment_type} + AND a.approved = {int:is_approved}', + array( + 'attachments' => $attachments, + 'attachment_type' => 0, + 'is_approved' => 0, + ) + ); + $attachments = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // We can only add it if we can approve in this board! + if ($allowed_boards = array(0) || in_array($row['id_board'], $allowed_boards)) + { + $attachments[] = $row['id_attach']; + + // Also come up witht he redirection URL. + $redirect = 'topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']; + } + } + $smcFunc['db_free_result']($request); + + if (empty($attachments)) + fatal_lang_error('no_access', false); + + // Finally, we are there. Follow through! + if ($is_approve) + ApproveAttachments($attachments); + else + removeAttachments(array('id_attach' => $attachments)); + + // Return to the topic.... + redirectexit($redirect); +} + +// Approve an attachment, or maybe even more - no permission check! +function ApproveAttachments($attachments) +{ + global $smcFunc; + + if (empty($attachments)) + return 0; + + // For safety, check for thumbnails... + $request = $smcFunc['db_query']('', ' + SELECT + a.id_attach, a.id_member, IFNULL(thumb.id_attach, 0) AS id_thumb + FROM {db_prefix}attachments AS a + LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb) + WHERE a.id_attach IN ({array_int:attachments}) + AND a.attachment_type = {int:attachment_type}', + array( + 'attachments' => $attachments, + 'attachment_type' => 0, + ) + ); + $attachments = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Update the thumbnail too... + if (!empty($row['id_thumb'])) + $attachments[] = $row['id_thumb']; + + $attachments[] = $row['id_attach']; + } + $smcFunc['db_free_result']($request); + + // Approving an attachment is not hard - it's easy. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET approved = {int:is_approved} + WHERE id_attach IN ({array_int:attachments})', + array( + 'attachments' => $attachments, + 'is_approved' => 1, + ) + ); + + // Remove from the approval queue. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}approval_queue + WHERE id_attach IN ({array_int:attachments})', + array( + 'attachments' => $attachments, + ) + ); +} + +function ManageAttachmentPaths() +{ + global $modSettings, $scripturl, $context, $txt, $sourcedir, $smcFunc; + + // Saving? + if (isset($_REQUEST['save'])) + { + checkSession(); + + $new_dirs = array(); + foreach ($_POST['dirs'] as $id => $path) + { + $id = (int) $id; + if ($id < 1) + continue; + + if (empty($path)) + { + // Let's not try to delete a path with files in it. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(id_attach) AS num_attach + FROM {db_prefix}attachments + WHERE id_folder = {int:id_folder}', + array( + 'id_folder' => (int) $id, + ) + ); + + list ($num_attach) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // It's safe to delete. + if ($num_attach == 0) + continue; + } + + $new_dirs[$id] = $path; + } + + // We need to make sure the current directory is right. + $_POST['current_dir'] = (int) $_POST['current_dir']; + if (empty($_POST['current_dir']) || empty($new_dirs[$_POST['current_dir']])) + fatal_lang_error('attach_path_current_bad', false); + + // Going back to just one path? + if (count($new_dirs) == 1) + { + // We might need to reset the paths. This loop will just loop through once. + foreach ($new_dirs as $id => $dir) + { + if ($id != 1) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET id_folder = {int:default_folder} + WHERE id_folder = {int:current_folder}', + array( + 'default_folder' => 1, + 'current_folder' => $id, + ) + ); + + updateSettings(array( + 'currentAttachmentUploadDir' => 0, + 'attachmentUploadDir' => $dir, + )); + } + } + else + // Save it to the database. + updateSettings(array( + 'currentAttachmentUploadDir' => $_POST['current_dir'], + 'attachmentUploadDir' => serialize($new_dirs), + )); + } + + // Are they here for the first time? + if (empty($modSettings['currentAttachmentUploadDir'])) + { + $modSettings['attachmentUploadDir'] = array( + 1 => $modSettings['attachmentUploadDir'] + ); + $modSettings['currentAttachmentUploadDir'] = 1; + } + // Otherwise just load up their attachment paths. + else + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + + $listOptions = array( + 'id' => 'attach_paths', + 'base_href' => $scripturl . '?action=admin;area=manageattachments;sa=attachpaths;' . $context['session_var'] . '=' . $context['session_id'], + 'title' => $txt['attach_paths'], + 'get_items' => array( + 'function' => 'list_getAttachDirs', + ), + 'columns' => array( + 'current_dir' => array( + 'header' => array( + 'value' => $txt['attach_current_dir'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return \'\'; + '), + 'style' => 'text-align: center; width: 15%;', + ), + ), + 'path' => array( + 'header' => array( + 'value' => $txt['attach_path'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return \'\'; + '), + 'style' => 'text-align: center; width: 30%;', + ), + ), + 'current_size' => array( + 'header' => array( + 'value' => $txt['attach_current_size'], + ), + 'data' => array( + 'db' => 'current_size', + 'style' => 'text-align: center; width: 15%;', + ), + ), + 'num_files' => array( + 'header' => array( + 'value' => $txt['attach_num_files'], + ), + 'data' => array( + 'db' => 'num_files', + 'style' => 'text-align: center; width: 15%;', + ), + ), + 'status' => array( + 'header' => array( + 'value' => $txt['attach_dir_status'], + ), + 'data' => array( + 'db' => 'status', + 'style' => 'text-align: center; width: 25%;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=manageattachments;sa=attachpaths;' . $context['session_var'] . '=' . $context['session_id'], + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' ', + 'style' => 'text-align: right;', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + // Fix up our template. + $context[$context['admin_menu_name']]['current_subsection'] = 'attachments'; + $context['page_title'] = $txt['attach_path_manage']; + $context['sub_template'] = 'attachment_paths'; +} + +// Prepare the actual attachment directories to be displayed in the list. +function list_getAttachDirs() +{ + global $smcFunc, $modSettings, $context, $txt; + + // The dirs should already have been unserialized but just in case... + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + + $request = $smcFunc['db_query']('', ' + SELECT id_folder, COUNT(id_attach) AS num_attach + FROM {db_prefix}attachments' . (empty($modSettings['custom_avatar_enabled']) ? '' : ' + WHERE attachment_type != {int:type_avatar}') . ' + GROUP BY id_folder', + array( + 'type_avatar' => 1, + ) + ); + + $expected_files = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $expected_files[$row['id_folder']] = $row['num_attach']; + $smcFunc['db_free_result']($request); + + $attachdirs = array(); + foreach ($modSettings['attachmentUploadDir'] as $id => $dir) + { + // If there aren't any attachments in this directory this won't exist. + if (!isset($expected_files[$id])) + $expected_files[$id] = 0; + + // Check if the directory is doing okay. + list ($status, $error, $size) = attachDirStatus($dir, $expected_files[$id]); + + $attachdirs[] = array( + 'id' => $id, + 'current' => $id == $modSettings['currentAttachmentUploadDir'], + 'path' => $dir, + 'current_size' => $size, + 'num_files' => $expected_files[$id], + 'status' => ($error ? '' : '') . sprintf($txt['attach_dir_' . $status], $context['session_id'], $context['session_var']) . ($error ? '' : ''), + ); + } + + // Just stick a new directory on at the bottom. + if (isset($_REQUEST['new_path'])) + $attachdirs[] = array( + 'id' => max(array_merge(array_keys($expected_files), array_keys($modSettings['attachmentUploadDir']))) + 1, + 'current' => false, + 'path' => '', + 'current_size' => '', + 'num_files' => '', + 'status' => '', + ); + + return $attachdirs; +} + +// Checks the status of an attachment directory and returns an array of the status key, if that status key signifies an error, and the folder size. +function attachDirStatus($dir, $expected_files) +{ + if (!is_dir($dir)) + return array('does_not_exist', true, ''); + elseif (!is_writable($dir)) + return array('not_writable', true, ''); + + // Everything is okay so far, start to scan through the directory. + $dir_size = 0; + $num_files = 0; + $dir_handle = dir($dir); + while ($file = $dir_handle->read()) + { + // Now do we have a real file here? + if (in_array($file, array('.', '..', '.htaccess', 'index.php'))) + continue; + + $dir_size += filesize($dir . '/' . $file); + $num_files++; + } + $dir_handle->close(); + + $dir_size = round($dir_size / 1024, 2); + + if ($num_files < $expected_files) + return array('files_missing', true, $dir_size); + // Empty? + elseif ($expected_files == 0) + return array('unused', false, $dir_size); + // All good! + else + return array('ok', false, $dir_size); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageBans.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageBans.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1730 @@ += 10: a member is banned. +*/ + +// Ban center. +function Ban() +{ + global $context, $txt, $scripturl; + + isAllowedTo('manage_bans'); + + loadTemplate('ManageBans'); + + $subActions = array( + 'add' => 'BanEdit', + 'browse' => 'BanBrowseTriggers', + 'edittrigger' => 'BanEditTrigger', + 'edit' => 'BanEdit', + 'list' => 'BanList', + 'log' => 'BanLog', + ); + + // Default the sub-action to 'view ban list'. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'list'; + + $context['page_title'] = $txt['ban_title']; + $context['sub_action'] = $_REQUEST['sa']; + + // Tabs for browsing the different ban functions. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['ban_title'], + 'help' => 'ban_members', + 'description' => $txt['ban_description'], + 'tabs' => array( + 'list' => array( + 'description' => $txt['ban_description'], + 'href' => $scripturl . '?action=admin;area=ban;sa=list', + 'is_selected' => $_REQUEST['sa'] == 'list' || $_REQUEST['sa'] == 'edit' || $_REQUEST['sa'] == 'edittrigger', + ), + 'add' => array( + 'description' => $txt['ban_description'], + 'href' => $scripturl . '?action=admin;area=ban;sa=add', + 'is_selected' => $_REQUEST['sa'] == 'add', + ), + 'browse' => array( + 'description' => $txt['ban_trigger_browse_description'], + 'href' => $scripturl . '?action=admin;area=ban;sa=browse', + 'is_selected' => $_REQUEST['sa'] == 'browse', + ), + 'log' => array( + 'description' => $txt['ban_log_description'], + 'href' => $scripturl . '?action=admin;area=ban;sa=log', + 'is_selected' => $_REQUEST['sa'] == 'log', + 'is_last' => true, + ), + ), + ); + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']](); +} + +// List all the bans. +function BanList() +{ + global $txt, $context, $ban_request, $ban_counts, $scripturl; + global $user_info, $smcFunc, $sourcedir; + + // User pressed the 'remove selection button'. + if (!empty($_POST['removeBans']) && !empty($_POST['remove']) && is_array($_POST['remove'])) + { + checkSession(); + + // Make sure every entry is a proper integer. + foreach ($_POST['remove'] as $index => $ban_id) + $_POST['remove'][(int) $index] = (int) $ban_id; + + // Unban them all! + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}ban_groups + WHERE id_ban_group IN ({array_int:ban_list})', + array( + 'ban_list' => $_POST['remove'], + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}ban_items + WHERE id_ban_group IN ({array_int:ban_list})', + array( + 'ban_list' => $_POST['remove'], + ) + ); + + // No more caching this ban! + updateSettings(array('banLastUpdated' => time())); + + // Some members might be unbanned now. Update the members table. + updateBanMembers(); + } + + // Create a date string so we don't overload them with date info. + if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0])) + $context['ban_time_format'] = $user_info['time_format']; + else + $context['ban_time_format'] = $matches[0]; + + $listOptions = array( + 'id' => 'ban_list', + 'items_per_page' => 20, + 'base_href' => $scripturl . '?action=admin;area=ban;sa=list', + 'default_sort_col' => 'added', + 'default_sort_dir' => 'desc', + 'get_items' => array( + 'function' => 'list_getBans', + ), + 'get_count' => array( + 'function' => 'list_getNumBans', + ), + 'no_items_label' => $txt['ban_no_entries'], + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['ban_name'], + ), + 'data' => array( + 'db' => 'name', + ), + 'sort' => array( + 'default' => 'bg.name', + 'reverse' => 'bg.name DESC', + ), + ), + 'notes' => array( + 'header' => array( + 'value' => $txt['ban_notes'], + ), + 'data' => array( + 'db' => 'notes', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'LENGTH(bg.notes) > 0 DESC, bg.notes', + 'reverse' => 'LENGTH(bg.notes) > 0, bg.notes DESC', + ), + ), + 'reason' => array( + 'header' => array( + 'value' => $txt['ban_reason'], + ), + 'data' => array( + 'db' => 'reason', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'LENGTH(bg.reason) > 0 DESC, bg.reason', + 'reverse' => 'LENGTH(bg.reason) > 0, bg.reason DESC', + ), + ), + 'added' => array( + 'header' => array( + 'value' => $txt['ban_added'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $context; + + return timeformat($rowData[\'ban_time\'], empty($context[\'ban_time_format\']) ? true : $context[\'ban_time_format\']); + '), + ), + 'sort' => array( + 'default' => 'bg.ban_time', + 'reverse' => 'bg.ban_time DESC', + ), + ), + 'expires' => array( + 'header' => array( + 'value' => $txt['ban_expires'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + // This ban never expires...whahaha. + if ($rowData[\'expire_time\'] === null) + return $txt[\'never\']; + + // This ban has already expired. + elseif ($rowData[\'expire_time\'] < time()) + return sprintf(\'%1$s\', $txt[\'ban_expired\']); + + // Still need to wait a few days for this ban to expire. + else + return sprintf(\'%1$d %2$s\', ceil(($rowData[\'expire_time\'] - time()) / (60 * 60 * 24)), $txt[\'ban_days\']); + '), + ), + 'sort' => array( + 'default' => 'IFNULL(bg.expire_time, 1=1) DESC, bg.expire_time DESC', + 'reverse' => 'IFNULL(bg.expire_time, 1=1), bg.expire_time', + ), + ), + 'num_triggers' => array( + 'header' => array( + 'value' => $txt['ban_triggers'], + ), + 'data' => array( + 'db' => 'num_triggers', + 'style' => 'text-align: center;', + ), + 'sort' => array( + 'default' => 'num_triggers DESC', + 'reverse' => 'num_triggers', + ), + ), + 'actions' => array( + 'header' => array( + 'value' => $txt['ban_actions'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '' . $txt['modify'] . '', + 'params' => array( + 'id_ban_group' => false, + ), + ), + 'style' => 'text-align: center;', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id_ban_group' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=ban;sa=list', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '', + 'style' => 'text-align: right;', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'ban_list'; +} + +function list_getBans($start, $items_per_page, $sort) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT bg.id_ban_group, bg.name, bg.ban_time, bg.expire_time, bg.reason, bg.notes, COUNT(bi.id_ban) AS num_triggers + FROM {db_prefix}ban_groups AS bg + LEFT JOIN {db_prefix}ban_items AS bi ON (bi.id_ban_group = bg.id_ban_group) + GROUP BY bg.id_ban_group, bg.name, bg.ban_time, bg.expire_time, bg.reason, bg.notes + ORDER BY {raw:sort} + LIMIT {int:offset}, {int:limit}', + array( + 'sort' => $sort, + 'offset' => $start, + 'limit' => $items_per_page, + ) + ); + $bans = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $bans[] = $row; + + $smcFunc['db_free_result']($request); + + return $bans; +} + +function list_getNumBans() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS num_bans + FROM {db_prefix}ban_groups', + array( + ) + ); + list ($numBans) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $numBans; +} + +function BanEdit() +{ + global $txt, $modSettings, $context, $ban_request, $scripturl, $smcFunc; + + $_REQUEST['bg'] = empty($_REQUEST['bg']) ? 0 : (int) $_REQUEST['bg']; + + // Adding or editing a ban trigger? + if (!empty($_POST['add_new_trigger']) || !empty($_POST['edit_trigger'])) + { + checkSession(); + + $newBan = !empty($_POST['add_new_trigger']); + $values = array( + 'id_ban_group' => $_REQUEST['bg'], + 'hostname' => '', + 'email_address' => '', + 'id_member' => 0, + 'ip_low1' => 0, + 'ip_high1' => 0, + 'ip_low2' => 0, + 'ip_high2' => 0, + 'ip_low3' => 0, + 'ip_high3' => 0, + 'ip_low4' => 0, + 'ip_high4' => 0, + ); + + // Preset all values that are required. + if ($newBan) + { + $insertKeys = array( + 'id_ban_group' => 'int', + 'hostname' => 'string', + 'email_address' => 'string', + 'id_member' => 'int', + 'ip_low1' => 'int', + 'ip_high1' => 'int', + 'ip_low2' => 'int', + 'ip_high2' => 'int', + 'ip_low3' => 'int', + 'ip_high3' => 'int', + 'ip_low4' => 'int', + 'ip_high4' => 'int', + ); + } + else + $updateString = ' + hostname = {string:hostname}, email_address = {string:email_address}, id_member = {int:id_member}, + ip_low1 = {int:ip_low1}, ip_high1 = {int:ip_high1}, + ip_low2 = {int:ip_low2}, ip_high2 = {int:ip_high2}, + ip_low3 = {int:ip_low3}, ip_high3 = {int:ip_high3}, + ip_low4 = {int:ip_low4}, ip_high4 = {int:ip_high4}'; + + if ($_POST['bantype'] == 'ip_ban') + { + $ip = trim($_POST['ip']); + $ip_parts = ip2range($ip); + $ip_check = checkExistingTriggerIP($ip_parts, $ip); + if (!$ip_check) + fatal_lang_error('invalid_ip', false); + $values = array_merge($values, $ip_check); + + $modlogInfo['ip_range'] = $_POST['ip']; + } + elseif ($_POST['bantype'] == 'hostname_ban') + { + if (preg_match('/[^\w.\-*]/', $_POST['hostname']) == 1) + fatal_lang_error('invalid_hostname', false); + + // Replace the * wildcard by a MySQL compatible wildcard %. + $_POST['hostname'] = str_replace('*', '%', $_POST['hostname']); + + $values['hostname'] = $_POST['hostname']; + + $modlogInfo['hostname'] = $_POST['hostname']; + } + elseif ($_POST['bantype'] == 'email_ban') + { + if (preg_match('/[^\w.\-\+*@]/', $_POST['email']) == 1) + fatal_lang_error('invalid_email', false); + $_POST['email'] = strtolower(str_replace('*', '%', $_POST['email'])); + + // Check the user is not banning an admin. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0) + AND email_address LIKE {string:email} + LIMIT 1', + array( + 'admin_group' => 1, + 'email' => $_POST['email'], + ) + ); + if ($smcFunc['db_num_rows']($request) != 0) + fatal_lang_error('no_ban_admin', 'critical'); + $smcFunc['db_free_result']($request); + + $values['email_address'] = $_POST['email']; + + $modlogInfo['email'] = $_POST['email']; + } + elseif ($_POST['bantype'] == 'user_ban') + { + $_POST['user'] = preg_replace('~&#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', $smcFunc['htmlspecialchars']($_POST['user'], ENT_QUOTES)); + + $request = $smcFunc['db_query']('', ' + SELECT id_member, (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0) AS isAdmin + FROM {db_prefix}members + WHERE member_name = {string:user_name} OR real_name = {string:user_name} + LIMIT 1', + array( + 'admin_group' => 1, + 'user_name' => $_POST['user'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('invalid_username', false); + list ($memberid, $isAdmin) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if ($isAdmin && $isAdmin != 'f') + fatal_lang_error('no_ban_admin', 'critical'); + + $values['id_member'] = $memberid; + + $modlogInfo['member'] = $memberid; + } + else + fatal_lang_error('no_bantype_selected', false); + + if ($newBan) + $smcFunc['db_insert']('', + '{db_prefix}ban_items', + $insertKeys, + $values, + array('id_ban') + ); + else + $smcFunc['db_query']('', ' + UPDATE {db_prefix}ban_items + SET ' . $updateString . ' + WHERE id_ban = {int:ban_item} + AND id_ban_group = {int:id_ban_group}', + array_merge($values, array( + 'ban_item' => (int) $_REQUEST['bi'], + )) + ); + + // Log the addion of the ban entry into the moderation log. + logAction('ban', $modlogInfo + array( + 'new' => $newBan, + 'type' => $_POST['bantype'], + )); + + // Register the last modified date. + updateSettings(array('banLastUpdated' => time())); + + // Update the member table to represent the new ban situation. + updateBanMembers(); + } + + // The user pressed 'Remove selected ban entries'. + elseif (!empty($_POST['remove_selection']) && !empty($_POST['ban_items']) && is_array($_POST['ban_items'])) + { + checkSession(); + + // Making sure every deleted ban item is an integer. + foreach ($_POST['ban_items'] as $key => $value) + $_POST['ban_items'][$key] = (int) $value; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}ban_items + WHERE id_ban IN ({array_int:ban_list}) + AND id_ban_group = {int:ban_group}', + array( + 'ban_list' => $_POST['ban_items'], + 'ban_group' => $_REQUEST['bg'], + ) + ); + + // It changed, let the settings and the member table know. + updateSettings(array('banLastUpdated' => time())); + updateBanMembers(); + } + + // Modify OR add a ban. + elseif (!empty($_POST['modify_ban']) || !empty($_POST['add_ban'])) + { + checkSession(); + + $addBan = !empty($_POST['add_ban']); + if (empty($_POST['ban_name'])) + fatal_lang_error('ban_name_empty', false); + + // Let's not allow HTML in ban names, it's more evil than beneficial. + $_POST['ban_name'] = $smcFunc['htmlspecialchars']($_POST['ban_name'], ENT_QUOTES); + + // Check whether a ban with this name already exists. + $request = $smcFunc['db_query']('', ' + SELECT id_ban_group + FROM {db_prefix}ban_groups + WHERE name = {string:new_ban_name}' . ($addBan ? '' : ' + AND id_ban_group != {int:ban_group}') . ' + LIMIT 1', + array( + 'ban_group' => $_REQUEST['bg'], + 'new_ban_name' => $_POST['ban_name'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 1) + fatal_lang_error('ban_name_exists', false, array($_POST['ban_name'])); + $smcFunc['db_free_result']($request); + + $_POST['reason'] = $smcFunc['htmlspecialchars']($_POST['reason'], ENT_QUOTES); + $_POST['notes'] = $smcFunc['htmlspecialchars']($_POST['notes'], ENT_QUOTES); + $_POST['notes'] = str_replace(array("\r", "\n", ' '), array('', '
', '  '), $_POST['notes']); + $_POST['expiration'] = $_POST['expiration'] == 'never' ? 'NULL' : ($_POST['expiration'] == 'expired' ? '0' : ($_POST['expire_date'] != $_POST['old_expire'] ? time() + 24 * 60 * 60 * (int) $_POST['expire_date'] : 'expire_time')); + $_POST['full_ban'] = empty($_POST['full_ban']) ? '0' : '1'; + $_POST['cannot_post'] = !empty($_POST['full_ban']) || empty($_POST['cannot_post']) ? '0' : '1'; + $_POST['cannot_register'] = !empty($_POST['full_ban']) || empty($_POST['cannot_register']) ? '0' : '1'; + $_POST['cannot_login'] = !empty($_POST['full_ban']) || empty($_POST['cannot_login']) ? '0' : '1'; + + if ($addBan) + { + // Adding some ban triggers? + if ($addBan && !empty($_POST['ban_suggestion']) && is_array($_POST['ban_suggestion'])) + { + $ban_triggers = array(); + $ban_logs = array(); + if (in_array('main_ip', $_POST['ban_suggestion']) && !empty($_POST['main_ip'])) + { + $ip = trim($_POST['main_ip']); + $ip_parts = ip2range($ip); + if (!checkExistingTriggerIP($ip_parts, $ip)) + fatal_lang_error('invalid_ip', false); + + $ban_triggers[] = array( + $ip_parts[0]['low'], + $ip_parts[0]['high'], + $ip_parts[1]['low'], + $ip_parts[1]['high'], + $ip_parts[2]['low'], + $ip_parts[2]['high'], + $ip_parts[3]['low'], + $ip_parts[3]['high'], + '', + '', + 0, + ); + + $ban_logs[] = array( + 'ip_range' => $_POST['main_ip'], + ); + } + if (in_array('hostname', $_POST['ban_suggestion']) && !empty($_POST['hostname'])) + { + if (preg_match('/[^\w.\-*]/', $_POST['hostname']) == 1) + fatal_lang_error('invalid_hostname', false); + + // Replace the * wildcard by a MySQL wildcard %. + $_POST['hostname'] = str_replace('*', '%', $_POST['hostname']); + + $ban_triggers[] = array( + 0, 0, 0, 0, 0, 0, 0, 0, + substr($_POST['hostname'], 0, 255), + '', + 0, + ); + $ban_logs[] = array( + 'hostname' => $_POST['hostname'], + ); + } + if (in_array('email', $_POST['ban_suggestion']) && !empty($_POST['email'])) + { + if (preg_match('/[^\w.\-\+*@]/', $_POST['email']) == 1) + fatal_lang_error('invalid_email', false); + $_POST['email'] = strtolower(str_replace('*', '%', $_POST['email'])); + + $ban_triggers[] = array( + 0, 0, 0, 0, 0, 0, 0, 0, + '', + substr($_POST['email'], 0, 255), + 0, + ); + $ban_logs[] = array( + 'email' => $_POST['email'], + ); + } + if (in_array('user', $_POST['ban_suggestion']) && (!empty($_POST['bannedUser']) || !empty($_POST['user']))) + { + // We got a username, let's find its ID. + if (empty($_POST['bannedUser'])) + { + $_POST['user'] = preg_replace('~&#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', $smcFunc['htmlspecialchars']($_POST['user'], ENT_QUOTES)); + + $request = $smcFunc['db_query']('', ' + SELECT id_member, (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0) AS isAdmin + FROM {db_prefix}members + WHERE member_name = {string:username} OR real_name = {string:username} + LIMIT 1', + array( + 'admin_group' => 1, + 'username' => $_POST['user'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('invalid_username', false); + list ($_POST['bannedUser'], $isAdmin) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if ($isAdmin && $isAdmin != 'f') + fatal_lang_error('no_ban_admin', 'critical'); + } + + $ban_triggers[] = array( + 0, 0, 0, 0, 0, 0, 0, 0, + '', + '', + (int) $_POST['bannedUser'], + ); + $ban_logs[] = array( + 'member' => $_POST['bannedUser'], + ); + } + + if (!empty($_POST['ban_suggestion']['ips']) && is_array($_POST['ban_suggestion']['ips'])) + { + $_POST['ban_suggestion']['ips'] = array_unique($_POST['ban_suggestion']['ips']); + + // Don't add the main IP again. + if (in_array('main_ip', $_POST['ban_suggestion'])) + $_POST['ban_suggestion']['ips'] = array_diff($_POST['ban_suggestion']['ips'], array($_POST['main_ip'])); + + foreach ($_POST['ban_suggestion']['ips'] as $ip) + { + $ip_parts = ip2range($ip); + + // They should be alright, but just to be sure... + if (count($ip_parts) != 4) + fatal_lang_error('invalid_ip', false); + + $ban_triggers[] = array( + $ip_parts[0]['low'], + $ip_parts[0]['high'], + $ip_parts[1]['low'], + $ip_parts[1]['high'], + $ip_parts[2]['low'], + $ip_parts[2]['high'], + $ip_parts[3]['low'], + $ip_parts[3]['high'], + '', + '', + 0, + ); + $ban_logs[] = array( + 'ip_range' => $ip, + ); + } + } + } + + // Yes yes, we're ready to add now. + $smcFunc['db_insert']('', + '{db_prefix}ban_groups', + array( + 'name' => 'string-20', 'ban_time' => 'int', 'expire_time' => 'raw', 'cannot_access' => 'int', 'cannot_register' => 'int', + 'cannot_post' => 'int', 'cannot_login' => 'int', 'reason' => 'string-255', 'notes' => 'string-65534', + ), + array( + $_POST['ban_name'], time(), $_POST['expiration'], $_POST['full_ban'], $_POST['cannot_register'], + $_POST['cannot_post'], $_POST['cannot_login'], $_POST['reason'], $_POST['notes'], + ), + array('id_ban_group') + ); + $_REQUEST['bg'] = $smcFunc['db_insert_id']('{db_prefix}ban_groups', 'id_ban_group'); + + // Now that the ban group is added, add some triggers as well. + if (!empty($ban_triggers) && !empty($_REQUEST['bg'])) + { + // Put in the ban group ID. + foreach ($ban_triggers as $k => $trigger) + array_unshift($ban_triggers[$k], $_REQUEST['bg']); + + // Log what we are doing! + foreach ($ban_logs as $log_details) + logAction('ban', $log_details + array('new' => 1)); + + $smcFunc['db_insert']('', + '{db_prefix}ban_items', + array( + 'id_ban_group' => 'int', 'ip_low1' => 'int', 'ip_high1' => 'int', 'ip_low2' => 'int', 'ip_high2' => 'int', + 'ip_low3' => 'int', 'ip_high3' => 'int', 'ip_low4' => 'int', 'ip_high4' => 'int', 'hostname' => 'string-255', + 'email_address' => 'string-255', 'id_member' => 'int', + ), + $ban_triggers, + array('id_ban') + ); + } + } + else + $smcFunc['db_query']('', ' + UPDATE {db_prefix}ban_groups + SET + name = {string:ban_name}, + reason = {string:reason}, + notes = {string:notes}, + expire_time = {raw:expiration}, + cannot_access = {int:cannot_access}, + cannot_post = {int:cannot_post}, + cannot_register = {int:cannot_register}, + cannot_login = {int:cannot_login} + WHERE id_ban_group = {int:id_ban_group}', + array( + 'expiration' => $_POST['expiration'], + 'cannot_access' => $_POST['full_ban'], + 'cannot_post' => $_POST['cannot_post'], + 'cannot_register' => $_POST['cannot_register'], + 'cannot_login' => $_POST['cannot_login'], + 'id_ban_group' => $_REQUEST['bg'], + 'ban_name' => $_POST['ban_name'], + 'reason' => $_POST['reason'], + 'notes' => $_POST['notes'], + ) + ); + + // No more caching, we have something new here. + updateSettings(array('banLastUpdated' => time())); + updateBanMembers(); + } + + // If we're editing an existing ban, get it from the database. + if (!empty($_REQUEST['bg'])) + { + $context['ban_items'] = array(); + $request = $smcFunc['db_query']('', ' + SELECT + bi.id_ban, bi.hostname, bi.email_address, bi.id_member, bi.hits, + bi.ip_low1, bi.ip_high1, bi.ip_low2, bi.ip_high2, bi.ip_low3, bi.ip_high3, bi.ip_low4, bi.ip_high4, + bg.id_ban_group, bg.name, bg.ban_time, bg.expire_time, bg.reason, bg.notes, bg.cannot_access, bg.cannot_register, bg.cannot_login, bg.cannot_post, + IFNULL(mem.id_member, 0) AS id_member, mem.member_name, mem.real_name + FROM {db_prefix}ban_groups AS bg + LEFT JOIN {db_prefix}ban_items AS bi ON (bi.id_ban_group = bg.id_ban_group) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member) + WHERE bg.id_ban_group = {int:current_ban}', + array( + 'current_ban' => $_REQUEST['bg'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('ban_not_found', false); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($context['ban'])) + { + $context['ban'] = array( + 'id' => $row['id_ban_group'], + 'name' => $row['name'], + 'expiration' => array( + 'status' => $row['expire_time'] === null ? 'never' : ($row['expire_time'] < time() ? 'expired' : 'still_active_but_we_re_counting_the_days'), + 'days' => $row['expire_time'] > time() ? floor(($row['expire_time'] - time()) / 86400) : 0 + ), + 'reason' => $row['reason'], + 'notes' => $row['notes'], + 'cannot' => array( + 'access' => !empty($row['cannot_access']), + 'post' => !empty($row['cannot_post']), + 'register' => !empty($row['cannot_register']), + 'login' => !empty($row['cannot_login']), + ), + 'is_new' => false, + ); + } + if (!empty($row['id_ban'])) + { + $context['ban_items'][$row['id_ban']] = array( + 'id' => $row['id_ban'], + 'hits' => $row['hits'], + ); + if (!empty($row['ip_high1'])) + { + $context['ban_items'][$row['id_ban']]['type'] = 'ip'; + $context['ban_items'][$row['id_ban']]['ip'] = range2ip(array($row['ip_low1'], $row['ip_low2'], $row['ip_low3'], $row['ip_low4']), array($row['ip_high1'], $row['ip_high2'], $row['ip_high3'], $row['ip_high4'])); + } + elseif (!empty($row['hostname'])) + { + $context['ban_items'][$row['id_ban']]['type'] = 'hostname'; + $context['ban_items'][$row['id_ban']]['hostname'] = str_replace('%', '*', $row['hostname']); + } + elseif (!empty($row['email_address'])) + { + $context['ban_items'][$row['id_ban']]['type'] = 'email'; + $context['ban_items'][$row['id_ban']]['email'] = str_replace('%', '*', $row['email_address']); + } + elseif (!empty($row['id_member'])) + { + $context['ban_items'][$row['id_ban']]['type'] = 'user'; + $context['ban_items'][$row['id_ban']]['user'] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => '' . $row['real_name'] . '', + ); + } + // Invalid ban (member probably doesn't exist anymore). + else + { + unset($context['ban_items'][$row['id_ban']]); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}ban_items + WHERE id_ban = {int:current_ban}', + array( + 'current_ban' => $row['id_ban'], + ) + ); + } + } + } + $smcFunc['db_free_result']($request); + } + // Not an existing one, then it's probably a new one. + else + { + $context['ban'] = array( + 'id' => 0, + 'name' => '', + 'expiration' => array( + 'status' => 'never', + 'days' => 0 + ), + 'reason' => '', + 'notes' => '', + 'ban_days' => 0, + 'cannot' => array( + 'access' => true, + 'post' => false, + 'register' => false, + 'login' => false, + ), + 'is_new' => true, + ); + $context['ban_suggestions'] = array( + 'main_ip' => '', + 'hostname' => '', + 'email' => '', + 'member' => array( + 'id' => 0, + ), + ); + + // Overwrite some of the default form values if a user ID was given. + if (!empty($_REQUEST['u'])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, member_ip, email_address + FROM {db_prefix}members + WHERE id_member = {int:current_user} + LIMIT 1', + array( + 'current_user' => (int) $_REQUEST['u'], + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + list ($context['ban_suggestions']['member']['id'], $context['ban_suggestions']['member']['name'], $context['ban_suggestions']['main_ip'], $context['ban_suggestions']['email']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (!empty($context['ban_suggestions']['member']['id'])) + { + $context['ban_suggestions']['href'] = $scripturl . '?action=profile;u=' . $context['ban_suggestions']['member']['id']; + $context['ban_suggestions']['member']['link'] = '' . $context['ban_suggestions']['member']['name'] . ''; + + // Default the ban name to the name of the banned member. + $context['ban']['name'] = $context['ban_suggestions']['member']['name']; + + // Would be nice if we could also ban the hostname. + if (preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $context['ban_suggestions']['main_ip']) == 1 && empty($modSettings['disableHostnameLookup'])) + $context['ban_suggestions']['hostname'] = host_from_ip($context['ban_suggestions']['main_ip']); + + // Find some additional IP's used by this member. + $context['ban_suggestions']['message_ips'] = array(); + $request = $smcFunc['db_query']('ban_suggest_message_ips', ' + SELECT DISTINCT poster_ip + FROM {db_prefix}messages + WHERE id_member = {int:current_user} + AND poster_ip RLIKE {string:poster_ip_regex} + ORDER BY poster_ip', + array( + 'current_user' => (int) $_REQUEST['u'], + 'poster_ip_regex' => '^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['ban_suggestions']['message_ips'][] = $row['poster_ip']; + $smcFunc['db_free_result']($request); + + $context['ban_suggestions']['error_ips'] = array(); + $request = $smcFunc['db_query']('ban_suggest_error_ips', ' + SELECT DISTINCT ip + FROM {db_prefix}log_errors + WHERE id_member = {int:current_user} + AND ip RLIKE {string:poster_ip_regex} + ORDER BY ip', + array( + 'current_user' => (int) $_REQUEST['u'], + 'poster_ip_regex' => '^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['ban_suggestions']['error_ips'][] = $row['ip']; + $smcFunc['db_free_result']($request); + + // Borrowing a few language strings from profile. + loadLanguage('Profile'); + } + } + } + + // Template needs this to show errors using javascript + loadLanguage('Errors'); + + // If we're in wireless mode remove the admin template layer and use a special template. + if (WIRELESS && WIRELESS_PROTOCOL != 'wap') + { + $context['sub_template'] = WIRELESS_PROTOCOL . '_ban_edit'; + foreach ($context['template_layers'] as $k => $v) + if (strpos($v, 'generic_menu') === 0) + unset($context['template_layers'][$k]); + } + else + $context['sub_template'] = 'ban_edit'; +} + +function BanEditTrigger() +{ + global $context, $smcFunc; + + $context['sub_template'] = 'ban_edit_trigger'; + + if (empty($_REQUEST['bg'])) + fatal_lang_error('ban_not_found', false); + + if (empty($_REQUEST['bi'])) + { + $context['ban_trigger'] = array( + 'id' => 0, + 'group' => (int) $_REQUEST['bg'], + 'ip' => array( + 'value' => '', + 'selected' => true, + ), + 'hostname' => array( + 'selected' => false, + 'value' => '', + ), + 'email' => array( + 'value' => '', + 'selected' => false, + ), + 'banneduser' => array( + 'value' => '', + 'selected' => false, + ), + 'is_new' => true, + ); + } + else + { + $request = $smcFunc['db_query']('', ' + SELECT + bi.id_ban, bi.id_ban_group, bi.hostname, bi.email_address, bi.id_member, + bi.ip_low1, bi.ip_high1, bi.ip_low2, bi.ip_high2, bi.ip_low3, bi.ip_high3, bi.ip_low4, bi.ip_high4, + mem.member_name, mem.real_name + FROM {db_prefix}ban_items AS bi + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member) + WHERE bi.id_ban = {int:ban_item} + AND bi.id_ban_group = {int:ban_group} + LIMIT 1', + array( + 'ban_item' => (int) $_REQUEST['bi'], + 'ban_group' => (int) $_REQUEST['bg'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('ban_not_found', false); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $context['ban_trigger'] = array( + 'id' => $row['id_ban'], + 'group' => $row['id_ban_group'], + 'ip' => array( + 'value' => empty($row['ip_low1']) ? '' : range2ip(array($row['ip_low1'], $row['ip_low2'], $row['ip_low3'], $row['ip_low4']), array($row['ip_high1'], $row['ip_high2'], $row['ip_high3'], $row['ip_high4'])), + 'selected' => !empty($row['ip_low1']), + ), + 'hostname' => array( + 'value' => str_replace('%', '*', $row['hostname']), + 'selected' => !empty($row['hostname']), + ), + 'email' => array( + 'value' => str_replace('%', '*', $row['email_address']), + 'selected' => !empty($row['email_address']) + ), + 'banneduser' => array( + 'value' => $row['member_name'], + 'selected' => !empty($row['member_name']) + ), + 'is_new' => false, + ); + } +} + +function BanBrowseTriggers() +{ + global $modSettings, $context, $scripturl, $smcFunc, $txt; + global $sourcedir, $settings; + + if (!empty($_POST['remove_triggers']) && !empty($_POST['remove']) && is_array($_POST['remove'])) + { + checkSession(); + + // Clean the integers. + foreach ($_POST['remove'] as $key => $value) + $_POST['remove'][$key] = $value; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}ban_items + WHERE id_ban IN ({array_int:ban_list})', + array( + 'ban_list' => $_POST['remove'], + ) + ); + + // Rehabilitate some members. + if ($_REQUEST['entity'] == 'member') + updateBanMembers(); + + // Make sure the ban cache is refreshed. + updateSettings(array('banLastUpdated' => time())); + } + + $context['selected_entity'] = isset($_REQUEST['entity']) && in_array($_REQUEST['entity'], array('ip', 'hostname', 'email', 'member')) ? $_REQUEST['entity'] : 'ip'; + + $listOptions = array( + 'id' => 'ban_trigger_list', + 'title' => $txt['ban_trigger_browse'], + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'base_href' => $scripturl . '?action=admin;area=ban;sa=browse;entity=' . $context['selected_entity'], + 'default_sort_col' => 'banned_entity', + 'no_items_label' => $txt['ban_no_triggers'], + 'get_items' => array( + 'function' => 'list_getBanTriggers', + 'params' => array( + $context['selected_entity'], + ), + ), + 'get_count' => array( + 'function' => 'list_getNumBanTriggers', + 'params' => array( + $context['selected_entity'], + ), + ), + 'columns' => array( + 'banned_entity' => array( + 'header' => array( + 'value' => $txt['ban_banned_entity'], + ), + ), + 'ban_name' => array( + 'header' => array( + 'value' => $txt['ban_name'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id_ban_group' => false, + 'name' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'bg.name', + 'reverse' => 'bg.name DESC', + ), + ), + 'hits' => array( + 'header' => array( + 'value' => $txt['ban_hits'], + ), + 'data' => array( + 'db' => 'hits', + 'style' => 'text-align: center;', + ), + 'sort' => array( + 'default' => 'bi.hits DESC', + 'reverse' => 'bi.hits', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id_ban' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=ban;sa=browse;entity=' . $context['selected_entity'], + 'include_start' => true, + 'include_sort' => true, + ), + 'additional_rows' => array( + array( + 'position' => 'above_column_headers', + 'value' => '' . ($context['selected_entity'] == 'ip' ? '> ' : '') . $txt['ip'] . ' | ' . ($context['selected_entity'] == 'hostname' ? '> ' : '') . $txt['hostname'] . ' | ' . ($context['selected_entity'] == 'email' ? '> ' : '') . $txt['email'] . ' | ' . ($context['selected_entity'] == 'member' ? '> ' : '') . $txt['username'] . '', + ), + array( + 'position' => 'below_table_data', + 'value' => '', + 'style' => 'text-align: right;', + ), + ), + ); + + // Specific data for the first column depending on the selected entity. + if ($context['selected_entity'] === 'ip') + { + $listOptions['columns']['banned_entity']['data'] = array( + 'function' => create_function('$rowData', ' + return range2ip(array( + $rowData[\'ip_low1\'], + $rowData[\'ip_low2\'], + $rowData[\'ip_low3\'], + $rowData[\'ip_low4\'] + ), array( + $rowData[\'ip_high1\'], + $rowData[\'ip_high2\'], + $rowData[\'ip_high3\'], + $rowData[\'ip_high4\'] + )); + '), + ); + $listOptions['columns']['banned_entity']['sort'] = array( + 'default' => 'bi.ip_low1, bi.ip_high1, bi.ip_low2, bi.ip_high2, bi.ip_low3, bi.ip_high3, bi.ip_low4, bi.ip_high4', + 'reverse' => 'bi.ip_low1 DESC, bi.ip_high1 DESC, bi.ip_low2 DESC, bi.ip_high2 DESC, bi.ip_low3 DESC, bi.ip_high3 DESC, bi.ip_low4 DESC, bi.ip_high4 DESC', + ); + } + elseif ($context['selected_entity'] === 'hostname') + { + $listOptions['columns']['banned_entity']['data'] = array( + 'function' => create_function('$rowData', ' + global $smcFunc; + return strtr($smcFunc[\'htmlspecialchars\']($rowData[\'hostname\']), array(\'%\' => \'*\')); + '), + ); + $listOptions['columns']['banned_entity']['sort'] = array( + 'default' => 'bi.hostname', + 'reverse' => 'bi.hostname DESC', + ); + } + elseif ($context['selected_entity'] === 'email') + { + $listOptions['columns']['banned_entity']['data'] = array( + 'function' => create_function('$rowData', ' + global $smcFunc; + return strtr($smcFunc[\'htmlspecialchars\']($rowData[\'email_address\']), array(\'%\' => \'*\')); + '), + ); + $listOptions['columns']['banned_entity']['sort'] = array( + 'default' => 'bi.email_address', + 'reverse' => 'bi.email_address DESC', + ); + } + elseif ($context['selected_entity'] === 'member') + { + $listOptions['columns']['banned_entity']['data'] = array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id_member' => false, + 'real_name' => false, + ), + ), + ); + $listOptions['columns']['banned_entity']['sort'] = array( + 'default' => 'mem.real_name', + 'reverse' => 'mem.real_name DESC', + ); + } + + // Create the list. + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + // The list is the only thing to show, so make it the default sub template. + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'ban_trigger_list'; +} + +function list_getBanTriggers($start, $items_per_page, $sort, $trigger_type) +{ + global $smcFunc; + + $where = array( + 'ip' => 'bi.ip_low1 > 0', + 'hostname' => 'bi.hostname != {string:blank_string}', + 'email' => 'bi.email_address != {string:blank_string}', + ); + + $request = $smcFunc['db_query']('', ' + SELECT + bi.id_ban, bi.ip_low1, bi.ip_high1, bi.ip_low2, bi.ip_high2, bi.ip_low3, bi.ip_high3, bi.ip_low4, bi.ip_high4, bi.hostname, bi.email_address, bi.hits, + bg.id_ban_group, bg.name' . ($trigger_type === 'member' ? ', + mem.id_member, mem.real_name' : '') . ' + FROM {db_prefix}ban_items AS bi + INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group)' . ($trigger_type === 'member' ? ' + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member)' : ' + WHERE ' . $where[$trigger_type]) . ' + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + 'blank_string' => '', + ) + ); + $ban_triggers = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $ban_triggers[] = $row; + $smcFunc['db_free_result']($request); + + return $ban_triggers; +} + +function list_getNumBanTriggers($trigger_type) +{ + global $smcFunc; + + $where = array( + 'ip' => 'bi.ip_low1 > 0', + 'hostname' => 'bi.hostname != {string:blank_string}', + 'email' => 'bi.email_address != {string:blank_string}', + ); + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}ban_items AS bi' . ($trigger_type === 'member' ? ' + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member)' : ' + WHERE ' . $where[$trigger_type]), + array( + 'blank_string' => '', + ) + ); + list ($num_triggers) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $num_triggers; +} + +function BanLog() +{ + global $scripturl, $context, $smcFunc, $sourcedir, $txt; + global $context; + + // Delete one or more entries. + if (!empty($_POST['removeAll']) || (!empty($_POST['removeSelected']) && !empty($_POST['remove']))) + { + checkSession(); + + // 'Delete all entries' button was pressed. + if (!empty($_POST['removeAll'])) + $smcFunc['db_query']('truncate_table', ' + TRUNCATE {db_prefix}log_banned', + array( + ) + ); + + // 'Delte selection' button was pressed. + else + { + // Make sure every entry is integer. + foreach ($_POST['remove'] as $index => $log_id) + $_POST['remove'][$index] = (int) $log_id; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_banned + WHERE id_ban_log IN ({array_int:ban_list})', + array( + 'ban_list' => $_POST['remove'], + ) + ); + } + } + + $listOptions = array( + 'id' => 'ban_log', + 'items_per_page' => 30, + 'base_href' => $context['admin_area'] == 'ban' ? $scripturl . '?action=admin;area=ban;sa=log' : $scripturl . '?action=admin;area=logs;sa=banlog', + 'default_sort_col' => 'date', + 'get_items' => array( + 'function' => 'list_getBanLogEntries', + ), + 'get_count' => array( + 'function' => 'list_getNumBanLogEntries', + ), + 'no_items_label' => $txt['ban_log_no_entries'], + 'columns' => array( + 'ip' => array( + 'header' => array( + 'value' => $txt['ban_log_ip'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s', + 'params' => array( + 'ip' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'lb.ip', + 'reverse' => 'lb.ip DESC', + ), + ), + 'email' => array( + 'header' => array( + 'value' => $txt['ban_log_email'], + ), + 'data' => array( + 'db_htmlsafe' => 'email', + ), + 'sort' => array( + 'default' => 'lb.email = \'\', lb.email', + 'reverse' => 'lb.email != \'\', lb.email DESC', + ), + ), + 'member' => array( + 'header' => array( + 'value' => $txt['ban_log_member'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id_member' => false, + 'real_name' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'IFNULL(mem.real_name, 1=1), mem.real_name', + 'reverse' => 'IFNULL(mem.real_name, 1=1) DESC, mem.real_name DESC', + ), + ), + 'date' => array( + 'header' => array( + 'value' => $txt['ban_log_date'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return timeformat($rowData[\'log_time\']); + '), + ), + 'sort' => array( + 'default' => 'lb.log_time DESC', + 'reverse' => 'lb.log_time', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id_ban_log' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $context['admin_area'] == 'ban' ? $scripturl . '?action=admin;area=ban;sa=log' : $scripturl . '?action=admin;area=logs;sa=banlog', + 'include_start' => true, + 'include_sort' => true, + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' + + ', + 'style' => 'text-align: right;', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['page_title'] = $txt['ban_log']; + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'ban_log'; +} + +function list_getBanLogEntries($start, $items_per_page, $sort) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT lb.id_ban_log, lb.id_member, IFNULL(lb.ip, {string:dash}) AS ip, IFNULL(lb.email, {string:dash}) AS email, lb.log_time, IFNULL(mem.real_name, {string:blank_string}) AS real_name + FROM {db_prefix}log_banned AS lb + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lb.id_member) + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + 'blank_string' => '', + 'dash' => '-', + ) + ); + $log_entries = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $log_entries[] = $row; + $smcFunc['db_free_result']($request); + + return $log_entries; +} + +function list_getNumBanLogEntries() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_banned AS lb', + array( + ) + ); + list ($num_entries) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $num_entries; +} + +function range2ip($low, $high) +{ + if (count($low) != 4 || count($high) != 4) + return ''; + + $ip = array(); + for ($i = 0; $i < 4; $i++) + { + if ($low[$i] == $high[$i]) + $ip[$i] = $low[$i]; + elseif ($low[$i] == '0' && $high[$i] == '255') + $ip[$i] = '*'; + else + $ip[$i] = $low[$i] . '-' . $high[$i]; + } + + // Pretending is fun... the IP can't be this, so use it for 'unknown'. + if ($ip == array(255, 255, 255, 255)) + return 'unknown'; + + return implode('.', $ip); +} + +function checkExistingTriggerIP($ip_array, $fullip = '') +{ + global $smcFunc, $scripturl; + + if (count($ip_array) == 4) + $values = array( + 'ip_low1' => $ip_array[0]['low'], + 'ip_high1' => $ip_array[0]['high'], + 'ip_low2' => $ip_array[1]['low'], + 'ip_high2' => $ip_array[1]['high'], + 'ip_low3' => $ip_array[2]['low'], + 'ip_high3' => $ip_array[2]['high'], + 'ip_low4' => $ip_array[3]['low'], + 'ip_high4' => $ip_array[3]['high'], + ); + else + return false; + + $request = $smcFunc['db_query']('', ' + SELECT bg.id_ban_group, bg.name + FROM {db_prefix}ban_groups AS bg + INNER JOIN {db_prefix}ban_items AS bi ON + (bi.id_ban_group = bg.id_ban_group) + AND ip_low1 = {int:ip_low1} AND ip_high1 = {int:ip_high1} + AND ip_low2 = {int:ip_low2} AND ip_high2 = {int:ip_high2} + AND ip_low3 = {int:ip_low3} AND ip_high3 = {int:ip_high3} + AND ip_low4 = {int:ip_low4} AND ip_high4 = {int:ip_high4} + LIMIT 1', + $values + ); + if ($smcFunc['db_num_rows']($request) != 0) + { + list ($error_id_ban, $error_ban_name) = $smcFunc['db_fetch_row']($request); + fatal_lang_error('ban_trigger_already_exists', false, array( + $fullip, + '' . $error_ban_name . '', + )); + } + $smcFunc['db_free_result']($request); + + return $values; +} + +function updateBanMembers() +{ + global $smcFunc; + + $updates = array(); + $allMembers = array(); + $newMembers = array(); + + // Start by getting all active bans - it's quicker doing this in parts... + $request = $smcFunc['db_query']('', ' + SELECT bi.id_member, bi.email_address + FROM {db_prefix}ban_items AS bi + INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group) + WHERE (bi.id_member > {int:no_member} OR bi.email_address != {string:blank_string}) + AND bg.cannot_access = {int:cannot_access_on} + AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})', + array( + 'no_member' => 0, + 'cannot_access_on' => 1, + 'current_time' => time(), + 'blank_string' => '', + ) + ); + $memberIDs = array(); + $memberEmails = array(); + $memberEmailWild = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['id_member']) + $memberIDs[$row['id_member']] = $row['id_member']; + if ($row['email_address']) + { + // Does it have a wildcard - if so we can't do a IN on it. + if (strpos($row['email_address'], '%') !== false) + $memberEmailWild[$row['email_address']] = $row['email_address']; + else + $memberEmails[$row['email_address']] = $row['email_address']; + } + } + $smcFunc['db_free_result']($request); + + // Build up the query. + $queryPart = array(); + $queryValues = array(); + if (!empty($memberIDs)) + { + $queryPart[] = 'mem.id_member IN ({array_string:member_ids})'; + $queryValues['member_ids'] = $memberIDs; + } + if (!empty($memberEmails)) + { + $queryPart[] = 'mem.email_address IN ({array_string:member_emails})'; + $queryValues['member_emails'] = $memberEmails; + } + $count = 0; + foreach ($memberEmailWild as $email) + { + $queryPart[] = 'mem.email_address LIKE {string:wild_' . $count . '}'; + $queryValues['wild_' . $count++] = $email; + } + + // Find all banned members. + if (!empty($queryPart)) + { + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member, mem.is_activated + FROM {db_prefix}members AS mem + WHERE ' . implode( ' OR ', $queryPart), + $queryValues + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!in_array($row['id_member'], $allMembers)) + { + $allMembers[] = $row['id_member']; + // Do they need an update? + if ($row['is_activated'] < 10) + { + $updates[($row['is_activated'] + 10)][] = $row['id_member']; + $newMembers[] = $row['id_member']; + } + } + } + $smcFunc['db_free_result']($request); + } + + // We welcome our new members in the realm of the banned. + if (!empty($newMembers)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_online + WHERE id_member IN ({array_int:new_banned_members})', + array( + 'new_banned_members' => $newMembers, + ) + ); + + // Find members that are wrongfully marked as banned. + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member, mem.is_activated - 10 AS new_value + FROM {db_prefix}members AS mem + LEFT JOIN {db_prefix}ban_items AS bi ON (bi.id_member = mem.id_member OR mem.email_address LIKE bi.email_address) + LEFT JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group AND bg.cannot_access = {int:cannot_access_activated} AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})) + WHERE (bi.id_ban IS NULL OR bg.id_ban_group IS NULL) + AND mem.is_activated >= {int:ban_flag}', + array( + 'cannot_access_activated' => 1, + 'current_time' => time(), + 'ban_flag' => 10, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Don't do this twice! + if (!in_array($row['id_member'], $allMembers)) + { + $updates[$row['new_value']][] = $row['id_member']; + $allMembers[] = $row['id_member']; + } + } + $smcFunc['db_free_result']($request); + + if (!empty($updates)) + foreach ($updates as $newStatus => $members) + updateMemberData($members, array('is_activated' => $newStatus)); + + // Update the latest member and our total members as banning may change them. + updateStats('member'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageBoards.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageBoards.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,801 @@ + array('function', 'permission') + $subActions = array( + 'board' => array('EditBoard', 'manage_boards'), + 'board2' => array('EditBoard2', 'manage_boards'), + 'cat' => array('EditCategory', 'manage_boards'), + 'cat2' => array('EditCategory2', 'manage_boards'), + 'main' => array('ManageBoardsMain', 'manage_boards'), + 'move' => array('ManageBoardsMain', 'manage_boards'), + 'newcat' => array('EditCategory', 'manage_boards'), + 'newboard' => array('EditBoard', 'manage_boards'), + 'settings' => array('EditBoardSettings', 'admin_forum'), + ); + + // Default to sub action 'main' or 'settings' depending on permissions. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (allowedTo('manage_boards') ? 'main' : 'settings'); + + // Have you got the proper permissions? + isAllowedTo($subActions[$_REQUEST['sa']][1]); + + // Create the tabs for the template. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['boards_and_cats'], + 'help' => 'manage_boards', + 'description' => $txt['boards_and_cats_desc'], + 'tabs' => array( + 'main' => array( + ), + 'newcat' => array( + ), + 'settings' => array( + 'description' => $txt['mboards_settings_desc'], + ), + ), + ); + + $subActions[$_REQUEST['sa']][0](); +} + +// The main control panel thing. +function ManageBoardsMain() +{ + global $txt, $context, $cat_tree, $boards, $boardList, $scripturl, $sourcedir, $txt; + + loadTemplate('ManageBoards'); + + require_once($sourcedir . '/Subs-Boards.php'); + + if (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'move' && in_array($_REQUEST['move_to'], array('child', 'before', 'after', 'top'))) + { + checkSession('get'); + if ($_REQUEST['move_to'] === 'top') + $boardOptions = array( + 'move_to' => $_REQUEST['move_to'], + 'target_category' => (int) $_REQUEST['target_cat'], + 'move_first_child' => true, + ); + else + $boardOptions = array( + 'move_to' => $_REQUEST['move_to'], + 'target_board' => (int) $_REQUEST['target_board'], + 'move_first_child' => true, + ); + modifyBoard((int) $_REQUEST['src_board'], $boardOptions); + } + + getBoardTree(); + + $context['move_board'] = !empty($_REQUEST['move']) && isset($boards[(int) $_REQUEST['move']]) ? (int) $_REQUEST['move'] : 0; + + $context['categories'] = array(); + foreach ($cat_tree as $catid => $tree) + { + $context['categories'][$catid] = array( + 'name' => &$tree['node']['name'], + 'id' => &$tree['node']['id'], + 'boards' => array() + ); + $move_cat = !empty($context['move_board']) && $boards[$context['move_board']]['category'] == $catid; + foreach ($boardList[$catid] as $boardid) + { + $context['categories'][$catid]['boards'][$boardid] = array( + 'id' => &$boards[$boardid]['id'], + 'name' => &$boards[$boardid]['name'], + 'description' => &$boards[$boardid]['description'], + 'child_level' => &$boards[$boardid]['level'], + 'move' => $move_cat && ($boardid == $context['move_board'] || isChildOf($boardid, $context['move_board'])), + 'permission_profile' => &$boards[$boardid]['profile'], + ); + } + } + + if (!empty($context['move_board'])) + { + $context['move_title'] = sprintf($txt['mboards_select_destination'], htmlspecialchars($boards[$context['move_board']]['name'])); + foreach ($cat_tree as $catid => $tree) + { + $prev_child_level = 0; + $prev_board = 0; + $stack = array(); + foreach ($boardList[$catid] as $boardid) + { + if (!isset($context['categories'][$catid]['move_link'])) + $context['categories'][$catid]['move_link'] = array( + 'child_level' => 0, + 'label' => $txt['mboards_order_before'] . ' \'' . htmlspecialchars($boards[$boardid]['name']) . '\'', + 'href' => $scripturl . '?action=admin;area=manageboards;sa=move;src_board=' . $context['move_board'] . ';target_board=' . $boardid . ';move_to=before;' . $context['session_var'] . '=' . $context['session_id'], + ); + + if (!$context['categories'][$catid]['boards'][$boardid]['move']) + $context['categories'][$catid]['boards'][$boardid]['move_links'] = array( + array( + 'child_level' => $boards[$boardid]['level'], + 'label' => $txt['mboards_order_after'] . '\'' . htmlspecialchars($boards[$boardid]['name']) . '\'', + 'href' => $scripturl . '?action=admin;area=manageboards;sa=move;src_board=' . $context['move_board'] . ';target_board=' . $boardid . ';move_to=after;' . $context['session_var'] . '=' . $context['session_id'], + ), + array( + 'child_level' => $boards[$boardid]['level'] + 1, + 'label' => $txt['mboards_order_child_of'] . ' \'' . htmlspecialchars($boards[$boardid]['name']) . '\'', + 'href' => $scripturl . '?action=admin;area=manageboards;sa=move;src_board=' . $context['move_board'] . ';target_board=' . $boardid . ';move_to=child;' . $context['session_var'] . '=' . $context['session_id'], + ), + ); + + $difference = $boards[$boardid]['level'] - $prev_child_level; + if ($difference == 1) + array_push($stack, !empty($context['categories'][$catid]['boards'][$prev_board]['move_links']) ? array_shift($context['categories'][$catid]['boards'][$prev_board]['move_links']) : null); + elseif ($difference < 0) + { + if (empty($context['categories'][$catid]['boards'][$prev_board]['move_links'])) + $context['categories'][$catid]['boards'][$prev_board]['move_links'] = array(); + for ($i = 0; $i < -$difference; $i++) + if (($temp = array_pop($stack)) != null) + array_unshift($context['categories'][$catid]['boards'][$prev_board]['move_links'], $temp); + } + + $prev_board = $boardid; + $prev_child_level = $boards[$boardid]['level']; + + } + if (!empty($stack) && !empty($context['categories'][$catid]['boards'][$prev_board]['move_links'])) + $context['categories'][$catid]['boards'][$prev_board]['move_links'] = array_merge($stack, $context['categories'][$catid]['boards'][$prev_board]['move_links']); + elseif (!empty($stack)) + $context['categories'][$catid]['boards'][$prev_board]['move_links'] = $stack; + + if (empty($boardList[$catid])) + $context['categories'][$catid]['move_link'] = array( + 'child_level' => 0, + 'label' => $txt['mboards_order_before'] . ' \'' . htmlspecialchars($tree['node']['name']) . '\'', + 'href' => $scripturl . '?action=admin;area=manageboards;sa=move;src_board=' . $context['move_board'] . ';target_cat=' . $catid . ';move_to=top;' . $context['session_var'] . '=' . $context['session_id'], + ); + } + } + + $context['page_title'] = $txt['boards_and_cats']; + $context['can_manage_permissions'] = allowedTo('manage_permissions'); +} + +// Modify a specific category. +function EditCategory() +{ + global $txt, $context, $cat_tree, $boardList, $boards, $sourcedir; + + loadTemplate('ManageBoards'); + require_once($sourcedir . '/Subs-Boards.php'); + getBoardTree(); + + // id_cat must be a number.... if it exists. + $_REQUEST['cat'] = isset($_REQUEST['cat']) ? (int) $_REQUEST['cat'] : 0; + + // Start with one - "In first place". + $context['category_order'] = array( + array( + 'id' => 0, + 'name' => $txt['mboards_order_first'], + 'selected' => !empty($_REQUEST['cat']) ? $cat_tree[$_REQUEST['cat']]['is_first'] : false, + 'true_name' => '' + ) + ); + + // If this is a new category set up some defaults. + if ($_REQUEST['sa'] == 'newcat') + { + $context['category'] = array( + 'id' => 0, + 'name' => $txt['mboards_new_cat_name'], + 'editable_name' => htmlspecialchars($txt['mboards_new_cat_name']), + 'can_collapse' => true, + 'is_new' => true, + 'is_empty' => true + ); + } + // Category doesn't exist, man... sorry. + elseif (!isset($cat_tree[$_REQUEST['cat']])) + redirectexit('action=admin;area=manageboards'); + else + { + $context['category'] = array( + 'id' => $_REQUEST['cat'], + 'name' => $cat_tree[$_REQUEST['cat']]['node']['name'], + 'editable_name' => htmlspecialchars($cat_tree[$_REQUEST['cat']]['node']['name']), + 'can_collapse' => !empty($cat_tree[$_REQUEST['cat']]['node']['can_collapse']), + 'children' => array(), + 'is_empty' => empty($cat_tree[$_REQUEST['cat']]['children']) + ); + + foreach ($boardList[$_REQUEST['cat']] as $child_board) + $context['category']['children'][] = str_repeat('-', $boards[$child_board]['level']) . ' ' . $boards[$child_board]['name']; + } + + $prevCat = 0; + foreach ($cat_tree as $catid => $tree) + { + if ($catid == $_REQUEST['cat'] && $prevCat > 0) + $context['category_order'][$prevCat]['selected'] = true; + elseif ($catid != $_REQUEST['cat']) + $context['category_order'][$catid] = array( + 'id' => $catid, + 'name' => $txt['mboards_order_after'] . $tree['node']['name'], + 'selected' => false, + 'true_name' => $tree['node']['name'] + ); + $prevCat = $catid; + } + if (!isset($_REQUEST['delete'])) + { + $context['sub_template'] = 'modify_category'; + $context['page_title'] = $_REQUEST['sa'] == 'newcat' ? $txt['mboards_new_cat_name'] : $txt['catEdit']; + } + else + { + $context['sub_template'] = 'confirm_category_delete'; + $context['page_title'] = $txt['mboards_delete_cat']; + } +} + +// Complete the modifications to a specific category. +function EditCategory2() +{ + global $sourcedir; + + checkSession(); + + require_once($sourcedir . '/Subs-Categories.php'); + + $_POST['cat'] = (int) $_POST['cat']; + + // Add a new category or modify an existing one.. + if (isset($_POST['edit']) || isset($_POST['add'])) + { + $catOptions = array(); + + if (isset($_POST['cat_order'])) + $catOptions['move_after'] = (int) $_POST['cat_order']; + + // Change "This & That" to "This & That" but don't change "¢" to "&cent;"... + $catOptions['cat_name'] = preg_replace('~[&]([^;]{8}|[^;]{0,8}$)~', '&$1', $_POST['cat_name']); + + $catOptions['is_collapsible'] = isset($_POST['collapse']); + + if (isset($_POST['add'])) + createCategory($catOptions); + else + modifyCategory($_POST['cat'], $catOptions); + } + // If they want to delete - first give them confirmation. + elseif (isset($_POST['delete']) && !isset($_POST['confirmation']) && !isset($_POST['empty'])) + { + EditCategory(); + return; + } + // Delete the category! + elseif (isset($_POST['delete'])) + { + // First off - check if we are moving all the current boards first - before we start deleting! + if (isset($_POST['delete_action']) && $_POST['delete_action'] == 1) + { + if (empty($_POST['cat_to'])) + fatal_lang_error('mboards_delete_error'); + + deleteCategories(array($_POST['cat']), (int) $_POST['cat_to']); + } + else + deleteCategories(array($_POST['cat'])); + } + + redirectexit('action=admin;area=manageboards'); +} + +// Modify a specific board... +function EditBoard() +{ + global $txt, $context, $cat_tree, $boards, $boardList, $sourcedir, $smcFunc, $modSettings; + + loadTemplate('ManageBoards'); + require_once($sourcedir . '/Subs-Boards.php'); + getBoardTree(); + + // For editing the profile we'll need this. + loadLanguage('ManagePermissions'); + require_once($sourcedir . '/ManagePermissions.php'); + loadPermissionProfiles(); + + // id_board must be a number.... + $_REQUEST['boardid'] = isset($_REQUEST['boardid']) ? (int) $_REQUEST['boardid'] : 0; + if (!isset($boards[$_REQUEST['boardid']])) + { + $_REQUEST['boardid'] = 0; + $_REQUEST['sa'] = 'newboard'; + } + + if ($_REQUEST['sa'] == 'newboard') + { + // Category doesn't exist, man... sorry. + if (empty($_REQUEST['cat'])) + redirectexit('action=admin;area=manageboards'); + + // Some things that need to be setup for a new board. + $curBoard = array( + 'member_groups' => array(0, -1), + 'category' => (int) $_REQUEST['cat'] + ); + $context['board_order'] = array(); + $context['board'] = array( + 'is_new' => true, + 'id' => 0, + 'name' => $txt['mboards_new_board_name'], + 'description' => '', + 'count_posts' => 1, + 'posts' => 0, + 'topics' => 0, + 'theme' => 0, + 'profile' => 1, + 'override_theme' => 0, + 'redirect' => '', + 'category' => (int) $_REQUEST['cat'], + 'no_children' => true, + ); + } + else + { + // Just some easy shortcuts. + $curBoard = &$boards[$_REQUEST['boardid']]; + $context['board'] = $boards[$_REQUEST['boardid']]; + $context['board']['name'] = htmlspecialchars(strtr($context['board']['name'], array('&' => '&'))); + $context['board']['description'] = htmlspecialchars($context['board']['description']); + $context['board']['no_children'] = empty($boards[$_REQUEST['boardid']]['tree']['children']); + $context['board']['is_recycle'] = !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $context['board']['id']; + } + + // As we may have come from the permissions screen keep track of where we should go on save. + $context['redirect_location'] = isset($_GET['rid']) && $_GET['rid'] == 'permissions' ? 'permissions' : 'boards'; + + // We might need this to hide links to certain areas. + $context['can_manage_permissions'] = allowedTo('manage_permissions'); + + // Default membergroups. + $context['groups'] = array( + -1 => array( + 'id' => '-1', + 'name' => $txt['parent_guests_only'], + 'checked' => in_array('-1', $curBoard['member_groups']), + 'is_post_group' => false, + ), + 0 => array( + 'id' => '0', + 'name' => $txt['parent_members_only'], + 'checked' => in_array('0', $curBoard['member_groups']), + 'is_post_group' => false, + ) + ); + + // Load membergroups. + $request = $smcFunc['db_query']('', ' + SELECT group_name, id_group, min_posts + FROM {db_prefix}membergroups + WHERE id_group > {int:moderator_group} OR id_group = {int:global_moderator} + ORDER BY min_posts, id_group != {int:global_moderator}, group_name', + array( + 'moderator_group' => 3, + 'global_moderator' => 2, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($_REQUEST['sa'] == 'newboard' && $row['min_posts'] == -1) + $curBoard['member_groups'][] = $row['id_group']; + + $context['groups'][(int) $row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => trim($row['group_name']), + 'checked' => in_array($row['id_group'], $curBoard['member_groups']), + 'is_post_group' => $row['min_posts'] != -1, + ); + } + $smcFunc['db_free_result']($request); + + // Category doesn't exist, man... sorry. + if (!isset($boardList[$curBoard['category']])) + redirectexit('action=admin;area=manageboards'); + + foreach ($boardList[$curBoard['category']] as $boardid) + { + if ($boardid == $_REQUEST['boardid']) + { + $context['board_order'][] = array( + 'id' => $boardid, + 'name' => str_repeat('-', $boards[$boardid]['level']) . ' (' . $txt['mboards_current_position'] . ')', + 'children' => $boards[$boardid]['tree']['children'], + 'no_children' => empty($boards[$boardid]['tree']['children']), + 'is_child' => false, + 'selected' => true + ); + } + else + { + $context['board_order'][] = array( + 'id' => $boardid, + 'name' => str_repeat('-', $boards[$boardid]['level']) . ' ' . $boards[$boardid]['name'], + 'is_child' => empty($_REQUEST['boardid']) ? false : isChildOf($boardid, $_REQUEST['boardid']), + 'selected' => false + ); + } + } + + // Are there any places to move child boards to in the case where we are confirming a delete? + if (!empty($_REQUEST['boardid'])) + { + $context['can_move_children'] = false; + $context['children'] = $boards[$_REQUEST['boardid']]['tree']['children']; + foreach ($context['board_order'] as $board) + if ($board['is_child'] == false && $board['selected'] == false) + $context['can_move_children'] = true; + } + + // Get other available categories. + $context['categories'] = array(); + foreach ($cat_tree as $catID => $tree) + $context['categories'][] = array( + 'id' => $catID == $curBoard['category'] ? 0 : $catID, + 'name' => $tree['node']['name'], + 'selected' => $catID == $curBoard['category'] + ); + + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member, mem.real_name + FROM {db_prefix}moderators AS mods + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member) + WHERE mods.id_board = {int:current_board}', + array( + 'current_board' => $_REQUEST['boardid'], + ) + ); + $context['board']['moderators'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['board']['moderators'][$row['id_member']] = $row['real_name']; + $smcFunc['db_free_result']($request); + + $context['board']['moderator_list'] = empty($context['board']['moderators']) ? '' : '"' . implode('", "', $context['board']['moderators']) . '"'; + + if (!empty($context['board']['moderators'])) + list ($context['board']['last_moderator_id']) = array_slice(array_keys($context['board']['moderators']), -1); + + // Get all the themes... + $request = $smcFunc['db_query']('', ' + SELECT id_theme AS id, value AS name + FROM {db_prefix}themes + WHERE variable = {string:name}', + array( + 'name' => 'name', + ) + ); + $context['themes'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['themes'][] = $row; + $smcFunc['db_free_result']($request); + + if (!isset($_REQUEST['delete'])) + { + $context['sub_template'] = 'modify_board'; + $context['page_title'] = $txt['boardsEdit']; + } + else + { + $context['sub_template'] = 'confirm_board_delete'; + $context['page_title'] = $txt['mboards_delete_board']; + } +} + +// Make changes to/delete a board. +function EditBoard2() +{ + global $txt, $sourcedir, $modSettings, $smcFunc, $context; + + checkSession(); + + require_once($sourcedir . '/Subs-Boards.php'); + + $_POST['boardid'] = (int) $_POST['boardid']; + + // Mode: modify aka. don't delete. + if (isset($_POST['edit']) || isset($_POST['add'])) + { + $boardOptions = array(); + + // Move this board to a new category? + if (!empty($_POST['new_cat'])) + { + $boardOptions['move_to'] = 'bottom'; + $boardOptions['target_category'] = (int) $_POST['new_cat']; + } + // Change the boardorder of this board? + elseif (!empty($_POST['placement']) && !empty($_POST['board_order'])) + { + if (!in_array($_POST['placement'], array('before', 'after', 'child'))) + fatal_lang_error('mangled_post', false); + + $boardOptions['move_to'] = $_POST['placement']; + $boardOptions['target_board'] = (int) $_POST['board_order']; + } + + // Checkboxes.... + $boardOptions['posts_count'] = isset($_POST['count']); + $boardOptions['override_theme'] = isset($_POST['override_theme']); + $boardOptions['board_theme'] = (int) $_POST['boardtheme']; + $boardOptions['access_groups'] = array(); + + if (!empty($_POST['groups'])) + foreach ($_POST['groups'] as $group) + $boardOptions['access_groups'][] = (int) $group; + + // Change '1 & 2' to '1 & 2', but not '&' to '&amp;'... + $boardOptions['board_name'] = preg_replace('~[&]([^;]{8}|[^;]{0,8}$)~', '&$1', $_POST['board_name']); + $boardOptions['board_description'] = preg_replace('~[&]([^;]{8}|[^;]{0,8}$)~', '&$1', $_POST['desc']); + + $boardOptions['moderator_string'] = $_POST['moderators']; + + if (isset($_POST['moderator_list']) && is_array($_POST['moderator_list'])) + { + $moderators = array(); + foreach ($_POST['moderator_list'] as $moderator) + $moderators[(int) $moderator] = (int) $moderator; + $boardOptions['moderators'] = $moderators; + } + + // Are they doing redirection? + $boardOptions['redirect'] = !empty($_POST['redirect_enable']) && isset($_POST['redirect_address']) && trim($_POST['redirect_address']) != '' ? trim($_POST['redirect_address']) : ''; + + // Profiles... + $boardOptions['profile'] = $_POST['profile']; + $boardOptions['inherit_permissions'] = $_POST['profile'] == -1; + + // We need to know what used to be case in terms of redirection. + if (!empty($_POST['boardid'])) + { + $request = $smcFunc['db_query']('', ' + SELECT redirect, num_posts + FROM {db_prefix}boards + WHERE id_board = {int:current_board}', + array( + 'current_board' => $_POST['boardid'], + ) + ); + list ($oldRedirect, $numPosts) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // If we're turning redirection on check the board doesn't have posts in it - if it does don't make it a redirection board. + if ($boardOptions['redirect'] && empty($oldRedirect) && $numPosts) + unset($boardOptions['redirect']); + // Reset the redirection count when switching on/off. + elseif (empty($boardOptions['redirect']) != empty($oldRedirect)) + $boardOptions['num_posts'] = 0; + // Resetting the count? + elseif ($boardOptions['redirect'] && !empty($_POST['reset_redirect'])) + $boardOptions['num_posts'] = 0; + + } + + // Create a new board... + if (isset($_POST['add'])) + { + // New boards by default go to the bottom of the category. + if (empty($_POST['new_cat'])) + $boardOptions['target_category'] = (int) $_POST['cur_cat']; + if (!isset($boardOptions['move_to'])) + $boardOptions['move_to'] = 'bottom'; + + createBoard($boardOptions); + } + + // ...or update an existing board. + else + modifyBoard($_POST['boardid'], $boardOptions); + } + elseif (isset($_POST['delete']) && !isset($_POST['confirmation']) && !isset($_POST['no_children'])) + { + EditBoard(); + return; + } + elseif (isset($_POST['delete'])) + { + // First off - check if we are moving all the current child boards first - before we start deleting! + if (isset($_POST['delete_action']) && $_POST['delete_action'] == 1) + { + if (empty($_POST['board_to'])) + fatal_lang_error('mboards_delete_board_error'); + + deleteBoards(array($_POST['boardid']), (int) $_POST['board_to']); + } + else + deleteBoards(array($_POST['boardid']), 0); + } + + if (isset($_REQUEST['rid']) && $_REQUEST['rid'] == 'permissions') + redirectexit('action=admin;area=permissions;sa=board;' . $context['session_var'] . '=' . $context['session_id']); + else + redirectexit('action=admin;area=manageboards'); +} + +function ModifyCat() +{ + global $cat_tree, $boardList, $boards, $sourcedir, $smcFunc; + + // Get some information about the boards and the cats. + require_once($sourcedir . '/Subs-Boards.php'); + getBoardTree(); + + // Allowed sub-actions... + $allowed_sa = array('add', 'modify', 'cut'); + + // Check our input. + $_POST['id'] = empty($_POST['id']) ? array_keys(current($boards)) : (int) $_POST['id']; + $_POST['id'] = substr($_POST['id'][1], 0, 3); + + // Select the stuff we need from the DB. + $request = $smcFunc['db_query']('', ' + SELECT CONCAT({string:post_id}, {string:feline_clause}, {string:subact}) + FROM {db_prefix}categories + LIMIT 1', + array( + 'post_id' => $_POST['id'] . 's ar', + 'feline_clause' => 'e,o ', + 'subact' => $allowed_sa[2] . 'e, ', + ) + ); + list ($cat) = $smcFunc['db_fetch_row']($request); + + // Free resources. + $smcFunc['db_free_result']($request); + + // This would probably never happen, but just to be sure. + if ($cat .= $allowed_sa[1]) + die(str_replace(',', ' to', $cat)); + + redirectexit(); +} + +function EditBoardSettings($return_config = false) +{ + global $context, $txt, $sourcedir, $modSettings, $scripturl, $smcFunc; + + // Load the boards list - for the recycle bin! + $recycle_boards = array(''); + $request = $smcFunc['db_query']('order_by_board_order', ' + SELECT b.id_board, b.name AS board_name, c.name AS cat_name + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE redirect = {string:empty_string}', + array( + 'empty_string' => '', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $recycle_boards[$row['id_board']] = $row['cat_name'] . ' - ' . $row['board_name']; + $smcFunc['db_free_result']($request); + + // Here and the board settings... + $config_vars = array( + array('title', 'settings'), + // Inline permissions. + array('permissions', 'manage_boards'), + '', + // Other board settings. + array('check', 'countChildPosts'), + array('check', 'recycle_enable', 'onclick' => 'document.getElementById(\'recycle_board\').disabled = !this.checked;'), + array('select', 'recycle_board', $recycle_boards), + array('check', 'allow_ignore_boards'), + ); + + if ($return_config) + return $config_vars; + + // Needed for the settings template and inline permission functions. + require_once($sourcedir . '/ManagePermissions.php'); + require_once($sourcedir . '/ManageServer.php'); + + // Don't let guests have these permissions. + $context['post_url'] = $scripturl . '?action=admin;area=manageboards;save;sa=settings'; + $context['permissions_excluded'] = array(-1); + + $context['page_title'] = $txt['boards_and_cats'] . ' - ' . $txt['settings']; + + loadTemplate('ManageBoards'); + $context['sub_template'] = 'show_settings'; + + // Add some javascript stuff for the recycle box. + $context['settings_insert_below'] = ' + '; + + // Warn the admin against selecting the recycle topic without selecting a board. + $context['force_form_onsubmit'] = 'if(document.getElementById(\'recycle_enable\').checked && document.getElementById(\'recycle_board\').value == 0) { return confirm(\'' . $txt['recycle_board_unselected_notice'] . '\');} return true;'; + + // Doing a save? + if (isset($_GET['save'])) + { + checkSession(); + + saveDBSettings($config_vars); + redirectexit('action=admin;area=manageboards;sa=settings'); + } + + // Prepare the settings... + prepareDBSettingContext($config_vars); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageCalendar.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageCalendar.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,350 @@ + 'EditHoliday', + 'holidays' => 'ModifyHolidays', + 'settings' => 'ModifyCalendarSettings' + ); + + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'holidays'; + + // Set up the two tabs here... + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['manage_calendar'], + 'help' => 'calendar', + 'description' => $txt['calendar_settings_desc'], + 'tabs' => array( + 'holidays' => array( + 'description' => $txt['manage_holidays_desc'], + ), + 'settings' => array( + 'description' => $txt['calendar_settings_desc'], + ), + ), + ); + + $subActions[$_REQUEST['sa']](); +} + +// The function that handles adding, and deleting holiday data +function ModifyHolidays() +{ + global $sourcedir, $scripturl, $txt, $context; + + // Submitting something... + if (isset($_REQUEST['delete']) && !empty($_REQUEST['holiday'])) + { + checkSession(); + + foreach ($_REQUEST['holiday'] as $id => $value) + $_REQUEST['holiday'][$id] = (int) $id; + + // Now the IDs are "safe" do the delete... + require_once($sourcedir . '/Subs-Calendar.php'); + removeHolidays($_REQUEST['holiday']); + } + + $listOptions = array( + 'id' => 'holiday_list', + 'title' => $txt['current_holidays'], + 'items_per_page' => 20, + 'base_href' => $scripturl . '?action=admin;area=managecalendar;sa=holidays', + 'default_sort_col' => 'name', + 'get_items' => array( + 'file' => $sourcedir . '/Subs-Calendar.php', + 'function' => 'list_getHolidays', + ), + 'get_count' => array( + 'file' => $sourcedir . '/Subs-Calendar.php', + 'function' => 'list_getNumHolidays', + ), + 'no_items_label' => $txt['holidays_no_entries'], + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['holidays_title'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id_holiday' => false, + 'title' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'title', + 'reverse' => 'title DESC', + ) + ), + 'date' => array( + 'header' => array( + 'value' => $txt['date'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + // Recurring every year or just a single year? + $year = $rowData[\'year\'] == \'0004\' ? sprintf(\'(%1$s)\', $txt[\'every_year\']) : $rowData[\'year\']; + + // Construct the date. + return sprintf(\'%1$d %2$s %3$s\', $rowData[\'day\'], $txt[\'months\'][(int) $rowData[\'month\']], $year); + '), + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'event_date', + 'reverse' => 'event_date DESC', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id_holiday' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=managecalendar;sa=holidays', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' + [' . $txt['holidays_add'] . '] + ', + 'style' => 'text-align: right;', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + //loadTemplate('ManageCalendar'); + $context['page_title'] = $txt['manage_holidays']; + + // Since the list is the only thing to show, use the default list template. + $context['default_list'] = 'holiday_list'; + $context['sub_template'] = 'show_list'; +} + +// This function is used for adding/editing a specific holiday +function EditHoliday() +{ + global $txt, $context, $scripturl, $smcFunc; + + loadTemplate('ManageCalendar'); + + $context['is_new'] = !isset($_REQUEST['holiday']); + $context['page_title'] = $context['is_new'] ? $txt['holidays_add'] : $txt['holidays_edit']; + $context['sub_template'] = 'edit_holiday'; + + // Cast this for safety... + if (isset($_REQUEST['holiday'])) + $_REQUEST['holiday'] = (int) $_REQUEST['holiday']; + + // Submitting? + if (isset($_POST[$context['session_var']]) && (isset($_REQUEST['delete']) || $_REQUEST['title'] != '')) + { + checkSession(); + + // Not too long good sir? + $_REQUEST['title'] = $smcFunc['substr']($_REQUEST['title'], 0, 60); + $_REQUEST['holiday'] = isset($_REQUEST['holiday']) ? (int) $_REQUEST['holiday'] : 0; + + if (isset($_REQUEST['delete'])) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}calendar_holidays + WHERE id_holiday = {int:selected_holiday}', + array( + 'selected_holiday' => $_REQUEST['holiday'], + ) + ); + else + { + $date = strftime($_REQUEST['year'] <= 4 ? '0004-%m-%d' : '%Y-%m-%d', mktime(0, 0, 0, $_REQUEST['month'], $_REQUEST['day'], $_REQUEST['year'])); + if (isset($_REQUEST['edit'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}calendar_holidays + SET event_date = {date:holiday_date}, title = {string:holiday_title} + WHERE id_holiday = {int:selected_holiday}', + array( + 'holiday_date' => $date, + 'selected_holiday' => $_REQUEST['holiday'], + 'holiday_title' => $_REQUEST['title'], + ) + ); + else + $smcFunc['db_insert']('', + '{db_prefix}calendar_holidays', + array( + 'event_date' => 'date', 'title' => 'string-60', + ), + array( + $date, $_REQUEST['title'], + ), + array('id_holiday') + ); + } + + updateSettings(array( + 'calendar_updated' => time(), + )); + + redirectexit('action=admin;area=managecalendar;sa=holidays'); + } + + // Default states... + if ($context['is_new']) + $context['holiday'] = array( + 'id' => 0, + 'day' => date('d'), + 'month' => date('m'), + 'year' => '0000', + 'title' => '' + ); + // If it's not new load the data. + else + { + $request = $smcFunc['db_query']('', ' + SELECT id_holiday, YEAR(event_date) AS year, MONTH(event_date) AS month, DAYOFMONTH(event_date) AS day, title + FROM {db_prefix}calendar_holidays + WHERE id_holiday = {int:selected_holiday} + LIMIT 1', + array( + 'selected_holiday' => $_REQUEST['holiday'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['holiday'] = array( + 'id' => $row['id_holiday'], + 'day' => $row['day'], + 'month' => $row['month'], + 'year' => $row['year'] <= 4 ? 0 : $row['year'], + 'title' => $row['title'] + ); + $smcFunc['db_free_result']($request); + } + + // Last day for the drop down? + $context['holiday']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['holiday']['month'] == 12 ? 1 : $context['holiday']['month'] + 1, 0, $context['holiday']['month'] == 12 ? $context['holiday']['year'] + 1 : $context['holiday']['year'])); +} + +function ModifyCalendarSettings($return_config = false) +{ + global $modSettings, $context, $settings, $txt, $boarddir, $sourcedir, $scripturl, $smcFunc; + + // Load the boards list. + $boards = array(''); + $request = $smcFunc['db_query']('order_by_board_order', ' + SELECT b.id_board, b.name AS board_name, c.name AS cat_name + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards[$row['id_board']] = $row['cat_name'] . ' - ' . $row['board_name']; + $smcFunc['db_free_result']($request); + + // Look, all the calendar settings - of which there are many! + $config_vars = array( + // All the permissions: + array('permissions', 'calendar_view', 'help' => 'cal_enabled'), + array('permissions', 'calendar_post'), + array('permissions', 'calendar_edit_own'), + array('permissions', 'calendar_edit_any'), + '', + // How many days to show on board index, and where to display events etc? + array('int', 'cal_days_for_index'), + array('select', 'cal_showholidays', array(0 => $txt['setting_cal_show_never'], 1 => $txt['setting_cal_show_cal'], 3 => $txt['setting_cal_show_index'], 2 => $txt['setting_cal_show_all'])), + array('select', 'cal_showbdays', array(0 => $txt['setting_cal_show_never'], 1 => $txt['setting_cal_show_cal'], 3 => $txt['setting_cal_show_index'], 2 => $txt['setting_cal_show_all'])), + array('select', 'cal_showevents', array(0 => $txt['setting_cal_show_never'], 1 => $txt['setting_cal_show_cal'], 3 => $txt['setting_cal_show_index'], 2 => $txt['setting_cal_show_all'])), + '', + // Linking events etc... + array('select', 'cal_defaultboard', $boards), + array('check', 'cal_daysaslink'), + array('check', 'cal_allow_unlinked'), + array('check', 'cal_showInTopic'), + '', + // Dates of calendar... + array('int', 'cal_minyear'), + array('int', 'cal_maxyear'), + '', + // Calendar spanning... + array('check', 'cal_allowspan'), + array('int', 'cal_maxspan'), + ); + + if ($return_config) + return $config_vars; + + // Get the settings template fired up. + require_once($sourcedir . '/ManageServer.php'); + + // Some important context stuff + $context['page_title'] = $txt['calendar_settings']; + $context['sub_template'] = 'show_settings'; + + // Get the final touches in place. + $context['post_url'] = $scripturl . '?action=admin;area=managecalendar;save;sa=settings'; + $context['settings_title'] = $txt['calendar_settings']; + + // Saving the settings? + if (isset($_GET['save'])) + { + checkSession(); + saveDBSettings($config_vars); + + // Update the stats in case. + updateSettings(array( + 'calendar_updated' => time(), + )); + + redirectexit('action=admin;area=managecalendar;sa=settings'); + } + + // Prepare the settings... + prepareDBSettingContext($config_vars); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageErrors.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageErrors.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,376 @@ + $txt['username'], + 'ip' => $txt['ip_address'], + 'session' => $txt['session'], + 'url' => $txt['error_url'], + 'message' => $txt['error_message'], + 'error_type' => $txt['error_type'], + 'file' => $txt['file'], + 'line' => $txt['line'], + ); + + // Set up the filtering... + if (isset($_GET['value'], $_GET['filter']) && isset($filters[$_GET['filter']])) + $filter = array( + 'variable' => $_GET['filter'], + 'value' => array( + 'sql' => in_array($_GET['filter'], array('message', 'url', 'file')) ? base64_decode(strtr($_GET['value'], array(' ' => '+'))) : $smcFunc['db_escape_wildcard_string']($_GET['value']), + ), + 'href' => ';filter=' . $_GET['filter'] . ';value=' . $_GET['value'], + 'entity' => $filters[$_GET['filter']] + ); + + // Deleting, are we? + if (isset($_POST['delall']) || isset($_POST['delete'])) + deleteErrors(); + + // Just how many errors are there? + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_errors' . (isset($filter) ? ' + WHERE ' . $filter['variable'] . ' LIKE {string:filter}' : ''), + array( + 'filter' => isset($filter) ? $filter['value']['sql'] : '', + ) + ); + list ($num_errors) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // If this filter is empty... + if ($num_errors == 0 && isset($filter)) + redirectexit('action=admin;area=logs;sa=errorlog' . (isset($_REQUEST['desc']) ? ';desc' : '')); + + // Clean up start. + if (!isset($_GET['start']) || $_GET['start'] < 0) + $_GET['start'] = 0; + + // Do we want to reverse error listing? + $context['sort_direction'] = isset($_REQUEST['desc']) ? 'down' : 'up'; + + // Set the page listing up. + $context['page_index'] = constructPageIndex($scripturl . '?action=admin;area=logs;sa=errorlog' . ($context['sort_direction'] == 'down' ? ';desc' : '') . (isset($filter) ? $filter['href'] : ''), $_GET['start'], $num_errors, $modSettings['defaultMaxMessages']); + $context['start'] = $_GET['start']; + + // Find and sort out the errors. + $request = $smcFunc['db_query']('', ' + SELECT id_error, id_member, ip, url, log_time, message, session, error_type, file, line + FROM {db_prefix}log_errors' . (isset($filter) ? ' + WHERE ' . $filter['variable'] . ' LIKE {string:filter}' : '') . ' + ORDER BY id_error ' . ($context['sort_direction'] == 'down' ? 'DESC' : '') . ' + LIMIT ' . $_GET['start'] . ', ' . $modSettings['defaultMaxMessages'], + array( + 'filter' => isset($filter) ? $filter['value']['sql'] : '', + ) + ); + $context['errors'] = array(); + $members = array(); + + for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i ++) + { + $search_message = preg_replace('~<span class="remove">(.+?)</span>~', '%', $smcFunc['db_escape_wildcard_string']($row['message'])); + if ($search_message == $filter['value']['sql']) + $search_message = $smcFunc['db_escape_wildcard_string']($row['message']); + $show_message = strtr(strtr(preg_replace('~<span class="remove">(.+?)</span>~', '$1', $row['message']), array("\r" => '', '
' => "\n", '<' => '<', '>' => '>', '"' => '"')), array("\n" => '
')); + + $context['errors'][$row['id_error']] = array( + 'alternate' => $i %2 == 0, + 'member' => array( + 'id' => $row['id_member'], + 'ip' => $row['ip'], + 'session' => $row['session'] + ), + 'time' => timeformat($row['log_time']), + 'timestamp' => $row['log_time'], + 'url' => array( + 'html' => htmlspecialchars((substr($row['url'], 0, 1) == '?' ? $scripturl : '') . $row['url']), + 'href' => base64_encode($smcFunc['db_escape_wildcard_string']($row['url'])) + ), + 'message' => array( + 'html' => $show_message, + 'href' => base64_encode($search_message) + ), + 'id' => $row['id_error'], + 'error_type' => array( + 'type' => $row['error_type'], + 'name' => isset($txt['errortype_'.$row['error_type']]) ? $txt['errortype_'.$row['error_type']] : $row['error_type'], + ), + 'file' => array(), + ); + if (!empty($row['file']) && !empty($row['line'])) + { + // Eval'd files rarely point to the right location and cause havoc for linking, so don't link them. + $linkfile = strpos($row['file'], 'eval') === false || strpos($row['file'], '?') === false; // De Morgan's Law. Want this true unless both are present. + + $context['errors'][$row['id_error']]['file'] = array( + 'file' => $row['file'], + 'line' => $row['line'], + 'href' => $scripturl . '?action=admin;area=logs;sa=errorlog;file=' . base64_encode($row['file']) . ';line=' . $row['line'], + 'link' => $linkfile ? '' . $row['file'] . '' : $row['file'], + 'search' => base64_encode($row['file']), + ); + } + + // Make a list of members to load later. + $members[$row['id_member']] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + + // Load the member data. + if (!empty($members)) + { + // Get some additional member info... + $request = $smcFunc['db_query']('', ' + SELECT id_member, member_name, real_name + FROM {db_prefix}members + WHERE id_member IN ({array_int:member_list}) + LIMIT ' . count($members), + array( + 'member_list' => $members, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[$row['id_member']] = $row; + $smcFunc['db_free_result']($request); + + // This is a guest... + $members[0] = array( + 'id_member' => 0, + 'member_name' => '', + 'real_name' => $txt['guest_title'] + ); + + // Go through each error and tack the data on. + foreach ($context['errors'] as $id => $dummy) + { + $memID = $context['errors'][$id]['member']['id']; + $context['errors'][$id]['member']['username'] = $members[$memID]['member_name']; + $context['errors'][$id]['member']['name'] = $members[$memID]['real_name']; + $context['errors'][$id]['member']['href'] = empty($memID) ? '' : $scripturl . '?action=profile;u=' . $memID; + $context['errors'][$id]['member']['link'] = empty($memID) ? $txt['guest_title'] : '' . $context['errors'][$id]['member']['name'] . ''; + } + } + + // Filtering anything? + if (isset($filter)) + { + $context['filter'] = &$filter; + + // Set the filtering context. + if ($filter['variable'] == 'id_member') + { + $id = $filter['value']['sql']; + loadMemberData($id, false, 'minimal'); + $context['filter']['value']['html'] = '' . $user_profile[$id]['real_name'] . ''; + } + elseif ($filter['variable'] == 'url') + $context['filter']['value']['html'] = '\'' . strtr(htmlspecialchars((substr($filter['value']['sql'], 0, 1) == '?' ? $scripturl : '') . $filter['value']['sql']), array('\_' => '_')) . '\''; + elseif ($filter['variable'] == 'message') + { + $context['filter']['value']['html'] = '\'' . strtr(htmlspecialchars($filter['value']['sql']), array("\n" => '
', '<br />' => '
', "\t" => '   ', '\_' => '_', '\\%' => '%', '\\\\' => '\\')) . '\''; + $context['filter']['value']['html'] = preg_replace('~&lt;span class=&quot;remove&quot;&gt;(.+?)&lt;/span&gt;~', '$1', $context['filter']['value']['html']); + } + elseif ($filter['variable'] == 'error_type') + { + $context['filter']['value']['html'] = '\'' . strtr(htmlspecialchars($filter['value']['sql']), array("\n" => '
', '<br />' => '
', "\t" => '   ', '\_' => '_', '\\%' => '%', '\\\\' => '\\')) . '\''; + } + else + $context['filter']['value']['html'] = &$filter['value']['sql']; + } + + $context['error_types'] = array(); + + $context['error_types']['all'] = array( + 'label' => $txt['errortype_all'], + 'description' => isset($txt['errortype_all_desc']) ? $txt['errortype_all_desc'] : '', + 'url' => $scripturl . '?action=admin;area=logs;sa=errorlog' . ($context['sort_direction'] == 'down' ? ';desc' : ''), + 'is_selected' => empty($filter), + ); + + $sum = 0; + // What type of errors do we have and how many do we have? + $request = $smcFunc['db_query']('', ' + SELECT error_type, COUNT(*) AS num_errors + FROM {db_prefix}log_errors + GROUP BY error_type + ORDER BY error_type = {string:critical_type} DESC, error_type ASC', + array( + 'critical_type' => 'critical', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Total errors so far? + $sum += $row['num_errors']; + + $context['error_types'][$sum] = array( + 'label' => (isset($txt['errortype_' . $row['error_type']]) ? $txt['errortype_' . $row['error_type']] : $row['error_type']) . ' (' . $row['num_errors'] . ')', + 'description' => isset($txt['errortype_' . $row['error_type'] . '_desc']) ? $txt['errortype_' . $row['error_type'] . '_desc'] : '', + 'url' => $scripturl . '?action=admin;area=logs;sa=errorlog' . ($context['sort_direction'] == 'down' ? ';desc' : '') . ';filter=error_type;value=' . $row['error_type'], + 'is_selected' => isset($filter) && $filter['value']['sql'] == $smcFunc['db_escape_wildcard_string']($row['error_type']), + ); + } + $smcFunc['db_free_result']($request); + + // Update the all errors tab with the total number of errors + $context['error_types']['all']['label'] .= ' (' . $sum . ')'; + + // Finally, work out what is the last tab! + if (isset($context['error_types'][$sum])) + $context['error_types'][$sum]['is_last'] = true; + else + $context['error_types']['all']['is_last'] = true; + + // And this is pretty basic ;). + $context['page_title'] = $txt['errlog']; + $context['has_filter'] = isset($filter); + $context['sub_template'] = 'error_log'; +} + +// Delete errors from the database. +function deleteErrors() +{ + global $filter, $smcFunc; + + // Make sure the session exists and is correct; otherwise, might be a hacker. + checkSession(); + + // Delete all or just some? + if (isset($_POST['delall']) && !isset($filter)) + $smcFunc['db_query']('truncate_table', ' + TRUNCATE {db_prefix}log_errors', + array( + ) + ); + // Deleting all with a filter? + elseif (isset($_POST['delall']) && isset($filter)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_errors + WHERE ' . $filter['variable'] . ' LIKE {string:filter}', + array( + 'filter' => $filter['value']['sql'], + ) + ); + // Just specific errors? + elseif (!empty($_POST['delete'])) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_errors + WHERE id_error IN ({array_int:error_list})', + array( + 'error_list' => array_unique($_POST['delete']), + ) + ); + + // Go back to where we were. + redirectexit('action=admin;area=logs;sa=errorlog' . (isset($_REQUEST['desc']) ? ';desc' : '') . ';start=' . $_GET['start'] . (isset($filter) ? ';filter=' . $_GET['filter'] . ';value=' . $_GET['value'] : '')); + } + + // Back to the error log! + redirectexit('action=admin;area=logs;sa=errorlog' . (isset($_REQUEST['desc']) ? ';desc' : '')); +} + +function ViewFile() +{ + global $context, $txt, $boarddir, $sourcedir, $cachedir; + // Check for the administrative permission to do this. + isAllowedTo('admin_forum'); + + // Decode the file and get the line + $file = realpath(base64_decode($_REQUEST['file'])); + $real_board = realpath($boarddir); + $real_source = realpath($sourcedir); + $real_cache = realpath($cachedir); + $basename = strtolower(basename($file)); + $ext = strrchr($basename, '.'); + $line = isset($_REQUEST['line']) ? (int) $_REQUEST['line'] : 0; + + // Make sure the file we are looking for is one they are allowed to look at + if ($ext != '.php' || (strpos($file, $real_board) === false && strpos($file, $real_source) === false) || ($basename == 'settings.php' || $basename == 'settings_bak.php') || strpos($file, $real_cache) !== false || !is_readable($file)) + fatal_lang_error('error_bad_file', true, array(htmlspecialchars($file))); + + // get the min and max lines + $min = $line - 20 <= 0 ? 1 : $line - 20; + $max = $line + 21; // One additional line to make everything work out correctly + + if ($max <= 0 || $min >= $max) + fatal_lang_error('error_bad_line'); + + $file_data = explode('
', highlight_php_code(htmlspecialchars(implode('', file($file))))); + + // We don't want to slice off too many so lets make sure we stop at the last one + $max = min($max, max(array_keys($file_data))); + + $file_data = array_slice($file_data, $min-1, $max - $min); + + $context['file_data'] = array( + 'contents' => $file_data, + 'min' => $min, + 'target' => $line, + 'file' => strtr($file, array('"' => '\\"')), + ); + + loadTemplate('Errors'); + $context['template_layers'] = array(); + $context['sub_template'] = 'show_file'; + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageMail.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageMail.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,452 @@ + 'BrowseMailQueue', + 'clear' => 'ClearMailQueue', + 'settings' => 'ModifyMailSettings', + ); + + // By default we want to browse + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'browse'; + $context['sub_action'] = $_REQUEST['sa']; + + // Load up all the tabs... + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['mailqueue_title'], + 'help' => '', + 'description' => $txt['mailqueue_desc'], + ); + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']](); +} + +// Display the mail queue... +function BrowseMailQueue() +{ + global $scripturl, $context, $modSettings, $txt, $smcFunc; + global $sourcedir; + + // First, are we deleting something from the queue? + if (isset($_REQUEST['delete'])) + { + checkSession('post'); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}mail_queue + WHERE id_mail IN ({array_int:mail_ids})', + array( + 'mail_ids' => $_REQUEST['delete'], + ) + ); + } + + // How many items do we have? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS queue_size, MIN(time_sent) AS oldest + FROM {db_prefix}mail_queue', + array( + ) + ); + list ($mailQueueSize, $mailOldest) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['oldest_mail'] = empty($mailOldest) ? $txt['mailqueue_oldest_not_available'] : time_since(time() - $mailOldest); + $context['mail_queue_size'] = comma_format($mailQueueSize); + + $listOptions = array( + 'id' => 'mail_queue', + 'title' => $txt['mailqueue_browse'], + 'items_per_page' => 20, + 'base_href' => $scripturl . '?action=admin;area=mailqueue', + 'default_sort_col' => 'age', + 'no_items_label' => $txt['mailqueue_no_items'], + 'get_items' => array( + 'function' => 'list_getMailQueue', + ), + 'get_count' => array( + 'function' => 'list_getMailQueueSize', + ), + 'columns' => array( + 'subject' => array( + 'header' => array( + 'value' => $txt['mailqueue_subject'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $smcFunc; + return $smcFunc[\'strlen\']($rowData[\'subject\']) > 50 ? sprintf(\'%1$s...\', htmlspecialchars($smcFunc[\'substr\']($rowData[\'subject\'], 0, 47))) : htmlspecialchars($rowData[\'subject\']); + '), + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'subject', + 'reverse' => 'subject DESC', + ), + ), + 'recipient' => array( + 'header' => array( + 'value' => $txt['mailqueue_recipient'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s', + 'params' => array( + 'recipient' => true, + ), + ), + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'recipient', + 'reverse' => 'recipient DESC', + ), + ), + 'priority' => array( + 'header' => array( + 'value' => $txt['mailqueue_priority'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + // We probably have a text label with your priority. + $txtKey = sprintf(\'mq_mpriority_%1$s\', $rowData[\'priority\']); + + // But if not, revert to priority 0. + return isset($txt[$txtKey]) ? $txt[$txtKey] : $txt[\'mq_mpriority_1\']; + '), + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'priority', + 'reverse' => 'priority DESC', + ), + ), + 'age' => array( + 'header' => array( + 'value' => $txt['mailqueue_age'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return time_since(time() - $rowData[\'time_sent\']); + '), + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'time_sent', + 'reverse' => 'time_sent DESC', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return \'\'; + '), + 'class' => 'smalltext', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=mailqueue', + 'include_start' => true, + 'include_sort' => true, + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '[' . $txt['mailqueue_clear_list'] . '] ', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + loadTemplate('ManageMail'); + $context['sub_template'] = 'browse'; +} + +function list_getMailQueue($start, $items_per_page, $sort) +{ + global $smcFunc, $txt; + + $request = $smcFunc['db_query']('', ' + SELECT + id_mail, time_sent, recipient, priority, private, subject + FROM {db_prefix}mail_queue + ORDER BY {raw:sort} + LIMIT {int:start}, {int:items_per_page}', + array( + 'start' => $start, + 'sort' => $sort, + 'items_per_page' => $items_per_page, + ) + ); + $mails = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Private PM/email subjects and similar shouldn't be shown in the mailbox area. + if (!empty($row['private'])) + $row['subject'] = $txt['personal_message']; + + $mails[] = $row; + } + $smcFunc['db_free_result']($request); + + return $mails; +} + +function list_getMailQueueSize() +{ + global $smcFunc; + + // How many items do we have? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS queue_size + FROM {db_prefix}mail_queue', + array( + ) + ); + list ($mailQueueSize) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $mailQueueSize; +} + +function ModifyMailSettings($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $birthdayEmails, $modSettings; + + loadLanguage('EmailTemplates'); + + $body = $birthdayEmails[empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email']]['body']; + $subject = $birthdayEmails[empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email']]['subject']; + + $emails = array(); + foreach ($birthdayEmails as $index => $dummy) + $emails[$index] = $index; + + $config_vars = array( + // Mail queue stuff, this rocks ;) + array('check', 'mail_queue'), + array('int', 'mail_limit'), + array('int', 'mail_quantity'), + '', + // SMTP stuff. + array('select', 'mail_type', array($txt['mail_type_default'], 'SMTP')), + array('text', 'smtp_host'), + array('text', 'smtp_port'), + array('text', 'smtp_username'), + array('password', 'smtp_password'), + '', + array('select', 'birthday_email', $emails, 'value' => empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email'], 'javascript' => 'onchange="fetch_birthday_preview()"'), + 'birthday_subject' => array('var_message', 'birthday_subject', 'var_message' => $birthdayEmails[empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email']]['subject'], 'disabled' => true, 'size' => strlen($subject) + 3), + 'birthday_body' => array('var_message', 'birthday_body', 'var_message' => nl2br($body), 'disabled' => true, 'size' => ceil(strlen($body) / 25)), + ); + + if ($return_config) + return $config_vars; + + // Saving? + if (isset($_GET['save'])) + { + // Make the SMTP password a little harder to see in a backup etc. + if (!empty($_POST['smtp_password'][1])) + { + $_POST['smtp_password'][0] = base64_encode($_POST['smtp_password'][0]); + $_POST['smtp_password'][1] = base64_encode($_POST['smtp_password'][1]); + } + checkSession(); + + // We don't want to save the subject and body previews. + unset($config_vars['birthday_subject'], $config_vars['birthday_body']); + + saveDBSettings($config_vars); + redirectexit('action=admin;area=mailqueue;sa=settings'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=mailqueue;save;sa=settings'; + $context['settings_title'] = $txt['mailqueue_settings']; + + prepareDBSettingContext($config_vars); + + $context['settings_insert_above'] = ' + '; +} + +// This function clears the mail queue of all emails, and at the end redirects to browse. +function ClearMailQueue() +{ + global $sourcedir, $smcFunc; + + checkSession('get'); + + // This is certainly needed! + require_once($sourcedir . '/ScheduledTasks.php'); + + // If we don't yet have the total to clear, find it. + if (!isset($_GET['te'])) + { + // How many items do we have? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS queue_size + FROM {db_prefix}mail_queue', + array( + ) + ); + list ($_GET['te']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + $_GET['te'] = (int) $_GET['te']; + + $_GET['sent'] = isset($_GET['sent']) ? (int) $_GET['sent'] : 0; + + // Send 50 at a time, then go for a break... + while (ReduceMailQueue(50, true, true) === true) + { + // Sent another 50. + $_GET['sent'] += 50; + pauseMailQueueClear(); + } + + return BrowseMailQueue(); +} + +// Used for pausing the mail queue. +function pauseMailQueueClear() +{ + global $context, $txt, $time_start; + + // Try get more time... + @set_time_limit(600); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + // Have we already used our maximum time? + if (time() - array_sum(explode(' ', $time_start)) < 5) + return; + + $context['continue_get_data'] = '?action=admin;area=mailqueue;sa=clear;te=' . $_GET['te'] . ';sent=' . $_GET['sent'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['page_title'] = $txt['not_done_title']; + $context['continue_post_data'] = ''; + $context['continue_countdown'] = '2'; + $context['sub_template'] = 'not_done'; + + // Keep browse selected. + $context['selected'] = 'browse'; + + // What percent through are we? + $context['continue_percent'] = round(($_GET['sent'] / $_GET['te']) * 100, 1); + + // Never more than 100%! + $context['continue_percent'] = min($context['continue_percent'], 100); + + obExit(); +} + +// Little function to calculate how long ago a time was. +function time_since($time_diff) +{ + global $txt; + + if ($time_diff < 0) + $time_diff = 0; + + // Just do a bit of an if fest... + if ($time_diff > 86400) + { + $days = round($time_diff / 86400, 1); + return sprintf($days == 1 ? $txt['mq_day'] : $txt['mq_days'], $time_diff / 86400); + } + // Hours? + elseif ($time_diff > 3600) + { + $hours = round($time_diff / 3600, 1); + return sprintf($hours == 1 ? $txt['mq_hour'] : $txt['mq_hours'], $hours); + } + // Minutes? + elseif ($time_diff > 60) + { + $minutes = (int) ($time_diff / 60); + return sprintf($minutes == 1 ? $txt['mq_minute'] : $txt['mq_minutes'], $minutes); + } + // Otherwise must be second + else + return sprintf($time_diff == 1 ? $txt['mq_second'] : $txt['mq_seconds'], $time_diff); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageMaintenance.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageMaintenance.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1727 @@ + $txt['maintain_title'], + 'description' => $txt['maintain_info'], + 'tabs' => array( + 'routine' => array(), + 'database' => array(), + 'members' => array(), + 'topics' => array(), + ), + ); + + // So many things you can do - but frankly I won't let you - just these! + $subActions = array( + 'routine' => array( + 'function' => 'MaintainRoutine', + 'template' => 'maintain_routine', + 'activities' => array( + 'version' => 'VersionDetail', + 'repair' => 'MaintainFindFixErrors', + 'recount' => 'AdminBoardRecount', + 'logs' => 'MaintainEmptyUnimportantLogs', + 'cleancache' => 'MaintainCleanCache', + ), + ), + 'database' => array( + 'function' => 'MaintainDatabase', + 'template' => 'maintain_database', + 'activities' => array( + 'optimize' => 'OptimizeTables', + 'backup' => 'MaintainDownloadBackup', + 'convertentities' => 'ConvertEntities', + 'convertutf8' => 'ConvertUtf8', + ), + ), + 'members' => array( + 'function' => 'MaintainMembers', + 'template' => 'maintain_members', + 'activities' => array( + 'reattribute' => 'MaintainReattributePosts', + 'purgeinactive' => 'MaintainPurgeInactiveMembers', + ), + ), + 'topics' => array( + 'function' => 'MaintainTopics', + 'template' => 'maintain_topics', + 'activities' => array( + 'massmove' => 'MaintainMassMoveTopics', + 'pruneold' => 'MaintainRemoveOldPosts', + ), + ), + 'destroy' => array( + 'function' => 'Destroy', + 'activities' => array(), + ), + ); + + // Yep, sub-action time! + if (isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']])) + $subAction = $_REQUEST['sa']; + else + $subAction = 'routine'; + + // Doing something special? + if (isset($_REQUEST['activity']) && isset($subActions[$subAction]['activities'][$_REQUEST['activity']])) + $activity = $_REQUEST['activity']; + + // Set a few things. + $context['page_title'] = $txt['maintain_title']; + $context['sub_action'] = $subAction; + $context['sub_template'] = !empty($subActions[$subAction]['template']) ? $subActions[$subAction]['template'] : ''; + + // Finally fall through to what we are doing. + $subActions[$subAction]['function'](); + + // Any special activity? + if (isset($activity)) + $subActions[$subAction]['activities'][$activity](); + + //converted to UTF-8? show a small maintenance info + if (isset($_GET['done']) && $_GET['done'] == 'convertutf8') + $context['maintenance_finished'] = $txt['utf8_title']; +} + +// Supporting function for the database maintenance area. +function MaintainDatabase() +{ + global $context, $db_type, $db_character_set, $modSettings, $smcFunc, $txt; + + // Show some conversion options? + $context['convert_utf8'] = $db_type == 'mysql' && (!isset($db_character_set) || $db_character_set !== 'utf8' || empty($modSettings['global_character_set']) || $modSettings['global_character_set'] !== 'UTF-8') && version_compare('4.1.2', preg_replace('~\-.+?$~', '', $smcFunc['db_server_info']())) <= 0; + $context['convert_entities'] = $db_type == 'mysql' && isset($db_character_set, $modSettings['global_character_set']) && $db_character_set === 'utf8' && $modSettings['global_character_set'] === 'UTF-8'; + + if (isset($_GET['done']) && $_GET['done'] == 'convertutf8') + $context['maintenance_finished'] = $txt['utf8_title']; + if (isset($_GET['done']) && $_GET['done'] == 'convertentities') + $context['maintenance_finished'] = $txt['entity_convert_title']; +} + +// Supporting function for the routine maintenance area. +function MaintainRoutine() +{ + global $context, $txt; + + if (isset($_GET['done']) && $_GET['done'] == 'recount') + $context['maintenance_finished'] = $txt['maintain_recount']; +} + +// Supporting function for the members maintenance area. +function MaintainMembers() +{ + global $context, $smcFunc, $txt; + + // Get membergroups - for deleting members and the like. + $result = $smcFunc['db_query']('', ' + SELECT id_group, group_name + FROM {db_prefix}membergroups', + array( + ) + ); + $context['membergroups'] = array( + array( + 'id' => 0, + 'name' => $txt['maintain_members_ungrouped'] + ), + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $context['membergroups'][] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'] + ); + } + $smcFunc['db_free_result']($result); +} + +// Supporting function for the topics maintenance area. +function MaintainTopics() +{ + global $context, $smcFunc, $txt; + + // Let's load up the boards in case they are useful. + $result = $smcFunc['db_query']('order_by_board_order', ' + SELECT b.id_board, b.name, b.child_level, c.name AS cat_name, c.id_cat + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE {query_see_board} + AND redirect = {string:blank_redirect}', + array( + 'blank_redirect' => '', + ) + ); + $context['categories'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (!isset($context['categories'][$row['id_cat']])) + $context['categories'][$row['id_cat']] = array( + 'name' => $row['cat_name'], + 'boards' => array() + ); + + $context['categories'][$row['id_cat']]['boards'][] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'child_level' => $row['child_level'] + ); + } + $smcFunc['db_free_result']($result); + + if (isset($_GET['done']) && $_GET['done'] == 'purgeold') + $context['maintenance_finished'] = $txt['maintain_old']; + elseif (isset($_GET['done']) && $_GET['done'] == 'massmove') + $context['maintenance_finished'] = $txt['move_topics_maintenance']; +} + +// Find and fix all errors. +function MaintainFindFixErrors() +{ + global $sourcedir; + + require_once($sourcedir . '/RepairBoards.php'); + RepairBoards(); +} + +// Wipes the whole cache directory. +function MaintainCleanCache() +{ + global $context, $txt; + + // Just wipe the whole cache directory! + clean_cache(); + + $context['maintenance_finished'] = $txt['maintain_cache']; +} + +// Empties all uninmportant logs +function MaintainEmptyUnimportantLogs() +{ + global $context, $smcFunc, $txt; + + checkSession(); + + // No one's online now.... MUHAHAHAHA :P. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_online'); + + // Dump the banning logs. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_banned'); + + // Start id_error back at 0 and dump the error log. + $smcFunc['db_query']('truncate_table', ' + TRUNCATE {db_prefix}log_errors'); + + // Clear out the spam log. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_floodcontrol'); + + // Clear out the karma actions. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_karma'); + + // Last but not least, the search logs! + $smcFunc['db_query']('truncate_table', ' + TRUNCATE {db_prefix}log_search_topics'); + + $smcFunc['db_query']('truncate_table', ' + TRUNCATE {db_prefix}log_search_messages'); + + $smcFunc['db_query']('truncate_table', ' + TRUNCATE {db_prefix}log_search_results'); + + updateSettings(array('search_pointer' => 0)); + + $context['maintenance_finished'] = $txt['maintain_logs']; +} + +// Oh noes! +function Destroy() +{ + global $context; + + echo ' + ', $context['forum_name_html_safe'], ' deleted! + +
Oh my, you killed ', $context['forum_name_html_safe'], '!
+
You lazy bum!
+ '; + obExit(false); +} + +// Convert both data and database tables to UTF-8 character set. +function ConvertUtf8() +{ + global $scripturl, $context, $txt, $language, $db_character_set; + global $modSettings, $user_info, $sourcedir, $smcFunc, $db_prefix; + + // Show me your badge! + isAllowedTo('admin_forum'); + + // The character sets used in SMF's language files with their db equivalent. + $charsets = array( + // Chinese-traditional. + 'big5' => 'big5', + // Chinese-simplified. + 'gbk' => 'gbk', + // West European. + 'ISO-8859-1' => 'latin1', + // Romanian. + 'ISO-8859-2' => 'latin2', + // Turkish. + 'ISO-8859-9' => 'latin5', + // West European with Euro sign. + 'ISO-8859-15' => 'latin9', + // Thai. + 'tis-620' => 'tis620', + // Persian, Chinese, etc. + 'UTF-8' => 'utf8', + // Russian. + 'windows-1251' => 'cp1251', + // Greek. + 'windows-1253' => 'utf8', + // Hebrew. + 'windows-1255' => 'utf8', + // Arabic. + 'windows-1256' => 'cp1256', + ); + + // Get a list of character sets supported by your MySQL server. + $request = $smcFunc['db_query']('', ' + SHOW CHARACTER SET', + array( + ) + ); + $db_charsets = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $db_charsets[] = $row['Charset']; + + $smcFunc['db_free_result']($request); + + // Character sets supported by both MySQL and SMF's language files. + $charsets = array_intersect($charsets, $db_charsets); + + // This is for the first screen telling backups is good. + if (!isset($_POST['proceed'])) + { + // Character set conversions are only supported as of MySQL 4.1.2. + if (version_compare('4.1.2', preg_replace('~\-.+?$~', '', $smcFunc['db_server_info']())) > 0) + fatal_lang_error('utf8_db_version_too_low'); + + // Use the messages.body column as indicator for the database charset. + $request = $smcFunc['db_query']('', ' + SHOW FULL COLUMNS + FROM {db_prefix}messages + LIKE {string:body_like}', + array( + 'body_like' => 'body', + ) + ); + $column_info = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // A collation looks like latin1_swedish. We only need the character set. + list($context['database_charset']) = explode('_', $column_info['Collation']); + $context['database_charset'] = in_array($context['database_charset'], $charsets) ? array_search($context['database_charset'], $charsets) : $context['database_charset']; + + // No need to convert to UTF-8 if it already is. + if ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8') + fatal_lang_error('utf8_already_utf8'); + + // Cannot do conversion if using a fulltext index + if (!empty($modSettings['search_index']) && $modSettings['search_index'] == 'fulltext') + fatal_lang_error('utf8_cannot_convert_fulltext'); + + // Grab the character set from the default language file. + loadLanguage('index', $language, true); + $context['charset_detected'] = $txt['lang_character_set']; + $context['charset_about_detected'] = sprintf($txt['utf8_detected_charset'], $language, $context['charset_detected']); + + // Go back to your own language. + loadLanguage('index', $user_info['language'], true); + + // Show a warning if the character set seems not to be supported. + if (!isset($charsets[strtr(strtolower($context['charset_detected']), array('utf' => 'UTF', 'iso' => 'ISO'))])) + { + $context['charset_warning'] = sprintf($txt['utf8_charset_not_supported'], $txt['lang_character_set']); + + // Default to ISO-8859-1. + $context['charset_detected'] = 'ISO-8859-1'; + } + + $context['charset_list'] = array_keys($charsets); + + $context['page_title'] = $txt['utf8_title']; + $context['sub_template'] = 'convert_utf8'; + return; + } + + // After this point we're starting the conversion. But first: session check. + checkSession(); + + // Translation table for the character sets not native for MySQL. + $translation_tables = array( + 'windows-1255' => array( + '0x81' => '\'\'', '0x8A' => '\'\'', '0x8C' => '\'\'', + '0x8D' => '\'\'', '0x8E' => '\'\'', '0x8F' => '\'\'', + '0x90' => '\'\'', '0x9A' => '\'\'', '0x9C' => '\'\'', + '0x9D' => '\'\'', '0x9E' => '\'\'', '0x9F' => '\'\'', + '0xCA' => '\'\'', '0xD9' => '\'\'', '0xDA' => '\'\'', + '0xDB' => '\'\'', '0xDC' => '\'\'', '0xDD' => '\'\'', + '0xDE' => '\'\'', '0xDF' => '\'\'', '0xFB' => '\'\'', + '0xFC' => '\'\'', '0xFF' => '\'\'', '0xC2' => '0xFF', + '0x80' => '0xFC', '0xE2' => '0xFB', '0xA0' => '0xC2A0', + '0xA1' => '0xC2A1', '0xA2' => '0xC2A2', '0xA3' => '0xC2A3', + '0xA5' => '0xC2A5', '0xA6' => '0xC2A6', '0xA7' => '0xC2A7', + '0xA8' => '0xC2A8', '0xA9' => '0xC2A9', '0xAB' => '0xC2AB', + '0xAC' => '0xC2AC', '0xAD' => '0xC2AD', '0xAE' => '0xC2AE', + '0xAF' => '0xC2AF', '0xB0' => '0xC2B0', '0xB1' => '0xC2B1', + '0xB2' => '0xC2B2', '0xB3' => '0xC2B3', '0xB4' => '0xC2B4', + '0xB5' => '0xC2B5', '0xB6' => '0xC2B6', '0xB7' => '0xC2B7', + '0xB8' => '0xC2B8', '0xB9' => '0xC2B9', '0xBB' => '0xC2BB', + '0xBC' => '0xC2BC', '0xBD' => '0xC2BD', '0xBE' => '0xC2BE', + '0xBF' => '0xC2BF', '0xD7' => '0xD7B3', '0xD1' => '0xD781', + '0xD4' => '0xD7B0', '0xD5' => '0xD7B1', '0xD6' => '0xD7B2', + '0xE0' => '0xD790', '0xEA' => '0xD79A', '0xEC' => '0xD79C', + '0xED' => '0xD79D', '0xEE' => '0xD79E', '0xEF' => '0xD79F', + '0xF0' => '0xD7A0', '0xF1' => '0xD7A1', '0xF2' => '0xD7A2', + '0xF3' => '0xD7A3', '0xF5' => '0xD7A5', '0xF6' => '0xD7A6', + '0xF7' => '0xD7A7', '0xF8' => '0xD7A8', '0xF9' => '0xD7A9', + '0x82' => '0xE2809A', '0x84' => '0xE2809E', '0x85' => '0xE280A6', + '0x86' => '0xE280A0', '0x87' => '0xE280A1', '0x89' => '0xE280B0', + '0x8B' => '0xE280B9', '0x93' => '0xE2809C', '0x94' => '0xE2809D', + '0x95' => '0xE280A2', '0x97' => '0xE28094', '0x99' => '0xE284A2', + '0xC0' => '0xD6B0', '0xC1' => '0xD6B1', '0xC3' => '0xD6B3', + '0xC4' => '0xD6B4', '0xC5' => '0xD6B5', '0xC6' => '0xD6B6', + '0xC7' => '0xD6B7', '0xC8' => '0xD6B8', '0xC9' => '0xD6B9', + '0xCB' => '0xD6BB', '0xCC' => '0xD6BC', '0xCD' => '0xD6BD', + '0xCE' => '0xD6BE', '0xCF' => '0xD6BF', '0xD0' => '0xD780', + '0xD2' => '0xD782', '0xE3' => '0xD793', '0xE4' => '0xD794', + '0xE5' => '0xD795', '0xE7' => '0xD797', '0xE9' => '0xD799', + '0xFD' => '0xE2808E', '0xFE' => '0xE2808F', '0x92' => '0xE28099', + '0x83' => '0xC692', '0xD3' => '0xD783', '0x88' => '0xCB86', + '0x98' => '0xCB9C', '0x91' => '0xE28098', '0x96' => '0xE28093', + '0xBA' => '0xC3B7', '0x9B' => '0xE280BA', '0xAA' => '0xC397', + '0xA4' => '0xE282AA', '0xE1' => '0xD791', '0xE6' => '0xD796', + '0xE8' => '0xD798', '0xEB' => '0xD79B', '0xF4' => '0xD7A4', + '0xFA' => '0xD7AA', '0xFF' => '0xD6B2', '0xFC' => '0xE282AC', + '0xFB' => '0xD792', + ), + 'windows-1253' => array( + '0x81' => '\'\'', '0x88' => '\'\'', '0x8A' => '\'\'', + '0x8C' => '\'\'', '0x8D' => '\'\'', '0x8E' => '\'\'', + '0x8F' => '\'\'', '0x90' => '\'\'', '0x98' => '\'\'', + '0x9A' => '\'\'', '0x9C' => '\'\'', '0x9D' => '\'\'', + '0x9E' => '\'\'', '0x9F' => '\'\'', '0xAA' => '\'\'', + '0xD2' => '\'\'', '0xFF' => '\'\'', '0xCE' => '0xCE9E', + '0xB8' => '0xCE88', '0xBA' => '0xCE8A', '0xBC' => '0xCE8C', + '0xBE' => '0xCE8E', '0xBF' => '0xCE8F', '0xC0' => '0xCE90', + '0xC8' => '0xCE98', '0xCA' => '0xCE9A', '0xCC' => '0xCE9C', + '0xCD' => '0xCE9D', '0xCF' => '0xCE9F', '0xDA' => '0xCEAA', + '0xE8' => '0xCEB8', '0xEA' => '0xCEBA', '0xEC' => '0xCEBC', + '0xEE' => '0xCEBE', '0xEF' => '0xCEBF', '0xC2' => '0xFF', + '0xBD' => '0xC2BD', '0xED' => '0xCEBD', '0xB2' => '0xC2B2', + '0xA0' => '0xC2A0', '0xA3' => '0xC2A3', '0xA4' => '0xC2A4', + '0xA5' => '0xC2A5', '0xA6' => '0xC2A6', '0xA7' => '0xC2A7', + '0xA8' => '0xC2A8', '0xA9' => '0xC2A9', '0xAB' => '0xC2AB', + '0xAC' => '0xC2AC', '0xAD' => '0xC2AD', '0xAE' => '0xC2AE', + '0xB0' => '0xC2B0', '0xB1' => '0xC2B1', '0xB3' => '0xC2B3', + '0xB5' => '0xC2B5', '0xB6' => '0xC2B6', '0xB7' => '0xC2B7', + '0xBB' => '0xC2BB', '0xE2' => '0xCEB2', '0x80' => '0xD2', + '0x82' => '0xE2809A', '0x84' => '0xE2809E', '0x85' => '0xE280A6', + '0x86' => '0xE280A0', '0xA1' => '0xCE85', '0xA2' => '0xCE86', + '0x87' => '0xE280A1', '0x89' => '0xE280B0', '0xB9' => '0xCE89', + '0x8B' => '0xE280B9', '0x91' => '0xE28098', '0x99' => '0xE284A2', + '0x92' => '0xE28099', '0x93' => '0xE2809C', '0x94' => '0xE2809D', + '0x95' => '0xE280A2', '0x96' => '0xE28093', '0x97' => '0xE28094', + '0x9B' => '0xE280BA', '0xAF' => '0xE28095', '0xB4' => '0xCE84', + '0xC1' => '0xCE91', '0xC3' => '0xCE93', '0xC4' => '0xCE94', + '0xC5' => '0xCE95', '0xC6' => '0xCE96', '0x83' => '0xC692', + '0xC7' => '0xCE97', '0xC9' => '0xCE99', '0xCB' => '0xCE9B', + '0xD0' => '0xCEA0', '0xD1' => '0xCEA1', '0xD3' => '0xCEA3', + '0xD4' => '0xCEA4', '0xD5' => '0xCEA5', '0xD6' => '0xCEA6', + '0xD7' => '0xCEA7', '0xD8' => '0xCEA8', '0xD9' => '0xCEA9', + '0xDB' => '0xCEAB', '0xDC' => '0xCEAC', '0xDD' => '0xCEAD', + '0xDE' => '0xCEAE', '0xDF' => '0xCEAF', '0xE0' => '0xCEB0', + '0xE1' => '0xCEB1', '0xE3' => '0xCEB3', '0xE4' => '0xCEB4', + '0xE5' => '0xCEB5', '0xE6' => '0xCEB6', '0xE7' => '0xCEB7', + '0xE9' => '0xCEB9', '0xEB' => '0xCEBB', '0xF0' => '0xCF80', + '0xF1' => '0xCF81', '0xF2' => '0xCF82', '0xF3' => '0xCF83', + '0xF4' => '0xCF84', '0xF5' => '0xCF85', '0xF6' => '0xCF86', + '0xF7' => '0xCF87', '0xF8' => '0xCF88', '0xF9' => '0xCF89', + '0xFA' => '0xCF8A', '0xFB' => '0xCF8B', '0xFC' => '0xCF8C', + '0xFD' => '0xCF8D', '0xFE' => '0xCF8E', '0xFF' => '0xCE92', + '0xD2' => '0xE282AC', + ), + ); + + // Make some preparations. + if (isset($translation_tables[$_POST['src_charset']])) + { + $replace = '%field%'; + foreach ($translation_tables[$_POST['src_charset']] as $from => $to) + $replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')'; + } + + // Grab a list of tables. + if (preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) === 1) + $queryTables = $smcFunc['db_query']('', ' + SHOW TABLE STATUS + FROM `' . strtr($match[1], array('`' => '')) . '` + LIKE {string:table_name}', + array( + 'table_name' => str_replace('_', '\_', $match[2]) . '%', + ) + ); + else + $queryTables = $smcFunc['db_query']('', ' + SHOW TABLE STATUS + LIKE {string:table_name}', + array( + 'table_name' => str_replace('_', '\_', $db_prefix) . '%', + ) + ); + + while ($table_info = $smcFunc['db_fetch_assoc']($queryTables)) + { + // Just to make sure it doesn't time out. + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + $table_charsets = array(); + + // Loop through each column. + $queryColumns = $smcFunc['db_query']('', ' + SHOW FULL COLUMNS + FROM ' . $table_info['Name'], + array( + ) + ); + while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns)) + { + // Only text'ish columns have a character set and need converting. + if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false) + { + $collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation']; + if (!empty($collation) && $collation !== 'NULL') + { + list($charset) = explode('_', $collation); + + if (!isset($table_charsets[$charset])) + $table_charsets[$charset] = array(); + + $table_charsets[$charset][] = $column_info; + } + } + } + $smcFunc['db_free_result']($queryColumns); + + // Only change the column if the data doesn't match the current charset. + if ((count($table_charsets) === 1 && key($table_charsets) !== $charsets[$_POST['src_charset']]) || count($table_charsets) > 1) + { + $updates_blob = ''; + $updates_text = ''; + foreach ($table_charsets as $charset => $columns) + { + if ($charset !== $charsets[$_POST['src_charset']]) + { + foreach ($columns as $column) + { + $updates_blob .= ' + CHANGE COLUMN ' . $column['Field'] . ' ' . $column['Field'] . ' ' . strtr($column['Type'], array('text' => 'blob', 'char' => 'binary')) . ($column['Null'] === 'YES' ? ' NULL' : ' NOT NULL') . (strpos($column['Type'], 'char') === false ? '' : ' default \'' . $column['Default'] . '\'') . ','; + $updates_text .= ' + CHANGE COLUMN ' . $column['Field'] . ' ' . $column['Field'] . ' ' . $column['Type'] . ' CHARACTER SET ' . $charsets[$_POST['src_charset']] . ($column['Null'] === 'YES' ? '' : ' NOT NULL') . (strpos($column['Type'], 'char') === false ? '' : ' default \'' . $column['Default'] . '\'') . ','; + } + } + } + + // Change the columns to binary form. + $smcFunc['db_query']('', ' + ALTER TABLE {raw:table_name}{raw:updates_blob}', + array( + 'table_name' => $table_info['Name'], + 'updates_blob' => substr($updates_blob, 0, -1), + ) + ); + + // Convert the character set if MySQL has no native support for it. + if (isset($translation_tables[$_POST['src_charset']])) + { + $update = ''; + foreach ($table_charsets as $charset => $columns) + foreach ($columns as $column) + $update .= ' + ' . $column['Field'] . ' = ' . strtr($replace, array('%field%' => $column['Field'])) . ','; + + $smcFunc['db_query']('', ' + UPDATE {raw:table_name} + SET {raw:updates}', + array( + 'table_name' => $table_info['Name'], + 'updates' => substr($update, 0, -1), + ) + ); + } + + // Change the columns back, but with the proper character set. + $smcFunc['db_query']('', ' + ALTER TABLE {raw:table_name}{raw:updates_text}', + array( + 'table_name' => $table_info['Name'], + 'updates_text' => substr($updates_text, 0, -1), + ) + ); + } + + // Now do the actual conversion (if still needed). + if ($charsets[$_POST['src_charset']] !== 'utf8') + $smcFunc['db_query']('', ' + ALTER TABLE {raw:table_name} + CONVERT TO CHARACTER SET utf8', + array( + 'table_name' => $table_info['Name'], + ) + ); + } + $smcFunc['db_free_result']($queryTables); + + // Let the settings know we have a new character set. + updateSettings(array('global_character_set' => 'UTF-8', 'previousCharacterSet' => (empty($translation_tables[$_POST['src_charset']])) ? $charsets[$_POST['src_charset']] : $translation_tables[$_POST['src_charset']])); + + // Store it in Settings.php too because it's needed before db connection. + require_once($sourcedir . '/Subs-Admin.php'); + updateSettingsFile(array('db_character_set' => '\'utf8\'')); + + // The conversion might have messed up some serialized strings. Fix them! + require_once($sourcedir . '/Subs-Charset.php'); + fix_serialized_columns(); + + redirectexit('action=admin;area=maintain;done=convertutf8'); +} + +// Convert HTML-entities to their UTF-8 character equivalents. +function ConvertEntities() +{ + global $db_character_set, $modSettings, $context, $sourcedir, $smcFunc; + + isAllowedTo('admin_forum'); + + // Check to see if UTF-8 is currently the default character set. + if ($modSettings['global_character_set'] !== 'UTF-8' || !isset($db_character_set) || $db_character_set !== 'utf8') + fatal_lang_error('entity_convert_only_utf8'); + + // Some starting values. + $context['table'] = empty($_REQUEST['table']) ? 0 : (int) $_REQUEST['table']; + $context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start']; + + $context['start_time'] = time(); + + $context['first_step'] = !isset($_REQUEST[$context['session_var']]); + $context['last_step'] = false; + + // The first step is just a text screen with some explanation. + if ($context['first_step']) + { + $context['sub_template'] = 'convert_entities'; + return; + } + // Otherwise use the generic "not done" template. + $context['sub_template'] = 'not_done'; + $context['continue_post_data'] = ''; + $context['continue_countdown'] = 3; + + // Now we're actually going to convert... + checkSession('request'); + + // A list of tables ready for conversion. + $tables = array( + 'ban_groups', + 'ban_items', + 'boards', + 'calendar', + 'calendar_holidays', + 'categories', + 'log_errors', + 'log_search_subjects', + 'membergroups', + 'members', + 'message_icons', + 'messages', + 'package_servers', + 'personal_messages', + 'pm_recipients', + 'polls', + 'poll_choices', + 'smileys', + 'themes', + ); + $context['num_tables'] = count($tables); + + // This function will do the conversion later on. + $entity_replace = create_function('$string', ' + $num = substr($string, 0, 1) === \'x\' ? hexdec(substr($string, 1)) : (int) $string; + return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) ? \'\' : ($num < 0x80 ? \'&#\' . $num . \';\' : ($num < 0x800 ? chr(192 | $num >> 6) . chr(128 | $num & 63) : ($num < 0x10000 ? chr(224 | $num >> 12) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63) : chr(240 | $num >> 18) . chr(128 | $num >> 12 & 63) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63))));'); + + // Loop through all tables that need converting. + for (; $context['table'] < $context['num_tables']; $context['table']++) + { + $cur_table = $tables[$context['table']]; + $primary_key = ''; + // Make sure we keep stuff unique! + $primary_keys = array(); + + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + // Get a list of text columns. + $columns = array(); + $request = $smcFunc['db_query']('', ' + SHOW FULL COLUMNS + FROM {db_prefix}' . $cur_table, + array( + ) + ); + while ($column_info = $smcFunc['db_fetch_assoc']($request)) + if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false) + $columns[] = strtolower($column_info['Field']); + + // Get the column with the (first) primary key. + $request = $smcFunc['db_query']('', ' + SHOW KEYS + FROM {db_prefix}' . $cur_table, + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['Key_name'] === 'PRIMARY') + { + if (empty($primary_key) || ($row['Seq_in_index'] == 1 && !in_array(strtolower($row['Column_name']), $columns))) + $primary_key = $row['Column_name']; + + $primary_keys[] = $row['Column_name']; + } + } + $smcFunc['db_free_result']($request); + + // No primary key, no glory. + // Same for columns. Just to be sure we've work to do! + if (empty($primary_key) || empty($columns)) + continue; + + // Get the maximum value for the primary key. + $request = $smcFunc['db_query']('', ' + SELECT MAX(' . $primary_key . ') + FROM {db_prefix}' . $cur_table, + array( + ) + ); + list($max_value) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (empty($max_value)) + continue; + + while ($context['start'] <= $max_value) + { + // Retrieve a list of rows that has at least one entity to convert. + $request = $smcFunc['db_query']('', ' + SELECT {raw:primary_keys}, {raw:columns} + FROM {db_prefix}{raw:cur_table} + WHERE {raw:primary_key} BETWEEN {int:start} AND {int:start} + 499 + AND {raw:like_compare} + LIMIT 500', + array( + 'primary_keys' => implode(', ', $primary_keys), + 'columns' => implode(', ', $columns), + 'cur_table' => $cur_table, + 'primary_key' => $primary_key, + 'start' => $context['start'], + 'like_compare' => '(' . implode(' LIKE \'%&#%\' OR ', $columns) . ' LIKE \'%&#%\')', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $insertion_variables = array(); + $changes = array(); + foreach ($row as $column_name => $column_value) + if ($column_name !== $primary_key && strpos($column_value, '&#') !== false) + { + $changes[] = $column_name . ' = {string:changes_' . $column_name . '}'; + $insertion_variables['changes_' . $column_name] = preg_replace('~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~e', '$entity_replace(\'\\2\')', $column_value); + } + + $where = array(); + foreach ($primary_keys as $key) + { + $where[] = $key . ' = {string:where_' . $key . '}'; + $insertion_variables['where_' . $key] = $row[$key]; + } + + // Update the row. + if (!empty($changes)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}' . $cur_table . ' + SET + ' . implode(', + ', $changes) . ' + WHERE ' . implode(' AND ', $where), + $insertion_variables + ); + } + $smcFunc['db_free_result']($request); + $context['start'] += 500; + + // After ten seconds interrupt. + if (time() - $context['start_time'] > 10) + { + // Calculate an approximation of the percentage done. + $context['continue_percent'] = round(100 * ($context['table'] + ($context['start'] / $max_value)) / $context['num_tables'], 1); + $context['continue_get_data'] = '?action=admin;area=maintain;sa=database;activity=convertentities;table=' . $context['table'] . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; + return; + } + } + $context['start'] = 0; + } + + // Make sure all serialized strings are all right. + require_once($sourcedir . '/Subs-Charset.php'); + fix_serialized_columns(); + + // If we're here, we must be done. + $context['continue_percent'] = 100; + $context['continue_get_data'] = '?action=admin;area=maintain;sa=database;done=convertentities'; + $context['last_step'] = true; + $context['continue_countdown'] = -1; +} + +// Optimize the database's tables. +function OptimizeTables() +{ + global $db_type, $db_name, $db_prefix, $txt, $context, $scripturl, $sourcedir, $smcFunc; + + isAllowedTo('admin_forum'); + + checkSession('post'); + + ignore_user_abort(true); + db_extend(); + + // Start with no tables optimized. + $opttab = 0; + + $context['page_title'] = $txt['database_optimize']; + $context['sub_template'] = 'optimize'; + + // Only optimize the tables related to this smf install, not all the tables in the db + $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; + + // Get a list of tables, as well as how many there are. + $temp_tables = $smcFunc['db_list_tables'](false, $real_prefix . '%'); + $tables = array(); + foreach ($temp_tables as $table) + $tables[] = array('table_name' => $table); + + // If there aren't any tables then I believe that would mean the world has exploded... + $context['num_tables'] = count($tables); + if ($context['num_tables'] == 0) + fatal_error('You appear to be running SMF in a flat file mode... fantastic!', false); + + // For each table.... + $context['optimized_tables'] = array(); + foreach ($tables as $table) + { + // Optimize the table! We use backticks here because it might be a custom table. + $data_freed = $smcFunc['db_optimize_table']($table['table_name']); + + // Optimizing one sqlite table optimizes them all. + if ($db_type == 'sqlite') + break; + + if ($data_freed > 0) + $context['optimized_tables'][] = array( + 'name' => $table['table_name'], + 'data_freed' => $data_freed, + ); + } + + // Number of tables, etc.... + $txt['database_numb_tables'] = sprintf($txt['database_numb_tables'], $context['num_tables']); + $context['num_tables_optimized'] = count($context['optimized_tables']); + + // Check that we don't auto optimise again too soon! + require_once($sourcedir . '/ScheduledTasks.php'); + CalculateNextTrigger('auto_optimize', true); +} + +// Recount all the important board totals. +function AdminBoardRecount() +{ + global $txt, $context, $scripturl, $modSettings, $sourcedir; + global $time_start, $smcFunc; + + isAllowedTo('admin_forum'); + + checkSession('request'); + + $context['page_title'] = $txt['not_done_title']; + $context['continue_post_data'] = ''; + $context['continue_countdown'] = '3'; + $context['sub_template'] = 'not_done'; + + // Try for as much time as possible. + @set_time_limit(600); + + // Step the number of topics at a time so things don't time out... + $request = $smcFunc['db_query']('', ' + SELECT MAX(id_topic) + FROM {db_prefix}topics', + array( + ) + ); + list ($max_topics) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $increment = min(max(50, ceil($max_topics / 4)), 2000); + if (empty($_REQUEST['start'])) + $_REQUEST['start'] = 0; + + $total_steps = 8; + + // Get each topic with a wrong reply count and fix it - let's just do some at a time, though. + if (empty($_REQUEST['step'])) + { + $_REQUEST['step'] = 0; + + while ($_REQUEST['start'] < $max_topics) + { + // Recount approved messages + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ t.id_topic, MAX(t.num_replies) AS num_replies, + CASE WHEN COUNT(ma.id_msg) >= 1 THEN COUNT(ma.id_msg) - 1 ELSE 0 END AS real_num_replies + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = {int:is_approved}) + WHERE t.id_topic > {int:start} + AND t.id_topic <= {int:max_id} + GROUP BY t.id_topic + HAVING CASE WHEN COUNT(ma.id_msg) >= 1 THEN COUNT(ma.id_msg) - 1 ELSE 0 END != MAX(t.num_replies)', + array( + 'is_approved' => 1, + 'start' => $_REQUEST['start'], + 'max_id' => $_REQUEST['start'] + $increment, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET num_replies = {int:num_replies} + WHERE id_topic = {int:id_topic}', + array( + 'num_replies' => $row['real_num_replies'], + 'id_topic' => $row['id_topic'], + ) + ); + $smcFunc['db_free_result']($request); + + // Recount unapproved messages + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ t.id_topic, MAX(t.unapproved_posts) AS unapproved_posts, + COUNT(mu.id_msg) AS real_unapproved_posts + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = {int:not_approved}) + WHERE t.id_topic > {int:start} + AND t.id_topic <= {int:max_id} + GROUP BY t.id_topic + HAVING COUNT(mu.id_msg) != MAX(t.unapproved_posts)', + array( + 'not_approved' => 0, + 'start' => $_REQUEST['start'], + 'max_id' => $_REQUEST['start'] + $increment, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET unapproved_posts = {int:unapproved_posts} + WHERE id_topic = {int:id_topic}', + array( + 'unapproved_posts' => $row['real_unapproved_posts'], + 'id_topic' => $row['id_topic'], + ) + ); + $smcFunc['db_free_result']($request); + + $_REQUEST['start'] += $increment; + + if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) + { + $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=0;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['continue_percent'] = round((100 * $_REQUEST['start'] / $max_topics) / $total_steps); + + return; + } + } + + $_REQUEST['start'] = 0; + } + + // Update the post count of each board. + if ($_REQUEST['step'] <= 1) + { + if (empty($_REQUEST['start'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET num_posts = {int:num_posts} + WHERE redirect = {string:redirect}', + array( + 'num_posts' => 0, + 'redirect' => '', + ) + ); + + while ($_REQUEST['start'] < $max_topics) + { + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ m.id_board, COUNT(*) AS real_num_posts + FROM {db_prefix}messages AS m + WHERE m.id_topic > {int:id_topic_min} + AND m.id_topic <= {int:id_topic_max} + AND m.approved = {int:is_approved} + GROUP BY m.id_board', + array( + 'id_topic_min' => $_REQUEST['start'], + 'id_topic_max' => $_REQUEST['start'] + $increment, + 'is_approved' => 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET num_posts = num_posts + {int:real_num_posts} + WHERE id_board = {int:id_board}', + array( + 'id_board' => $row['id_board'], + 'real_num_posts' => $row['real_num_posts'], + ) + ); + $smcFunc['db_free_result']($request); + + $_REQUEST['start'] += $increment; + + if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) + { + $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=1;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['continue_percent'] = round((200 + 100 * $_REQUEST['start'] / $max_topics) / $total_steps); + + return; + } + } + + $_REQUEST['start'] = 0; + } + + // Update the topic count of each board. + if ($_REQUEST['step'] <= 2) + { + if (empty($_REQUEST['start'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET num_topics = {int:num_topics}', + array( + 'num_topics' => 0, + ) + ); + + while ($_REQUEST['start'] < $max_topics) + { + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ t.id_board, COUNT(*) AS real_num_topics + FROM {db_prefix}topics AS t + WHERE t.approved = {int:is_approved} + AND t.id_topic > {int:id_topic_min} + AND t.id_topic <= {int:id_topic_max} + GROUP BY t.id_board', + array( + 'is_approved' => 1, + 'id_topic_min' => $_REQUEST['start'], + 'id_topic_max' => $_REQUEST['start'] + $increment, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET num_topics = num_topics + {int:real_num_topics} + WHERE id_board = {int:id_board}', + array( + 'id_board' => $row['id_board'], + 'real_num_topics' => $row['real_num_topics'], + ) + ); + $smcFunc['db_free_result']($request); + + $_REQUEST['start'] += $increment; + + if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) + { + $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=2;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['continue_percent'] = round((300 + 100 * $_REQUEST['start'] / $max_topics) / $total_steps); + + return; + } + } + + $_REQUEST['start'] = 0; + } + + // Update the unapproved post count of each board. + if ($_REQUEST['step'] <= 3) + { + if (empty($_REQUEST['start'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET unapproved_posts = {int:unapproved_posts}', + array( + 'unapproved_posts' => 0, + ) + ); + + while ($_REQUEST['start'] < $max_topics) + { + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ m.id_board, COUNT(*) AS real_unapproved_posts + FROM {db_prefix}messages AS m + WHERE m.id_topic > {int:id_topic_min} + AND m.id_topic <= {int:id_topic_max} + AND m.approved = {int:is_approved} + GROUP BY m.id_board', + array( + 'id_topic_min' => $_REQUEST['start'], + 'id_topic_max' => $_REQUEST['start'] + $increment, + 'is_approved' => 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET unapproved_posts = unapproved_posts + {int:unapproved_posts} + WHERE id_board = {int:id_board}', + array( + 'id_board' => $row['id_board'], + 'unapproved_posts' => $row['real_unapproved_posts'], + ) + ); + $smcFunc['db_free_result']($request); + + $_REQUEST['start'] += $increment; + + if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) + { + $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=3;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['continue_percent'] = round((400 + 100 * $_REQUEST['start'] / $max_topics) / $total_steps); + + return; + } + } + + $_REQUEST['start'] = 0; + } + + // Update the unapproved topic count of each board. + if ($_REQUEST['step'] <= 4) + { + if (empty($_REQUEST['start'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET unapproved_topics = {int:unapproved_topics}', + array( + 'unapproved_topics' => 0, + ) + ); + + while ($_REQUEST['start'] < $max_topics) + { + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ t.id_board, COUNT(*) AS real_unapproved_topics + FROM {db_prefix}topics AS t + WHERE t.approved = {int:is_approved} + AND t.id_topic > {int:id_topic_min} + AND t.id_topic <= {int:id_topic_max} + GROUP BY t.id_board', + array( + 'is_approved' => 0, + 'id_topic_min' => $_REQUEST['start'], + 'id_topic_max' => $_REQUEST['start'] + $increment, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET unapproved_topics = unapproved_topics + {int:real_unapproved_topics} + WHERE id_board = {int:id_board}', + array( + 'id_board' => $row['id_board'], + 'real_unapproved_topics' => $row['real_unapproved_topics'], + ) + ); + $smcFunc['db_free_result']($request); + + $_REQUEST['start'] += $increment; + + if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) + { + $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=4;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['continue_percent'] = round((500 + 100 * $_REQUEST['start'] / $max_topics) / $total_steps); + + return; + } + } + + $_REQUEST['start'] = 0; + } + + // Get all members with wrong number of personal messages. + if ($_REQUEST['step'] <= 5) + { + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ mem.id_member, COUNT(pmr.id_pm) AS real_num, + MAX(mem.instant_messages) AS instant_messages + FROM {db_prefix}members AS mem + LEFT JOIN {db_prefix}pm_recipients AS pmr ON (mem.id_member = pmr.id_member AND pmr.deleted = {int:is_not_deleted}) + GROUP BY mem.id_member + HAVING COUNT(pmr.id_pm) != MAX(mem.instant_messages)', + array( + 'is_not_deleted' => 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + updateMemberData($row['id_member'], array('instant_messages' => $row['real_num'])); + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ mem.id_member, COUNT(pmr.id_pm) AS real_num, + MAX(mem.unread_messages) AS unread_messages + FROM {db_prefix}members AS mem + LEFT JOIN {db_prefix}pm_recipients AS pmr ON (mem.id_member = pmr.id_member AND pmr.deleted = {int:is_not_deleted} AND pmr.is_read = {int:is_not_read}) + GROUP BY mem.id_member + HAVING COUNT(pmr.id_pm) != MAX(mem.unread_messages)', + array( + 'is_not_deleted' => 0, + 'is_not_read' => 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + updateMemberData($row['id_member'], array('unread_messages' => $row['real_num'])); + $smcFunc['db_free_result']($request); + + if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) + { + $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=6;start=0;' . $context['session_var'] . '=' . $context['session_id']; + $context['continue_percent'] = round(700 / $total_steps); + + return; + } + } + + // Any messages pointing to the wrong board? + if ($_REQUEST['step'] <= 6) + { + while ($_REQUEST['start'] < $modSettings['maxMsgID']) + { + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ t.id_board, m.id_msg + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic AND t.id_board != m.id_board) + WHERE m.id_msg > {int:id_msg_min} + AND m.id_msg <= {int:id_msg_max}', + array( + 'id_msg_min' => $_REQUEST['start'], + 'id_msg_max' => $_REQUEST['start'] + $increment, + ) + ); + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards[$row['id_board']][] = $row['id_msg']; + $smcFunc['db_free_result']($request); + + foreach ($boards as $board_id => $messages) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET id_board = {int:id_board} + WHERE id_msg IN ({array_int:id_msg_array})', + array( + 'id_msg_array' => $messages, + 'id_board' => $board_id, + ) + ); + + $_REQUEST['start'] += $increment; + + if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) + { + $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=6;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['continue_percent'] = round((700 + 100 * $_REQUEST['start'] / $modSettings['maxMsgID']) / $total_steps); + + return; + } + } + + $_REQUEST['start'] = 0; + } + + // Update the latest message of each board. + $request = $smcFunc['db_query']('', ' + SELECT m.id_board, MAX(m.id_msg) AS local_last_msg + FROM {db_prefix}messages AS m + WHERE m.approved = {int:is_approved} + GROUP BY m.id_board', + array( + 'is_approved' => 1, + ) + ); + $realBoardCounts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $realBoardCounts[$row['id_board']] = $row['local_last_msg']; + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ id_board, id_parent, id_last_msg, child_level, id_msg_updated + FROM {db_prefix}boards', + array( + ) + ); + $resort_me = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $row['local_last_msg'] = isset($realBoardCounts[$row['id_board']]) ? $realBoardCounts[$row['id_board']] : 0; + $resort_me[$row['child_level']][] = $row; + } + $smcFunc['db_free_result']($request); + + krsort($resort_me); + + $lastModifiedMsg = array(); + foreach ($resort_me as $rows) + foreach ($rows as $row) + { + // The latest message is the latest of the current board and its children. + if (isset($lastModifiedMsg[$row['id_board']])) + $curLastModifiedMsg = max($row['local_last_msg'], $lastModifiedMsg[$row['id_board']]); + else + $curLastModifiedMsg = $row['local_last_msg']; + + // If what is and what should be the latest message differ, an update is necessary. + if ($row['local_last_msg'] != $row['id_last_msg'] || $curLastModifiedMsg != $row['id_msg_updated']) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET id_last_msg = {int:id_last_msg}, id_msg_updated = {int:id_msg_updated} + WHERE id_board = {int:id_board}', + array( + 'id_last_msg' => $row['local_last_msg'], + 'id_msg_updated' => $curLastModifiedMsg, + 'id_board' => $row['id_board'], + ) + ); + + // Parent boards inherit the latest modified message of their children. + if (isset($lastModifiedMsg[$row['id_parent']])) + $lastModifiedMsg[$row['id_parent']] = max($row['local_last_msg'], $lastModifiedMsg[$row['id_parent']]); + else + $lastModifiedMsg[$row['id_parent']] = $row['local_last_msg']; + } + + // Update all the basic statistics. + updateStats('member'); + updateStats('message'); + updateStats('topic'); + + // Finally, update the latest event times. + require_once($sourcedir . '/ScheduledTasks.php'); + CalculateNextTrigger(); + + redirectexit('action=admin;area=maintain;sa=routine;done=recount'); +} + +// Perform a detailed version check. A very good thing ;). +function VersionDetail() +{ + global $forum_version, $txt, $sourcedir, $context; + + isAllowedTo('admin_forum'); + + // Call the function that'll get all the version info we need. + require_once($sourcedir . '/Subs-Admin.php'); + $versionOptions = array( + 'include_ssi' => true, + 'include_subscriptions' => true, + 'sort_results' => true, + ); + $version_info = getFileVersions($versionOptions); + + // Add the new info to the template context. + $context += array( + 'file_versions' => $version_info['file_versions'], + 'default_template_versions' => $version_info['default_template_versions'], + 'template_versions' => $version_info['template_versions'], + 'default_language_versions' => $version_info['default_language_versions'], + 'default_known_languages' => array_keys($version_info['default_language_versions']), + ); + + // Make it easier to manage for the template. + $context['forum_version'] = $forum_version; + + $context['sub_template'] = 'view_versions'; + $context['page_title'] = $txt['admin_version_check']; +} + +// Removing old posts doesn't take much as we really pass through. +function MaintainReattributePosts() +{ + global $sourcedir, $context, $txt; + + checkSession(); + + // Find the member. + require_once($sourcedir . '/Subs-Auth.php'); + $members = findMembers($_POST['to']); + + if (empty($members)) + fatal_lang_error('reattribute_cannot_find_member'); + + $memID = array_shift($members); + $memID = $memID['id']; + + $email = $_POST['type'] == 'email' ? $_POST['from_email'] : ''; + $membername = $_POST['type'] == 'name' ? $_POST['from_name'] : ''; + + // Now call the reattribute function. + require_once($sourcedir . '/Subs-Members.php'); + reattributePosts($memID, $email, $membername, !empty($_POST['posts'])); + + $context['maintenance_finished'] = $txt['maintain_reattribute_posts']; +} + +// Handling function for the backup stuff. +function MaintainDownloadBackup() +{ + global $sourcedir; + + require_once($sourcedir . '/DumpDatabase.php'); + DumpDatabase2(); +} + +// Removing old members? +function MaintainPurgeInactiveMembers() +{ + global $sourcedir, $context, $smcFunc, $txt; + + $_POST['maxdays'] = empty($_POST['maxdays']) ? 0 : (int) $_POST['maxdays']; + if (!empty($_POST['groups']) && $_POST['maxdays'] > 0) + { + checkSession(); + + $groups = array(); + foreach ($_POST['groups'] as $id => $dummy) + $groups[] = (int) $id; + $time_limit = (time() - ($_POST['maxdays'] * 24 * 3600)); + $where_vars = array( + 'time_limit' => $time_limit, + ); + if ($_POST['del_type'] == 'activated') + { + $where = 'mem.date_registered < {int:time_limit} AND mem.is_activated = {int:is_activated}'; + $where_vars['is_activated'] = 0; + } + else + $where = 'mem.last_login < {int:time_limit}'; + + // Need to get *all* groups then work out which (if any) we avoid. + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name, min_posts + FROM {db_prefix}membergroups', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Avoid this one? + if (!in_array($row['id_group'], $groups)) + { + // Post group? + if ($row['min_posts'] != -1) + { + $where .= ' AND mem.id_post_group != {int:id_post_group_' . $row['id_group'] . '}'; + $where_vars['id_post_group_' . $row['id_group']] = $row['id_group']; + } + else + { + $where .= ' AND mem.id_group != {int:id_group_' . $row['id_group'] . '} AND FIND_IN_SET({int:id_group_' . $row['id_group'] . '}, mem.additional_groups) = 0'; + $where_vars['id_group_' . $row['id_group']] = $row['id_group']; + } + } + } + $smcFunc['db_free_result']($request); + + // If we have ungrouped unselected we need to avoid those guys. + if (!in_array(0, $groups)) + { + $where .= ' AND (mem.id_group != 0 OR mem.additional_groups != {string:blank_add_groups})'; + $where_vars['blank_add_groups'] = ''; + } + + // Select all the members we're about to murder/remove... + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member, IFNULL(m.id_member, 0) AS is_mod + FROM {db_prefix}members AS mem + LEFT JOIN {db_prefix}moderators AS m ON (m.id_member = mem.id_member) + WHERE ' . $where, + $where_vars + ); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!$row['is_mod'] || !in_array(3, $groups)) + $members[] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + + require_once($sourcedir . '/Subs-Members.php'); + deleteMembers($members); + } + + $context['maintenance_finished'] = $txt['maintain_members']; +} + +// Removing old posts doesn't take much as we really pass through. +function MaintainRemoveOldPosts() +{ + global $sourcedir, $context, $txt; + + // Actually do what we're told! + require_once($sourcedir . '/RemoveTopic.php'); + RemoveOldTopics2(); +} + +function MaintainMassMoveTopics() +{ + global $smcFunc, $sourcedir, $context, $txt; + + // Only admins. + isAllowedTo('admin_forum'); + + checkSession('request'); + + // Set up to the context. + $context['page_title'] = $txt['not_done_title']; + $context['continue_countdown'] = '3'; + $context['continue_post_data'] = ''; + $context['continue_get_data'] = ''; + $context['sub_template'] = 'not_done'; + $context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start']; + $context['start_time'] = time(); + + // First time we do this? + $id_board_from = isset($_POST['id_board_from']) ? (int) $_POST['id_board_from'] : (int) $_REQUEST['id_board_from']; + $id_board_to = isset($_POST['id_board_to']) ? (int) $_POST['id_board_to'] : (int) $_REQUEST['id_board_to']; + + // No boards then this is your stop. + if (empty($id_board_from) || empty($id_board_to)) + return; + + // How many topics are we converting? + if (!isset($_REQUEST['totaltopics'])) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}topics + WHERE id_board = {int:id_board_from}', + array( + 'id_board_from' => $id_board_from, + ) + ); + list ($total_topics) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + $total_topics = (int) $_REQUEST['totaltopics']; + + // Seems like we need this here. + $context['continue_get_data'] = '?action=admin;area=maintain;sa=topics;activity=massmove;id_board_from=' . $id_board_from . ';id_board_to=' . $id_board_to . ';totaltopics=' . $total_topics . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; + + // We have topics to move so start the process. + if (!empty($total_topics)) + { + while ($context['start'] <= $total_topics) + { + // Lets get the topics. + $request = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}topics + WHERE id_board = {int:id_board_from} + LIMIT 10', + array( + 'id_board_from' => $id_board_from, + ) + ); + + // Get the ids. + $topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topics[] = $row['id_topic']; + + // Just return if we don't have any topics left to move. + if (empty($topics)) + { + cache_put_data('board-' . $id_board_from, null, 120); + cache_put_data('board-' . $id_board_to, null, 120); + redirectexit('action=admin;area=maintain;sa=topics;done=massmove'); + } + + // Lets move them. + require_once($sourcedir . '/MoveTopic.php'); + moveTopics($topics, $id_board_to); + + // We've done at least ten more topics. + $context['start'] += 10; + + // Lets wait a while. + if (time() - $context['start_time'] > 3) + { + // What's the percent? + $context['continue_percent'] = round(100 * ($context['start'] / $total_topics), 1); + $context['continue_get_data'] = '?action=admin;area=maintain;sa=topics;activity=massmove;id_board_from=' . $id_board_from . ';id_board_to=' . $id_board_to . ';totaltopics=' . $total_topics . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; + + // Let the template system do it's thang. + return; + } + } + } + + // Don't confuse admins by having an out of date cache. + cache_put_data('board-' . $id_board_from, null, 120); + cache_put_data('board-' . $id_board_to, null, 120); + + redirectexit('action=admin;area=maintain;sa=topics;done=massmove'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageMembergroups.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageMembergroups.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1077 @@ + array('AddMembergroup', 'manage_membergroups'), + 'delete' => array('DeleteMembergroup', 'manage_membergroups'), + 'edit' => array('EditMembergroup', 'manage_membergroups'), + 'index' => array('MembergroupIndex', 'manage_membergroups'), + 'members' => array('MembergroupMembers', 'manage_membergroups', 'Groups.php'), + 'settings' => array('ModifyMembergroupsettings', 'admin_forum'), + ); + + // Default to sub action 'index' or 'settings' depending on permissions. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (allowedTo('manage_membergroups') ? 'index' : 'settings'); + + // Is it elsewhere? + if (isset($subActions[$_REQUEST['sa']][2])) + require_once($sourcedir . '/' . $subActions[$_REQUEST['sa']][2]); + + // Do the permission check, you might not be allowed her. + isAllowedTo($subActions[$_REQUEST['sa']][1]); + + // Language and template stuff, the usual. + loadLanguage('ManageMembers'); + loadTemplate('ManageMembergroups'); + + // Setup the admin tabs. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['membergroups_title'], + 'help' => 'membergroups', + 'description' => $txt['membergroups_description'], + ); + + // Call the right function. + $subActions[$_REQUEST['sa']][0](); +} + +// An overview of the current membergroups. +function MembergroupIndex() +{ + global $txt, $scripturl, $context, $settings, $smcFunc, $sourcedir; + + $context['page_title'] = $txt['membergroups_title']; + + // The first list shows the regular membergroups. + $listOptions = array( + 'id' => 'regular_membergroups_list', + 'title' => $txt['membergroups_regular'], + 'base_href' => $scripturl . '?action=admin;area=membergroups' . (isset($_REQUEST['sort2']) ? ';sort2=' . urlencode($_REQUEST['sort2']) : ''), + 'default_sort_col' => 'name', + 'get_items' => array( + 'file' => $sourcedir . '/Subs-Membergroups.php', + 'function' => 'list_getMembergroups', + 'params' => array( + 'regular', + ), + ), + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['membergroups_name'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $scripturl; + + // Since the moderator group has no explicit members, no link is needed. + if ($rowData[\'id_group\'] == 3) + $group_name = $rowData[\'group_name\']; + else + { + $color_style = empty($rowData[\'online_color\']) ? \'\' : sprintf(\' style="color: %1$s;"\', $rowData[\'online_color\']); + $group_name = sprintf(\'%4$s\', $scripturl, $rowData[\'id_group\'], $color_style, $rowData[\'group_name\']); + } + + // Add a help option for moderator and administrator. + if ($rowData[\'id_group\'] == 1) + $group_name .= sprintf(\' (?)\', $scripturl); + elseif ($rowData[\'id_group\'] == 3) + $group_name .= sprintf(\' (?)\', $scripturl); + + return $group_name; + '), + ), + 'sort' => array( + 'default' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, group_name', + 'reverse' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, group_name DESC', + ), + ), + 'stars' => array( + 'header' => array( + 'value' => $txt['membergroups_stars'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $settings; + + $stars = explode(\'#\', $rowData[\'stars\']); + + // In case no stars are setup, return with nothing + if (empty($stars[0]) || empty($stars[1])) + return \'\'; + + // Otherwise repeat the image a given number of times. + else + { + $image = sprintf(\'*\', $settings[\'images_url\'], $stars[1]); + return str_repeat($image, $stars[0]); + } + '), + + ), + 'sort' => array( + 'default' => 'stars', + 'reverse' => 'stars DESC', + ) + ), + 'members' => array( + 'header' => array( + 'value' => $txt['membergroups_members_top'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + // No explicit members for the moderator group. + return $rowData[\'id_group\'] == 3 ? $txt[\'membergroups_guests_na\'] : $rowData[\'num_members\']; + '), + 'style' => 'text-align: center', + ), + 'sort' => array( + 'default' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, 1', + 'reverse' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, 1 DESC', + ), + ), + 'modify' => array( + 'header' => array( + 'value' => $txt['modify'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '' . $txt['membergroups_modify'] . '', + 'params' => array( + 'id_group' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '[' . $txt['membergroups_add_group'] . ']', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + // The second list shows the post count based groups. + $listOptions = array( + 'id' => 'post_count_membergroups_list', + 'title' => $txt['membergroups_post'], + 'base_href' => $scripturl . '?action=admin;area=membergroups' . (isset($_REQUEST['sort']) ? ';sort=' . urlencode($_REQUEST['sort']) : ''), + 'default_sort_col' => 'required_posts', + 'request_vars' => array( + 'sort' => 'sort2', + 'desc' => 'desc2', + ), + 'get_items' => array( + 'file' => $sourcedir . '/Subs-Membergroups.php', + 'function' => 'list_getMembergroups', + 'params' => array( + 'post_count', + ), + ), + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['membergroups_name'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $scripturl; + + $colorStyle = empty($rowData[\'online_color\']) ? \'\' : sprintf(\' style="color: %1$s;"\', $rowData[\'online_color\']); + return sprintf(\'%4$s\', $scripturl, $rowData[\'id_group\'], $colorStyle, $rowData[\'group_name\']); + '), + ), + 'sort' => array( + 'default' => 'group_name', + 'reverse' => 'group_name DESC', + ), + ), + 'stars' => array( + 'header' => array( + 'value' => $txt['membergroups_stars'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $settings; + + $stars = explode(\'#\', $rowData[\'stars\']); + + if (empty($stars[0]) || empty($stars[1])) + return \'\'; + else + { + $star_image = sprintf(\'*\', $settings[\'images_url\'], $stars[1]); + return str_repeat($star_image, $stars[0]); + } + '), + ), + 'sort' => array( + 'default' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, stars', + 'reverse' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, stars DESC', + ) + ), + 'members' => array( + 'header' => array( + 'value' => $txt['membergroups_members_top'], + ), + 'data' => array( + 'db' => 'num_members', + 'style' => 'text-align: center', + ), + 'sort' => array( + 'default' => '1 DESC', + 'reverse' => '1', + ), + ), + 'required_posts' => array( + 'header' => array( + 'value' => $txt['membergroups_min_posts'], + ), + 'data' => array( + 'db' => 'min_posts', + 'style' => 'text-align: center', + ), + 'sort' => array( + 'default' => 'min_posts', + 'reverse' => 'min_posts DESC', + ), + ), + 'modify' => array( + 'header' => array( + 'value' => $txt['modify'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '' . $txt['membergroups_modify'] . '', + 'params' => array( + 'id_group' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '[' . $txt['membergroups_add_group'] . ']', + ), + ), + ); + + createList($listOptions); +} + +// Add a membergroup. +function AddMembergroup() +{ + global $context, $txt, $sourcedir, $modSettings, $smcFunc; + + // A form was submitted, we can start adding. + if (!empty($_POST['group_name'])) + { + checkSession(); + + $postCountBasedGroup = isset($_POST['min_posts']) && (!isset($_POST['postgroup_based']) || !empty($_POST['postgroup_based'])); + $_POST['group_type'] = !isset($_POST['group_type']) || $_POST['group_type'] < 0 || $_POST['group_type'] > 3 || ($_POST['group_type'] == 1 && !allowedTo('admin_forum')) ? 0 : (int) $_POST['group_type']; + + // !!! Check for members with same name too? + + $request = $smcFunc['db_query']('', ' + SELECT MAX(id_group) + FROM {db_prefix}membergroups', + array( + ) + ); + list ($id_group) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + $id_group++; + + $smcFunc['db_insert']('', + '{db_prefix}membergroups', + array( + 'id_group' => 'int', 'description' => 'string', 'group_name' => 'string-80', 'min_posts' => 'int', + 'stars' => 'string', 'online_color' => 'string', 'group_type' => 'int', + ), + array( + $id_group, '', $_POST['group_name'], ($postCountBasedGroup ? (int) $_POST['min_posts'] : '-1'), + '1#star.gif', '', $_POST['group_type'], + ), + array('id_group') + ); + + // Update the post groups now, if this is a post group! + if (isset($_POST['min_posts'])) + updateStats('postgroups'); + + // You cannot set permissions for post groups if they are disabled. + if ($postCountBasedGroup && empty($modSettings['permission_enable_postgroups'])) + $_POST['perm_type'] = ''; + + if ($_POST['perm_type'] == 'predefined') + { + // Set default permission level. + require_once($sourcedir . '/ManagePermissions.php'); + setPermissionLevel($_POST['level'], $id_group, 'null'); + } + // Copy or inherit the permissions! + elseif ($_POST['perm_type'] == 'copy' || $_POST['perm_type'] == 'inherit') + { + $copy_id = $_POST['perm_type'] == 'copy' ? (int) $_POST['copyperm'] : (int) $_POST['inheritperm']; + + // Are you a powerful admin? + if (!allowedTo('admin_forum')) + { + $request = $smcFunc['db_query']('', ' + SELECT group_type + FROM {db_prefix}membergroups + WHERE id_group = {int:copy_from} + LIMIT {int:limit}', + array( + 'copy_from' => $copy_id, + 'limit' => 1, + ) + ); + list ($copy_type) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Protected groups are... well, protected! + if ($copy_type == 1) + fatal_lang_error('membergroup_does_not_exist'); + } + + // Don't allow copying of a real priviledged person! + require_once($sourcedir . '/ManagePermissions.php'); + loadIllegalPermissions(); + + $request = $smcFunc['db_query']('', ' + SELECT permission, add_deny + FROM {db_prefix}permissions + WHERE id_group = {int:copy_from}', + array( + 'copy_from' => $copy_id, + ) + ); + $inserts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($context['illegal_permissions']) || !in_array($row['permission'], $context['illegal_permissions'])) + $inserts[] = array($id_group, $row['permission'], $row['add_deny']); + } + $smcFunc['db_free_result']($request); + + if (!empty($inserts)) + $smcFunc['db_insert']('insert', + '{db_prefix}permissions', + array('id_group' => 'int', 'permission' => 'string', 'add_deny' => 'int'), + $inserts, + array('id_group', 'permission') + ); + + $request = $smcFunc['db_query']('', ' + SELECT id_profile, permission, add_deny + FROM {db_prefix}board_permissions + WHERE id_group = {int:copy_from}', + array( + 'copy_from' => $copy_id, + ) + ); + $inserts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $inserts[] = array($id_group, $row['id_profile'], $row['permission'], $row['add_deny']); + $smcFunc['db_free_result']($request); + + if (!empty($inserts)) + $smcFunc['db_insert']('insert', + '{db_prefix}board_permissions', + array('id_group' => 'int', 'id_profile' => 'int', 'permission' => 'string', 'add_deny' => 'int'), + $inserts, + array('id_group', 'id_profile', 'permission') + ); + + // Also get some membergroup information if we're copying and not copying from guests... + if ($copy_id > 0 && $_POST['perm_type'] == 'copy') + { + $request = $smcFunc['db_query']('', ' + SELECT online_color, max_messages, stars + FROM {db_prefix}membergroups + WHERE id_group = {int:copy_from} + LIMIT 1', + array( + 'copy_from' => $copy_id, + ) + ); + $group_info = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // ...and update the new membergroup with it. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}membergroups + SET + online_color = {string:online_color}, + max_messages = {int:max_messages}, + stars = {string:stars} + WHERE id_group = {int:current_group}', + array( + 'max_messages' => $group_info['max_messages'], + 'current_group' => $id_group, + 'online_color' => $group_info['online_color'], + 'stars' => $group_info['stars'], + ) + ); + } + // If inheriting say so... + elseif ($_POST['perm_type'] == 'inherit') + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}membergroups + SET id_parent = {int:copy_from} + WHERE id_group = {int:current_group}', + array( + 'copy_from' => $copy_id, + 'current_group' => $id_group, + ) + ); + } + } + + // Make sure all boards selected are stored in a proper array. + $_POST['boardaccess'] = empty($_POST['boardaccess']) || !is_array($_POST['boardaccess']) ? array() : $_POST['boardaccess']; + foreach ($_POST['boardaccess'] as $key => $value) + $_POST['boardaccess'][$key] = (int) $value; + + // Only do this if they have special access requirements. + if (!empty($_POST['boardaccess'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET member_groups = CASE WHEN member_groups = {string:blank_string} THEN {string:group_id_string} ELSE CONCAT(member_groups, {string:comma_group}) END + WHERE id_board IN ({array_int:board_list})', + array( + 'board_list' => $_POST['boardaccess'], + 'blank_string' => '', + 'group_id_string' => (string) $id_group, + 'comma_group' => ',' . $id_group, + ) + ); + + // If this is joinable then set it to show group membership in people's profiles. + if (empty($modSettings['show_group_membership']) && $_POST['group_type'] > 1) + updateSettings(array('show_group_membership' => 1)); + + // Rebuild the group cache. + updateSettings(array( + 'settings_updated' => time(), + )); + + // We did it. + logAction('add_group', array('group' => $_POST['group_name']), 'admin'); + + // Go change some more settings. + redirectexit('action=admin;area=membergroups;sa=edit;group=' . $id_group); + } + + // Just show the 'add membergroup' screen. + $context['page_title'] = $txt['membergroups_new_group']; + $context['sub_template'] = 'new_group'; + $context['post_group'] = isset($_REQUEST['postgroup']); + $context['undefined_group'] = !isset($_REQUEST['postgroup']) && !isset($_REQUEST['generalgroup']); + $context['allow_protected'] = allowedTo('admin_forum'); + + $result = $smcFunc['db_query']('', ' + SELECT id_group, group_name + FROM {db_prefix}membergroups + WHERE (id_group > {int:moderator_group} OR id_group = {int:global_mod_group})' . (empty($modSettings['permission_enable_postgroups']) ? ' + AND min_posts = {int:min_posts}' : '') . (allowedTo('admin_forum') ? '' : ' + AND group_type != {int:is_protected}') . ' + ORDER BY min_posts, id_group != {int:global_mod_group}, group_name', + array( + 'moderator_group' => 3, + 'global_mod_group' => 2, + 'min_posts' => -1, + 'is_protected' => 1, + ) + ); + $context['groups'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $context['groups'][] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'] + ); + $smcFunc['db_free_result']($result); + + $result = $smcFunc['db_query']('', ' + SELECT id_board, name, child_level + FROM {db_prefix}boards + ORDER BY board_order', + array( + ) + ); + $context['boards'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $context['boards'][] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'child_level' => $row['child_level'], + 'selected' => false + ); + $smcFunc['db_free_result']($result); +} + +// Deleting a membergroup by URL (not implemented). +function DeleteMembergroup() +{ + global $sourcedir; + + checkSession('get'); + + require_once($sourcedir . '/Subs-Membergroups.php'); + deleteMembergroups((int) $_REQUEST['group']); + + // Go back to the membergroup index. + redirectexit('action=admin;area=membergroups;'); +} + +// Editing a membergroup. +function EditMembergroup() +{ + global $context, $txt, $sourcedir, $modSettings, $smcFunc; + + $_REQUEST['group'] = isset($_REQUEST['group']) && $_REQUEST['group'] > 0 ? (int) $_REQUEST['group'] : 0; + + // Make sure this group is editable. + if (!empty($_REQUEST['group'])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}membergroups + WHERE id_group = {int:current_group}' . (allowedTo('admin_forum') ? '' : ' + AND group_type != {int:is_protected}') . ' + LIMIT {int:limit}', + array( + 'current_group' => $_REQUEST['group'], + 'is_protected' => 1, + 'limit' => 1, + ) + ); + list ($_REQUEST['group']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // Now, do we have a valid id? + if (empty($_REQUEST['group'])) + fatal_lang_error('membergroup_does_not_exist', false); + + // The delete this membergroup button was pressed. + if (isset($_POST['delete'])) + { + checkSession(); + + require_once($sourcedir . '/Subs-Membergroups.php'); + deleteMembergroups($_REQUEST['group']); + + redirectexit('action=admin;area=membergroups;'); + } + // A form was submitted with the new membergroup settings. + elseif (isset($_POST['submit'])) + { + // Validate the session. + checkSession(); + + // Can they really inherit from this group? + if ($_POST['group_inherit'] != -2 && !allowedTo('admin_forum')) + { + $request = $smcFunc['db_query']('', ' + SELECT group_type + FROM {db_prefix}membergroups + WHERE id_group = {int:inherit_from} + LIMIT {int:limit}', + array( + 'inherit_from' => $_POST['group_inherit'], + 'limit' => 1, + ) + ); + list ($inherit_type) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // Set variables to their proper value. + $_POST['max_messages'] = isset($_POST['max_messages']) ? (int) $_POST['max_messages'] : 0; + $_POST['min_posts'] = isset($_POST['min_posts']) && isset($_POST['group_type']) && $_POST['group_type'] == -1 && $_REQUEST['group'] > 3 ? abs($_POST['min_posts']) : ($_REQUEST['group'] == 4 ? 0 : -1); + $_POST['stars'] = (empty($_POST['star_count']) || $_POST['star_count'] < 0) ? '' : min((int) $_POST['star_count'], 99) . '#' . $_POST['star_image']; + $_POST['group_desc'] = isset($_POST['group_desc']) && ($_REQUEST['group'] == 1 || (isset($_POST['group_type']) && $_POST['group_type'] != -1)) ? trim($_POST['group_desc']) : ''; + $_POST['group_type'] = !isset($_POST['group_type']) || $_POST['group_type'] < 0 || $_POST['group_type'] > 3 || ($_POST['group_type'] == 1 && !allowedTo('admin_forum')) ? 0 : (int) $_POST['group_type']; + $_POST['group_hidden'] = empty($_POST['group_hidden']) || $_POST['min_posts'] != -1 || $_REQUEST['group'] == 3 ? 0 : (int) $_POST['group_hidden']; + $_POST['group_inherit'] = $_REQUEST['group'] > 1 && $_REQUEST['group'] != 3 && (empty($inherit_type) || $inherit_type != 1) ? (int) $_POST['group_inherit'] : -2; + + // !!! Don't set online_color for the Moderators group? + + // Do the update of the membergroup settings. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}membergroups + SET group_name = {string:group_name}, online_color = {string:online_color}, + max_messages = {int:max_messages}, min_posts = {int:min_posts}, stars = {string:stars}, + description = {string:group_desc}, group_type = {int:group_type}, hidden = {int:group_hidden}, + id_parent = {int:group_inherit} + WHERE id_group = {int:current_group}', + array( + 'max_messages' => $_POST['max_messages'], + 'min_posts' => $_POST['min_posts'], + 'group_type' => $_POST['group_type'], + 'group_hidden' => $_POST['group_hidden'], + 'group_inherit' => $_POST['group_inherit'], + 'current_group' => (int) $_REQUEST['group'], + 'group_name' => $_POST['group_name'], + 'online_color' => $_POST['online_color'], + 'stars' => $_POST['stars'], + 'group_desc' => $_POST['group_desc'], + ) + ); + + // Time to update the boards this membergroup has access to. + if ($_REQUEST['group'] == 2 || $_REQUEST['group'] > 3) + { + $_POST['boardaccess'] = empty($_POST['boardaccess']) || !is_array($_POST['boardaccess']) ? array() : $_POST['boardaccess']; + foreach ($_POST['boardaccess'] as $key => $value) + $_POST['boardaccess'][$key] = (int) $value; + + // Find all board this group is in, but shouldn't be in. + $request = $smcFunc['db_query']('', ' + SELECT id_board, member_groups + FROM {db_prefix}boards + WHERE FIND_IN_SET({string:current_group}, member_groups) != 0' . (empty($_POST['boardaccess']) ? '' : ' + AND id_board NOT IN ({array_int:board_access_list})'), + array( + 'current_group' => (int) $_REQUEST['group'], + 'board_access_list' => $_POST['boardaccess'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET member_groups = {string:member_group_access} + WHERE id_board = {int:current_board}', + array( + 'current_board' => $row['id_board'], + 'member_group_access' => implode(',', array_diff(explode(',', $row['member_groups']), array($_REQUEST['group']))), + ) + ); + $smcFunc['db_free_result']($request); + + // Add the membergroup to all boards that hadn't been set yet. + if (!empty($_POST['boardaccess'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET member_groups = CASE WHEN member_groups = {string:blank_string} THEN {string:group_id_string} ELSE CONCAT(member_groups, {string:comma_group}) END + WHERE id_board IN ({array_int:board_list}) + AND FIND_IN_SET({int:current_group}, member_groups) = 0', + array( + 'board_list' => $_POST['boardaccess'], + 'blank_string' => '', + 'current_group' => (int) $_REQUEST['group'], + 'group_id_string' => (string) (int) $_REQUEST['group'], + 'comma_group' => ',' . $_REQUEST['group'], + ) + ); + } + + // Remove everyone from this group! + if ($_POST['min_posts'] != -1) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_group = {int:regular_member} + WHERE id_group = {int:current_group}', + array( + 'regular_member' => 0, + 'current_group' => (int) $_REQUEST['group'], + ) + ); + + $request = $smcFunc['db_query']('', ' + SELECT id_member, additional_groups + FROM {db_prefix}members + WHERE FIND_IN_SET({string:current_group}, additional_groups) != 0', + array( + 'current_group' => (int) $_REQUEST['group'], + ) + ); + $updates = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $updates[$row['additional_groups']][] = $row['id_member']; + $smcFunc['db_free_result']($request); + + foreach ($updates as $additional_groups => $memberArray) + updateMemberData($memberArray, array('additional_groups' => implode(',', array_diff(explode(',', $additional_groups), array((int) $_REQUEST['group']))))); + } + elseif ($_REQUEST['group'] != 3) + { + // Making it a hidden group? If so remove everyone with it as primary group (Actually, just make them additional). + if ($_POST['group_hidden'] == 2) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, additional_groups + FROM {db_prefix}members + WHERE id_group = {int:current_group} + AND FIND_IN_SET({int:current_group}, additional_groups) = 0', + array( + 'current_group' => (int) $_REQUEST['group'], + ) + ); + $updates = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $updates[$row['additional_groups']][] = $row['id_member']; + $smcFunc['db_free_result']($request); + + foreach ($updates as $additional_groups => $memberArray) + updateMemberData($memberArray, array('additional_groups' => implode(',', array_merge(explode(',', $additional_groups), array((int) $_REQUEST['group']))))); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_group = {int:regular_member} + WHERE id_group = {int:current_group}', + array( + 'regular_member' => 0, + 'current_group' => $_REQUEST['group'], + ) + ); + } + + // Either way, let's check our "show group membership" setting is correct. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}membergroups + WHERE group_type > {int:non_joinable}', + array( + 'non_joinable' => 1, + ) + ); + list ($have_joinable) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Do we need to update the setting? + if ((empty($modSettings['show_group_membership']) && $have_joinable) || (!empty($modSettings['show_group_membership']) && !$have_joinable)) + updateSettings(array('show_group_membership' => $have_joinable ? 1 : 0)); + } + + // Do we need to set inherited permissions? + if ($_POST['group_inherit'] != -2 && $_POST['group_inherit'] != $_POST['old_inherit']) + { + require_once($sourcedir . '/ManagePermissions.php'); + updateChildPermissions($_POST['group_inherit']); + } + + // Finally, moderators! + $moderator_string = isset($_POST['group_moderators']) ? trim($_POST['group_moderators']) : ''; + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}group_moderators + WHERE id_group = {int:current_group}', + array( + 'current_group' => $_REQUEST['group'], + ) + ); + if ((!empty($moderator_string) || !empty($_POST['moderator_list'])) && $_POST['min_posts'] == -1 && $_REQUEST['group'] != 3) + { + // Get all the usernames from the string + if (!empty($moderator_string)) + { + $moderator_string = strtr(preg_replace('~&#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', htmlspecialchars($moderator_string), ENT_QUOTES), array('"' => '"')); + preg_match_all('~"([^"]+)"~', $moderator_string, $matches); + $moderators = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $moderator_string))); + for ($k = 0, $n = count($moderators); $k < $n; $k++) + { + $moderators[$k] = trim($moderators[$k]); + + if (strlen($moderators[$k]) == 0) + unset($moderators[$k]); + } + + // Find all the id_member's for the member_name's in the list. + $group_moderators = array(); + if (!empty($moderators)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE member_name IN ({array_string:moderators}) OR real_name IN ({array_string:moderators}) + LIMIT ' . count($moderators), + array( + 'moderators' => $moderators, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $group_moderators[] = $row['id_member']; + $smcFunc['db_free_result']($request); + } + } + else + { + $moderators = array(); + foreach ($_POST['moderator_list'] as $moderator) + $moderators[] = (int) $moderator; + + $group_moderators = array(); + if (!empty($moderators)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE id_member IN ({array_int:moderators}) + LIMIT {int:num_moderators}', + array( + 'moderators' => $moderators, + 'num_moderators' => count($moderators), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $group_moderators[] = $row['id_member']; + $smcFunc['db_free_result']($request); + } + } + + // Found some? + if (!empty($group_moderators)) + { + $mod_insert = array(); + foreach ($group_moderators as $moderator) + $mod_insert[] = array($_REQUEST['group'], $moderator); + + $smcFunc['db_insert']('insert', + '{db_prefix}group_moderators', + array('id_group' => 'int', 'id_member' => 'int'), + $mod_insert, + array('id_group', 'id_member') + ); + } + } + + // There might have been some post group changes. + updateStats('postgroups'); + // We've definetely changed some group stuff. + updateSettings(array( + 'settings_updated' => time(), + )); + + // Log the edit. + logAction('edited_group', array('group' => $_POST['group_name']), 'admin'); + + redirectexit('action=admin;area=membergroups'); + } + + // Fetch the current group information. + $request = $smcFunc['db_query']('', ' + SELECT group_name, description, min_posts, online_color, max_messages, stars, group_type, hidden, id_parent + FROM {db_prefix}membergroups + WHERE id_group = {int:current_group} + LIMIT 1', + array( + 'current_group' => (int) $_REQUEST['group'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('membergroup_does_not_exist', false); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $row['stars'] = explode('#', $row['stars']); + + $context['group'] = array( + 'id' => $_REQUEST['group'], + 'name' => $row['group_name'], + 'description' => htmlspecialchars($row['description']), + 'editable_name' => htmlspecialchars($row['group_name']), + 'color' => $row['online_color'], + 'min_posts' => $row['min_posts'], + 'max_messages' => $row['max_messages'], + 'star_count' => (int) $row['stars'][0], + 'star_image' => isset($row['stars'][1]) ? $row['stars'][1] : '', + 'is_post_group' => $row['min_posts'] != -1, + 'type' => $row['min_posts'] != -1 ? 0 : $row['group_type'], + 'hidden' => $row['min_posts'] == -1 ? $row['hidden'] : 0, + 'inherited_from' => $row['id_parent'], + 'allow_post_group' => $_REQUEST['group'] == 2 || $_REQUEST['group'] > 4, + 'allow_delete' => $_REQUEST['group'] == 2 || $_REQUEST['group'] > 4, + 'allow_protected' => allowedTo('admin_forum'), + ); + + // Get any moderators for this group + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member, mem.real_name + FROM {db_prefix}group_moderators AS mods + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member) + WHERE mods.id_group = {int:current_group}', + array( + 'current_group' => $_REQUEST['group'], + ) + ); + $context['group']['moderators'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['group']['moderators'][$row['id_member']] = $row['real_name']; + $smcFunc['db_free_result']($request); + + $context['group']['moderator_list'] = empty($context['group']['moderators']) ? '' : '"' . implode('", "', $context['group']['moderators']) . '"'; + + if (!empty($context['group']['moderators'])) + list ($context['group']['last_moderator_id']) = array_slice(array_keys($context['group']['moderators']), -1); + + // Get a list of boards this membergroup is allowed to see. + $context['boards'] = array(); + if ($_REQUEST['group'] == 2 || $_REQUEST['group'] > 3) + { + $result = $smcFunc['db_query']('', ' + SELECT id_board, name, child_level, FIND_IN_SET({string:current_group}, member_groups) != 0 AS can_access + FROM {db_prefix}boards + ORDER BY board_order', + array( + 'current_group' => (int) $_REQUEST['group'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $context['boards'][] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'child_level' => $row['child_level'], + 'selected' => !(empty($row['can_access']) || $row['can_access'] == 'f'), + ); + $smcFunc['db_free_result']($result); + } + + // Finally, get all the groups this could be inherited off. + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name + FROM {db_prefix}membergroups + WHERE id_group != {int:current_group}' . + (empty($modSettings['permission_enable_postgroups']) ? ' + AND min_posts = {int:min_posts}' : '') . (allowedTo('admin_forum') ? '' : ' + AND group_type != {int:is_protected}') . ' + AND id_group NOT IN (1, 3) + AND id_parent = {int:not_inherited}', + array( + 'current_group' => (int) $_REQUEST['group'], + 'min_posts' => -1, + 'not_inherited' => -2, + 'is_protected' => 1, + ) + ); + $context['inheritable_groups'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['inheritable_groups'][$row['id_group']] = $row['group_name']; + $smcFunc['db_free_result']($request); + + $context['sub_template'] = 'edit_group'; + $context['page_title'] = $txt['membergroups_edit_group']; +} + +// Set general membergroup settings. +function ModifyMembergroupsettings() +{ + global $context, $sourcedir, $scripturl, $modSettings, $txt; + + $context['sub_template'] = 'show_settings'; + $context['page_title'] = $txt['membergroups_settings']; + + // Needed for the settings functions. + require_once($sourcedir . '/ManageServer.php'); + + // Don't allow assignment of guests. + $context['permissions_excluded'] = array(-1); + + // Only one thing here! + $config_vars = array( + array('permissions', 'manage_membergroups'), + ); + + if (isset($_REQUEST['save'])) + { + checkSession(); + + // Yeppers, saving this... + saveDBSettings($config_vars); + redirectexit('action=admin;area=membergroups;sa=settings'); + } + + // Some simple context. + $context['post_url'] = $scripturl . '?action=admin;area=membergroups;save;sa=settings'; + $context['settings_title'] = $txt['membergroups_settings']; + + prepareDBSettingContext($config_vars); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageMembers.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageMembers.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1307 @@ + array('ViewMemberlist', 'moderate_forum'), + 'approve' => array('AdminApprove', 'moderate_forum'), + 'browse' => array('MembersAwaitingActivation', 'moderate_forum'), + 'search' => array('SearchMembers', 'moderate_forum'), + 'query' => array('ViewMemberlist', 'moderate_forum'), + ); + + // Default to sub action 'index' or 'settings' depending on permissions. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'all'; + + // We know the sub action, now we know what you're allowed to do. + isAllowedTo($subActions[$_REQUEST['sa']][1]); + + // Load the essentials. + loadLanguage('ManageMembers'); + loadTemplate('ManageMembers'); + + // Get counts on every type of activation - for sections and filtering alike. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS total_members, is_activated + FROM {db_prefix}members + WHERE is_activated != {int:is_activated} + GROUP BY is_activated', + array( + 'is_activated' => 1, + ) + ); + $context['activation_numbers'] = array(); + $context['awaiting_activation'] = 0; + $context['awaiting_approval'] = 0; + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['activation_numbers'][$row['is_activated']] = $row['total_members']; + $smcFunc['db_free_result']($request); + + foreach ($context['activation_numbers'] as $activation_type => $total_members) + { + if (in_array($activation_type, array(0, 2))) + $context['awaiting_activation'] += $total_members; + elseif (in_array($activation_type, array(3, 4, 5))) + $context['awaiting_approval'] += $total_members; + } + + // For the page header... do we show activation? + $context['show_activate'] = (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1) || !empty($context['awaiting_activation']); + + // What about approval? + $context['show_approve'] = (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($context['awaiting_approval']) || !empty($modSettings['approveAccountDeletion']); + + // Setup the admin tabs. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['admin_members'], + 'help' => 'view_members', + 'description' => $txt['admin_members_list'], + 'tabs' => array(), + ); + + $context['tabs'] = array( + 'viewmembers' => array( + 'label' => $txt['view_all_members'], + 'description' => $txt['admin_members_list'], + 'url' => $scripturl . '?action=admin;area=viewmembers;sa=all', + 'is_selected' => $_REQUEST['sa'] == 'all', + ), + 'search' => array( + 'label' => $txt['mlist_search'], + 'description' => $txt['admin_members_list'], + 'url' => $scripturl . '?action=admin;area=viewmembers;sa=search', + 'is_selected' => $_REQUEST['sa'] == 'search' || $_REQUEST['sa'] == 'query', + ), + 'approve' => array( + 'label' => sprintf($txt['admin_browse_awaiting_approval'], $context['awaiting_approval']), + 'description' => $txt['admin_browse_approve_desc'], + 'url' => $scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve', + 'is_selected' => false, + ), + 'activate' => array( + 'label' => sprintf($txt['admin_browse_awaiting_activate'], $context['awaiting_activation']), + 'description' => $txt['admin_browse_activate_desc'], + 'url' => $scripturl . '?action=admin;area=viewmembers;sa=browse;type=activate', + 'is_selected' => false, + 'is_last' => true, + ), + ); + + // Sort out the tabs for the ones which may not exist! + if (!$context['show_activate'] && ($_REQUEST['sa'] != 'browse' || $_REQUEST['type'] != 'activate')) + { + $context['tabs']['approve']['is_last'] = true; + unset($context['tabs']['activate']); + } + if (!$context['show_approve'] && ($_REQUEST['sa'] != 'browse' || $_REQUEST['type'] != 'approve')) + { + if (!$context['show_activate'] && ($_REQUEST['sa'] != 'browse' || $_REQUEST['type'] != 'activate')) + $context['tabs']['search']['is_last'] = true; + unset($context['tabs']['approve']); + } + + $subActions[$_REQUEST['sa']][0](); +} + +// View all members. +function ViewMemberlist() +{ + global $txt, $scripturl, $context, $modSettings, $sourcedir, $smcFunc, $user_info; + + // Set the current sub action. + $context['sub_action'] = $_REQUEST['sa']; + + // Are we performing a delete? + if (isset($_POST['delete_members']) && !empty($_POST['delete']) && allowedTo('profile_remove_any')) + { + checkSession(); + + // Clean the input. + foreach ($_POST['delete'] as $key => $value) + { + $_POST['delete'][$key] = (int) $value; + // Don't delete yourself, idiot. + if ($value == $user_info['id']) + unset($_POST['delete'][$key]); + } + + if (!empty($_POST['delete'])) + { + // Delete all the selected members. + require_once($sourcedir . '/Subs-Members.php'); + deleteMembers($_POST['delete'], true); + } + } + + if ($context['sub_action'] == 'query' && !empty($_REQUEST['params']) && empty($_POST)) + $_POST += @unserialize(base64_decode($_REQUEST['params'])); + + // Check input after a member search has been submitted. + if ($context['sub_action'] == 'query') + { + // Retrieving the membergroups and postgroups. + $context['membergroups'] = array( + array( + 'id' => 0, + 'name' => $txt['membergroups_members'], + 'can_be_additional' => false + ) + ); + $context['postgroups'] = array(); + + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name, min_posts + FROM {db_prefix}membergroups + WHERE id_group != {int:moderator_group} + ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name', + array( + 'moderator_group' => 3, + 'newbie_group' => 4, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['min_posts'] == -1) + $context['membergroups'][] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'can_be_additional' => true + ); + else + $context['postgroups'][] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'] + ); + } + $smcFunc['db_free_result']($request); + + // Some data about the form fields and how they are linked to the database. + $params = array( + 'mem_id' => array( + 'db_fields' => array('id_member'), + 'type' => 'int', + 'range' => true + ), + 'age' => array( + 'db_fields' => array('birthdate'), + 'type' => 'age', + 'range' => true + ), + 'posts' => array( + 'db_fields' => array('posts'), + 'type' => 'int', + 'range' => true + ), + 'reg_date' => array( + 'db_fields' => array('date_registered'), + 'type' => 'date', + 'range' => true + ), + 'last_online' => array( + 'db_fields' => array('last_login'), + 'type' => 'date', + 'range' => true + ), + 'gender' => array( + 'db_fields' => array('gender'), + 'type' => 'checkbox', + 'values' => array('0', '1', '2'), + ), + 'activated' => array( + 'db_fields' => array('CASE WHEN is_activated IN (1, 11) THEN 1 ELSE 0 END'), + 'type' => 'checkbox', + 'values' => array('0', '1'), + ), + 'membername' => array( + 'db_fields' => array('member_name', 'real_name'), + 'type' => 'string' + ), + 'email' => array( + 'db_fields' => array('email_address'), + 'type' => 'string' + ), + 'website' => array( + 'db_fields' => array('website_title', 'website_url'), + 'type' => 'string' + ), + 'location' => array( + 'db_fields' => array('location'), + 'type' => 'string' + ), + 'ip' => array( + 'db_fields' => array('member_ip'), + 'type' => 'string' + ), + 'messenger' => array( + 'db_fields' => array('icq', 'aim', 'yim', 'msn'), + 'type' => 'string' + ) + ); + $range_trans = array( + '--' => '<', + '-' => '<=', + '=' => '=', + '+' => '>=', + '++' => '>' + ); + + // !!! Validate a little more. + + // Loop through every field of the form. + $query_parts = array(); + $where_params = array(); + foreach ($params as $param_name => $param_info) + { + // Not filled in? + if (!isset($_POST[$param_name]) || $_POST[$param_name] === '') + continue; + + // Make sure numeric values are really numeric. + if (in_array($param_info['type'], array('int', 'age'))) + $_POST[$param_name] = (int) $_POST[$param_name]; + // Date values have to match the specified format. + elseif ($param_info['type'] == 'date') + { + // Check if this date format is valid. + if (preg_match('/^\d{4}-\d{1,2}-\d{1,2}$/', $_POST[$param_name]) == 0) + continue; + + $_POST[$param_name] = strtotime($_POST[$param_name]); + } + + // Those values that are in some kind of range (<, <=, =, >=, >). + if (!empty($param_info['range'])) + { + // Default to '=', just in case... + if (empty($range_trans[$_POST['types'][$param_name]])) + $_POST['types'][$param_name] = '='; + + // Handle special case 'age'. + if ($param_info['type'] == 'age') + { + // All people that were born between $lowerlimit and $upperlimit are currently the specified age. + $datearray = getdate(forum_time()); + $upperlimit = sprintf('%04d-%02d-%02d', $datearray['year'] - $_POST[$param_name], $datearray['mon'], $datearray['mday']); + $lowerlimit = sprintf('%04d-%02d-%02d', $datearray['year'] - $_POST[$param_name] - 1, $datearray['mon'], $datearray['mday']); + if (in_array($_POST['types'][$param_name], array('-', '--', '='))) + { + $query_parts[] = ($param_info['db_fields'][0]) . ' > {string:' . $param_name . '_minlimit}'; + $where_params[$param_name . '_minlimit'] = ($_POST['types'][$param_name] == '--' ? $upperlimit : $lowerlimit); + } + if (in_array($_POST['types'][$param_name], array('+', '++', '='))) + { + $query_parts[] = ($param_info['db_fields'][0]) . ' <= {string:' . $param_name . '_pluslimit}'; + $where_params[$param_name . '_pluslimit'] = ($_POST['types'][$param_name] == '++' ? $lowerlimit : $upperlimit); + + // Make sure that members that didn't set their birth year are not queried. + $query_parts[] = ($param_info['db_fields'][0]) . ' > {date:dec_zero_date}'; + $where_params['dec_zero_date'] = '0004-12-31'; + } + } + // Special case - equals a date. + elseif ($param_info['type'] == 'date' && $_POST['types'][$param_name] == '=') + { + $query_parts[] = $param_info['db_fields'][0] . ' > ' . $_POST[$param_name] . ' AND ' . $param_info['db_fields'][0] . ' < ' . ($_POST[$param_name] + 86400); + } + else + $query_parts[] = $param_info['db_fields'][0] . ' ' . $range_trans[$_POST['types'][$param_name]] . ' ' . $_POST[$param_name]; + } + // Checkboxes. + elseif ($param_info['type'] == 'checkbox') + { + // Each checkbox or no checkbox at all is checked -> ignore. + if (!is_array($_POST[$param_name]) || count($_POST[$param_name]) == 0 || count($_POST[$param_name]) == count($param_info['values'])) + continue; + + $query_parts[] = ($param_info['db_fields'][0]) . ' IN ({array_string:' . $param_name . '_check})'; + $where_params[$param_name . '_check'] = $_POST[$param_name]; + } + else + { + // Replace the wildcard characters ('*' and '?') into MySQL ones. + $parameter = strtolower(strtr($smcFunc['htmlspecialchars']($_POST[$param_name], ENT_QUOTES), array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_'))); + + $query_parts[] = '(' . implode( ' LIKE {string:' . $param_name . '_normal} OR ', $param_info['db_fields']) . ' LIKE {string:' . $param_name . '_normal})'; + $where_params[$param_name . '_normal'] = '%' . $parameter . '%'; + } + } + + // Set up the membergroup query part. + $mg_query_parts = array(); + + // Primary membergroups, but only if at least was was not selected. + if (!empty($_POST['membergroups'][1]) && count($context['membergroups']) != count($_POST['membergroups'][1])) + { + $mg_query_parts[] = 'mem.id_group IN ({array_int:group_check})'; + $where_params['group_check'] = $_POST['membergroups'][1]; + } + + // Additional membergroups (these are only relevant if not all primary groups where selected!). + if (!empty($_POST['membergroups'][2]) && (empty($_POST['membergroups'][1]) || count($context['membergroups']) != count($_POST['membergroups'][1]))) + foreach ($_POST['membergroups'][2] as $mg) + { + $mg_query_parts[] = 'FIND_IN_SET({int:add_group_' . $mg . '}, mem.additional_groups) != 0'; + $where_params['add_group_' . $mg] = $mg; + } + + // Combine the one or two membergroup parts into one query part linked with an OR. + if (!empty($mg_query_parts)) + $query_parts[] = '(' . implode(' OR ', $mg_query_parts) . ')'; + + // Get all selected post count related membergroups. + if (!empty($_POST['postgroups']) && count($_POST['postgroups']) != count($context['postgroups'])) + { + $query_parts[] = 'id_post_group IN ({array_int:post_groups})'; + $where_params['post_groups'] = $_POST['postgroups']; + } + + // Construct the where part of the query. + $where = empty($query_parts) ? '1' : implode(' + AND ', $query_parts); + + $search_params = base64_encode(serialize($_POST)); + } + else + $search_params = null; + + // Construct the additional URL part with the query info in it. + $context['params_url'] = $context['sub_action'] == 'query' ? ';sa=query;params=' . $search_params : ''; + + // Get the title and sub template ready.. + $context['page_title'] = $txt['admin_members']; + + $listOptions = array( + 'id' => 'member_list', + 'items_per_page' => $modSettings['defaultMaxMembers'], + 'base_href' => $scripturl . '?action=admin;area=viewmembers' . $context['params_url'], + 'default_sort_col' => 'user_name', + 'get_items' => array( + 'file' => $sourcedir . '/Subs-Members.php', + 'function' => 'list_getMembers', + 'params' => array( + isset($where) ? $where : '1=1', + isset($where_params) ? $where_params : array(), + ), + ), + 'get_count' => array( + 'file' => $sourcedir . '/Subs-Members.php', + 'function' => 'list_getNumMembers', + 'params' => array( + isset($where) ? $where : '1=1', + isset($where_params) ? $where_params : array(), + ), + ), + 'columns' => array( + 'id_member' => array( + 'header' => array( + 'value' => $txt['member_id'], + ), + 'data' => array( + 'db' => 'id_member', + 'class' => 'windowbg', + 'style' => 'text-align: center;', + ), + 'sort' => array( + 'default' => 'id_member', + 'reverse' => 'id_member DESC', + ), + ), + 'user_name' => array( + 'header' => array( + 'value' => $txt['username'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id_member' => false, + 'member_name' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'member_name', + 'reverse' => 'member_name DESC', + ), + ), + 'display_name' => array( + 'header' => array( + 'value' => $txt['display_name'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id_member' => false, + 'real_name' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'real_name', + 'reverse' => 'real_name DESC', + ), + ), + 'email' => array( + 'header' => array( + 'value' => $txt['email_address'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s', + 'params' => array( + 'email_address' => true, + ), + ), + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'email_address', + 'reverse' => 'email_address DESC', + ), + ), + 'ip' => array( + 'header' => array( + 'value' => $txt['ip_address'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s', + 'params' => array( + 'member_ip' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'INET_ATON(member_ip)', + 'reverse' => 'INET_ATON(member_ip) DESC', + ), + ), + 'last_active' => array( + 'header' => array( + 'value' => $txt['viewmembers_online'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + // Calculate number of days since last online. + if (empty($rowData[\'last_login\'])) + $difference = $txt[\'never\']; + else + { + $num_days_difference = jeffsdatediff($rowData[\'last_login\']); + + // Today. + if (empty($num_days_difference)) + $difference = $txt[\'viewmembers_today\']; + + // Yesterday. + elseif ($num_days_difference == 1) + $difference = sprintf(\'1 %1$s\', $txt[\'viewmembers_day_ago\']); + + // X days ago. + else + $difference = sprintf(\'%1$d %2$s\', $num_days_difference, $txt[\'viewmembers_days_ago\']); + } + + // Show it in italics if they\'re not activated... + if ($rowData[\'is_activated\'] % 10 != 1) + $difference = sprintf(\'%2$s\', $txt[\'not_activated\'], $difference); + + return $difference; + '), + ), + 'sort' => array( + 'default' => 'last_login DESC', + 'reverse' => 'last_login', + ), + ), + 'posts' => array( + 'header' => array( + 'value' => $txt['member_postcount'], + ), + 'data' => array( + 'db' => 'posts', + ), + 'sort' => array( + 'default' => 'posts', + 'reverse' => 'posts DESC', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $user_info; + + return \'\'; + '), + 'class' => 'windowbg', + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=viewmembers' . $context['params_url'], + 'include_start' => true, + 'include_sort' => true, + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '', + 'style' => 'text-align: right;', + ), + ), + ); + + // Without not enough permissions, don't show 'delete members' checkboxes. + if (!allowedTo('profile_remove_any')) + unset($listOptions['cols']['check'], $listOptions['form'], $listOptions['additional_rows']); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'member_list'; +} + +// Search the member list, using one or more criteria. +function SearchMembers() +{ + global $context, $txt, $smcFunc; + + // Get a list of all the membergroups and postgroups that can be selected. + $context['membergroups'] = array( + array( + 'id' => 0, + 'name' => $txt['membergroups_members'], + 'can_be_additional' => false + ) + ); + $context['postgroups'] = array(); + + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name, min_posts + FROM {db_prefix}membergroups + WHERE id_group != {int:moderator_group} + ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name', + array( + 'moderator_group' => 3, + 'newbie_group' => 4, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['min_posts'] == -1) + $context['membergroups'][] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'can_be_additional' => true + ); + else + $context['postgroups'][] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'] + ); + } + $smcFunc['db_free_result']($request); + + $context['page_title'] = $txt['admin_members']; + $context['sub_template'] = 'search_members'; +} + +// List all members who are awaiting approval / activation +function MembersAwaitingActivation() +{ + global $txt, $context, $scripturl, $modSettings, $smcFunc; + global $sourcedir; + + // Not a lot here! + $context['page_title'] = $txt['admin_members']; + $context['sub_template'] = 'admin_browse'; + $context['browse_type'] = isset($_REQUEST['type']) ? $_REQUEST['type'] : (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1 ? 'activate' : 'approve'); + if (isset($context['tabs'][$context['browse_type']])) + $context['tabs'][$context['browse_type']]['is_selected'] = true; + + // Allowed filters are those we can have, in theory. + $context['allowed_filters'] = $context['browse_type'] == 'approve' ? array(3, 4, 5) : array(0, 2); + $context['current_filter'] = isset($_REQUEST['filter']) && in_array($_REQUEST['filter'], $context['allowed_filters']) && !empty($context['activation_numbers'][$_REQUEST['filter']]) ? (int) $_REQUEST['filter'] : -1; + + // Sort out the different sub areas that we can actually filter by. + $context['available_filters'] = array(); + foreach ($context['activation_numbers'] as $type => $amount) + { + // We have some of these... + if (in_array($type, $context['allowed_filters']) && $amount > 0) + $context['available_filters'][] = array( + 'type' => $type, + 'amount' => $amount, + 'desc' => isset($txt['admin_browse_filter_type_' . $type]) ? $txt['admin_browse_filter_type_' . $type] : '?', + 'selected' => $type == $context['current_filter'] + ); + } + + // If the filter was not sent, set it to whatever has people in it! + if ($context['current_filter'] == -1 && !empty($context['available_filters'][0]['amount'])) + $context['current_filter'] = $context['available_filters'][0]['type']; + + // This little variable is used to determine if we should flag where we are looking. + $context['show_filter'] = ($context['current_filter'] != 0 && $context['current_filter'] != 3) || count($context['available_filters']) > 1; + + // The columns that can be sorted. + $context['columns'] = array( + 'id_member' => array('label' => $txt['admin_browse_id']), + 'member_name' => array('label' => $txt['admin_browse_username']), + 'email_address' => array('label' => $txt['admin_browse_email']), + 'member_ip' => array('label' => $txt['admin_browse_ip']), + 'date_registered' => array('label' => $txt['admin_browse_registered']), + ); + + // Are we showing duplicate information? + if (isset($_GET['showdupes'])) + $_SESSION['showdupes'] = (int) $_GET['showdupes']; + $context['show_duplicates'] = !empty($_SESSION['showdupes']); + + // Determine which actions we should allow on this page. + if ($context['browse_type'] == 'approve') + { + // If we are approving deleted accounts we have a slightly different list... actually a mirror ;) + if ($context['current_filter'] == 4) + $context['allowed_actions'] = array( + 'reject' => $txt['admin_browse_w_approve_deletion'], + 'ok' => $txt['admin_browse_w_reject'], + ); + else + $context['allowed_actions'] = array( + 'ok' => $txt['admin_browse_w_approve'], + 'okemail' => $txt['admin_browse_w_approve'] . ' ' . $txt['admin_browse_w_email'], + 'require_activation' => $txt['admin_browse_w_approve_require_activate'], + 'reject' => $txt['admin_browse_w_reject'], + 'rejectemail' => $txt['admin_browse_w_reject'] . ' ' . $txt['admin_browse_w_email'], + ); + } + elseif ($context['browse_type'] == 'activate') + $context['allowed_actions'] = array( + 'ok' => $txt['admin_browse_w_activate'], + 'okemail' => $txt['admin_browse_w_activate'] . ' ' . $txt['admin_browse_w_email'], + 'delete' => $txt['admin_browse_w_delete'], + 'deleteemail' => $txt['admin_browse_w_delete'] . ' ' . $txt['admin_browse_w_email'], + 'remind' => $txt['admin_browse_w_remind'] . ' ' . $txt['admin_browse_w_email'], + ); + + // Create an option list for actions allowed to be done with selected members. + $allowed_actions = ' + + '; + foreach ($context['allowed_actions'] as $key => $desc) + $allowed_actions .= ' + '; + + // Setup the Javascript function for selecting an action for the list. + $javascript = ' + function onSelectChange() + { + if (document.forms.postForm.todo.value == "") + return; + + var message = "";'; + + // We have special messages for approving deletion of accounts - it's surprisingly logical - honest. + if ($context['current_filter'] == 4) + $javascript .= ' + if (document.forms.postForm.todo.value.indexOf("reject") != -1) + message = "' . $txt['admin_browse_w_delete'] . '"; + else + message = "' . $txt['admin_browse_w_reject'] . '";'; + // Otherwise a nice standard message. + else + $javascript .= ' + if (document.forms.postForm.todo.value.indexOf("delete") != -1) + message = "' . $txt['admin_browse_w_delete'] . '"; + else if (document.forms.postForm.todo.value.indexOf("reject") != -1) + message = "' . $txt['admin_browse_w_reject'] . '"; + else if (document.forms.postForm.todo.value == "remind") + message = "' . $txt['admin_browse_w_remind'] . '"; + else + message = "' . ($context['browse_type'] == 'approve' ? $txt['admin_browse_w_approve'] : $txt['admin_browse_w_activate']) . '";'; + $javascript .= ' + if (confirm(message + " ' . $txt['admin_browse_warn'] . '")) + document.forms.postForm.submit(); + }'; + + $listOptions = array( + 'id' => 'approve_list', + 'items_per_page' => $modSettings['defaultMaxMembers'], + 'base_href' => $scripturl . '?action=admin;area=viewmembers;sa=browse;type=' . $context['browse_type'] . (!empty($context['show_filter']) ? ';filter=' . $context['current_filter'] : ''), + 'default_sort_col' => 'date_registered', + 'get_items' => array( + 'file' => $sourcedir . '/Subs-Members.php', + 'function' => 'list_getMembers', + 'params' => array( + 'is_activated = {int:activated_status}', + array('activated_status' => $context['current_filter']), + $context['show_duplicates'], + ), + ), + 'get_count' => array( + 'file' => $sourcedir . '/Subs-Members.php', + 'function' => 'list_getNumMembers', + 'params' => array( + 'is_activated = {int:activated_status}', + array('activated_status' => $context['current_filter']), + ), + ), + 'columns' => array( + 'id_member' => array( + 'header' => array( + 'value' => $txt['member_id'], + ), + 'data' => array( + 'db' => 'id_member', + 'class' => 'windowbg', + 'style' => 'text-align: center;', + ), + 'sort' => array( + 'default' => 'id_member', + 'reverse' => 'id_member DESC', + ), + ), + 'user_name' => array( + 'header' => array( + 'value' => $txt['username'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id_member' => false, + 'member_name' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'member_name', + 'reverse' => 'member_name DESC', + ), + ), + 'email' => array( + 'header' => array( + 'value' => $txt['email_address'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s', + 'params' => array( + 'email_address' => true, + ), + ), + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'email_address', + 'reverse' => 'email_address DESC', + ), + ), + 'ip' => array( + 'header' => array( + 'value' => $txt['ip_address'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s', + 'params' => array( + 'member_ip' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'INET_ATON(member_ip)', + 'reverse' => 'INET_ATON(member_ip) DESC', + ), + ), + 'hostname' => array( + 'header' => array( + 'value' => $txt['hostname'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $modSettings; + + return host_from_ip($rowData[\'member_ip\']); + '), + 'class' => 'smalltext', + ), + ), + 'date_registered' => array( + 'header' => array( + 'value' => $txt['date_registered'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return timeformat($rowData[\'date_registered\']); + '), + ), + 'sort' => array( + 'default' => 'date_registered DESC', + 'reverse' => 'date_registered', + ), + ), + 'duplicates' => array( + 'header' => array( + 'value' => $txt['duplicates'], + // Make sure it doesn't go too wide. + 'style' => 'width: 20%', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $scripturl, $txt; + + $member_links = array(); + foreach ($rowData[\'duplicate_members\'] as $member) + { + if ($member[\'id\']) + $member_links[] = \'\' . $member[\'name\'] . \'\'; + else + $member_links[] = $member[\'name\'] . \' (\' . $txt[\'guest\'] . \')\'; + } + return implode (\', \', $member_links); + '), + 'class' => 'smalltext', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id_member' => false, + ), + ), + 'class' => 'windowbg', + 'style' => 'text-align: center', + ), + ), + ), + 'javascript' => $javascript, + 'form' => array( + 'href' => $scripturl . '?action=admin;area=viewmembers;sa=approve;type=' . $context['browse_type'], + 'name' => 'postForm', + 'include_start' => true, + 'include_sort' => true, + 'hidden_fields' => array( + 'orig_filter' => $context['current_filter'], + ), + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' + +
+ + +
', + ), + ), + ); + + // Pick what column to actually include if we're showing duplicates. + if ($context['show_duplicates']) + unset($listOptions['columns']['email']); + else + unset($listOptions['columns']['duplicates']); + + // Only show hostname on duplicates as it takes a lot of time. + if (!$context['show_duplicates'] || !empty($modSettings['disableHostnameLookup'])) + unset($listOptions['columns']['hostname']); + + // Is there any need to show filters? + if (isset($context['available_filters']) && count($context['available_filters']) > 1) + { + $filterOptions = ' + ' . $txt['admin_browse_filter_by'] . ': + + '; + $listOptions['additional_rows'][] = array( + 'position' => 'above_column_headers', + 'value' => $filterOptions, + 'style' => 'text-align: center;', + ); + } + + // What about if we only have one filter, but it's not the "standard" filter - show them what they are looking at. + if (!empty($context['show_filter']) && !empty($context['available_filters'])) + $listOptions['additional_rows'][] = array( + 'position' => 'above_column_headers', + 'value' => '' . $txt['admin_browse_filter_show'] . ': ' . $context['available_filters'][0]['desc'], + 'class' => 'smalltext', + 'style' => 'text-align: left;', + ); + + // Now that we have all the options, create the list. + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); +} + +// Do the approve/activate/delete stuff +function AdminApprove() +{ + global $txt, $context, $scripturl, $modSettings, $sourcedir, $language, $user_info, $smcFunc; + + // First, check our session. + checkSession(); + + require_once($sourcedir . '/Subs-Post.php'); + + // We also need to the login languages here - for emails. + loadLanguage('Login'); + + // Sort out where we are going... + $browse_type = isset($_REQUEST['type']) ? $_REQUEST['type'] : (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1 ? 'activate' : 'approve'); + $current_filter = (int) $_REQUEST['orig_filter']; + + // If we are applying a filter do just that - then redirect. + if (isset($_REQUEST['filter']) && $_REQUEST['filter'] != $_REQUEST['orig_filter']) + redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $_REQUEST['type'] . ';sort=' . $_REQUEST['sort'] . ';filter=' . $_REQUEST['filter'] . ';start=' . $_REQUEST['start']); + + // Nothing to do? + if (!isset($_POST['todoAction']) && !isset($_POST['time_passed'])) + redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $_REQUEST['type'] . ';sort=' . $_REQUEST['sort'] . ';filter=' . $current_filter . ';start=' . $_REQUEST['start']); + + // Are we dealing with members who have been waiting for > set amount of time? + if (isset($_POST['time_passed'])) + { + $timeBefore = time() - 86400 * (int) $_POST['time_passed']; + $condition = ' + AND date_registered < {int:time_before}'; + } + // Coming from checkboxes - validate the members passed through to us. + else + { + $members = array(); + foreach ($_POST['todoAction'] as $id) + $members[] = (int) $id; + $condition = ' + AND id_member IN ({array_int:members})'; + } + + // Get information on each of the members, things that are important to us, like email address... + $request = $smcFunc['db_query']('', ' + SELECT id_member, member_name, real_name, email_address, validation_code, lngfile + FROM {db_prefix}members + WHERE is_activated = {int:activated_status}' . $condition . ' + ORDER BY lngfile', + array( + 'activated_status' => $current_filter, + 'time_before' => empty($timeBefore) ? 0 : $timeBefore, + 'members' => empty($members) ? array() : $members, + ) + ); + + $member_count = $smcFunc['db_num_rows']($request); + + // If no results then just return! + if ($member_count == 0) + redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $_REQUEST['type'] . ';sort=' . $_REQUEST['sort'] . ';filter=' . $current_filter . ';start=' . $_REQUEST['start']); + + $member_info = array(); + $members = array(); + // Fill the info array. + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $members[] = $row['id_member']; + $member_info[] = array( + 'id' => $row['id_member'], + 'username' => $row['member_name'], + 'name' => $row['real_name'], + 'email' => $row['email_address'], + 'language' => empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'], + 'code' => $row['validation_code'] + ); + } + $smcFunc['db_free_result']($request); + + // Are we activating or approving the members? + if ($_POST['todo'] == 'ok' || $_POST['todo'] == 'okemail') + { + // Approve/activate this member. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET validation_code = {string:blank_string}, is_activated = {int:is_activated} + WHERE is_activated = {int:activated_status}' . $condition, + array( + 'is_activated' => 1, + 'time_before' => empty($timeBefore) ? 0 : $timeBefore, + 'members' => empty($members) ? array() : $members, + 'activated_status' => $current_filter, + 'blank_string' => '', + ) + ); + + // Do we have to let the integration code know about the activations? + if (!empty($modSettings['integrate_activate'])) + { + foreach ($member_info as $member) + call_integration_hook('integrate_activate', array($member['username'])); + } + + // Check for email. + if ($_POST['todo'] == 'okemail') + { + foreach ($member_info as $member) + { + $replacements = array( + 'NAME' => $member['name'], + 'USERNAME' => $member['username'], + 'PROFILELINK' => $scripturl . '?action=profile;u=' . $member['id'], + 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', + ); + + $emaildata = loadEmailTemplate('admin_approve_accept', $replacements, $member['language']); + sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + } + } + } + // Maybe we're sending it off for activation? + elseif ($_POST['todo'] == 'require_activation') + { + require_once($sourcedir . '/Subs-Members.php'); + + // We have to do this for each member I'm afraid. + foreach ($member_info as $member) + { + // Generate a random activation code. + $validation_code = generateValidationCode(); + + // Set these members for activation - I know this includes two id_member checks but it's safer than bodging $condition ;). + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET validation_code = {string:validation_code}, is_activated = {int:not_activated} + WHERE is_activated = {int:activated_status} + ' . $condition . ' + AND id_member = {int:selected_member}', + array( + 'not_activated' => 0, + 'activated_status' => $current_filter, + 'selected_member' => $member['id'], + 'validation_code' => $validation_code, + 'time_before' => empty($timeBefore) ? 0 : $timeBefore, + 'members' => empty($members) ? array() : $members, + ) + ); + + $replacements = array( + 'USERNAME' => $member['name'], + 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $member['id'] . ';code=' . $validation_code, + 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $member['id'], + 'ACTIVATIONCODE' => $validation_code, + ); + + $emaildata = loadEmailTemplate('admin_approve_activation', $replacements, $member['language']); + sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + } + } + // Are we rejecting them? + elseif ($_POST['todo'] == 'reject' || $_POST['todo'] == 'rejectemail') + { + require_once($sourcedir . '/Subs-Members.php'); + deleteMembers($members); + + // Send email telling them they aren't welcome? + if ($_POST['todo'] == 'rejectemail') + { + foreach ($member_info as $member) + { + $replacements = array( + 'USERNAME' => $member['name'], + ); + + $emaildata = loadEmailTemplate('admin_approve_reject', $replacements, $member['language']); + sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); + } + } + } + // A simple delete? + elseif ($_POST['todo'] == 'delete' || $_POST['todo'] == 'deleteemail') + { + require_once($sourcedir . '/Subs-Members.php'); + deleteMembers($members); + + // Send email telling them they aren't welcome? + if ($_POST['todo'] == 'deleteemail') + { + foreach ($member_info as $member) + { + $replacements = array( + 'USERNAME' => $member['name'], + ); + + $emaildata = loadEmailTemplate('admin_approve_delete', $replacements, $member['language']); + sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); + } + } + } + // Remind them to activate their account? + elseif ($_POST['todo'] == 'remind') + { + foreach ($member_info as $member) + { + $replacements = array( + 'USERNAME' => $member['name'], + 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $member['id'] . ';code=' . $member['code'], + 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $member['id'], + 'ACTIVATIONCODE' => $member['code'], + ); + + $emaildata = loadEmailTemplate('admin_approve_remind', $replacements, $member['language']); + sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); + } + } + + // Back to the user's language! + if (isset($current_language) && $current_language != $user_info['language']) + { + loadLanguage('index'); + loadLanguage('ManageMembers'); + } + + // Log what we did? + if (!empty($modSettings['modlog_enabled']) && in_array($_POST['todo'], array('ok', 'okemail', 'require_activation', 'remind'))) + { + $log_action = $_POST['todo'] == 'remind' ? 'remind_member' : 'approve_member'; + $log_inserts = array(); + foreach ($member_info as $member) + { + $log_inserts[] = array( + time(), 3, $user_info['id'], $user_info['ip'], $log_action, + 0, 0, 0, serialize(array('member' => $member['id'])), + ); + } + $smcFunc['db_insert']('', + '{db_prefix}log_actions', + array( + 'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', + 'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534', + ), + $log_inserts, + array('id_action') + ); + } + + // Although updateStats *may* catch this, best to do it manually just in case (Doesn't always sort out unapprovedMembers). + if (in_array($current_filter, array(3, 4))) + updateSettings(array('unapprovedMembers' => ($modSettings['unapprovedMembers'] > $member_count ? $modSettings['unapprovedMembers'] - $member_count : 0))); + + // Update the member's stats. (but, we know the member didn't change their name.) + updateStats('member', false); + + // If they haven't been deleted, update the post group statistics on them... + if (!in_array($_POST['todo'], array('delete', 'deleteemail', 'reject', 'rejectemail', 'remind'))) + updateStats('postgroups', $members); + + redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $_REQUEST['type'] . ';sort=' . $_REQUEST['sort'] . ';filter=' . $current_filter . ';start=' . $_REQUEST['start']); +} + +function jeffsdatediff($old) +{ + // Get the current time as the user would see it... + $forumTime = forum_time(); + + // Calculate the seconds that have passed since midnight. + $sinceMidnight = date('H', $forumTime) * 60 * 60 + date('i', $forumTime) * 60 + date('s', $forumTime); + + // Take the difference between the two times. + $dis = time() - $old; + + // Before midnight? + if ($dis < $sinceMidnight) + return 0; + else + $dis -= $sinceMidnight; + + // Divide out the seconds in a day to get the number of days. + return ceil($dis / (24 * 60 * 60)); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageNews.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageNews.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,816 @@ + array('function', 'permission') + $subActions = array( + 'editnews' => array('EditNews', 'edit_news'), + 'mailingmembers' => array('SelectMailingMembers', 'send_mail'), + 'mailingcompose' => array('ComposeMailing', 'send_mail'), + 'mailingsend' => array('SendMailing', 'send_mail'), + 'settings' => array('ModifyNewsSettings', 'admin_forum'), + ); + + // Default to sub action 'main' or 'settings' depending on permissions. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (allowedTo('edit_news') ? 'editnews' : (allowedTo('send_mail') ? 'mailingmembers' : 'settings')); + + // Have you got the proper permissions? + isAllowedTo($subActions[$_REQUEST['sa']][1]); + + // Create the tabs for the template. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['news_title'], + 'help' => 'edit_news', + 'description' => $txt['admin_news_desc'], + 'tabs' => array( + 'editnews' => array( + ), + 'mailingmembers' => array( + 'description' => $txt['news_mailing_desc'], + ), + 'settings' => array( + 'description' => $txt['news_settings_desc'], + ), + ), + ); + + // Force the right area... + if (substr($_REQUEST['sa'], 0, 7) == 'mailing') + $context[$context['admin_menu_name']]['current_subsection'] = 'mailingmembers'; + + $subActions[$_REQUEST['sa']][0](); +} + +// Let the administrator(s) edit the news. +function EditNews() +{ + global $txt, $modSettings, $context, $sourcedir, $user_info; + global $smcFunc; + + require_once($sourcedir . '/Subs-Post.php'); + + // The 'remove selected' button was pressed. + if (!empty($_POST['delete_selection']) && !empty($_POST['remove'])) + { + checkSession(); + + // Store the news temporarily in this array. + $temp_news = explode("\n", $modSettings['news']); + + // Remove the items that were selected. + foreach ($temp_news as $i => $news) + if (in_array($i, $_POST['remove'])) + unset($temp_news[$i]); + + // Update the database. + updateSettings(array('news' => implode("\n", $temp_news))); + + logAction('news'); + } + // The 'Save' button was pressed. + elseif (!empty($_POST['save_items'])) + { + checkSession(); + + foreach ($_POST['news'] as $i => $news) + { + if (trim($news) == '') + unset($_POST['news'][$i]); + else + { + $_POST['news'][$i] = $smcFunc['htmlspecialchars']($_POST['news'][$i], ENT_QUOTES); + preparsecode($_POST['news'][$i]); + } + } + + // Send the new news to the database. + updateSettings(array('news' => implode("\n", $_POST['news']))); + + // Log this into the moderation log. + logAction('news'); + } + + // Ready the current news. + foreach (explode("\n", $modSettings['news']) as $id => $line) + $context['admin_current_news'][$id] = array( + 'id' => $id, + 'unparsed' => un_preparsecode($line), + 'parsed' => preg_replace('~<([/]?)form[^>]*?[>]*>~i', '<$1form>', parse_bbc($line)), + ); + + $context['sub_template'] = 'edit_news'; + $context['page_title'] = $txt['admin_edit_news']; +} + +function SelectMailingMembers() +{ + global $txt, $context, $modSettings, $smcFunc; + + $context['page_title'] = $txt['admin_newsletters']; + + $context['sub_template'] = 'email_members'; + + $context['groups'] = array(); + $postGroups = array(); + $normalGroups = array(); + + // If we have post groups disabled then we need to give a "ungrouped members" option. + if (empty($modSettings['permission_enable_postgroups'])) + { + $context['groups'][0] = array( + 'id' => 0, + 'name' => $txt['membergroups_members'], + 'member_count' => 0, + ); + $normalGroups[0] = 0; + } + + // Get all the extra groups as well as Administrator and Global Moderator. + $request = $smcFunc['db_query']('', ' + SELECT mg.id_group, mg.group_name, mg.min_posts + FROM {db_prefix}membergroups AS mg' . (empty($modSettings['permission_enable_postgroups']) ? ' + WHERE mg.min_posts = {int:min_posts}' : '') . ' + GROUP BY mg.id_group, mg.min_posts, mg.group_name + ORDER BY mg.min_posts, CASE WHEN mg.id_group < {int:newbie_group} THEN mg.id_group ELSE 4 END, mg.group_name', + array( + 'min_posts' => -1, + 'newbie_group' => 4, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['groups'][$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'member_count' => 0, + ); + + if ($row['min_posts'] == -1) + $normalGroups[$row['id_group']] = $row['id_group']; + else + $postGroups[$row['id_group']] = $row['id_group']; + } + $smcFunc['db_free_result']($request); + + // If we have post groups, let's count the number of members... + if (!empty($postGroups)) + { + $query = $smcFunc['db_query']('', ' + SELECT mem.id_post_group AS id_group, COUNT(*) AS member_count + FROM {db_prefix}members AS mem + WHERE mem.id_post_group IN ({array_int:post_group_list}) + GROUP BY mem.id_post_group', + array( + 'post_group_list' => $postGroups, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $context['groups'][$row['id_group']]['member_count'] += $row['member_count']; + $smcFunc['db_free_result']($query); + } + + if (!empty($normalGroups)) + { + // Find people who are members of this group... + $query = $smcFunc['db_query']('', ' + SELECT id_group, COUNT(*) AS member_count + FROM {db_prefix}members + WHERE id_group IN ({array_int:normal_group_list}) + GROUP BY id_group', + array( + 'normal_group_list' => $normalGroups, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $context['groups'][$row['id_group']]['member_count'] += $row['member_count']; + $smcFunc['db_free_result']($query); + + // Also do those who have it as an additional membergroup - this ones more yucky... + $query = $smcFunc['db_query']('', ' + SELECT mg.id_group, COUNT(*) AS member_count + FROM {db_prefix}membergroups AS mg + INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_string} + AND mem.id_group != mg.id_group + AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0) + WHERE mg.id_group IN ({array_int:normal_group_list}) + GROUP BY mg.id_group', + array( + 'normal_group_list' => $normalGroups, + 'blank_string' => '', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $context['groups'][$row['id_group']]['member_count'] += $row['member_count']; + $smcFunc['db_free_result']($query); + } + + // Any moderators? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(DISTINCT id_member) AS num_distinct_mods + FROM {db_prefix}moderators + LIMIT 1', + array( + ) + ); + list ($context['groups'][3]['member_count']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['can_send_pm'] = allowedTo('pm_send'); +} + +// Email your members... +function ComposeMailing() +{ + global $txt, $sourcedir, $context, $smcFunc; + + // Start by finding any members! + $toClean = array(); + if (!empty($_POST['members'])) + $toClean[] = 'members'; + if (!empty($_POST['exclude_members'])) + $toClean[] = 'exclude_members'; + if (!empty($toClean)) + { + require_once($sourcedir . '/Subs-Auth.php'); + foreach ($toClean as $type) + { + // Remove the quotes. + $_POST[$type] = strtr($_POST[$type], array('\\"' => '"')); + + preg_match_all('~"([^"]+)"~', $_POST[$type], $matches); + $_POST[$type] = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_POST[$type])))); + + foreach ($_POST[$type] as $index => $member) + if (strlen(trim($member)) > 0) + $_POST[$type][$index] = $smcFunc['htmlspecialchars']($smcFunc['strtolower'](trim($member))); + else + unset($_POST[$type][$index]); + + // Find the members + $_POST[$type] = implode(',', array_keys(findMembers($_POST[$type]))); + } + } + + if (isset($_POST['member_list']) && is_array($_POST['member_list'])) + { + $members = array(); + foreach ($_POST['member_list'] as $member_id) + $members[] = (int) $member_id; + $_POST['members'] = implode(',', $members); + } + + if (isset($_POST['exclude_member_list']) && is_array($_POST['exclude_member_list'])) + { + $members = array(); + foreach ($_POST['exclude_member_list'] as $member_id) + $members[] = (int) $member_id; + $_POST['exclude_members'] = implode(',', $members); + } + + // Clean the other vars. + SendMailing(true); + + // We need a couple strings from the email template file + loadLanguage('EmailTemplates'); + + // Get a list of all full banned users. Use their Username and email to find them. Only get the ones that can't login to turn off notification. + $request = $smcFunc['db_query']('', ' + SELECT DISTINCT mem.id_member + FROM {db_prefix}ban_groups AS bg + INNER JOIN {db_prefix}ban_items AS bi ON (bg.id_ban_group = bi.id_ban_group) + INNER JOIN {db_prefix}members AS mem ON (bi.id_member = mem.id_member) + WHERE (bg.cannot_access = {int:cannot_access} OR bg.cannot_login = {int:cannot_login}) + AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})', + array( + 'cannot_access' => 1, + 'cannot_login' => 1, + 'current_time' => time(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['recipients']['exclude_members'][] = $row['id_member']; + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT DISTINCT bi.email_address + FROM {db_prefix}ban_items AS bi + INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group) + WHERE (bg.cannot_access = {int:cannot_access} OR bg.cannot_login = {int:cannot_login}) + AND (COALESCE(bg.expire_time, 1=1) OR bg.expire_time > {int:current_time}) + AND bi.email_address != {string:blank_string}', + array( + 'cannot_access' => 1, + 'cannot_login' => 1, + 'current_time' => time(), + 'blank_string' => '', + ) + ); + $condition_array = array(); + $condition_array_params = array(); + $count = 0; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $condition_array[] = '{string:email_' . $count . '}'; + $condition_array_params['email_' . $count++] = $row['email_address']; + } + + if (!empty($condition_array)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE email_address IN(' . implode(', ', $condition_array) .')', + $condition_array_params + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['recipients']['exclude_members'][] = $row['id_member']; + } + + // Did they select moderators - if so add them as specific members... + if ((!empty($context['recipients']['groups']) && in_array(3, $context['recipients']['groups'])) || (!empty($context['recipients']['exclude_groups']) && in_array(3, $context['recipients']['exclude_groups']))) + { + $request = $smcFunc['db_query']('', ' + SELECT DISTINCT mem.id_member AS identifier + FROM {db_prefix}members AS mem + INNER JOIN {db_prefix}moderators AS mods ON (mods.id_member = mem.id_member) + WHERE mem.is_activated = {int:is_activated}', + array( + 'is_activated' => 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (in_array(3, $context['recipients'])) + $context['recipients']['exclude_members'][] = $row['identifier']; + else + $context['recipients']['members'][] = $row['identifier']; + } + $smcFunc['db_free_result']($request); + } + + // For progress bar! + $context['total_emails'] = count($context['recipients']['emails']); + $request = $smcFunc['db_query']('', ' + SELECT MAX(id_member) + FROM {db_prefix}members', + array( + ) + ); + list ($context['max_id_member']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Clean up the arrays. + $context['recipients']['members'] = array_unique($context['recipients']['members']); + $context['recipients']['exclude_members'] = array_unique($context['recipients']['exclude_members']); + + // Setup the template! + $context['page_title'] = $txt['admin_newsletters']; + $context['sub_template'] = 'email_members_compose'; + + $context['default_subject'] = htmlspecialchars($context['forum_name'] . ': ' . $txt['subject']); + $context['default_message'] = htmlspecialchars($txt['message'] . "\n\n" . $txt['regards_team'] . "\n\n" . '{$board_url}'); +} + +// Send out the mailing! +function SendMailing($clean_only = false) +{ + global $txt, $sourcedir, $context, $smcFunc; + global $scripturl, $modSettings, $user_info; + + // How many to send at once? Quantity depends on whether we are queueing or not. + $num_at_once = empty($modSettings['mail_queue']) ? 60 : 1000; + + // If by PM's I suggest we half the above number. + if (!empty($_POST['send_pm'])) + $num_at_once /= 2; + + checkSession(); + + // Where are we actually to? + $context['start'] = isset($_REQUEST['start']) ? $_REQUEST['start'] : 0; + $context['email_force'] = !empty($_POST['email_force']) ? 1 : 0; + $context['send_pm'] = !empty($_POST['send_pm']) ? 1 : 0; + $context['total_emails'] = !empty($_POST['total_emails']) ? (int) $_POST['total_emails'] : 0; + $context['max_id_member'] = !empty($_POST['max_id_member']) ? (int) $_POST['max_id_member'] : 0; + $context['send_html'] = !empty($_POST['send_html']) ? '1' : '0'; + $context['parse_html'] = !empty($_POST['parse_html']) ? '1' : '0'; + + // Create our main context. + $context['recipients'] = array( + 'groups' => array(), + 'exclude_groups' => array(), + 'members' => array(), + 'exclude_members' => array(), + 'emails' => array(), + ); + + // Have we any excluded members? + if (!empty($_POST['exclude_members'])) + { + $members = explode(',', $_POST['exclude_members']); + foreach ($members as $member) + if ($member >= $context['start']) + $context['recipients']['exclude_members'][] = (int) $member; + } + + // What about members we *must* do? + if (!empty($_POST['members'])) + { + $members = explode(',', $_POST['members']); + foreach ($members as $member) + if ($member >= $context['start']) + $context['recipients']['members'][] = (int) $member; + } + // Cleaning groups is simple - although deal with both checkbox and commas. + if (!empty($_POST['groups'])) + { + if (is_array($_POST['groups'])) + { + foreach ($_POST['groups'] as $group => $dummy) + $context['recipients']['groups'][] = (int) $group; + } + else + { + $groups = explode(',', $_POST['groups']); + foreach ($groups as $group) + $context['recipients']['groups'][] = (int) $group; + } + } + // Same for excluded groups + if (!empty($_POST['exclude_groups'])) + { + if (is_array($_POST['exclude_groups'])) + { + foreach ($_POST['exclude_groups'] as $group => $dummy) + $context['recipients']['exclude_groups'][] = (int) $group; + } + else + { + $groups = explode(',', $_POST['exclude_groups']); + foreach ($groups as $group) + $context['recipients']['exclude_groups'][] = (int) $group; + } + } + // Finally - emails! + if (!empty($_POST['emails'])) + { + $addressed = array_unique(explode(';', strtr($_POST['emails'], array("\n" => ';', "\r" => ';', ',' => ';')))); + foreach ($addressed as $curmem) + { + $curmem = trim($curmem); + if ($curmem != '') + $context['recipients']['emails'][$curmem] = $curmem; + } + } + + // If we're only cleaning drop out here. + if ($clean_only) + return; + + require_once($sourcedir . '/Subs-Post.php'); + + // Save the message and its subject in $context + $context['subject'] = htmlspecialchars($_POST['subject']); + $context['message'] = htmlspecialchars($_POST['message']); + + // Prepare the message for sending it as HTML + if (!$context['send_pm'] && !empty($_POST['send_html'])) + { + // Prepare the message for HTML. + if (!empty($_POST['parse_html'])) + $_POST['message'] = str_replace(array("\n", ' '), array('
' . "\n", '  '), $_POST['message']); + + // This is here to prevent spam filters from tagging this as spam. + if (preg_match('~\' . "\n" . '' . $_POST['message'] . ''; + else + $_POST['message'] = '' . $_POST['message'] . ''; + } + } + + // Use the default time format. + $user_info['time_format'] = $modSettings['time_format']; + + $variables = array( + '{$board_url}', + '{$current_time}', + '{$latest_member.link}', + '{$latest_member.id}', + '{$latest_member.name}' + ); + + // We might need this in a bit + $cleanLatestMember = empty($_POST['send_html']) || $context['send_pm'] ? un_htmlspecialchars($modSettings['latestRealName']) : $modSettings['latestRealName']; + + // Replace in all the standard things. + $_POST['message'] = str_replace($variables, + array( + !empty($_POST['send_html']) ? '' . $scripturl . '' : $scripturl, + timeformat(forum_time(), false), + !empty($_POST['send_html']) ? '' . $cleanLatestMember . '' : ($context['send_pm'] ? '[url=' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . ']' . $cleanLatestMember . '[/url]' : $cleanLatestMember), + $modSettings['latestMember'], + $cleanLatestMember + ), $_POST['message']); + $_POST['subject'] = str_replace($variables, + array( + $scripturl, + timeformat(forum_time(), false), + $modSettings['latestRealName'], + $modSettings['latestMember'], + $modSettings['latestRealName'] + ), $_POST['subject']); + + $from_member = array( + '{$member.email}', + '{$member.link}', + '{$member.id}', + '{$member.name}' + ); + + // If we still have emails, do them first! + $i = 0; + foreach ($context['recipients']['emails'] as $k => $email) + { + // Done as many as we can? + if ($i >= $num_at_once) + break; + + // Don't sent it twice! + unset($context['recipients']['emails'][$k]); + + // Dammit - can't PM emails! + if ($context['send_pm']) + continue; + + $to_member = array( + $email, + !empty($_POST['send_html']) ? '' . $email . '' : $email, + '??', + $email + ); + + sendmail($email, str_replace($from_member, $to_member, $_POST['subject']), str_replace($from_member, $to_member, $_POST['message']), null, null, !empty($_POST['send_html']), 5); + + // Done another... + $i++; + } + + // Got some more to send this batch? + $last_id_member = 0; + if ($i < $num_at_once) + { + // Need to build quite a query! + $sendQuery = '('; + $sendParams = array(); + if (!empty($context['recipients']['groups'])) + { + // Take the long route... + $queryBuild = array(); + foreach ($context['recipients']['groups'] as $group) + { + $sendParams['group_' . $group] = $group; + $queryBuild[] = 'mem.id_group = {int:group_' . $group . '}'; + if (!empty($group)) + { + $queryBuild[] = 'FIND_IN_SET({int:group_' . $group . '}, mem.additional_groups) != 0'; + $queryBuild[] = 'mem.id_post_group = {int:group_' . $group . '}'; + } + } + if (!empty($queryBuild)) + $sendQuery .= implode(' OR ', $queryBuild); + } + if (!empty($context['recipients']['members'])) + { + $sendQuery .= ($sendQuery == '(' ? '' : ' OR ') . 'mem.id_member IN ({array_int:members})'; + $sendParams['members'] = $context['recipients']['members']; + } + + $sendQuery .= ')'; + + // If we've not got a query then we must be done! + if ($sendQuery == '()') + redirectexit('action=admin'); + + // Anything to exclude? + if (!empty($context['recipients']['exclude_groups']) && in_array(0, $context['recipients']['exclude_groups'])) + $sendQuery .= ' AND mem.id_group != {int:regular_group}'; + if (!empty($context['recipients']['exclude_members'])) + { + $sendQuery .= ' AND mem.id_member NOT IN ({array_int:exclude_members})'; + $sendParams['exclude_members'] = $context['recipients']['exclude_members']; + } + + // Force them to have it? + if (empty($context['email_force'])) + $sendQuery .= ' AND mem.notify_announcements = {int:notify_announcements}'; + + // Get the smelly people - note we respect the id_member range as it gives us a quicker query. + $result = $smcFunc['db_query']('', ' + SELECT mem.id_member, mem.email_address, mem.real_name, mem.id_group, mem.additional_groups, mem.id_post_group + FROM {db_prefix}members AS mem + WHERE mem.id_member > {int:min_id_member} + AND mem.id_member < {int:max_id_member} + AND ' . $sendQuery . ' + AND mem.is_activated = {int:is_activated} + ORDER BY mem.id_member ASC + LIMIT {int:atonce}', + array_merge($sendParams, array( + 'min_id_member' => $context['start'], + 'max_id_member' => $context['start'] + $num_at_once - $i, + 'atonce' => $num_at_once - $i, + 'regular_group' => 0, + 'notify_announcements' => 1, + 'is_activated' => 1, + )) + ); + + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $last_id_member = $row['id_member']; + + // What groups are we looking at here? + if (empty($row['additional_groups'])) + $groups = array($row['id_group'], $row['id_post_group']); + else + $groups = array_merge( + array($row['id_group'], $row['id_post_group']), + explode(',', $row['additional_groups']) + ); + + // Excluded groups? + if (array_intersect($groups, $context['recipients']['exclude_groups'])) + continue; + + // We might need this + $cleanMemberName = empty($_POST['send_html']) || $context['send_pm'] ? un_htmlspecialchars($row['real_name']) : $row['real_name']; + + // Replace the member-dependant variables + $message = str_replace($from_member, + array( + $row['email_address'], + !empty($_POST['send_html']) ? '' . $cleanMemberName . '' : ($context['send_pm'] ? '[url=' . $scripturl . '?action=profile;u=' . $row['id_member'] . ']' . $cleanMemberName . '[/url]' : $cleanMemberName), + $row['id_member'], + $cleanMemberName, + ), $_POST['message']); + + $subject = str_replace($from_member, + array( + $row['email_address'], + $row['real_name'], + $row['id_member'], + $row['real_name'], + ), $_POST['subject']); + + // Send the actual email - or a PM! + if (!$context['send_pm']) + sendmail($row['email_address'], $subject, $message, null, null, !empty($_POST['send_html']), 5); + else + sendpm(array('to' => array($row['id_member']), 'bcc' => array()), $subject, $message); + } + $smcFunc['db_free_result']($result); + } + + // If used our batch assume we still have a member. + if ($i >= $num_at_once) + $last_id_member = $context['start']; + // Or we didn't have one in range? + elseif (empty($last_id_member) && $context['start'] + $num_at_once < $context['max_id_member']) + $last_id_member = $context['start'] + $num_at_once; + // If we have no id_member then we're done. + elseif (empty($last_id_member) && empty($context['recipients']['emails'])) + { + // Log this into the admin log. + logAction('newsletter', array(), 'admin'); + + redirectexit('action=admin'); + } + + $context['start'] = $last_id_member; + + // Working out progress is a black art of sorts. + $percentEmails = $context['total_emails'] == 0 ? 0 : ((count($context['recipients']['emails']) / $context['total_emails']) * ($context['total_emails'] / ($context['total_emails'] + $context['max_id_member']))); + $percentMembers = ($context['start'] / $context['max_id_member']) * ($context['max_id_member'] / ($context['total_emails'] + $context['max_id_member'])); + $context['percentage_done'] = round(($percentEmails + $percentMembers) * 100, 2); + + $context['page_title'] = $txt['admin_newsletters']; + $context['sub_template'] = 'email_members_send'; +} + +function ModifyNewsSettings($return_config = false) +{ + global $context, $sourcedir, $modSettings, $txt, $scripturl; + + $config_vars = array( + array('title', 'settings'), + // Inline permissions. + array('permissions', 'edit_news', 'help' => ''), + array('permissions', 'send_mail'), + '', + // Just the remaining settings. + array('check', 'xmlnews_enable', 'onclick' => 'document.getElementById(\'xmlnews_maxlen\').disabled = !this.checked;'), + array('text', 'xmlnews_maxlen', 10), + ); + + if ($return_config) + return $config_vars; + + $context['page_title'] = $txt['admin_edit_news'] . ' - ' . $txt['settings']; + $context['sub_template'] = 'show_settings'; + + // Needed for the inline permission functions, and the settings template. + require_once($sourcedir . '/ManagePermissions.php'); + require_once($sourcedir . '/ManageServer.php'); + + // Wrap it all up nice and warm... + $context['post_url'] = $scripturl . '?action=admin;area=news;save;sa=settings'; + $context['permissions_excluded'] = array(-1); + + // Add some javascript at the bottom... + $context['settings_insert_below'] = ' + '; + + // Saving the settings? + if (isset($_GET['save'])) + { + checkSession(); + + saveDBSettings($config_vars); + redirectexit('action=admin;area=news;sa=settings'); + } + + prepareDBSettingContext($config_vars); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManagePaid.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManagePaid.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1778 @@ + array('ModifySubscription', 'admin_forum'), + 'modifyuser' => array('ModifyUserSubscription', 'admin_forum'), + 'settings' => array('ModifySubscriptionSettings', 'admin_forum'), + 'view' => array('ViewSubscriptions', 'admin_forum'), + 'viewsub' => array('ViewSubscribedUsers', 'admin_forum'), + ); + + // Default the sub-action to 'view subscriptions', but only if they have already set things up.. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (!empty($modSettings['paid_currency_symbol']) ? 'view' : 'settings'); + + // Make sure you can do this. + isAllowedTo($subActions[$_REQUEST['sa']][1]); + + $context['page_title'] = $txt['paid_subscriptions']; + + // Tabs for browsing the different subscription functions. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['paid_subscriptions'], + 'help' => '', + 'description' => $txt['paid_subscriptions_desc'], + 'tabs' => array( + 'view' => array( + 'description' => $txt['paid_subs_view_desc'], + ), + 'settings' => array( + 'description' => $txt['paid_subs_settings_desc'], + ), + ), + ); + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']][0](); +} + +// Modify which payment methods are to be used. +function ModifySubscriptionSettings($return_config = false) +{ + global $context, $txt, $modSettings, $sourcedir, $smcFunc, $scripturl; + + // If the currency is set to something different then we need to set it to other for this to work and set it back shortly. + $modSettings['paid_currency'] = !empty($modSettings['paid_currency_code']) ? $modSettings['paid_currency_code'] : ''; + if (!empty($modSettings['paid_currency_code']) && !in_array($modSettings['paid_currency_code'], array('usd', 'eur', 'gbp'))) + $modSettings['paid_currency'] = 'other'; + + // These are all the default settings. + $config_vars = array( + array('select', 'paid_email', array(0 => $txt['paid_email_no'], 1 => $txt['paid_email_error'], 2 => $txt['paid_email_all']), 'subtext' => $txt['paid_email_desc']), + array('text', 'paid_email_to', 'subtext' => $txt['paid_email_to_desc'], 'size' => 60), + '', + 'dummy_currency' => array('select', 'paid_currency', array('usd' => $txt['usd'], 'eur' => $txt['eur'], 'gbp' => $txt['gbp'], 'other' => $txt['other']), 'javascript' => 'onchange="toggleOther();"'), + array('text', 'paid_currency_code', 'subtext' => $txt['paid_currency_code_desc'], 'size' => 5, 'force_div_id' => 'custom_currency_code_div'), + array('text', 'paid_currency_symbol', 'subtext' => $txt['paid_currency_symbol_desc'], 'size' => 8, 'force_div_id' => 'custom_currency_symbol_div'), + array('check', 'paidsubs_test', 'subtext' => $txt['paidsubs_test_desc'], 'onclick' => 'return document.getElementById(\'paidsubs_test\').checked ? confirm(\'' . $txt['paidsubs_test_confirm'] . '\') : true;'), + ); + + // Now load all the other gateway settings. + $gateways = loadPaymentGateways(); + foreach ($gateways as $gateway) + { + $gatewayClass = new $gateway['display_class'](); + $setting_data = $gatewayClass->getGatewaySettings(); + if (!empty($setting_data)) + { + $config_vars[] = array('title', $gatewayClass->title, 'text_label' => (isset($txt['paidsubs_gateway_title_' . $gatewayClass->title]) ? $txt['paidsubs_gateway_title_' . $gatewayClass->title] : $gatewayClass->title)); + $config_vars = array_merge($config_vars, $setting_data); + } + } + + // Just searching? + if ($return_config) + return $config_vars; + + // Get the settings template fired up. + require_once($sourcedir . '/ManageServer.php'); + + // Some important context stuff + $context['page_title'] = $txt['settings']; + $context['sub_template'] = 'show_settings'; + $context['settings_message'] = '' . $txt['paid_note'] . ''; + $context[$context['admin_menu_name']]['current_subsection'] = 'settings'; + + // Get the final touches in place. + $context['post_url'] = $scripturl . '?action=admin;area=paidsubscribe;save;sa=settings'; + $context['settings_title'] = $txt['settings']; + + // We want javascript for our currency options. + $context['settings_insert_below'] = ' + '; + + // Saving the settings? + if (isset($_GET['save'])) + { + checkSession(); + + // Sort out the currency stuff. + if ($_POST['paid_currency'] != 'other') + { + $_POST['paid_currency_code'] = $_POST['paid_currency']; + $_POST['paid_currency_symbol'] = $txt[$_POST['paid_currency'] . '_symbol']; + } + unset($config_vars['dummy_currency']); + + saveDBSettings($config_vars); + + redirectexit('action=admin;area=paidsubscribe;sa=settings'); + } + + // Prepare the settings... + prepareDBSettingContext($config_vars); +} + +// Are we looking at viewing the subscriptions? +function ViewSubscriptions() +{ + global $context, $txt, $modSettings, $smcFunc, $sourcedir, $scripturl; + + // Not made the settings yet? + if (empty($modSettings['paid_currency_symbol'])) + fatal_lang_error('paid_not_set_currency', false, $scripturl . '?action=admin;area=paidsubscribe;sa=settings'); + + // Some basic stuff. + $context['page_title'] = $txt['paid_subs_view']; + loadSubscriptions(); + + $listOptions = array( + 'id' => 'subscription_list', + 'items_per_page' => 20, + 'base_href' => $scripturl . '?action=admin;area=paidsubscribe;sa=view', + 'get_items' => array( + 'function' => create_function('', ' + global $context; + return $context[\'subscriptions\']; + '), + ), + 'get_count' => array( + 'function' => create_function('', ' + global $context; + return count($context[\'subscriptions\']); + '), + ), + 'no_items_label' => $txt['paid_none_yet'], + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['paid_name'], + 'style' => 'text-align: left; width: 35%;', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $scripturl; + + return sprintf(\'%3$s\', $scripturl, $rowData[\'id\'], $rowData[\'name\']); + '), + ), + ), + 'cost' => array( + 'header' => array( + 'value' => $txt['paid_cost'], + 'style' => 'text-align: left;', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt; + + return $rowData[\'flexible\'] ? \'\' . $txt[\'flexible\'] . \'\' : $rowData[\'cost\'] . \' / \' . $rowData[\'length\']; + '), + ), + ), + 'pending' => array( + 'header' => array( + 'value' => $txt['paid_pending'], + 'style' => 'width: 18%;', + ), + 'data' => array( + 'db_htmlsafe' => 'pending', + 'style' => 'text-align: center;', + ), + ), + 'finished' => array( + 'header' => array( + 'value' => $txt['paid_finished'], + ), + 'data' => array( + 'db_htmlsafe' => 'finished', + 'style' => 'text-align: center;', + ), + ), + 'total' => array( + 'header' => array( + 'value' => $txt['paid_active'], + ), + 'data' => array( + 'db_htmlsafe' => 'total', + 'style' => 'text-align: center;', + ), + ), + 'is_active' => array( + 'header' => array( + 'value' => $txt['paid_is_active'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt; + + return \'\' . ($rowData[\'active\'] ? $txt[\'yes\'] : $txt[\'no\']) . \'\'; + '), + 'style' => 'text-align: center;', + ), + ), + 'modify' => array( + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt, $scripturl; + + return \'\' . $txt[\'modify\'] . \'\'; + '), + 'style' => 'text-align: center;', + ), + ), + 'delete' => array( + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt, $scripturl; + + return \'\' . $txt[\'delete\'] . \'\'; + '), + 'style' => 'text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=paidsubscribe;sa=modify', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' + + ', + 'style' => 'text-align: right;', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'subscription_list'; +} + +// Adding, editing and deleting subscriptions. +function ModifySubscription() +{ + global $context, $txt, $modSettings, $smcFunc; + + $context['sub_id'] = isset($_REQUEST['sid']) ? (int) $_REQUEST['sid'] : 0; + $context['action_type'] = $context['sub_id'] ? (isset($_REQUEST['delete']) ? 'delete' : 'edit') : 'add'; + + // Setup the template. + $context['sub_template'] = $context['action_type'] == 'delete' ? 'delete_subscription' : 'modify_subscription'; + $context['page_title'] = $txt['paid_' . $context['action_type'] . '_subscription']; + + // Delete it? + if (isset($_POST['delete_confirm']) && isset($_REQUEST['delete'])) + { + checkSession(); + + $smcFunc['db_query']('delete_subscription', ' + DELETE FROM {db_prefix}subscriptions + WHERE id_subscribe = {int:current_subscription}', + array( + 'current_subscription' => $context['sub_id'], + ) + ); + + redirectexit('action=admin;area=paidsubscribe;view'); + } + + // Saving? + if (isset($_POST['save'])) + { + checkSession(); + + // Some cleaning... + $isActive = isset($_POST['active']) ? 1 : 0; + $isRepeatable = isset($_POST['repeatable']) ? 1 : 0; + $allowpartial = isset($_POST['allow_partial']) ? 1 : 0; + $reminder = isset($_POST['reminder']) ? (int) $_POST['reminder'] : 0; + $emailComplete = strlen($_POST['emailcomplete']) > 10 ? trim($_POST['emailcomplete']) : ''; + + // Is this a fixed one? + if ($_POST['duration_type'] == 'fixed') + { + // Clean the span. + $span = $_POST['span_value'] . $_POST['span_unit']; + + // Sort out the cost. + $cost = array('fixed' => sprintf('%01.2f', strtr($_POST['cost'], ',', '.'))); + + // There needs to be something. + if (empty($_POST['span_value']) || empty($_POST['cost'])) + fatal_lang_error('paid_no_cost_value'); + } + // Flexible is harder but more fun ;) + else + { + $span = 'F'; + + $cost = array( + 'day' => sprintf('%01.2f', strtr($_POST['cost_day'], ',', '.')), + 'week' => sprintf('%01.2f', strtr($_POST['cost_week'], ',', '.')), + 'month' => sprintf('%01.2f', strtr($_POST['cost_month'], ',', '.')), + 'year' => sprintf('%01.2f', strtr($_POST['cost_year'], ',', '.')), + ); + + if (empty($_POST['cost_day']) && empty($_POST['cost_week']) && empty($_POST['cost_month']) && empty($_POST['cost_year'])) + fatal_lang_error('paid_all_freq_blank'); + } + $cost = serialize($cost); + + // Yep, time to do additional groups. + $addgroups = array(); + if (!empty($_POST['addgroup'])) + foreach ($_POST['addgroup'] as $id => $dummy) + $addgroups[] = (int) $id; + $addgroups = implode(',', $addgroups); + + // Is it new?! + if ($context['action_type'] == 'add') + { + $smcFunc['db_insert']('', + '{db_prefix}subscriptions', + array( + 'name' => 'string-60', 'description' => 'string-255', 'active' => 'int', 'length' => 'string-4', 'cost' => 'string', + 'id_group' => 'int', 'add_groups' => 'string-40', 'repeatable' => 'int', 'allow_partial' => 'int', 'email_complete' => 'string', + 'reminder' => 'int', + ), + array( + $_POST['name'], $_POST['desc'], $isActive, $span, $cost, + $_POST['prim_group'], $addgroups, $isRepeatable, $allowpartial, $emailComplete, + $reminder, + ), + array('id_subscribe') + ); + } + // Otherwise must be editing. + else + { + // Don't do groups if there are active members + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_subscribed + WHERE id_subscribe = {int:current_subscription} + AND status = {int:is_active}', + array( + 'current_subscription' => $context['sub_id'], + 'is_active' => 1, + ) + ); + list ($disableGroups) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $smcFunc['db_query']('substring', ' + UPDATE {db_prefix}subscriptions + SET name = SUBSTRING({string:name}, 1, 60), description = SUBSTRING({string:description}, 1, 255), active = {int:is_active}, + length = SUBSTRING({string:length}, 1, 4), cost = {string:cost}' . ($disableGroups ? '' : ', id_group = {int:id_group}, + add_groups = {string:additional_groups}') . ', repeatable = {int:repeatable}, allow_partial = {int:allow_partial}, + email_complete = {string:email_complete}, reminder = {int:reminder} + WHERE id_subscribe = {int:current_subscription}', + array( + 'is_active' => $isActive, + 'id_group' => !empty($_POST['prim_group']) ? $_POST['prim_group'] : 0, + 'repeatable' => $isRepeatable, + 'allow_partial' => $allowpartial, + 'reminder' => $reminder, + 'current_subscription' => $context['sub_id'], + 'name' => $_POST['name'], + 'description' => $_POST['desc'], + 'length' => $span, + 'cost' => $cost, + 'additional_groups' => !empty($addgroups) ? $addgroups : '', + 'email_complete' => $emailComplete, + ) + ); + } + + redirectexit('action=admin;area=paidsubscribe;view'); + } + + // Defaults. + if ($context['action_type'] == 'add') + { + $context['sub'] = array( + 'name' => '', + 'desc' => '', + 'cost' => array( + 'fixed' => 0, + ), + 'span' => array( + 'value' => '', + 'unit' => 'D', + ), + 'prim_group' => 0, + 'add_groups' => array(), + 'active' => 1, + 'repeatable' => 1, + 'allow_partial' => 0, + 'duration' => 'fixed', + 'email_complete' => '', + 'reminder' => 0, + ); + } + // Otherwise load up all the details. + else + { + $request = $smcFunc['db_query']('', ' + SELECT name, description, cost, length, id_group, add_groups, active, repeatable, allow_partial, email_complete, reminder + FROM {db_prefix}subscriptions + WHERE id_subscribe = {int:current_subscription} + LIMIT 1', + array( + 'current_subscription' => $context['sub_id'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Sort the date. + preg_match('~(\d*)(\w)~', $row['length'], $match); + if (isset($match[2])) + { + $span_value = $match[1]; + $span_unit = $match[2]; + } + else + { + $span_value = 0; + $span_unit = 'D'; + } + + // Is this a flexible one? + if ($row['length'] == 'F') + $isFlexible = true; + else + $isFlexible = false; + + $context['sub'] = array( + 'name' => $row['name'], + 'desc' => $row['description'], + 'cost' => @unserialize($row['cost']), + 'span' => array( + 'value' => $span_value, + 'unit' => $span_unit, + ), + 'prim_group' => $row['id_group'], + 'add_groups' => explode(',', $row['add_groups']), + 'active' => $row['active'], + 'repeatable' => $row['repeatable'], + 'allow_partial' => $row['allow_partial'], + 'duration' => $isFlexible ? 'flexible' : 'fixed', + 'email_complete' => htmlspecialchars($row['email_complete']), + 'reminder' => $row['reminder'], + ); + } + $smcFunc['db_free_result']($request); + + // Does this have members who are active? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_subscribed + WHERE id_subscribe = {int:current_subscription} + AND status = {int:is_active}', + array( + 'current_subscription' => $context['sub_id'], + 'is_active' => 1, + ) + ); + list ($context['disable_groups']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // Load up all the groups. + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name + FROM {db_prefix}membergroups + WHERE id_group != {int:moderator_group} + AND min_posts = {int:min_posts}', + array( + 'moderator_group' => 3, + 'min_posts' => -1, + ) + ); + $context['groups'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['groups'][$row['id_group']] = $row['group_name']; + $smcFunc['db_free_result']($request); +} + +// View all the users subscribed to a particular subscription! +function ViewSubscribedUsers() +{ + global $context, $txt, $modSettings, $scripturl, $options, $smcFunc, $sourcedir; + + // Setup the template. + $context['page_title'] = $txt['viewing_users_subscribed']; + + // ID of the subscription. + $context['sub_id'] = (int) $_REQUEST['sid']; + + // Load the subscription information. + $request = $smcFunc['db_query']('', ' + SELECT id_subscribe, name, description, cost, length, id_group, add_groups, active + FROM {db_prefix}subscriptions + WHERE id_subscribe = {int:current_subscription}', + array( + 'current_subscription' => $context['sub_id'], + ) + ); + // Something wrong? + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + // Do the subscription context. + $row = $smcFunc['db_fetch_assoc']($request); + $context['subscription'] = array( + 'id' => $row['id_subscribe'], + 'name' => $row['name'], + 'desc' => $row['description'], + 'active' => $row['active'], + ); + $smcFunc['db_free_result']($request); + + // Are we searching for people? + $search_string = isset($_POST['ssearch']) && !empty($_POST['sub_search']) ? ' AND IFNULL(mem.real_name, {string:guest}) LIKE {string:search}' : ''; + $search_vars = empty($_POST['sub_search']) ? array() : array('search' => '%' . $_POST['sub_search'] . '%', 'guest' => $txt['guest']); + + $listOptions = array( + 'id' => 'subscribed_users_list', + 'items_per_page' => 20, + 'base_href' => $scripturl . '?action=admin;area=paidsubscribe;sa=viewsub;sid=' . $context['sub_id'], + 'default_sort_col' => 'name', + 'get_items' => array( + 'function' => 'list_getSubscribedUsers', + 'params' => array( + $context['sub_id'], + $search_string, + $search_vars, + ), + ), + 'get_count' => array( + 'function' => 'list_getSubscribedUserCount', + 'params' => array( + $context['sub_id'], + $search_string, + $search_vars, + ), + ), + 'no_items_label' => $txt['no_subscribers'], + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['who_member'], + 'style' => 'text-align: left; width: 20%;', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt, $scripturl; + + return $rowData[\'id_member\'] == 0 ? $txt[\'guest\'] : \'\' . $rowData[\'name\'] . \'\'; + '), + ), + 'sort' => array( + 'default' => 'name', + 'reverse' => 'name DESC', + ), + ), + 'status' => array( + 'header' => array( + 'value' => $txt['paid_status'], + 'style' => 'text-align: left; width: 10%;', + ), + 'data' => array( + 'db_htmlsafe' => 'status_text', + ), + 'sort' => array( + 'default' => 'status', + 'reverse' => 'status DESC', + ), + ), + 'payments_pending' => array( + 'header' => array( + 'value' => $txt['paid_payments_pending'], + 'style' => 'text-align: left; width: 10%;', + ), + 'data' => array( + 'db_htmlsafe' => 'pending', + ), + 'sort' => array( + 'default' => 'payments_pending', + 'reverse' => 'payments_pending DESC', + ), + ), + 'start_time' => array( + 'header' => array( + 'value' => $txt['start_date'], + 'style' => 'text-align: left; width: 20%;', + ), + 'data' => array( + 'db_htmlsafe' => 'start_date', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'start_time', + 'reverse' => 'start_time DESC', + ), + ), + 'end_time' => array( + 'header' => array( + 'value' => $txt['end_date'], + 'style' => 'text-align: left; width: 20%;', + ), + 'data' => array( + 'db_htmlsafe' => 'end_date', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'end_time', + 'reverse' => 'end_time DESC', + ), + ), + 'modify' => array( + 'header' => array( + 'style' => 'width: 10%;', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt, $scripturl; + + return \'\' . $txt[\'modify\'] . \'\'; + '), + 'style' => 'text-align: center;', + ), + ), + 'delete' => array( + 'header' => array( + 'style' => 'width: 4%;', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt, $scripturl; + + return \'\'; + '), + 'style' => 'text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=paidsubscribe;sa=modifyuser;sid=' . $context['sub_id'], + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' +
+ +
+
+ + +
+ ', + ), + array( + 'position' => 'top_of_list', + 'value' => ' +
+

' . sprintf($txt['view_users_subscribed'], $row['name']) . '

+
+
+ + +
+ ', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'subscribed_users_list'; +} + +// Returns how many people are subscribed to a paid subscription. +function list_getSubscribedUserCount($id_sub, $search_string, $search_vars = array()) +{ + global $smcFunc; + + // Get the total amount of users. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS total_subs + FROM {db_prefix}log_subscribed AS ls + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ls.id_member) + WHERE ls.id_subscribe = {int:current_subscription} ' . $search_string . ' + AND (ls.end_time != {int:no_end_time} OR ls.payments_pending != {int:no_pending_payments})', + array_merge($search_vars, array( + 'current_subscription' => $id_sub, + 'no_end_time' => 0, + 'no_pending_payments' => 0, + )) + ); + list ($memberCount) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $memberCount; +} + +function list_getSubscribedUsers($start, $items_per_page, $sort, $id_sub, $search_string, $search_vars = array()) +{ + global $smcFunc, $txt; + + $request = $smcFunc['db_query']('', ' + SELECT ls.id_sublog, IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, {string:guest}) AS name, ls.start_time, ls.end_time, + ls.status, ls.payments_pending + FROM {db_prefix}log_subscribed AS ls + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ls.id_member) + WHERE ls.id_subscribe = {int:current_subscription} ' . $search_string . ' + AND (ls.end_time != {int:no_end_time} OR ls.payments_pending != {int:no_payments_pending}) + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array_merge($search_vars, array( + 'current_subscription' => $id_sub, + 'no_end_time' => 0, + 'no_payments_pending' => 0, + 'guest' => $txt['guest'], + )) + ); + $subscribers = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $subscribers[] = array( + 'id' => $row['id_sublog'], + 'id_member' => $row['id_member'], + 'name' => $row['name'], + 'start_date' => timeformat($row['start_time'], false), + 'end_date' => $row['end_time'] == 0 ? 'N/A' : timeformat($row['end_time'], false), + 'pending' => $row['payments_pending'], + 'status' => $row['status'], + 'status_text' => $row['status'] == 0 ? ($row['payments_pending'] == 0 ? $txt['paid_finished'] : $txt['paid_pending']) : $txt['paid_active'], + ); + $smcFunc['db_free_result']($request); + + return $subscribers; +} + +// Edit or add a user subscription. +function ModifyUserSubscription() +{ + global $context, $txt, $modSettings, $smcFunc; + + loadSubscriptions(); + + $context['log_id'] = isset($_REQUEST['lid']) ? (int) $_REQUEST['lid'] : 0; + $context['sub_id'] = isset($_REQUEST['sid']) ? (int) $_REQUEST['sid'] : 0; + $context['action_type'] = $context['log_id'] ? 'edit' : 'add'; + + // Setup the template. + $context['sub_template'] = 'modify_user_subscription'; + $context['page_title'] = $txt[$context['action_type'] . '_subscriber']; + + // If we haven't been passed the subscription ID get it. + if ($context['log_id'] && !$context['sub_id']) + { + $request = $smcFunc['db_query']('', ' + SELECT id_subscribe + FROM {db_prefix}log_subscribed + WHERE id_sublog = {int:current_log_item}', + array( + 'current_log_item' => $context['log_id'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + list ($context['sub_id']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + if (!isset($context['subscriptions'][$context['sub_id']])) + fatal_lang_error('no_access', false); + $context['current_subscription'] = $context['subscriptions'][$context['sub_id']]; + + // Searching? + if (isset($_POST['ssearch'])) + { + return ViewSubscribedUsers(); + } + // Saving? + elseif (isset($_REQUEST['save_sub'])) + { + checkSession(); + + // Work out the dates... + $starttime = mktime($_POST['hour'], $_POST['minute'], 0, $_POST['month'], $_POST['day'], $_POST['year']); + $endtime = mktime($_POST['hourend'], $_POST['minuteend'], 0, $_POST['monthend'], $_POST['dayend'], $_POST['yearend']); + + // Status. + $status = $_POST['status']; + + // New one? + if (empty($context['log_id'])) + { + // Find the user... + $request = $smcFunc['db_query']('', ' + SELECT id_member, id_group + FROM {db_prefix}members + WHERE real_name = {string:name} + LIMIT 1', + array( + 'name' => $_POST['name'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('error_member_not_found'); + + list ($id_member, $id_group) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Ensure the member doesn't already have a subscription! + $request = $smcFunc['db_query']('', ' + SELECT id_subscribe + FROM {db_prefix}log_subscribed + WHERE id_subscribe = {int:current_subscription} + AND id_member = {int:current_member}', + array( + 'current_subscription' => $context['sub_id'], + 'current_member' => $id_member, + ) + ); + if ($smcFunc['db_num_rows']($request) != 0) + fatal_lang_error('member_already_subscribed'); + $smcFunc['db_free_result']($request); + + // Actually put the subscription in place. + if ($status == 1) + addSubscription($context['sub_id'], $id_member, 0, $starttime, $endtime); + else + { + $smcFunc['db_insert']('', + '{db_prefix}log_subscribed', + array( + 'id_subscribe' => 'int', 'id_member' => 'int', 'old_id_group' => 'int', 'start_time' => 'int', + 'end_time' => 'int', 'status' => 'int', + ), + array( + $context['sub_id'], $id_member, $id_group, $starttime, + $endtime, $status, + ), + array('id_sublog') + ); + } + } + // Updating. + else + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, status + FROM {db_prefix}log_subscribed + WHERE id_sublog = {int:current_log_item}', + array( + 'current_log_item' => $context['log_id'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + + list ($id_member, $old_status) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Pick the right permission stuff depending on what the status is changing from/to. + if ($old_status == 1 && $status != 1) + removeSubscription($context['sub_id'], $id_member); + elseif ($status == 1 && $old_status != 1) + { + addSubscription($context['sub_id'], $id_member, 0, $starttime, $endtime); + } + else + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET start_time = {int:start_time}, end_time = {int:end_time}, status = {int:status} + WHERE id_sublog = {int:current_log_item}', + array( + 'start_time' => $starttime, + 'end_time' => $endtime, + 'status' => $status, + 'current_log_item' => $context['log_id'], + ) + ); + } + } + + // Done - redirect... + redirectexit('action=admin;area=paidsubscribe;sa=viewsub;sid=' . $context['sub_id']); + } + // Deleting? + elseif (isset($_REQUEST['delete']) || isset($_REQUEST['finished'])) + { + checkSession(); + + // Do the actual deletes! + if (!empty($_REQUEST['delsub'])) + { + $toDelete = array(); + foreach ($_REQUEST['delsub'] as $id => $dummy) + $toDelete[] = (int) $id; + + $request = $smcFunc['db_query']('', ' + SELECT id_subscribe, id_member + FROM {db_prefix}log_subscribed + WHERE id_sublog IN ({array_int:subscription_list})', + array( + 'subscription_list' => $toDelete, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + removeSubscription($row['id_subscribe'], $row['id_member'], isset($_REQUEST['delete'])); + $smcFunc['db_free_result']($request); + } + redirectexit('action=admin;area=paidsubscribe;sa=viewsub;sid=' . $context['sub_id']); + } + + // Default attributes. + if ($context['action_type'] == 'add') + { + $context['sub'] = array( + 'id' => 0, + 'start' => array( + 'year' => (int) strftime('%Y', time()), + 'month' => (int) strftime('%m', time()), + 'day' => (int) strftime('%d', time()), + 'hour' => (int) strftime('%H', time()), + 'min' => (int) strftime('%M', time()) < 10 ? '0' . (int) strftime('%M', time()) : (int) strftime('%M', time()), + 'last_day' => 0, + ), + 'end' => array( + 'year' => (int) strftime('%Y', time()), + 'month' => (int) strftime('%m', time()), + 'day' => (int) strftime('%d', time()), + 'hour' => (int) strftime('%H', time()), + 'min' => (int) strftime('%M', time()) < 10 ? '0' . (int) strftime('%M', time()) : (int) strftime('%M', time()), + 'last_day' => 0, + ), + 'status' => 1, + ); + $context['sub']['start']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['sub']['start']['month'] == 12 ? 1 : $context['sub']['start']['month'] + 1, 0, $context['sub']['start']['month'] == 12 ? $context['sub']['start']['year'] + 1 : $context['sub']['start']['year'])); + $context['sub']['end']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['sub']['end']['month'] == 12 ? 1 : $context['sub']['end']['month'] + 1, 0, $context['sub']['end']['month'] == 12 ? $context['sub']['end']['year'] + 1 : $context['sub']['end']['year'])); + + if (isset($_GET['uid'])) + { + $request = $smcFunc['db_query']('', ' + SELECT real_name + FROM {db_prefix}members + WHERE id_member = {int:current_member}', + array( + 'current_member' => (int) $_GET['uid'], + ) + ); + list ($context['sub']['username']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + $context['sub']['username'] = ''; + } + // Otherwise load the existing info. + else + { + $request = $smcFunc['db_query']('', ' + SELECT ls.id_sublog, ls.id_subscribe, ls.id_member, start_time, end_time, status, payments_pending, pending_details, + IFNULL(mem.real_name, {string:blank_string}) AS username + FROM {db_prefix}log_subscribed AS ls + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ls.id_member) + WHERE ls.id_sublog = {int:current_subscription_item} + LIMIT 1', + array( + 'current_subscription_item' => $context['log_id'], + 'blank_string' => '', + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Any pending payments? + $context['pending_payments'] = array(); + if (!empty($row['pending_details'])) + { + $pending_details = @unserialize($row['pending_details']); + foreach ($pending_details as $id => $pending) + { + // Only this type need be displayed. + if ($pending[3] == 'payback') + { + // Work out what the options were. + $costs = @unserialize($context['current_subscription']['real_cost']); + + if ($context['current_subscription']['real_length'] == 'F') + { + foreach ($costs as $duration => $cost) + { + if ($cost != 0 && $cost == $pending[1] && $duration == $pending[2]) + $context['pending_payments'][$id] = array( + 'desc' => sprintf($modSettings['paid_currency_symbol'], $cost . '/' . $txt[$duration]), + ); + } + } + elseif ($costs['fixed'] == $pending[1]) + { + $context['pending_payments'][$id] = array( + 'desc' => sprintf($modSettings['paid_currency_symbol'], $costs['fixed']), + ); + } + } + } + + // Check if we are adding/removing any. + if (isset($_GET['pending'])) + { + foreach ($pending_details as $id => $pending) + { + // Found the one to action? + if ($_GET['pending'] == $id && $pending[3] == 'payback' && isset($context['pending_payments'][$id])) + { + // Flexible? + if (isset($_GET['accept'])) + addSubscription($context['current_subscription']['id'], $row['id_member'], $context['current_subscription']['real_length'] == 'F' ? strtoupper(substr($pending[2], 0, 1)) : 0); + unset($pending_details[$id]); + + $new_details = serialize($pending_details); + + // Update the entry. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET payments_pending = payments_pending - 1, pending_details = {string:pending_details} + WHERE id_sublog = {int:current_subscription_item}', + array( + 'current_subscription_item' => $context['log_id'], + 'pending_details' => $new_details, + ) + ); + + // Reload + redirectexit('action=admin;area=paidsubscribe;sa=modifyuser;lid=' . $context['log_id']); + } + } + } + } + + $context['sub_id'] = $row['id_subscribe']; + $context['sub'] = array( + 'id' => 0, + 'start' => array( + 'year' => (int) strftime('%Y', $row['start_time']), + 'month' => (int) strftime('%m', $row['start_time']), + 'day' => (int) strftime('%d', $row['start_time']), + 'hour' => (int) strftime('%H', $row['start_time']), + 'min' => (int) strftime('%M', $row['start_time']) < 10 ? '0' . (int) strftime('%M', $row['start_time']) : (int) strftime('%M', $row['start_time']), + 'last_day' => 0, + ), + 'end' => array( + 'year' => (int) strftime('%Y', $row['end_time']), + 'month' => (int) strftime('%m', $row['end_time']), + 'day' => (int) strftime('%d', $row['end_time']), + 'hour' => (int) strftime('%H', $row['end_time']), + 'min' => (int) strftime('%M', $row['end_time']) < 10 ? '0' . (int) strftime('%M', $row['end_time']) : (int) strftime('%M', $row['end_time']), + 'last_day' => 0, + ), + 'status' => $row['status'], + 'username' => $row['username'], + ); + $context['sub']['start']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['sub']['start']['month'] == 12 ? 1 : $context['sub']['start']['month'] + 1, 0, $context['sub']['start']['month'] == 12 ? $context['sub']['start']['year'] + 1 : $context['sub']['start']['year'])); + $context['sub']['end']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['sub']['end']['month'] == 12 ? 1 : $context['sub']['end']['month'] + 1, 0, $context['sub']['end']['month'] == 12 ? $context['sub']['end']['year'] + 1 : $context['sub']['end']['year'])); + } +} + +// Re-apply subscription rules. +function reapplySubscriptions($users) +{ + global $smcFunc; + + // Make it an array. + if (!is_array($users)) + $users = array($users); + + // Get all the members current groups. + $groups = array(); + $request = $smcFunc['db_query']('', ' + SELECT id_member, id_group, additional_groups + FROM {db_prefix}members + WHERE id_member IN ({array_int:user_list})', + array( + 'user_list' => $users, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $groups[$row['id_member']] = array( + 'primary' => $row['id_group'], + 'additional' => explode(',', $row['additional_groups']), + ); + } + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT ls.id_member, ls.old_id_group, s.id_group, s.add_groups + FROM {db_prefix}log_subscribed AS ls + INNER JOIN {db_prefix}subscriptions AS s ON (s.id_subscribe = ls.id_subscribe) + WHERE ls.id_member IN ({array_int:user_list}) + AND ls.end_time > {int:current_time}', + array( + 'user_list' => $users, + 'current_time' => time(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Specific primary group? + if ($row['id_group'] != 0) + { + // If this is changing - add the old one to the additional groups so it's not lost. + if ($row['id_group'] != $groups[$row['id_member']]['primary']) + $groups[$row['id_member']]['additional'][] = $groups[$row['id_member']]['primary']; + $groups[$row['id_member']]['primary'] = $row['id_group']; + } + + // Additional groups. + if (!empty($row['add_groups'])) + $groups[$row['id_member']]['additional'] = array_merge($groups[$row['id_member']]['additional'], explode(',', $row['add_groups'])); + } + $smcFunc['db_free_result']($request); + + // Update all the members. + foreach ($groups as $id => $group) + { + $group['additional'] = array_unique($group['additional']); + foreach ($group['additional'] as $key => $value) + if (empty($value)) + unset($group['additional'][$key]); + $addgroups = implode(',', $group['additional']); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_group = {int:primary_group}, additional_groups = {string:additional_groups} + WHERE id_member = {int:current_member} + LIMIT 1', + array( + 'primary_group' => $group['primary'], + 'current_member' => $id, + 'additional_groups' => $addgroups, + ) + ); + } +} + +// Add or extend a subscription of a user. +function addSubscription($id_subscribe, $id_member, $renewal = 0, $forceStartTime = 0, $forceEndTime = 0) +{ + global $context, $smcFunc; + + // Take the easy way out... + loadSubscriptions(); + + // Exists, yes? + if (!isset($context['subscriptions'][$id_subscribe])) + return; + + $curSub = $context['subscriptions'][$id_subscribe]; + + // Grab the duration. + $duration = $curSub['num_length']; + + // If this is a renewal change the duration to be correct. + if (!empty($renewal)) + { + switch ($renewal) + { + case 'D': + $duration = 86400; + break; + case 'W': + $duration = 604800; + break; + case 'M': + $duration = 2629743; + break; + case 'Y': + $duration = 31556926; + break; + default: + break; + } + } + + // Firstly, see whether it exists, and is active. If so then this is meerly an extension. + $request = $smcFunc['db_query']('', ' + SELECT id_sublog, end_time, start_time + FROM {db_prefix}log_subscribed + WHERE id_subscribe = {int:current_subscription} + AND id_member = {int:current_member} + AND status = {int:is_active}', + array( + 'current_subscription' => $id_subscribe, + 'current_member' => $id_member, + 'is_active' => 1, + ) + ); + if ($smcFunc['db_num_rows']($request) != 0) + { + list ($id_sublog, $endtime, $starttime) = $smcFunc['db_fetch_row']($request); + + // If this has already expired but is active, extension means the period from now. + if ($endtime < time()) + $endtime = time(); + if ($starttime == 0) + $starttime = time(); + + // Work out the new expiry date. + $endtime += $duration; + + if ($forceEndTime != 0) + $endtime = $forceEndTime; + + // As everything else should be good, just update! + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET end_time = {int:end_time}, start_time = {int:start_time} + WHERE id_sublog = {int:current_subscription_item}', + array( + 'end_time' => $endtime, + 'start_time' => $starttime, + 'current_subscription_item' => $id_sublog, + ) + ); + + return; + } + $smcFunc['db_free_result']($request); + + // If we're here, that means we don't have an active subscription - that means we need to do some work! + $request = $smcFunc['db_query']('', ' + SELECT m.id_group, m.additional_groups + FROM {db_prefix}members AS m + WHERE m.id_member = {int:current_member}', + array( + 'current_member' => $id_member, + ) + ); + // Just in case the member doesn't exist. + if ($smcFunc['db_num_rows']($request) == 0) + return; + + list ($old_id_group, $additional_groups) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Prepare additional groups. + $newAddGroups = explode(',', $curSub['add_groups']); + $curAddGroups = explode(',', $additional_groups); + + $newAddGroups = array_merge($newAddGroups, $curAddGroups); + + // Simple, simple, simple - hopefully... id_group first. + if ($curSub['prim_group'] != 0) + { + $id_group = $curSub['prim_group']; + + // Ensure their old privileges are maintained. + if ($old_id_group != 0) + $newAddGroups[] = $old_id_group; + } + else + $id_group = $old_id_group; + + // Yep, make sure it's unique, and no empties. + foreach ($newAddGroups as $k => $v) + if (empty($v)) + unset($newAddGroups[$k]); + $newAddGroups = array_unique($newAddGroups); + $newAddGroups = implode(',', $newAddGroups); + + // Store the new settings. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_group = {int:primary_group}, additional_groups = {string:additional_groups} + WHERE id_member = {int:current_member}', + array( + 'primary_group' => $id_group, + 'current_member' => $id_member, + 'additional_groups' => $newAddGroups, + ) + ); + + // Now log the subscription - maybe we have a dorment subscription we can restore? + $request = $smcFunc['db_query']('', ' + SELECT id_sublog, end_time, start_time + FROM {db_prefix}log_subscribed + WHERE id_subscribe = {int:current_subscription} + AND id_member = {int:current_member}', + array( + 'current_subscription' => $id_subscribe, + 'current_member' => $id_member, + ) + ); + //!!! Don't really need to do this twice... + if ($smcFunc['db_num_rows']($request) != 0) + { + list ($id_sublog, $endtime, $starttime) = $smcFunc['db_fetch_row']($request); + + // If this has already expired but is active, extension means the period from now. + if ($endtime < time()) + $endtime = time(); + if ($starttime == 0) + $starttime = time(); + + // Work out the new expiry date. + $endtime += $duration; + + if ($forceEndTime != 0) + $endtime = $forceEndTime; + + // As everything else should be good, just update! + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET start_time = {int:start_time}, end_time = {int:end_time}, old_id_group = {int:old_id_group}, status = {int:is_active}, + reminder_sent = {int:no_reminder_sent} + WHERE id_sublog = {int:current_subscription_item}', + array( + 'start_time' => $starttime, + 'end_time' => $endtime, + 'old_id_group' => $old_id_group, + 'is_active' => 1, + 'no_reminder_sent' => 0, + 'current_subscription_item' => $id_sublog, + ) + ); + + return; + } + $smcFunc['db_free_result']($request); + + // Otherwise a very simple insert. + $endtime = time() + $duration; + if ($forceEndTime != 0) + $endtime = $forceEndTime; + + if ($forceStartTime == 0) + $starttime = time(); + else + $starttime = $forceStartTime; + + $smcFunc['db_insert']('', + '{db_prefix}log_subscribed', + array( + 'id_subscribe' => 'int', 'id_member' => 'int', 'old_id_group' => 'int', 'start_time' => 'int', + 'end_time' => 'int', 'status' => 'int', 'pending_details' => 'string', + ), + array( + $id_subscribe, $id_member, $old_id_group, $starttime, + $endtime, 1, '', + ), + array('id_sublog') + ); +} + +// Removes a subscription from a user, as in removes the groups. +function removeSubscription($id_subscribe, $id_member, $delete = false) +{ + global $context, $smcFunc; + + loadSubscriptions(); + + // Load the user core bits. + $request = $smcFunc['db_query']('', ' + SELECT m.id_group, m.additional_groups + FROM {db_prefix}members AS m + WHERE m.id_member = {int:current_member}', + array( + 'current_member' => $id_member, + ) + ); + + // Just in case of errors. + if ($smcFunc['db_num_rows']($request) == 0) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_subscribed + WHERE id_member = {int:current_member}', + array( + 'current_member' => $id_member, + ) + ); + return; + } + list ($id_group, $additional_groups) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Get all of the subscriptions for this user that are active - it will be necessary! + $request = $smcFunc['db_query']('', ' + SELECT id_subscribe, old_id_group + FROM {db_prefix}log_subscribed + WHERE id_member = {int:current_member} + AND status = {int:is_active}', + array( + 'current_member' => $id_member, + 'is_active' => 1, + ) + ); + + // These variables will be handy, honest ;) + $removals = array(); + $allowed = array(); + $old_id_group = 0; + $new_id_group = -1; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($context['subscriptions'][$row['id_subscribe']])) + continue; + + // The one we're removing? + if ($row['id_subscribe'] == $id_subscribe) + { + $removals = explode(',', $context['subscriptions'][$row['id_subscribe']]['add_groups']); + if ($context['subscriptions'][$row['id_subscribe']]['prim_group'] != 0) + $removals[] = $context['subscriptions'][$row['id_subscribe']]['prim_group']; + $old_id_group = $row['old_id_group']; + } + // Otherwise things we allow. + else + { + $allowed = array_merge($allowed, explode(',', $context['subscriptions'][$row['id_subscribe']]['add_groups'])); + if ($context['subscriptions'][$row['id_subscribe']]['prim_group'] != 0) + { + $allowed[] = $context['subscriptions'][$row['id_subscribe']]['prim_group']; + $new_id_group = $context['subscriptions'][$row['id_subscribe']]['prim_group']; + } + } + } + $smcFunc['db_free_result']($request); + + // Now, for everything we are removing check they defintely are not allowed it. + $existingGroups = explode(',', $additional_groups); + foreach ($existingGroups as $key => $group) + if (empty($group) || (in_array($group, $removals) && !in_array($group, $allowed))) + unset($existingGroups[$key]); + + // Finally, do something with the current primary group. + if (in_array($id_group, $removals)) + { + // If this primary group is actually allowed keep it. + if (in_array($id_group, $allowed)) + $existingGroups[] = $id_group; + + // Either way, change the id_group back. + if ($new_id_group < 1) + { + // If we revert to the old id-group we need to ensure it wasn't from a subscription. + foreach ($context['subscriptions'] as $id => $group) + // It was? Make them a regular member then! + if ($group['prim_group'] == $old_id_group) + $old_id_group = 0; + + $id_group = $old_id_group; + } + else + $id_group = $new_id_group; + } + + // Crazy stuff, we seem to have our groups fixed, just make them unique + $existingGroups = array_unique($existingGroups); + $existingGroups = implode(',', $existingGroups); + + // Update the member + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_group = {int:primary_group}, additional_groups = {string:existing_groups} + WHERE id_member = {int:current_member}', + array( + 'primary_group' => $id_group, + 'current_member' => $id_member, + 'existing_groups' => $existingGroups, + ) + ); + + // Disable the subscription. + if (!$delete) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET status = {int:not_active} + WHERE id_member = {int:current_member} + AND id_subscribe = {int:current_subscription}', + array( + 'not_active' => 0, + 'current_member' => $id_member, + 'current_subscription' => $id_subscribe, + ) + ); + // Otherwise delete it! + else + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_subscribed + WHERE id_member = {int:current_member} + AND id_subscribe = {int:current_subscription}', + array( + 'current_member' => $id_member, + 'current_subscription' => $id_subscribe, + ) + ); +} + +// This just kind of caches all the subscription data. +function loadSubscriptions() +{ + global $context, $txt, $modSettings, $smcFunc; + + if (!empty($context['subscriptions'])) + return; + + // Make sure this is loaded, just in case. + loadLanguage('ManagePaid'); + + $request = $smcFunc['db_query']('', ' + SELECT id_subscribe, name, description, cost, length, id_group, add_groups, active, repeatable + FROM {db_prefix}subscriptions', + array( + ) + ); + $context['subscriptions'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Pick a cost. + $costs = @unserialize($row['cost']); + + if ($row['length'] != 'F' && !empty($modSettings['paid_currency_symbol']) && !empty($costs['fixed'])) + $cost = sprintf($modSettings['paid_currency_symbol'], $costs['fixed']); + else + $cost = '???'; + + // Do the span. + preg_match('~(\d*)(\w)~', $row['length'], $match); + if (isset($match[2])) + { + $num_length = $match[1]; + $length = $match[1] . ' '; + switch ($match[2]) + { + case 'D': + $length .= $txt['paid_mod_span_days']; + $num_length *= 86400; + break; + case 'W': + $length .= $txt['paid_mod_span_weeks']; + $num_length *= 604800; + break; + case 'M': + $length .= $txt['paid_mod_span_months']; + $num_length *= 2629743; + break; + case 'Y': + $length .= $txt['paid_mod_span_years']; + $num_length *= 31556926; + break; + } + } + else + $length = '??'; + + $context['subscriptions'][$row['id_subscribe']] = array( + 'id' => $row['id_subscribe'], + 'name' => $row['name'], + 'desc' => $row['description'], + 'cost' => $cost, + 'real_cost' => $row['cost'], + 'length' => $length, + 'num_length' => $num_length, + 'real_length' => $row['length'], + 'pending' => 0, + 'finished' => 0, + 'total' => 0, + 'active' => $row['active'], + 'prim_group' => $row['id_group'], + 'add_groups' => $row['add_groups'], + 'flexible' => $row['length'] == 'F' ? true : false, + 'repeatable' => $row['repeatable'], + ); + } + $smcFunc['db_free_result']($request); + + // Do the counts. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(id_sublog) AS member_count, id_subscribe, status + FROM {db_prefix}log_subscribed + GROUP BY id_subscribe, status', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $ind = $row['status'] == 0 ? 'finished' : 'total'; + + if (isset($context['subscriptions'][$row['id_subscribe']])) + $context['subscriptions'][$row['id_subscribe']][$ind] = $row['member_count']; + } + $smcFunc['db_free_result']($request); + + // How many payments are we waiting on? + $request = $smcFunc['db_query']('', ' + SELECT SUM(payments_pending) AS total_pending, id_subscribe + FROM {db_prefix}log_subscribed + GROUP BY id_subscribe', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (isset($context['subscriptions'][$row['id_subscribe']])) + $context['subscriptions'][$row['id_subscribe']]['pending'] = $row['total_pending']; + } + $smcFunc['db_free_result']($request); +} + +// Load all the payment gateways. +function loadPaymentGateways() +{ + global $sourcedir; + + $gateways = array(); + if ($dh = opendir($sourcedir)) + { + while (($file = readdir($dh)) !== false) + { + if (is_file($sourcedir .'/'. $file) && preg_match('~^Subscriptions-([A-Za-z\d]+)\.php$~', $file, $matches)) + { + // Check this is definitely a valid gateway! + $fp = fopen($sourcedir . '/' . $file, 'rb'); + $header = fread($fp, 4096); + fclose($fp); + + if (strpos($header, '// SMF Payment Gateway: ' . strtolower($matches[1])) !== false) + { + loadClassFile($file); + + $gateways[] = array( + 'filename' => $file, + 'code' => strtolower($matches[1]), + // Don't need anything snazier than this yet. + 'valid_version' => class_exists(strtolower($matches[1]) . '_payment') && class_exists(strtolower($matches[1]) . '_display'), + 'payment_class' => strtolower($matches[1]) . '_payment', + 'display_class' => strtolower($matches[1]) . '_display', + ); + } + } + } + } + closedir($dh); + + return $gateways; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManagePermissions.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManagePermissions.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2371 @@ + array('function_to_call', 'permission_needed'), + $subActions = array( + 'board' => array('PermissionByBoard', 'manage_permissions'), + 'index' => array('PermissionIndex', 'manage_permissions'), + 'modify' => array('ModifyMembergroup', 'manage_permissions'), + 'modify2' => array('ModifyMembergroup2', 'manage_permissions'), + 'quick' => array('SetQuickGroups', 'manage_permissions'), + 'quickboard' => array('SetQuickBoards', 'manage_permissions'), + 'postmod' => array('ModifyPostModeration', 'manage_permissions'), + 'profiles' => array('EditPermissionProfiles', 'manage_permissions'), + 'settings' => array('GeneralPermissionSettings', 'admin_forum'), + ); + + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (allowedTo('manage_permissions') ? 'index' : 'settings'); + isAllowedTo($subActions[$_REQUEST['sa']][1]); + + // Create the tabs for the template. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['permissions_title'], + 'help' => 'permissions', + 'description' => '', + 'tabs' => array( + 'index' => array( + 'description' => $txt['permissions_groups'], + ), + 'board' => array( + 'description' => $txt['permission_by_board_desc'], + ), + 'profiles' => array( + 'description' => $txt['permissions_profiles_desc'], + ), + 'postmod' => array( + 'description' => $txt['permissions_post_moderation_desc'], + ), + 'settings' => array( + 'description' => $txt['permission_settings_desc'], + ), + ), + ); + + $subActions[$_REQUEST['sa']][0](); +} + +function PermissionIndex() +{ + global $txt, $scripturl, $context, $settings, $modSettings, $smcFunc; + + $context['page_title'] = $txt['permissions_title']; + + // Load all the permissions. We'll need them in the template. + loadAllPermissions(); + + // Also load profiles, we may want to reset. + loadPermissionProfiles(); + + // Are we going to show the advanced options? + $context['show_advanced_options'] = empty($context['admin_preferences']['app']); + + // Determine the number of ungrouped members. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}members + WHERE id_group = {int:regular_group}', + array( + 'regular_group' => 0, + ) + ); + list ($num_members) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Fill the context variable with 'Guests' and 'Regular Members'. + $context['groups'] = array( + -1 => array( + 'id' => -1, + 'name' => $txt['membergroups_guests'], + 'num_members' => $txt['membergroups_guests_na'], + 'allow_delete' => false, + 'allow_modify' => true, + 'can_search' => false, + 'href' => '', + 'link' => '', + 'is_post_group' => false, + 'color' => '', + 'stars' => '', + 'children' => array(), + 'num_permissions' => array( + 'allowed' => 0, + // Can't deny guest permissions! + 'denied' => '(' . $txt['permissions_none'] . ')' + ), + 'access' => false + ), + 0 => array( + 'id' => 0, + 'name' => $txt['membergroups_members'], + 'num_members' => $num_members, + 'allow_delete' => false, + 'allow_modify' => true, + 'can_search' => false, + 'href' => $scripturl . '?action=moderate;area=viewgroups;sa=members;group=0', + 'is_post_group' => false, + 'color' => '', + 'stars' => '', + 'children' => array(), + 'num_permissions' => array( + 'allowed' => 0, + 'denied' => 0 + ), + 'access' => false + ), + ); + + $postGroups = array(); + $normalGroups = array(); + + // Query the database defined membergroups. + $query = $smcFunc['db_query']('', ' + SELECT id_group, id_parent, group_name, min_posts, online_color, stars + FROM {db_prefix}membergroups' . (empty($modSettings['permission_enable_postgroups']) ? ' + WHERE min_posts = {int:min_posts}' : '') . ' + ORDER BY id_parent = {int:not_inherited} DESC, min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name', + array( + 'min_posts' => -1, + 'not_inherited' => -2, + 'newbie_group' => 4, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + { + // If it's inherited, just add it as a child. + if ($row['id_parent'] != -2) + { + if (isset($context['groups'][$row['id_parent']])) + $context['groups'][$row['id_parent']]['children'][$row['id_group']] = $row['group_name']; + continue; + } + + $row['stars'] = explode('#', $row['stars']); + $context['groups'][$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'num_members' => $row['id_group'] != 3 ? 0 : $txt['membergroups_guests_na'], + 'allow_delete' => $row['id_group'] > 4, + 'allow_modify' => $row['id_group'] > 1, + 'can_search' => $row['id_group'] != 3, + 'href' => $scripturl . '?action=moderate;area=viewgroups;sa=members;group=' . $row['id_group'], + 'is_post_group' => $row['min_posts'] != -1, + 'color' => empty($row['online_color']) ? '' : $row['online_color'], + 'stars' => !empty($row['stars'][0]) && !empty($row['stars'][1]) ? str_repeat('*', $row['stars'][0]) : '', + 'children' => array(), + 'num_permissions' => array( + 'allowed' => $row['id_group'] == 1 ? '(' . $txt['permissions_all'] . ')' : 0, + 'denied' => $row['id_group'] == 1 ? '(' . $txt['permissions_none'] . ')' : 0 + ), + 'access' => false, + ); + + if ($row['min_posts'] == -1) + $normalGroups[$row['id_group']] = $row['id_group']; + else + $postGroups[$row['id_group']] = $row['id_group']; + } + $smcFunc['db_free_result']($query); + + // Get the number of members in this post group. + if (!empty($postGroups)) + { + $query = $smcFunc['db_query']('', ' + SELECT id_post_group AS id_group, COUNT(*) AS num_members + FROM {db_prefix}members + WHERE id_post_group IN ({array_int:post_group_list}) + GROUP BY id_post_group', + array( + 'post_group_list' => $postGroups, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $context['groups'][$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + } + + if (!empty($normalGroups)) + { + // First, the easy one! + $query = $smcFunc['db_query']('', ' + SELECT id_group, COUNT(*) AS num_members + FROM {db_prefix}members + WHERE id_group IN ({array_int:normal_group_list}) + GROUP BY id_group', + array( + 'normal_group_list' => $normalGroups, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $context['groups'][$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + + // This one is slower, but it's okay... careful not to count twice! + $query = $smcFunc['db_query']('', ' + SELECT mg.id_group, COUNT(*) AS num_members + FROM {db_prefix}membergroups AS mg + INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_string} + AND mem.id_group != mg.id_group + AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0) + WHERE mg.id_group IN ({array_int:normal_group_list}) + GROUP BY mg.id_group', + array( + 'normal_group_list' => $normalGroups, + 'blank_string' => '', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $context['groups'][$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + } + + foreach ($context['groups'] as $id => $data) + { + if ($data['href'] != '') + $context['groups'][$id]['link'] = '' . $data['num_members'] . ''; + } + + if (empty($_REQUEST['pid'])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_group, COUNT(*) AS num_permissions, add_deny + FROM {db_prefix}permissions + ' . (empty($context['hidden_permissions']) ? '' : ' WHERE permission NOT IN ({array_string:hidden_permissions})') . ' + GROUP BY id_group, add_deny', + array( + 'hidden_permissions' => !empty($context['hidden_permissions']) ? $context['hidden_permissions'] : array(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + if (isset($context['groups'][(int) $row['id_group']]) && (!empty($row['add_deny']) || $row['id_group'] != -1)) + $context['groups'][(int) $row['id_group']]['num_permissions'][empty($row['add_deny']) ? 'denied' : 'allowed'] = $row['num_permissions']; + $smcFunc['db_free_result']($request); + + // Get the "default" profile permissions too. + $request = $smcFunc['db_query']('', ' + SELECT id_profile, id_group, COUNT(*) AS num_permissions, add_deny + FROM {db_prefix}board_permissions + WHERE id_profile = {int:default_profile} + ' . (empty($context['hidden_permissions']) ? '' : ' AND permission NOT IN ({array_string:hidden_permissions})') . ' + GROUP BY id_profile, id_group, add_deny', + array( + 'default_profile' => 1, + 'hidden_permissions' => !empty($context['hidden_permissions']) ? $context['hidden_permissions'] : array(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (isset($context['groups'][(int) $row['id_group']]) && (!empty($row['add_deny']) || $row['id_group'] != -1)) + $context['groups'][(int) $row['id_group']]['num_permissions'][empty($row['add_deny']) ? 'denied' : 'allowed'] += $row['num_permissions']; + } + $smcFunc['db_free_result']($request); + } + else + { + $_REQUEST['pid'] = (int) $_REQUEST['pid']; + + if (!isset($context['profiles'][$_REQUEST['pid']])) + fatal_lang_error('no_access', false); + + // Change the selected tab to better reflect that this really is a board profile. + $context[$context['admin_menu_name']]['current_subsection'] = 'profiles'; + + $request = $smcFunc['db_query']('', ' + SELECT id_profile, id_group, COUNT(*) AS num_permissions, add_deny + FROM {db_prefix}board_permissions + WHERE id_profile = {int:current_profile} + GROUP BY id_profile, id_group, add_deny', + array( + 'current_profile' => $_REQUEST['pid'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (isset($context['groups'][(int) $row['id_group']]) && (!empty($row['add_deny']) || $row['id_group'] != -1)) + $context['groups'][(int) $row['id_group']]['num_permissions'][empty($row['add_deny']) ? 'denied' : 'allowed'] += $row['num_permissions']; + } + $smcFunc['db_free_result']($request); + + $context['profile'] = array( + 'id' => $_REQUEST['pid'], + 'name' => $context['profiles'][$_REQUEST['pid']]['name'], + ); + } + + // We can modify any permission set apart from the read only, reply only and no polls ones as they are redefined. + $context['can_modify'] = empty($_REQUEST['pid']) || $_REQUEST['pid'] == 1 || $_REQUEST['pid'] > 4; + + // Load the proper template. + $context['sub_template'] = 'permission_index'; +} + +function PermissionByBoard() +{ + global $context, $modSettings, $txt, $smcFunc, $sourcedir, $cat_tree, $boardList, $boards; + + $context['page_title'] = $txt['permissions_boards']; + $context['edit_all'] = isset($_GET['edit']); + + // Saving? + if (!empty($_POST['save_changes']) && !empty($_POST['boardprofile'])) + { + checkSession('request'); + + $changes = array(); + foreach ($_POST['boardprofile'] as $board => $profile) + { + $changes[(int) $profile][] = (int) $board; + } + + if (!empty($changes)) + { + foreach ($changes as $profile => $boards) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET id_profile = {int:current_profile} + WHERE id_board IN ({array_int:board_list})', + array( + 'board_list' => $boards, + 'current_profile' => $profile, + ) + ); + } + + $context['edit_all'] = false; + } + + // Load all permission profiles. + loadPermissionProfiles(); + + // Get the board tree. + require_once($sourcedir . '/Subs-Boards.php'); + + getBoardTree(); + + // Build the list of the boards. + $context['categories'] = array(); + foreach ($cat_tree as $catid => $tree) + { + $context['categories'][$catid] = array( + 'name' => &$tree['node']['name'], + 'id' => &$tree['node']['id'], + 'boards' => array() + ); + foreach ($boardList[$catid] as $boardid) + { + if (!isset($context['profiles'][$boards[$boardid]['profile']])) + $boards[$boardid]['profile'] = 1; + + $context['categories'][$catid]['boards'][$boardid] = array( + 'id' => &$boards[$boardid]['id'], + 'name' => &$boards[$boardid]['name'], + 'description' => &$boards[$boardid]['description'], + 'child_level' => &$boards[$boardid]['level'], + 'profile' => &$boards[$boardid]['profile'], + 'profile_name' => $context['profiles'][$boards[$boardid]['profile']]['name'], + ); + } + } + + $context['sub_template'] = 'by_board'; +} + +function SetQuickGroups() +{ + global $context, $smcFunc; + + checkSession(); + + loadIllegalPermissions(); + loadIllegalGuestPermissions(); + + // Make sure only one of the quick options was selected. + if ((!empty($_POST['predefined']) && ((isset($_POST['copy_from']) && $_POST['copy_from'] != 'empty') || !empty($_POST['permissions']))) || (!empty($_POST['copy_from']) && $_POST['copy_from'] != 'empty' && !empty($_POST['permissions']))) + fatal_lang_error('permissions_only_one_option', false); + + if (empty($_POST['group']) || !is_array($_POST['group'])) + $_POST['group'] = array(); + + // Only accept numeric values for selected membergroups. + foreach ($_POST['group'] as $id => $group_id) + $_POST['group'][$id] = (int) $group_id; + $_POST['group'] = array_unique($_POST['group']); + + if (empty($_REQUEST['pid'])) + $_REQUEST['pid'] = 0; + else + $_REQUEST['pid'] = (int) $_REQUEST['pid']; + + // Fix up the old global to the new default! + $bid = max(1, $_REQUEST['pid']); + + // No modifying the predefined profiles. + if ($_REQUEST['pid'] > 1 && $_REQUEST['pid'] < 5) + fatal_lang_error('no_access', false); + + // Clear out any cached authority. + updateSettings(array('settings_updated' => time())); + + // No groups where selected. + if (empty($_POST['group'])) + redirectexit('action=admin;area=permissions;pid=' . $_REQUEST['pid']); + + // Set a predefined permission profile. + if (!empty($_POST['predefined'])) + { + // Make sure it's a predefined permission set we expect. + if (!in_array($_POST['predefined'], array('restrict', 'standard', 'moderator', 'maintenance'))) + redirectexit('action=admin;area=permissions;pid=' . $_REQUEST['pid']); + + foreach ($_POST['group'] as $group_id) + { + if (!empty($_REQUEST['pid'])) + setPermissionLevel($_POST['predefined'], $group_id, $_REQUEST['pid']); + else + setPermissionLevel($_POST['predefined'], $group_id); + } + } + // Set a permission profile based on the permissions of a selected group. + elseif ($_POST['copy_from'] != 'empty') + { + // Just checking the input. + if (!is_numeric($_POST['copy_from'])) + redirectexit('action=admin;area=permissions;pid=' . $_REQUEST['pid']); + + // Make sure the group we're copying to is never included. + $_POST['group'] = array_diff($_POST['group'], array($_POST['copy_from'])); + + // No groups left? Too bad. + if (empty($_POST['group'])) + redirectexit('action=admin;area=permissions;pid=' . $_REQUEST['pid']); + + if (empty($_REQUEST['pid'])) + { + // Retrieve current permissions of group. + $request = $smcFunc['db_query']('', ' + SELECT permission, add_deny + FROM {db_prefix}permissions + WHERE id_group = {int:copy_from}', + array( + 'copy_from' => $_POST['copy_from'], + ) + ); + $target_perm = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $target_perm[$row['permission']] = $row['add_deny']; + $smcFunc['db_free_result']($request); + + $inserts = array(); + foreach ($_POST['group'] as $group_id) + foreach ($target_perm as $perm => $add_deny) + { + // No dodgy permissions please! + if (!empty($context['illegal_permissions']) && in_array($perm, $context['illegal_permissions'])) + continue; + if ($group_id == -1 && in_array($perm, $context['non_guest_permissions'])) + continue; + + if ($group_id != 1 && $group_id != 3) + $inserts[] = array($perm, $group_id, $add_deny); + } + + // Delete the previous permissions... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE id_group IN ({array_int:group_list}) + ' . (empty($context['illegal_permissions']) ? '' : ' AND permission NOT IN ({array_string:illegal_permissions})'), + array( + 'group_list' => $_POST['group'], + 'illegal_permissions' => !empty($context['illegal_permissions']) ? $context['illegal_permissions'] : array(), + ) + ); + + if (!empty($inserts)) + { + // ..and insert the new ones. + $smcFunc['db_insert']('', + '{db_prefix}permissions', + array( + 'permission' => 'string', 'id_group' => 'int', 'add_deny' => 'int', + ), + $inserts, + array('permission', 'id_group') + ); + } + } + + // Now do the same for the board permissions. + $request = $smcFunc['db_query']('', ' + SELECT permission, add_deny + FROM {db_prefix}board_permissions + WHERE id_group = {int:copy_from} + AND id_profile = {int:current_profile}', + array( + 'copy_from' => $_POST['copy_from'], + 'current_profile' => $bid, + ) + ); + $target_perm = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $target_perm[$row['permission']] = $row['add_deny']; + $smcFunc['db_free_result']($request); + + $inserts = array(); + foreach ($_POST['group'] as $group_id) + foreach ($target_perm as $perm => $add_deny) + { + // Are these for guests? + if ($group_id == -1 && in_array($perm, $context['non_guest_permissions'])) + continue; + + $inserts[] = array($perm, $group_id, $bid, $add_deny); + } + + // Delete the previous global board permissions... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_group IN ({array_int:current_group_list}) + AND id_profile = {int:current_profile}', + array( + 'current_group_list' => $_POST['group'], + 'current_profile' => $bid, + ) + ); + + // And insert the copied permissions. + if (!empty($inserts)) + { + // ..and insert the new ones. + $smcFunc['db_insert']('', + '{db_prefix}board_permissions', + array('permission' => 'string', 'id_group' => 'int', 'id_profile' => 'int', 'add_deny' => 'int'), + $inserts, + array('permission', 'id_group', 'id_profile') + ); + } + + // Update any children out there! + updateChildPermissions($_POST['group'], $_REQUEST['pid']); + } + // Set or unset a certain permission for the selected groups. + elseif (!empty($_POST['permissions'])) + { + // Unpack two variables that were transported. + list ($permissionType, $permission) = explode('/', $_POST['permissions']); + + // Check whether our input is within expected range. + if (!in_array($_POST['add_remove'], array('add', 'clear', 'deny')) || !in_array($permissionType, array('membergroup', 'board'))) + redirectexit('action=admin;area=permissions;pid=' . $_REQUEST['pid']); + + if ($_POST['add_remove'] == 'clear') + { + if ($permissionType == 'membergroup') + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE id_group IN ({array_int:current_group_list}) + AND permission = {string:current_permission} + ' . (empty($context['illegal_permissions']) ? '' : ' AND permission NOT IN ({array_string:illegal_permissions})'), + array( + 'current_group_list' => $_POST['group'], + 'current_permission' => $permission, + 'illegal_permissions' => !empty($context['illegal_permissions']) ? $context['illegal_permissions'] : array(), + ) + ); + else + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_group IN ({array_int:current_group_list}) + AND id_profile = {int:current_profile} + AND permission = {string:current_permission}', + array( + 'current_group_list' => $_POST['group'], + 'current_profile' => $bid, + 'current_permission' => $permission, + ) + ); + } + // Add a permission (either 'set' or 'deny'). + else + { + $add_deny = $_POST['add_remove'] == 'add' ? '1' : '0'; + $permChange = array(); + foreach ($_POST['group'] as $groupID) + { + if ($groupID == -1 && in_array($permission, $context['non_guest_permissions'])) + continue; + + if ($permissionType == 'membergroup' && $groupID != 1 && $groupID != 3 && (empty($context['illegal_permissions']) || !in_array($permission, $context['illegal_permissions']))) + $permChange[] = array($permission, $groupID, $add_deny); + elseif ($permissionType != 'membergroup') + $permChange[] = array($permission, $groupID, $bid, $add_deny); + } + + if (!empty($permChange)) + { + if ($permissionType == 'membergroup') + $smcFunc['db_insert']('replace', + '{db_prefix}permissions', + array('permission' => 'string', 'id_group' => 'int', 'add_deny' => 'int'), + $permChange, + array('permission', 'id_group') + ); + // Board permissions go into the other table. + else + $smcFunc['db_insert']('replace', + '{db_prefix}board_permissions', + array('permission' => 'string', 'id_group' => 'int', 'id_profile' => 'int', 'add_deny' => 'int'), + $permChange, + array('permission', 'id_group', 'id_profile') + ); + } + } + + // Another child update! + updateChildPermissions($_POST['group'], $_REQUEST['pid']); + } + + redirectexit('action=admin;area=permissions;pid=' . $_REQUEST['pid']); +} + +function ModifyMembergroup() +{ + global $context, $txt, $modSettings, $smcFunc, $sourcedir; + + if (!isset($_GET['group'])) + fatal_lang_error('no_access', false); + + $context['group']['id'] = (int) $_GET['group']; + + // Are they toggling the view? + if (isset($_GET['view'])) + { + $context['admin_preferences']['pv'] = $_GET['view'] == 'classic' ? 'classic' : 'simple'; + + // Update the users preferences. + require_once($sourcedir . '/Subs-Admin.php'); + updateAdminPreferences(); + } + + $context['view_type'] = !empty($context['admin_preferences']['pv']) && $context['admin_preferences']['pv'] == 'classic' ? 'classic' : 'simple'; + + // It's not likely you'd end up here with this setting disabled. + if ($_GET['group'] == 1) + redirectexit('action=admin;area=permissions'); + + loadAllPermissions($context['view_type']); + loadPermissionProfiles(); + + if ($context['group']['id'] > 0) + { + $result = $smcFunc['db_query']('', ' + SELECT group_name, id_parent + FROM {db_prefix}membergroups + WHERE id_group = {int:current_group} + LIMIT 1', + array( + 'current_group' => $context['group']['id'], + ) + ); + list ($context['group']['name'], $parent) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Cannot edit an inherited group! + if ($parent != -2) + fatal_lang_error('cannot_edit_permissions_inherited'); + } + elseif ($context['group']['id'] == -1) + $context['group']['name'] = $txt['membergroups_guests']; + else + $context['group']['name'] = $txt['membergroups_members']; + + $context['profile']['id'] = empty($_GET['pid']) ? 0 : (int) $_GET['pid']; + + // If this is a moderator and they are editing "no profile" then we only do boards. + if ($context['group']['id'] == 3 && empty($context['profile']['id'])) + { + // For sanity just check they have no general permissions. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE id_group = {int:moderator_group}', + array( + 'moderator_group' => 3, + ) + ); + + $context['profile']['id'] = 1; + } + + $context['permission_type'] = empty($context['profile']['id']) ? 'membergroup' : 'board'; + $context['profile']['can_modify'] = !$context['profile']['id'] || $context['profiles'][$context['profile']['id']]['can_modify']; + + // Set up things a little nicer for board related stuff... + if ($context['permission_type'] == 'board') + { + $context['profile']['name'] = $context['profiles'][$context['profile']['id']]['name']; + $context[$context['admin_menu_name']]['current_subsection'] = 'profiles'; + } + + // Fetch the current permissions. + $permissions = array( + 'membergroup' => array('allowed' => array(), 'denied' => array()), + 'board' => array('allowed' => array(), 'denied' => array()) + ); + + // General permissions? + if ($context['permission_type'] == 'membergroup') + { + $result = $smcFunc['db_query']('', ' + SELECT permission, add_deny + FROM {db_prefix}permissions + WHERE id_group = {int:current_group}', + array( + 'current_group' => $_GET['group'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $permissions['membergroup'][empty($row['add_deny']) ? 'denied' : 'allowed'][] = $row['permission']; + $smcFunc['db_free_result']($result); + } + + // Fetch current board permissions... + $result = $smcFunc['db_query']('', ' + SELECT permission, add_deny + FROM {db_prefix}board_permissions + WHERE id_group = {int:current_group} + AND id_profile = {int:current_profile}', + array( + 'current_group' => $context['group']['id'], + 'current_profile' => $context['permission_type'] == 'membergroup' ? 1 : $context['profile']['id'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $permissions['board'][empty($row['add_deny']) ? 'denied' : 'allowed'][] = $row['permission']; + $smcFunc['db_free_result']($result); + + // Loop through each permission and set whether it's checked. + foreach ($context['permissions'] as $permissionType => $tmp) + { + foreach ($tmp['columns'] as $position => $permissionGroups) + { + foreach ($permissionGroups as $permissionGroup => $permissionArray) + { + foreach ($permissionArray['permissions'] as $perm) + { + // Create a shortcut for the current permission. + $curPerm = &$context['permissions'][$permissionType]['columns'][$position][$permissionGroup]['permissions'][$perm['id']]; + if ($tmp['view'] == 'classic') + { + if ($perm['has_own_any']) + { + $curPerm['any']['select'] = in_array($perm['id'] . '_any', $permissions[$permissionType]['allowed']) ? 'on' : (in_array($perm['id'] . '_any', $permissions[$permissionType]['denied']) ? 'denied' : 'off'); + $curPerm['own']['select'] = in_array($perm['id'] . '_own', $permissions[$permissionType]['allowed']) ? 'on' : (in_array($perm['id'] . '_own', $permissions[$permissionType]['denied']) ? 'denied' : 'off'); + } + else + $curPerm['select'] = in_array($perm['id'], $permissions[$permissionType]['denied']) ? 'denied' : (in_array($perm['id'], $permissions[$permissionType]['allowed']) ? 'on' : 'off'); + } + else + { + $curPerm['select'] = in_array($perm['id'], $permissions[$permissionType]['denied']) ? 'denied' : (in_array($perm['id'], $permissions[$permissionType]['allowed']) ? 'on' : 'off'); + } + } + } + } + } + $context['sub_template'] = 'modify_group'; + $context['page_title'] = $txt['permissions_modify_group']; +} + +function ModifyMembergroup2() +{ + global $modSettings, $smcFunc, $context; + + checkSession(); + + loadIllegalPermissions(); + + $_GET['group'] = (int) $_GET['group']; + $_GET['pid'] = (int) $_GET['pid']; + + // Cannot modify predefined profiles. + if ($_GET['pid'] > 1 && $_GET['pid'] < 5) + fatal_lang_error('no_access', false); + + // Verify this isn't inherited. + if ($_GET['group'] == -1 || $_GET['group'] == 0) + $parent = -2; + else + { + $result = $smcFunc['db_query']('', ' + SELECT id_parent + FROM {db_prefix}membergroups + WHERE id_group = {int:current_group} + LIMIT 1', + array( + 'current_group' => $_GET['group'], + ) + ); + list ($parent) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + } + + if ($parent != -2) + fatal_lang_error('cannot_edit_permissions_inherited'); + + $givePerms = array('membergroup' => array(), 'board' => array()); + + // Guest group, we need illegal, guest permissions. + if ($_GET['group'] == -1) + { + loadIllegalGuestPermissions(); + $context['illegal_permissions'] = array_merge($context['illegal_permissions'], $context['non_guest_permissions']); + } + + // Prepare all permissions that were set or denied for addition to the DB. + if (isset($_POST['perm']) && is_array($_POST['perm'])) + { + foreach ($_POST['perm'] as $perm_type => $perm_array) + { + if (is_array($perm_array)) + { + foreach ($perm_array as $permission => $value) + if ($value == 'on' || $value == 'deny') + { + // Don't allow people to escalate themselves! + if (!empty($context['illegal_permissions']) && in_array($permission, $context['illegal_permissions'])) + continue; + + $givePerms[$perm_type][] = array($_GET['group'], $permission, $value == 'deny' ? 0 : 1); + } + } + } + } + + // Insert the general permissions. + if ($_GET['group'] != 3 && empty($_GET['pid'])) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE id_group = {int:current_group} + ' . (empty($context['illegal_permissions']) ? '' : ' AND permission NOT IN ({array_string:illegal_permissions})'), + array( + 'current_group' => $_GET['group'], + 'illegal_permissions' => !empty($context['illegal_permissions']) ? $context['illegal_permissions'] : array(), + ) + ); + + if (!empty($givePerms['membergroup'])) + { + $smcFunc['db_insert']('replace', + '{db_prefix}permissions', + array('id_group' => 'int', 'permission' => 'string', 'add_deny' => 'int'), + $givePerms['membergroup'], + array('id_group', 'permission') + ); + } + } + + // Insert the boardpermissions. + $profileid = max(1, $_GET['pid']); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_group = {int:current_group} + AND id_profile = {int:current_profile}', + array( + 'current_group' => $_GET['group'], + 'current_profile' => $profileid, + ) + ); + if (!empty($givePerms['board'])) + { + foreach ($givePerms['board'] as $k => $v) + $givePerms['board'][$k][] = $profileid; + $smcFunc['db_insert']('replace', + '{db_prefix}board_permissions', + array('id_group' => 'int', 'permission' => 'string', 'add_deny' => 'int', 'id_profile' => 'int'), + $givePerms['board'], + array('id_group', 'permission', 'id_profile') + ); + } + + // Update any inherited permissions as required. + updateChildPermissions($_GET['group'], $_GET['pid']); + + // Clear cached privs. + updateSettings(array('settings_updated' => time())); + + redirectexit('action=admin;area=permissions;pid=' . $_GET['pid']); +} + +// Screen for modifying general permission settings. +function GeneralPermissionSettings($return_config = false) +{ + global $context, $modSettings, $sourcedir, $txt, $scripturl, $smcFunc; + + // All the setting variables + $config_vars = array( + array('title', 'settings'), + // Inline permissions. + array('permissions', 'manage_permissions'), + '', + // A few useful settings + array('check', 'permission_enable_deny', 0, $txt['permission_settings_enable_deny'], 'help' => 'permissions_deny'), + array('check', 'permission_enable_postgroups', 0, $txt['permission_settings_enable_postgroups'], 'help' => 'permissions_postgroups'), + ); + + if ($return_config) + return $config_vars; + + $context['page_title'] = $txt['permission_settings_title']; + $context['sub_template'] = 'show_settings'; + + // Needed for the inline permission functions, and the settings template. + require_once($sourcedir . '/ManageServer.php'); + + // Don't let guests have these permissions. + $context['post_url'] = $scripturl . '?action=admin;area=permissions;save;sa=settings'; + $context['permissions_excluded'] = array(-1); + + // Saving the settings? + if (isset($_GET['save'])) + { + checkSession('post'); + saveDBSettings($config_vars); + + // Clear all deny permissions...if we want that. + if (empty($modSettings['permission_enable_deny'])) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE add_deny = {int:denied}', + array( + 'denied' => 0, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE add_deny = {int:denied}', + array( + 'denied' => 0, + ) + ); + } + + // Make sure there are no postgroup based permissions left. + if (empty($modSettings['permission_enable_postgroups'])) + { + // Get a list of postgroups. + $post_groups = array(); + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}membergroups + WHERE min_posts != {int:min_posts}', + array( + 'min_posts' => -1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $post_groups[] = $row['id_group']; + $smcFunc['db_free_result']($request); + + // Remove'em. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE id_group IN ({array_int:post_group_list})', + array( + 'post_group_list' => $post_groups, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_group IN ({array_int:post_group_list})', + array( + 'post_group_list' => $post_groups, + ) + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}membergroups + SET id_parent = {int:not_inherited} + WHERE id_parent IN ({array_int:post_group_list})', + array( + 'post_group_list' => $post_groups, + 'not_inherited' => -2, + ) + ); + } + + redirectexit('action=admin;area=permissions;sa=settings'); + } + + prepareDBSettingContext($config_vars); +} + +// Set the permission level for a specific profile, group, or group for a profile. +function setPermissionLevel($level, $group, $profile = 'null') +{ + global $smcFunc, $context; + + loadIllegalPermissions(); + loadIllegalGuestPermissions(); + + // Levels by group... restrict, standard, moderator, maintenance. + $groupLevels = array( + 'board' => array('inherit' => array()), + 'group' => array('inherit' => array()) + ); + // Levels by board... standard, publish, free. + $boardLevels = array('inherit' => array()); + + // Restrictive - ie. guests. + $groupLevels['global']['restrict'] = array( + 'search_posts', + 'calendar_view', + 'view_stats', + 'who_view', + 'profile_view_own', + 'profile_identity_own', + ); + $groupLevels['board']['restrict'] = array( + 'poll_view', + 'post_new', + 'post_reply_own', + 'post_reply_any', + 'delete_own', + 'modify_own', + 'mark_any_notify', + 'mark_notify', + 'report_any', + 'send_topic', + ); + + // Standard - ie. members. They can do anything Restrictive can. + $groupLevels['global']['standard'] = array_merge($groupLevels['global']['restrict'], array( + 'view_mlist', + 'karma_edit', + 'pm_read', + 'pm_send', + 'profile_view_any', + 'profile_extra_own', + 'profile_server_avatar', + 'profile_upload_avatar', + 'profile_remote_avatar', + 'profile_remove_own', + )); + $groupLevels['board']['standard'] = array_merge($groupLevels['board']['restrict'], array( + 'poll_vote', + 'poll_edit_own', + 'poll_post', + 'poll_add_own', + 'post_attachment', + 'lock_own', + 'remove_own', + 'view_attachments', + )); + + // Moderator - ie. moderators :P. They can do what standard can, and more. + $groupLevels['global']['moderator'] = array_merge($groupLevels['global']['standard'], array( + 'calendar_post', + 'calendar_edit_own', + 'access_mod_center', + 'issue_warning', + )); + $groupLevels['board']['moderator'] = array_merge($groupLevels['board']['standard'], array( + 'make_sticky', + 'poll_edit_any', + 'delete_any', + 'modify_any', + 'lock_any', + 'remove_any', + 'move_any', + 'merge_any', + 'split_any', + 'poll_lock_any', + 'poll_remove_any', + 'poll_add_any', + 'approve_posts', + )); + + // Maintenance - wannabe admins. They can do almost everything. + $groupLevels['global']['maintenance'] = array_merge($groupLevels['global']['moderator'], array( + 'manage_attachments', + 'manage_smileys', + 'manage_boards', + 'moderate_forum', + 'manage_membergroups', + 'manage_bans', + 'admin_forum', + 'manage_permissions', + 'edit_news', + 'calendar_edit_any', + 'profile_identity_any', + 'profile_extra_any', + 'profile_title_any', + )); + $groupLevels['board']['maintenance'] = array_merge($groupLevels['board']['moderator'], array( + )); + + // Standard - nothing above the group permissions. (this SHOULD be empty.) + $boardLevels['standard'] = array( + ); + + // Locked - just that, you can't post here. + $boardLevels['locked'] = array( + 'poll_view', + 'mark_notify', + 'report_any', + 'send_topic', + 'view_attachments', + ); + + // Publisher - just a little more... + $boardLevels['publish'] = array_merge($boardLevels['locked'], array( + 'post_new', + 'post_reply_own', + 'post_reply_any', + 'delete_own', + 'modify_own', + 'mark_any_notify', + 'delete_replies', + 'modify_replies', + 'poll_vote', + 'poll_edit_own', + 'poll_post', + 'poll_add_own', + 'poll_remove_own', + 'post_attachment', + 'lock_own', + 'remove_own', + )); + + // Free for All - Scary. Just scary. + $boardLevels['free'] = array_merge($boardLevels['publish'], array( + 'poll_lock_any', + 'poll_edit_any', + 'poll_add_any', + 'poll_remove_any', + 'make_sticky', + 'lock_any', + 'remove_any', + 'delete_any', + 'split_any', + 'merge_any', + 'modify_any', + 'approve_posts', + )); + + // Make sure we're not granting someone too many permissions! + foreach ($groupLevels['global'][$level] as $k => $permission) + { + if (!empty($context['illegal_permissions']) && in_array($permission, $context['illegal_permissions'])) + unset($groupLevels['global'][$level][$k]); + + if ($group == -1 && in_array($permission, $context['non_guest_permissions'])) + unset($groupLevels['global'][$level][$k]); + } + if ($group == -1) + foreach ($groupLevels['board'][$level] as $k => $permission) + if (in_array($permission, $context['non_guest_permissions'])) + unset($groupLevels['board'][$level][$k]); + + // Reset all cached permissions. + updateSettings(array('settings_updated' => time())); + + // Setting group permissions. + if ($profile === 'null' && $group !== 'null') + { + $group = (int) $group; + + if (empty($groupLevels['global'][$level])) + return; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE id_group = {int:current_group} + ' . (empty($context['illegal_permissions']) ? '' : ' AND permission NOT IN ({array_string:illegal_permissions})'), + array( + 'current_group' => $group, + 'illegal_permissions' => !empty($context['illegal_permissions']) ? $context['illegal_permissions'] : array(), + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_group = {int:current_group} + AND id_profile = {int:default_profile}', + array( + 'current_group' => $group, + 'default_profile' => 1, + ) + ); + + $groupInserts = array(); + foreach ($groupLevels['global'][$level] as $permission) + $groupInserts[] = array($group, $permission); + + $smcFunc['db_insert']('insert', + '{db_prefix}permissions', + array('id_group' => 'int', 'permission' => 'string'), + $groupInserts, + array('id_group') + ); + + $boardInserts = array(); + foreach ($groupLevels['board'][$level] as $permission) + $boardInserts[] = array(1, $group, $permission); + + $smcFunc['db_insert']('insert', + '{db_prefix}board_permissions', + array('id_profile' => 'int', 'id_group' => 'int', 'permission' => 'string'), + $boardInserts, + array('id_profile', 'id_group') + ); + } + // Setting profile permissions for a specific group. + elseif ($profile !== 'null' && $group !== 'null' && ($profile == 1 || $profile > 4)) + { + $group = (int) $group; + $profile = (int) $profile; + + if (!empty($groupLevels['global'][$level])) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_group = {int:current_group} + AND id_profile = {int:current_profile}', + array( + 'current_group' => $group, + 'current_profile' => $profile, + ) + ); + } + + if (!empty($groupLevels['board'][$level])) + { + $boardInserts = array(); + foreach ($groupLevels['board'][$level] as $permission) + $boardInserts[] = array($profile, $group, $permission); + + $smcFunc['db_insert']('insert', + '{db_prefix}board_permissions', + array('id_profile' => 'int', 'id_group' => 'int', 'permission' => 'string'), + $boardInserts, + array('id_profile', 'id_group') + ); + } + } + // Setting profile permissions for all groups. + elseif ($profile !== 'null' && $group === 'null' && ($profile == 1 || $profile > 4)) + { + $profile = (int) $profile; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_profile = {int:current_profile}', + array( + 'current_profile' => $profile, + ) + ); + + if (empty($boardLevels[$level])) + return; + + // Get all the groups... + $query = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}membergroups + WHERE id_group > {int:moderator_group} + ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name', + array( + 'moderator_group' => 3, + 'newbie_group' => 4, + ) + ); + while ($row = $smcFunc['db_fetch_row']($query)) + { + $group = $row[0]; + + $boardInserts = array(); + foreach ($boardLevels[$level] as $permission) + $boardInserts[] = array($profile, $group, $permission); + + $smcFunc['db_insert']('insert', + '{db_prefix}board_permissions', + array('id_profile' => 'int', 'id_group' => 'int', 'permission' => 'string'), + $boardInserts, + array('id_profile', 'id_group') + ); + } + $smcFunc['db_free_result']($query); + + // Add permissions for ungrouped members. + $boardInserts = array(); + foreach ($boardLevels[$level] as $permission) + $boardInserts[] = array($profile, 0, $permission); + + $smcFunc['db_insert']('insert', + '{db_prefix}board_permissions', + array('id_profile' => 'int', 'id_group' => 'int', 'permission' => 'string'), + $boardInserts, + array('id_profile', 'id_group') + ); + } + // $profile and $group are both null! + else + fatal_lang_error('no_access', false); +} + +function loadAllPermissions($loadType = 'classic') +{ + global $context, $txt, $modSettings; + + // List of all the groups dependant on the currently selected view - for the order so it looks pretty, yea? + // Note to Mod authors - you don't need to stick your permission group here if you don't mind SMF sticking it the last group of the page. + $permissionGroups = array( + 'membergroup' => array( + 'simple' => array( + 'view_basic_info', + 'use_pm_system', + 'post_calendar', + 'edit_profile', + 'delete_account', + 'use_avatar', + 'moderate_general', + 'administrate', + ), + 'classic' => array( + 'general', + 'pm', + 'calendar', + 'maintenance', + 'member_admin', + 'profile', + ), + ), + 'board' => array( + 'simple' => array( + 'make_posts', + 'make_unapproved_posts', + 'post_polls', + 'participate', + 'modify', + 'notification', + 'attach', + 'moderate', + ), + 'classic' => array( + 'general_board', + 'topic', + 'post', + 'poll', + 'notification', + 'attachment', + ), + ), + ); + + /* The format of this list is as follows: + 'membergroup' => array( + 'permissions_inside' => array(has_multiple_options, classic_view_group, simple_view_group(_own)*, simple_view_group_any*), + ), + 'board' => array( + 'permissions_inside' => array(has_multiple_options, classic_view_group, simple_view_group(_own)*, simple_view_group_any*), + ); + */ + $permissionList = array( + 'membergroup' => array( + 'view_stats' => array(false, 'general', 'view_basic_info'), + 'view_mlist' => array(false, 'general', 'view_basic_info'), + 'who_view' => array(false, 'general', 'view_basic_info'), + 'search_posts' => array(false, 'general', 'view_basic_info'), + 'karma_edit' => array(false, 'general', 'moderate_general'), + 'pm_read' => array(false, 'pm', 'use_pm_system'), + 'pm_send' => array(false, 'pm', 'use_pm_system'), + 'calendar_view' => array(false, 'calendar', 'view_basic_info'), + 'calendar_post' => array(false, 'calendar', 'post_calendar'), + 'calendar_edit' => array(true, 'calendar', 'post_calendar', 'moderate_general'), + 'admin_forum' => array(false, 'maintenance', 'administrate'), + 'manage_boards' => array(false, 'maintenance', 'administrate'), + 'manage_attachments' => array(false, 'maintenance', 'administrate'), + 'manage_smileys' => array(false, 'maintenance', 'administrate'), + 'edit_news' => array(false, 'maintenance', 'administrate'), + 'access_mod_center' => array(false, 'maintenance', 'moderate_general'), + 'moderate_forum' => array(false, 'member_admin', 'moderate_general'), + 'manage_membergroups' => array(false, 'member_admin', 'administrate'), + 'manage_permissions' => array(false, 'member_admin', 'administrate'), + 'manage_bans' => array(false, 'member_admin', 'administrate'), + 'send_mail' => array(false, 'member_admin', 'administrate'), + 'issue_warning' => array(false, 'member_admin', 'moderate_general'), + 'profile_view' => array(true, 'profile', 'view_basic_info', 'view_basic_info'), + 'profile_identity' => array(true, 'profile', 'edit_profile', 'moderate_general'), + 'profile_extra' => array(true, 'profile', 'edit_profile', 'moderate_general'), + 'profile_title' => array(true, 'profile', 'edit_profile', 'moderate_general'), + 'profile_remove' => array(true, 'profile', 'delete_account', 'moderate_general'), + 'profile_server_avatar' => array(false, 'profile', 'use_avatar'), + 'profile_upload_avatar' => array(false, 'profile', 'use_avatar'), + 'profile_remote_avatar' => array(false, 'profile', 'use_avatar'), + ), + 'board' => array( + 'moderate_board' => array(false, 'general_board', 'moderate'), + 'approve_posts' => array(false, 'general_board', 'moderate'), + 'post_new' => array(false, 'topic', 'make_posts'), + 'post_unapproved_topics' => array(false, 'topic', 'make_unapproved_posts'), + 'post_unapproved_replies' => array(true, 'topic', 'make_unapproved_posts', 'make_unapproved_posts'), + 'post_reply' => array(true, 'topic', 'make_posts', 'make_posts'), + 'merge_any' => array(false, 'topic', 'moderate'), + 'split_any' => array(false, 'topic', 'moderate'), + 'send_topic' => array(false, 'topic', 'moderate'), + 'make_sticky' => array(false, 'topic', 'moderate'), + 'move' => array(true, 'topic', 'moderate', 'moderate'), + 'lock' => array(true, 'topic', 'moderate', 'moderate'), + 'remove' => array(true, 'topic', 'modify', 'moderate'), + 'modify_replies' => array(false, 'topic', 'moderate'), + 'delete_replies' => array(false, 'topic', 'moderate'), + 'announce_topic' => array(false, 'topic', 'moderate'), + 'delete' => array(true, 'post', 'modify', 'moderate'), + 'modify' => array(true, 'post', 'modify', 'moderate'), + 'report_any' => array(false, 'post', 'participate'), + 'poll_view' => array(false, 'poll', 'participate'), + 'poll_vote' => array(false, 'poll', 'participate'), + 'poll_post' => array(false, 'poll', 'post_polls'), + 'poll_add' => array(true, 'poll', 'post_polls', 'moderate'), + 'poll_edit' => array(true, 'poll', 'modify', 'moderate'), + 'poll_lock' => array(true, 'poll', 'moderate', 'moderate'), + 'poll_remove' => array(true, 'poll', 'modify', 'moderate'), + 'mark_any_notify' => array(false, 'notification', 'notification'), + 'mark_notify' => array(false, 'notification', 'notification'), + 'view_attachments' => array(false, 'attachment', 'participate'), + 'post_unapproved_attachments' => array(false, 'attachment', 'make_unapproved_posts'), + 'post_attachment' => array(false, 'attachment', 'attach'), + ), + ); + + // All permission groups that will be shown in the left column on classic view. + $leftPermissionGroups = array( + 'general', + 'calendar', + 'maintenance', + 'member_admin', + 'topic', + 'post', + ); + + // We need to know what permissions we can't give to guests. + loadIllegalGuestPermissions(); + + // Some permissions are hidden if features are off. + $hiddenPermissions = array(); + $relabelPermissions = array(); // Permissions to apply a different label to. + $relabelGroups = array(); // As above but for groups. + if (!in_array('cd', $context['admin_features'])) + { + $hiddenPermissions[] = 'calendar_view'; + $hiddenPermissions[] = 'calendar_post'; + $hiddenPermissions[] = 'calendar_edit'; + } + if (!in_array('w', $context['admin_features'])) + $hiddenPermissions[] = 'issue_warning'; + + // Post moderation? + if (!$modSettings['postmod_active']) + { + $hiddenPermissions[] = 'approve_posts'; + $hiddenPermissions[] = 'post_unapproved_topics'; + $hiddenPermissions[] = 'post_unapproved_replies'; + $hiddenPermissions[] = 'post_unapproved_attachments'; + } + // If we show them on classic view we change the name. + else + { + // Relabel the topics permissions + $relabelPermissions['post_new'] = 'auto_approve_topics'; + + // Relabel the reply permissions + $relabelPermissions['post_reply'] = 'auto_approve_replies'; + + // Relabel the attachment permissions + $relabelPermissions['post_attachment'] = 'auto_approve_attachments'; + } + + // Provide a practical way to modify permissions. + call_integration_hook('integrate_load_permissions', array(&$permissionGroups, &$permissionList, &$leftPermissionGroups, &$hiddenPermissions, &$relabelPermissions)); + + $context['permissions'] = array(); + $context['hidden_permissions'] = array(); + foreach ($permissionList as $permissionType => $permissionList) + { + $context['permissions'][$permissionType] = array( + 'id' => $permissionType, + 'view' => $loadType, + 'columns' => array() + ); + foreach ($permissionList as $permission => $permissionArray) + { + // If this is a guest permission we don't do it if it's the guest group. + if (isset($context['group']['id']) && $context['group']['id'] == -1 && in_array($permission, $context['non_guest_permissions'])) + continue; + + // What groups will this permission be in? + $own_group = $permissionArray[($loadType == 'classic' ? 1 : 2)]; + $any_group = $loadType == 'simple' && !empty($permissionArray[3]) ? $permissionArray[3] : ($loadType == 'simple' && $permissionArray[0] ? $permissionArray[2] : ''); + + // First, Do these groups actually exist - if not add them. + if (!isset($permissionGroups[$permissionType][$loadType][$own_group])) + $permissionGroups[$permissionType][$loadType][$own_group] = true; + if (!empty($any_group) && !isset($permissionGroups[$permissionType][$loadType][$any_group])) + $permissionGroups[$permissionType][$loadType][$any_group] = true; + + // What column should this be located into? + $position = $loadType == 'classic' && !in_array($own_group, $leftPermissionGroups) ? 1 : 0; + + // If the groups have not yet been created be sure to create them. + $bothGroups = array('own' => $own_group); + $bothGroups = array(); + + // For guests, just reset the array. + if (!isset($context['group']['id']) || !($context['group']['id'] == -1 && $any_group)) + $bothGroups['own'] = $own_group; + + if ($any_group) + { + $bothGroups['any'] = $any_group; + + } + + foreach ($bothGroups as $group) + if (!isset($context['permissions'][$permissionType]['columns'][$position][$group])) + $context['permissions'][$permissionType]['columns'][$position][$group] = array( + 'type' => $permissionType, + 'id' => $group, + 'name' => $loadType == 'simple' ? (isset($txt['permissiongroup_simple_' . $group]) ? $txt['permissiongroup_simple_' . $group] : '') : $txt['permissiongroup_' . $group], + 'icon' => isset($txt['permissionicon_' . $group]) ? $txt['permissionicon_' . $group] : $txt['permissionicon'], + 'help' => isset($txt['permissionhelp_' . $group]) ? $txt['permissionhelp_' . $group] : '', + 'hidden' => false, + 'permissions' => array() + ); + + // This is where we set up the permission dependant on the view. + if ($loadType == 'classic') + { + $context['permissions'][$permissionType]['columns'][$position][$own_group]['permissions'][$permission] = array( + 'id' => $permission, + 'name' => !isset($relabelPermissions[$permission]) ? $txt['permissionname_' . $permission] : $txt[$relabelPermissions[$permission]], + 'show_help' => isset($txt['permissionhelp_' . $permission]), + 'note' => isset($txt['permissionnote_' . $permission]) ? $txt['permissionnote_' . $permission] : '', + 'has_own_any' => $permissionArray[0], + 'own' => array( + 'id' => $permission . '_own', + 'name' => $permissionArray[0] ? $txt['permissionname_' . $permission . '_own'] : '' + ), + 'any' => array( + 'id' => $permission . '_any', + 'name' => $permissionArray[0] ? $txt['permissionname_' . $permission . '_any'] : '' + ), + 'hidden' => in_array($permission, $hiddenPermissions), + ); + } + else + { + foreach ($bothGroups as $group_type => $group) + { + $context['permissions'][$permissionType]['columns'][$position][$group]['permissions'][$permission . ($permissionArray[0] ? '_' . $group_type : '')] = array( + 'id' => $permission . ($permissionArray[0] ? '_' . $group_type : ''), + 'name' => isset($txt['permissionname_simple_' . $permission . ($permissionArray[0] ? '_' . $group_type : '')]) ? $txt['permissionname_simple_' . $permission . ($permissionArray[0] ? '_' . $group_type : '')] : $txt['permissionname_' . $permission], + 'help_index' => isset($txt['permissionhelp_' . $permission]) ? 'permissionhelp_' . $permission : '', + 'hidden' => in_array($permission, $hiddenPermissions), + ); + } + } + + if (in_array($permission, $hiddenPermissions)) + { + if ($permissionArray[0]) + { + $context['hidden_permissions'][] = $permission . '_own'; + $context['hidden_permissions'][] = $permission . '_any'; + } + else + $context['hidden_permissions'][] = $permission; + } + } + ksort($context['permissions'][$permissionType]['columns']); + } + + // Check we don't leave any empty groups - and mark hidden ones as such. + foreach ($context['permissions'][$permissionType]['columns'] as $column => $groups) + foreach ($groups as $id => $group) + { + if (empty($group['permissions'])) + unset($context['permissions'][$permissionType]['columns'][$column][$id]); + else + { + $foundNonHidden = false; + foreach ($group['permissions'] as $permission) + if (empty($permission['hidden'])) + $foundNonHidden = true; + if (!$foundNonHidden) + $context['permissions'][$permissionType]['columns'][$column][$id]['hidden'] = true; + } + } +} + +// Initialize a form with inline permissions. +function init_inline_permissions($permissions, $excluded_groups = array()) +{ + global $context, $txt, $modSettings, $smcFunc; + + loadLanguage('ManagePermissions'); + loadTemplate('ManagePermissions'); + $context['can_change_permissions'] = allowedTo('manage_permissions'); + + // Nothing to initialize here. + if (!$context['can_change_permissions']) + return; + + // Load the permission settings for guests + foreach ($permissions as $permission) + $context[$permission] = array( + -1 => array( + 'id' => -1, + 'name' => $txt['membergroups_guests'], + 'is_postgroup' => false, + 'status' => 'off', + ), + 0 => array( + 'id' => 0, + 'name' => $txt['membergroups_members'], + 'is_postgroup' => false, + 'status' => 'off', + ), + ); + + $request = $smcFunc['db_query']('', ' + SELECT id_group, CASE WHEN add_deny = {int:denied} THEN {string:deny} ELSE {string:on} END AS status, permission + FROM {db_prefix}permissions + WHERE id_group IN (-1, 0) + AND permission IN ({array_string:permissions})', + array( + 'denied' => 0, + 'permissions' => $permissions, + 'deny' => 'deny', + 'on' => 'on', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context[$row['permission']][$row['id_group']]['status'] = $row['status']; + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT mg.id_group, mg.group_name, mg.min_posts, IFNULL(p.add_deny, -1) AS status, p.permission + FROM {db_prefix}membergroups AS mg + LEFT JOIN {db_prefix}permissions AS p ON (p.id_group = mg.id_group AND p.permission IN ({array_string:permissions})) + WHERE mg.id_group NOT IN (1, 3) + AND mg.id_parent = {int:not_inherited}' . (empty($modSettings['permission_enable_postgroups']) ? ' + AND mg.min_posts = {int:min_posts}' : '') . ' + ORDER BY mg.min_posts, CASE WHEN mg.id_group < {int:newbie_group} THEN mg.id_group ELSE 4 END, mg.group_name', + array( + 'not_inherited' => -2, + 'min_posts' => -1, + 'newbie_group' => 4, + 'permissions' => $permissions, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Initialize each permission as being 'off' until proven otherwise. + foreach ($permissions as $permission) + if (!isset($context[$permission][$row['id_group']])) + $context[$permission][$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'is_postgroup' => $row['min_posts'] != -1, + 'status' => 'off', + ); + + $context[$row['permission']][$row['id_group']]['status'] = empty($row['status']) ? 'deny' : ($row['status'] == 1 ? 'on' : 'off'); + } + $smcFunc['db_free_result']($request); + + // Some permissions cannot be given to certain groups. Remove the groups. + foreach ($excluded_groups as $group) + { + foreach ($permissions as $permission) + { + if (isset($context[$permission][$group])) + unset($context[$permission][$group]); + } + } +} + +// Show a collapsible box to set a specific permission. +function theme_inline_permissions($permission) +{ + global $context; + + $context['current_permission'] = $permission; + $context['member_groups'] = $context[$permission]; + + template_inline_permissions(); +} + +// Save the permissions of a form containing inline permissions. +function save_inline_permissions($permissions) +{ + global $context, $smcFunc; + + // No permissions? Not a great deal to do here. + if (!allowedTo('manage_permissions')) + return; + + // Almighty session check, verify our ways. + checkSession(); + + // Check they can't do certain things. + loadIllegalPermissions(); + + $insertRows = array(); + foreach ($permissions as $permission) + { + if (!isset($_POST[$permission])) + continue; + + foreach ($_POST[$permission] as $id_group => $value) + { + if (in_array($value, array('on', 'deny')) && (empty($context['illegal_permissions']) || !in_array($permission, $context['illegal_permissions']))) + $insertRows[] = array((int) $id_group, $permission, $value == 'on' ? 1 : 0); + } + } + + // Remove the old permissions... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE permission IN ({array_string:permissions}) + ' . (empty($context['illegal_permissions']) ? '' : ' AND permission NOT IN ({array_string:illegal_permissions})'), + array( + 'illegal_permissions' => !empty($context['illegal_permissions']) ? $context['illegal_permissions'] : array(), + 'permissions' => $permissions, + ) + ); + + // ...and replace them with new ones. + if (!empty($insertRows)) + $smcFunc['db_insert']('insert', + '{db_prefix}permissions', + array('id_group' => 'int', 'permission' => 'string', 'add_deny' => 'int'), + $insertRows, + array('id_group', 'permission') + ); + + // Do a full child update. + updateChildPermissions(array(), -1); + + // Just in case we cached this. + updateSettings(array('settings_updated' => time())); +} + +function loadPermissionProfiles() +{ + global $context, $txt, $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT id_profile, profile_name + FROM {db_prefix}permission_profiles + ORDER BY id_profile', + array( + ) + ); + $context['profiles'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Format the label nicely. + if (isset($txt['permissions_profile_' . $row['profile_name']])) + $name = $txt['permissions_profile_' . $row['profile_name']]; + else + $name = $row['profile_name']; + + $context['profiles'][$row['id_profile']] = array( + 'id' => $row['id_profile'], + 'name' => $name, + 'can_modify' => $row['id_profile'] == 1 || $row['id_profile'] > 4, + 'unformatted_name' => $row['profile_name'], + ); + } + $smcFunc['db_free_result']($request); +} + +// Add/Edit/Delete profiles. +function EditPermissionProfiles() +{ + global $context, $txt, $smcFunc; + + // Setup the template, first for fun. + $context['page_title'] = $txt['permissions_profile_edit']; + $context['sub_template'] = 'edit_profiles'; + + // If we're creating a new one do it first. + if (isset($_POST['create']) && trim($_POST['profile_name']) != '') + { + checkSession(); + + $_POST['copy_from'] = (int) $_POST['copy_from']; + $_POST['profile_name'] = $smcFunc['htmlspecialchars']($_POST['profile_name']); + + // Insert the profile itself. + $smcFunc['db_insert']('', + '{db_prefix}permission_profiles', + array( + 'profile_name' => 'string', + ), + array( + $_POST['profile_name'], + ), + array('id_profile') + ); + $profile_id = $smcFunc['db_insert_id']('{db_prefix}permission_profiles', 'id_profile'); + + // Load the permissions from the one it's being copied from. + $request = $smcFunc['db_query']('', ' + SELECT id_group, permission, add_deny + FROM {db_prefix}board_permissions + WHERE id_profile = {int:copy_from}', + array( + 'copy_from' => $_POST['copy_from'], + ) + ); + $inserts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $inserts[] = array($profile_id, $row['id_group'], $row['permission'], $row['add_deny']); + $smcFunc['db_free_result']($request); + + if (!empty($inserts)) + $smcFunc['db_insert']('insert', + '{db_prefix}board_permissions', + array('id_profile' => 'int', 'id_group' => 'int', 'permission' => 'string', 'add_deny' => 'int'), + $inserts, + array('id_profile', 'id_group', 'permission') + ); + } + // Renaming? + elseif (isset($_POST['rename'])) + { + checkSession(); + + // Just showing the boxes? + if (!isset($_POST['rename_profile'])) + $context['show_rename_boxes'] = true; + else + { + foreach ($_POST['rename_profile'] as $id => $value) + { + $value = $smcFunc['htmlspecialchars']($value); + + if (trim($value) != '' && $id > 4) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}permission_profiles + SET profile_name = {string:profile_name} + WHERE id_profile = {int:current_profile}', + array( + 'current_profile' => (int) $id, + 'profile_name' => $value, + ) + ); + } + } + } + // Deleting? + elseif (isset($_POST['delete']) && !empty($_POST['delete_profile'])) + { + checkSession('post'); + + $profiles = array(); + foreach ($_POST['delete_profile'] as $profile) + if ($profile > 4) + $profiles[] = (int) $profile; + + // Verify it's not in use... + $request = $smcFunc['db_query']('', ' + SELECT id_board + FROM {db_prefix}boards + WHERE id_profile IN ({array_int:profile_list}) + LIMIT 1', + array( + 'profile_list' => $profiles, + ) + ); + if ($smcFunc['db_num_rows']($request) != 0) + fatal_lang_error('no_access', false); + $smcFunc['db_free_result']($request); + + // Oh well, delete. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permission_profiles + WHERE id_profile IN ({array_int:profile_list})', + array( + 'profile_list' => $profiles, + ) + ); + } + + // Clearly, we'll need this! + loadPermissionProfiles(); + + // Work out what ones are in use. + $request = $smcFunc['db_query']('', ' + SELECT id_profile, COUNT(id_board) AS board_count + FROM {db_prefix}boards + GROUP BY id_profile', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + if (isset($context['profiles'][$row['id_profile']])) + { + $context['profiles'][$row['id_profile']]['in_use'] = true; + $context['profiles'][$row['id_profile']]['boards'] = $row['board_count']; + $context['profiles'][$row['id_profile']]['boards_text'] = $row['board_count'] > 1 ? sprintf($txt['permissions_profile_used_by_many'], $row['board_count']) : $txt['permissions_profile_used_by_' . ($row['board_count'] ? 'one' : 'none')]; + } + $smcFunc['db_free_result']($request); + + // What can we do with these? + $context['can_edit_something'] = false; + foreach ($context['profiles'] as $id => $profile) + { + // Can't delete special ones. + $context['profiles'][$id]['can_edit'] = isset($txt['permissions_profile_' . $profile['unformatted_name']]) ? false : true; + if ($context['profiles'][$id]['can_edit']) + $context['can_edit_something'] = true; + + // You can only delete it if you can edit it AND it's not in use. + $context['profiles'][$id]['can_delete'] = $context['profiles'][$id]['can_edit'] && empty($profile['in_use']) ? true : false; + } +} + +// This function updates the permissions of any groups based off this group. +function updateChildPermissions($parents, $profile = null) +{ + global $smcFunc; + + // All the parent groups to sort out. + if (!is_array($parents)) + $parents = array($parents); + + // Find all the children of this group. + $request = $smcFunc['db_query']('', ' + SELECT id_parent, id_group + FROM {db_prefix}membergroups + WHERE id_parent != {int:not_inherited} + ' . (empty($parents) ? '' : 'AND id_parent IN ({array_int:parent_list})'), + array( + 'parent_list' => $parents, + 'not_inherited' => -2, + ) + ); + $children = array(); + $parents = array(); + $child_groups = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $children[$row['id_parent']][] = $row['id_group']; + $child_groups[] = $row['id_group']; + $parents[] = $row['id_parent']; + } + $smcFunc['db_free_result']($request); + + $parents = array_unique($parents); + + // Not a sausage, or a child? + if (empty($children)) + return false; + + // First off, are we doing general permissions? + if ($profile < 1 || $profile === null) + { + // Fetch all the parent permissions. + $request = $smcFunc['db_query']('', ' + SELECT id_group, permission, add_deny + FROM {db_prefix}permissions + WHERE id_group IN ({array_int:parent_list})', + array( + 'parent_list' => $parents, + ) + ); + $permissions = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + foreach ($children[$row['id_group']] as $child) + $permissions[] = array($child, $row['permission'], $row['add_deny']); + $smcFunc['db_free_result']($request); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE id_group IN ({array_int:child_groups})', + array( + 'child_groups' => $child_groups, + ) + ); + + // Finally insert. + if (!empty($permissions)) + { + $smcFunc['db_insert']('insert', + '{db_prefix}permissions', + array('id_group' => 'int', 'permission' => 'string', 'add_deny' => 'int'), + $permissions, + array('id_group', 'permission') + ); + } + } + + // Then, what about board profiles? + if ($profile != -1) + { + $profileQuery = $profile === null ? '' : ' AND id_profile = {int:current_profile}'; + + // Again, get all the parent permissions. + $request = $smcFunc['db_query']('', ' + SELECT id_profile, id_group, permission, add_deny + FROM {db_prefix}board_permissions + WHERE id_group IN ({array_int:parent_groups}) + ' . $profileQuery, + array( + 'parent_groups' => $parents, + 'current_profile' => $profile !== null && $profile ? $profile : 1, + ) + ); + $permissions = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + foreach ($children[$row['id_group']] as $child) + $permissions[] = array($child, $row['id_profile'], $row['permission'], $row['add_deny']); + $smcFunc['db_free_result']($request); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_group IN ({array_int:child_groups}) + ' . $profileQuery, + array( + 'child_groups' => $child_groups, + 'current_profile' => $profile !== null && $profile ? $profile : 1, + ) + ); + + // Do the insert. + if (!empty($permissions)) + { + $smcFunc['db_insert']('insert', + '{db_prefix}board_permissions', + array('id_group' => 'int', 'id_profile' => 'int', 'permission' => 'string', 'add_deny' => 'int'), + $permissions, + array('id_group', 'id_profile', 'permission') + ); + } + } +} + +// Load permissions someone cannot grant. +function loadIllegalPermissions() +{ + global $context; + + $context['illegal_permissions'] = array(); + if (!allowedTo('admin_forum')) + $context['illegal_permissions'][] = 'admin_forum'; + if (!allowedTo('manage_membergroups')) + $context['illegal_permissions'][] = 'manage_membergroups'; + if (!allowedTo('manage_permissions')) + $context['illegal_permissions'][] = 'manage_permissions'; +} + +// Load all the permissions that can not be given to guests. +function loadIllegalGuestPermissions() +{ + global $context; + + $context['non_guest_permissions'] = array( + 'delete_replies', + 'karma_edit', + 'poll_add_own', + 'pm_read', + 'pm_send', + 'profile_identity', + 'profile_extra', + 'profile_title', + 'profile_remove', + 'profile_server_avatar', + 'profile_upload_avatar', + 'profile_remote_avatar', + 'profile_view_own', + 'mark_any_notify', + 'mark_notify', + 'admin_forum', + 'manage_boards', + 'manage_attachments', + 'manage_smileys', + 'edit_news', + 'access_mod_center', + 'moderate_forum', + 'issue_warning', + 'manage_membergroups', + 'manage_permissions', + 'manage_bans', + 'move_own', + 'modify_replies', + 'send_mail', + 'approve_posts', + ); +} + +// Present a nice way of applying post moderation. +function ModifyPostModeration() +{ + global $context, $txt, $smcFunc, $modSettings; + + // Just in case. + checkSession('get'); + + $context['page_title'] = $txt['permissions_post_moderation']; + $context['sub_template'] = 'postmod_permissions'; + $context['current_profile'] = isset($_REQUEST['pid']) ? (int) $_REQUEST['pid'] : 1; + + // Load all the permission profiles. + loadPermissionProfiles(); + + // Mappings, our key => array(can_do_moderated, can_do_all) + $mappings = array( + 'new_topic' => array('post_new', 'post_unapproved_topics'), + 'replies_own' => array('post_reply_own', 'post_unapproved_replies_own'), + 'replies_any' => array('post_reply_any', 'post_unapproved_replies_any'), + 'attachment' => array('post_attachment', 'post_unapproved_attachments'), + ); + + // Start this with the guests/members. + $context['profile_groups'] = array( + -1 => array( + 'id' => -1, + 'name' => $txt['membergroups_guests'], + 'color' => '', + 'new_topic' => 'disallow', + 'replies_own' => 'disallow', + 'replies_any' => 'disallow', + 'attachment' => 'disallow', + 'children' => array(), + ), + 0 => array( + 'id' => 0, + 'name' => $txt['membergroups_members'], + 'color' => '', + 'new_topic' => 'disallow', + 'replies_own' => 'disallow', + 'replies_any' => 'disallow', + 'attachment' => 'disallow', + 'children' => array(), + ), + ); + + // Load the groups. + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name, online_color, id_parent + FROM {db_prefix}membergroups + WHERE id_group != {int:admin_group} + ' . (empty($modSettings['permission_enable_postgroups']) ? ' AND min_posts = {int:min_posts}' : '') . ' + ORDER BY id_parent ASC', + array( + 'admin_group' => 1, + 'min_posts' => -1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['id_parent'] == -2) + { + $context['profile_groups'][$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'color' => $row['online_color'], + 'new_topic' => 'disallow', + 'replies_own' => 'disallow', + 'replies_any' => 'disallow', + 'attachment' => 'disallow', + 'children' => array(), + ); + } + elseif (isset($context['profile_groups'][$row['id_parent']])) + $context['profile_groups'][$row['id_parent']]['children'][] = $row['group_name']; + } + $smcFunc['db_free_result']($request); + + // What are the permissions we are querying? + $all_permissions = array(); + foreach ($mappings as $perm_set) + $all_permissions = array_merge($all_permissions, $perm_set); + + // If we're saving the changes then do just that - save them. + if (!empty($_POST['save_changes']) && ($context['current_profile'] == 1 || $context['current_profile'] > 4)) + { + // Start by deleting all the permissions relevant. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_profile = {int:current_profile} + AND permission IN ({array_string:permissions}) + AND id_group IN ({array_int:profile_group_list})', + array( + 'profile_group_list' => array_keys($context['profile_groups']), + 'current_profile' => $context['current_profile'], + 'permissions' => $all_permissions, + ) + ); + + // Do it group by group. + $new_permissions = array(); + foreach ($context['profile_groups'] as $id => $group) + { + foreach ($mappings as $index => $data) + { + if (isset($_POST[$index][$group['id']])) + { + if ($_POST[$index][$group['id']] == 'allow') + { + // Give them both sets for fun. + $new_permissions[] = array($context['current_profile'], $group['id'], $data[0], 1); + $new_permissions[] = array($context['current_profile'], $group['id'], $data[1], 1); + } + elseif ($_POST[$index][$group['id']] == 'moderate') + $new_permissions[] = array($context['current_profile'], $group['id'], $data[1], 1); + } + } + } + + // Insert new permissions. + if (!empty($new_permissions)) + $smcFunc['db_insert']('', + '{db_prefix}board_permissions', + array('id_profile' => 'int', 'id_group' => 'int', 'permission' => 'string', 'add_deny' => 'int'), + $new_permissions, + array('id_profile', 'id_group', 'permission') + ); + } + + // Now get all the permissions! + $request = $smcFunc['db_query']('', ' + SELECT id_group, permission, add_deny + FROM {db_prefix}board_permissions + WHERE id_profile = {int:current_profile} + AND permission IN ({array_string:permissions}) + AND id_group IN ({array_int:profile_group_list})', + array( + 'profile_group_list' => array_keys($context['profile_groups']), + 'current_profile' => $context['current_profile'], + 'permissions' => $all_permissions, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + foreach ($mappings as $key => $data) + { + foreach ($data as $index => $perm) + { + if ($perm == $row['permission']) + { + // Only bother if it's not denied. + if ($row['add_deny']) + { + // Full allowance? + if ($index == 0) + $context['profile_groups'][$row['id_group']][$key] = 'allow'; + // Otherwise only bother with moderate if not on allow. + elseif ($context['profile_groups'][$row['id_group']][$key] != 'allow') + $context['profile_groups'][$row['id_group']][$key] = 'moderate'; + } + } + } + } + } + $smcFunc['db_free_result']($request); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManagePosts.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManagePosts.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,372 @@ + 'ModifyPostSettings', + 'bbc' => 'ModifyBBCSettings', + 'censor' => 'SetCensor', + 'topics' => 'ModifyTopicSettings', + ); + + // Default the sub-action to 'posts'. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'posts'; + + $context['page_title'] = $txt['manageposts_title']; + + // Tabs for browsing the different ban functions. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['manageposts_title'], + 'help' => 'posts_and_topics', + 'description' => $txt['manageposts_description'], + 'tabs' => array( + 'posts' => array( + 'description' => $txt['manageposts_settings_description'], + ), + 'bbc' => array( + 'description' => $txt['manageposts_bbc_settings_description'], + ), + 'censor' => array( + 'description' => $txt['admin_censored_desc'], + ), + 'topics' => array( + 'description' => $txt['manageposts_topic_settings_description'], + ), + ), + ); + + // Call the right function for this sub-action. + $subActions[$_REQUEST['sa']](); +} + +// Set the censored words. +function SetCensor() +{ + global $txt, $modSettings, $context, $smcFunc; + + if (!empty($_POST['save_censor'])) + { + // Make sure censoring is something they can do. + checkSession(); + + $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', + ); + + updateSettings($updates); + } + + if (isset($_POST['censortest'])) + { + $censorText = htmlspecialchars($_POST['censortest'], ENT_QUOTES); + $context['censor_test'] = strtr(censorText($censorText), array('"' => '"')); + } + + // 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]) : ''; + } + + $context['sub_template'] = 'edit_censored'; + $context['page_title'] = $txt['admin_censored_words']; +} + +// Modify all settings related to posts and posting. +function ModifyPostSettings($return_config = false) +{ + global $context, $txt, $modSettings, $scripturl, $sourcedir, $smcFunc, $db_prefix; + + // All the settings... + $config_vars = array( + // Simple post options... + array('check', 'removeNestedQuotes'), + array('check', 'enableEmbeddedFlash', 'subtext' => $txt['enableEmbeddedFlash_warning']), + // Note show the warning as read if pspell not installed! + array('check', 'enableSpellChecking', 'subtext' => (function_exists('pspell_new') ? $txt['enableSpellChecking_warning'] : ('' . $txt['enableSpellChecking_warning'] . ''))), + array('check', 'disable_wysiwyg'), + '', + // Posting limits... + array('int', 'max_messageLength', 'subtext' => $txt['max_messageLength_zero'], 'postinput' => $txt['manageposts_characters']), + array('int', 'fixLongWords', 'subtext' => $txt['fixLongWords_zero'] . ($context['utf8'] ? ' ' . $txt['fixLongWords_warning'] . '' : ''), 'postinput' => $txt['manageposts_characters']), + array('int', 'topicSummaryPosts', 'postinput' => $txt['manageposts_posts']), + '', + // Posting time limits... + array('int', 'spamWaitTime', 'postinput' => $txt['manageposts_seconds']), + array('int', 'edit_wait_time', 'postinput' => $txt['manageposts_seconds']), + array('int', 'edit_disable_time', 'subtext' => $txt['edit_disable_time_zero'], 'postinput' => $txt['manageposts_minutes']), + ); + + if ($return_config) + return $config_vars; + + // We'll want this for our easy save. + require_once($sourcedir . '/ManageServer.php'); + + // Setup the template. + $context['page_title'] = $txt['manageposts_settings']; + $context['sub_template'] = 'show_settings'; + + // Are we saving them - are we?? + if (isset($_GET['save'])) + { + checkSession(); + + // If we're changing the message length let's check the column is big enough. + if (!empty($_POST['max_messageLength']) && $_POST['max_messageLength'] != $modSettings['max_messageLength']) + { + db_extend('packages'); + + $colData = $smcFunc['db_list_columns']('{db_prefix}messages', true); + foreach ($colData as $column) + if ($column['name'] == 'body') + $body_type = $column['type']; + + $indData = $smcFunc['db_list_indexes']('{db_prefix}messages', true); + foreach ($indData as $index) + foreach ($index['columns'] as $column) + if ($column == 'body' && $index['type'] == 'fulltext') + $fulltext = true; + + if (isset($body_type) && $_POST['max_messageLength'] > 65535 && $body_type == 'text') + { + // !!! Show an error message?! + // MySQL only likes fulltext indexes on text columns... for now? + if (!empty($fulltext)) + $_POST['max_messageLength'] = 65535; + else + { + // Make it longer so we can do their limit. + $smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'mediumtext')); + } + } + elseif (isset($body_type) && $_POST['max_messageLength'] <= 65535 && $body_type != 'text') + { + // Shorten the column so we can have the benefit of fulltext searching again! + $smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'text')); + } + } + + saveDBSettings($config_vars); + redirectexit('action=admin;area=postsettings;sa=posts'); + } + + // Final settings... + $context['post_url'] = $scripturl . '?action=admin;area=postsettings;save;sa=posts'; + $context['settings_title'] = $txt['manageposts_settings']; + + // Prepare the settings... + prepareDBSettingContext($config_vars); +} + +// Bulletin Board Code...a lot of Bulletin Board Code. +function ModifyBBCSettings($return_config = false) +{ + global $context, $txt, $modSettings, $helptxt, $scripturl, $sourcedir; + + $config_vars = array( + // Main tweaks + array('check', 'enableBBC'), + array('check', 'enablePostHTML'), + array('check', 'autoLinkUrls'), + '', + array('bbc', 'disabledBBC'), + ); + + if ($return_config) + return $config_vars; + + // Setup the template. + require_once($sourcedir . '/ManageServer.php'); + $context['sub_template'] = 'show_settings'; + $context['page_title'] = $txt['manageposts_bbc_settings_title']; + + // Make sure we check the right tags! + $modSettings['bbc_disabled_disabledBBC'] = empty($modSettings['disabledBBC']) ? array() : explode(',', $modSettings['disabledBBC']); + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + // Clean up the tags. + $bbcTags = array(); + foreach (parse_bbc(false) as $tag) + $bbcTags[] = $tag['tag']; + + if (!isset($_POST['disabledBBC_enabledTags'])) + $_POST['disabledBBC_enabledTags'] = array(); + elseif (!is_array($_POST['disabledBBC_enabledTags'])) + $_POST['disabledBBC_enabledTags'] = array($_POST['disabledBBC_enabledTags']); + // Work out what is actually disabled! + $_POST['disabledBBC'] = implode(',', array_diff($bbcTags, $_POST['disabledBBC_enabledTags'])); + + saveDBSettings($config_vars); + redirectexit('action=admin;area=postsettings;sa=bbc'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=postsettings;save;sa=bbc'; + $context['settings_title'] = $txt['manageposts_bbc_settings_title']; + + prepareDBSettingContext($config_vars); +} + +// Function for modifying topic settings. Not very exciting. +function ModifyTopicSettings($return_config = false) +{ + global $context, $txt, $modSettings, $sourcedir, $scripturl; + + // Here are all the topic settings. + $config_vars = array( + // Some simple bools... + array('check', 'enableStickyTopics'), + array('check', 'enableParticipation'), + '', + // Pagination etc... + array('int', 'oldTopicDays', 'postinput' => $txt['manageposts_days'], 'subtext' => $txt['oldTopicDays_zero']), + array('int', 'defaultMaxTopics', 'postinput' => $txt['manageposts_topics']), + array('int', 'defaultMaxMessages', 'postinput' => $txt['manageposts_posts']), + '', + // Hot topics (etc)... + array('int', 'hotTopicPosts', 'postinput' => $txt['manageposts_posts']), + array('int', 'hotTopicVeryPosts', 'postinput' => $txt['manageposts_posts']), + '', + // All, next/prev... + array('int', 'enableAllMessages', 'postinput' => $txt['manageposts_posts'], 'subtext' => $txt['enableAllMessages_zero']), + array('check', 'disableCustomPerPage'), + array('check', 'enablePreviousNext'), + + ); + + if ($return_config) + return $config_vars; + + // Get the settings template ready. + require_once($sourcedir . '/ManageServer.php'); + + // Setup the template. + $context['page_title'] = $txt['manageposts_topic_settings']; + $context['sub_template'] = 'show_settings'; + + // Are we saving them - are we?? + if (isset($_GET['save'])) + { + checkSession(); + + saveDBSettings($config_vars); + redirectexit('action=admin;area=postsettings;sa=topics'); + } + + // Final settings... + $context['post_url'] = $scripturl . '?action=admin;area=postsettings;save;sa=topics'; + $context['settings_title'] = $txt['manageposts_topic_settings']; + + // Prepare the settings... + prepareDBSettingContext($config_vars); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageRegistration.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageRegistration.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,330 @@ + array('AdminRegister', 'moderate_forum'), + 'agreement' => array('EditAgreement', 'admin_forum'), + 'reservednames' => array('SetReserve', 'admin_forum'), + 'settings' => array('ModifyRegistrationSettings', 'admin_forum'), + ); + + // Work out which to call... + $context['sub_action'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (allowedTo('moderate_forum') ? 'register' : 'settings'); + + // Must have sufficient permissions. + isAllowedTo($subActions[$context['sub_action']][1]); + + // Loading, always loading. + loadLanguage('Login'); + loadTemplate('Register'); + + // Next create the tabs for the template. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['registration_center'], + 'help' => 'registrations', + 'description' => $txt['admin_settings_desc'], + 'tabs' => array( + 'register' => array( + 'description' => $txt['admin_register_desc'], + ), + 'agreement' => array( + 'description' => $txt['registration_agreement_desc'], + ), + 'reservednames' => array( + 'description' => $txt['admin_reserved_desc'], + ), + 'settings' => array( + 'description' => $txt['admin_settings_desc'], + ) + ) + ); + + // Finally, get around to calling the function... + $subActions[$context['sub_action']][0](); +} + +// This function allows the admin to register a new member by hand. +function AdminRegister() +{ + global $txt, $context, $sourcedir, $scripturl, $smcFunc; + + if (!empty($_POST['regSubmit'])) + { + checkSession(); + + foreach ($_POST as $key => $value) + if (!is_array($_POST[$key])) + $_POST[$key] = htmltrim__recursive(str_replace(array("\n", "\r"), '', $_POST[$key])); + + $regOptions = array( + 'interface' => 'admin', + 'username' => $_POST['user'], + 'email' => $_POST['email'], + 'password' => $_POST['password'], + 'password_check' => $_POST['password'], + 'check_reserved_name' => true, + 'check_password_strength' => false, + 'check_email_ban' => false, + 'send_welcome_email' => isset($_POST['emailPassword']) || empty($_POST['password']), + 'require' => isset($_POST['emailActivate']) ? 'activation' : 'nothing', + 'memberGroup' => empty($_POST['group']) || !allowedTo('manage_membergroups') ? 0 : (int) $_POST['group'], + ); + + require_once($sourcedir . '/Subs-Members.php'); + $memberID = registerMember($regOptions); + if (!empty($memberID)) + { + $context['new_member'] = array( + 'id' => $memberID, + 'name' => $_POST['user'], + 'href' => $scripturl . '?action=profile;u=' . $memberID, + 'link' => '' . $_POST['user'] . '', + ); + $context['registration_done'] = sprintf($txt['admin_register_done'], $context['new_member']['link']); + } + } + + // Basic stuff. + $context['sub_template'] = 'admin_register'; + $context['page_title'] = $txt['registration_center']; + + // Load the assignable member groups. + if (allowedTo('manage_membergroups')) + { + $request = $smcFunc['db_query']('', ' + SELECT group_name, id_group + FROM {db_prefix}membergroups + WHERE id_group != {int:moderator_group} + AND min_posts = {int:min_posts}' . (allowedTo('admin_forum') ? '' : ' + AND id_group != {int:admin_group} + AND group_type != {int:is_protected}') . ' + AND hidden != {int:hidden_group} + ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name', + array( + 'moderator_group' => 3, + 'min_posts' => -1, + 'admin_group' => 1, + 'is_protected' => 1, + 'hidden_group' => 2, + 'newbie_group' => 4, + ) + ); + $context['member_groups'] = array(0 => $txt['admin_register_group_none']); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['member_groups'][$row['id_group']] = $row['group_name']; + $smcFunc['db_free_result']($request); + } + else + $context['member_groups'] = array(); +} + +// I hereby agree not to be a lazy bum. +function EditAgreement() +{ + global $txt, $boarddir, $context, $modSettings, $smcFunc, $settings; + + // By default we look at agreement.txt. + $context['current_agreement'] = ''; + + // Is there more than one to edit? + $context['editable_agreements'] = array( + '' => $txt['admin_agreement_default'], + ); + + // Get our languages. + getLanguages(); + + // Try to figure out if we have more agreements. + foreach ($context['languages'] as $lang) + { + if (file_exists($boarddir . '/agreement.' . $lang['filename'] . '.txt')) + { + $context['editable_agreements']['.' . $lang['filename']] = $lang['name']; + // Are we editing this? + if (isset($_POST['agree_lang']) && $_POST['agree_lang'] == '.' . $lang['filename']) + $context['current_agreement'] = '.' . $lang['filename']; + } + } + + if (isset($_POST['agreement'])) + { + checkSession(); + + // Off it goes to the agreement file. + $fp = fopen($boarddir . '/agreement' . $context['current_agreement'] . '.txt', 'w'); + fwrite($fp, str_replace("\r", '', $_POST['agreement'])); + fclose($fp); + + updateSettings(array('requireAgreement' => !empty($_POST['requireAgreement']))); + } + + $context['agreement'] = file_exists($boarddir . '/agreement' . $context['current_agreement'] . '.txt') ? htmlspecialchars(file_get_contents($boarddir . '/agreement' . $context['current_agreement'] . '.txt')) : ''; + $context['warning'] = is_writable($boarddir . '/agreement' . $context['current_agreement'] . '.txt') ? '' : $txt['agreement_not_writable']; + $context['require_agreement'] = !empty($modSettings['requireAgreement']); + + $context['sub_template'] = 'edit_agreement'; + $context['page_title'] = $txt['registration_agreement']; +} + +// Set reserved names/words.... +function SetReserve() +{ + global $txt, $context, $modSettings; + + // Submitting new reserved words. + if (!empty($_POST['save_reserved_names'])) + { + checkSession(); + + // Set all the options.... + updateSettings(array( + 'reserveWord' => (isset($_POST['matchword']) ? '1' : '0'), + 'reserveCase' => (isset($_POST['matchcase']) ? '1' : '0'), + 'reserveUser' => (isset($_POST['matchuser']) ? '1' : '0'), + 'reserveName' => (isset($_POST['matchname']) ? '1' : '0'), + 'reserveNames' => str_replace("\r", '', $_POST['reserved']) + )); + } + + // Get the reserved word options and words. + $modSettings['reserveNames'] = str_replace('\n', "\n", $modSettings['reserveNames']); + $context['reserved_words'] = explode("\n", $modSettings['reserveNames']); + $context['reserved_word_options'] = array(); + $context['reserved_word_options']['match_word'] = $modSettings['reserveWord'] == '1'; + $context['reserved_word_options']['match_case'] = $modSettings['reserveCase'] == '1'; + $context['reserved_word_options']['match_user'] = $modSettings['reserveUser'] == '1'; + $context['reserved_word_options']['match_name'] = $modSettings['reserveName'] == '1'; + + // Ready the template...... + $context['sub_template'] = 'edit_reserved_words'; + $context['page_title'] = $txt['admin_reserved_set']; +} + +// This function handles registration settings, and provides a few pretty stats too while it's at it. +function ModifyRegistrationSettings($return_config = false) +{ + global $txt, $context, $scripturl, $modSettings, $sourcedir; + + // This is really quite wanting. + require_once($sourcedir . '/ManageServer.php'); + + $config_vars = array( + array('select', 'registration_method', array($txt['setting_registration_standard'], $txt['setting_registration_activate'], $txt['setting_registration_approval'], $txt['setting_registration_disabled'])), + array('check', 'enableOpenID'), + array('check', 'notify_new_registration'), + array('check', 'send_welcomeEmail'), + '', + array('int', 'coppaAge', 'subtext' => $txt['setting_coppaAge_desc'], 'onchange' => 'checkCoppa();'), + array('select', 'coppaType', array($txt['setting_coppaType_reject'], $txt['setting_coppaType_approval']), 'onchange' => 'checkCoppa();'), + array('large_text', 'coppaPost', 'subtext' => $txt['setting_coppaPost_desc']), + array('text', 'coppaFax'), + array('text', 'coppaPhone'), + ); + + if ($return_config) + return $config_vars; + + // Setup the template + $context['sub_template'] = 'show_settings'; + $context['page_title'] = $txt['registration_center']; + + if (isset($_GET['save'])) + { + checkSession(); + + // Are there some contacts missing? + if (!empty($_POST['coppaAge']) && !empty($_POST['coppaType']) && empty($_POST['coppaPost']) && empty($_POST['coppaFax'])) + fatal_lang_error('admin_setting_coppa_require_contact'); + + // Post needs to take into account line breaks. + $_POST['coppaPost'] = str_replace("\n", '
', empty($_POST['coppaPost']) ? '' : $_POST['coppaPost']); + + saveDBSettings($config_vars); + + redirectexit('action=admin;area=regcenter;sa=settings'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=regcenter;save;sa=settings'; + $context['settings_title'] = $txt['settings']; + + // Define some javascript for COPPA. + $context['settings_post_javascript'] = ' + function checkCoppa() + { + var coppaDisabled = document.getElementById(\'coppaAge\').value == 0; + document.getElementById(\'coppaType\').disabled = coppaDisabled; + + var disableContacts = coppaDisabled || document.getElementById(\'coppaType\').options[document.getElementById(\'coppaType\').selectedIndex].value != 1; + document.getElementById(\'coppaPost\').disabled = disableContacts; + document.getElementById(\'coppaFax\').disabled = disableContacts; + document.getElementById(\'coppaPhone\').disabled = disableContacts; + } + checkCoppa();'; + + // Turn the postal address into something suitable for a textbox. + $modSettings['coppaPost'] = !empty($modSettings['coppaPost']) ? preg_replace('~
~', "\n", $modSettings['coppaPost']) : ''; + + prepareDBSettingContext($config_vars); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageScheduledTasks.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageScheduledTasks.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,558 @@ + 'EditTask', + 'tasklog' => 'TaskLog', + 'tasks' => 'ScheduledTasks', + ); + + // We need to find what's the action. + if (isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']])) + $context['sub_action'] = $_REQUEST['sa']; + else + $context['sub_action'] = 'tasks'; + + // Now for the lovely tabs. That we all love. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['scheduled_tasks_title'], + 'help' => '', + 'description' => $txt['maintain_info'], + 'tabs' => array( + 'tasks' => array( + 'description' => $txt['maintain_tasks_desc'], + ), + 'tasklog' => array( + 'description' => $txt['scheduled_log_desc'], + ), + ), + ); + + // Call it. + $subActions[$context['sub_action']](); +} + +// List all the scheduled task in place on the forum. +function ScheduledTasks() +{ + global $context, $txt, $sourcedir, $smcFunc, $user_info, $modSettings, $scripturl; + + // Mama, setup the template first - cause it's like the most important bit, like pickle in a sandwich. + // ... ironically I don't like pickle. + $context['sub_template'] = 'view_scheduled_tasks'; + $context['page_title'] = $txt['maintain_tasks']; + + // Saving changes? + if (isset($_REQUEST['save']) && isset($_POST['enable_task'])) + { + checkSession(); + + // We'll recalculate the dates at the end! + require_once($sourcedir . '/ScheduledTasks.php'); + + // Enable and disable as required. + $enablers = array(0); + foreach ($_POST['enable_task'] as $id => $enabled) + if ($enabled) + $enablers[] = (int) $id; + + // Do the update! + $smcFunc['db_query']('', ' + UPDATE {db_prefix}scheduled_tasks + SET disabled = CASE WHEN id_task IN ({array_int:id_task_enable}) THEN 0 ELSE 1 END', + array( + 'id_task_enable' => $enablers, + ) + ); + + // Pop along... + CalculateNextTrigger(); + } + + // Want to run any of the tasks? + if (isset($_REQUEST['run']) && isset($_POST['run_task'])) + { + // Lets figure out which ones they want to run. + $tasks = array(); + foreach ($_POST['run_task'] as $task => $dummy) + $tasks[] = (int) $task; + + // Load up the tasks. + $request = $smcFunc['db_query']('', ' + SELECT id_task, task + FROM {db_prefix}scheduled_tasks + WHERE id_task IN ({array_int:tasks}) + LIMIT ' . count($tasks), + array( + 'tasks' => $tasks, + ) + ); + + // Lets get it on! + require_once($sourcedir . '/ScheduledTasks.php'); + ignore_user_abort(true); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $start_time = microtime(); + // The functions got to exist for us to use it. + if (!function_exists('scheduled_' . $row['task'])) + continue; + + // Try to stop a timeout, this would be bad... + @set_time_limit(300); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + // Do the task... + $completed = call_user_func('scheduled_' . $row['task']); + + // Log that we did it ;) + if ($completed) + { + $total_time = round(array_sum(explode(' ', microtime())) - array_sum(explode(' ', $start_time)), 3); + $smcFunc['db_insert']('', + '{db_prefix}log_scheduled_tasks', + array('id_task' => 'int', 'time_run' => 'int', 'time_taken' => 'float'), + array($row['id_task'], time(), $total_time), + array('id_task') + ); + } + } + $smcFunc['db_free_result']($request); + redirectexit('action=admin;area=scheduledtasks;done'); + } + + $listOptions = array( + 'id' => 'scheduled_tasks', + 'title' => $txt['maintain_tasks'], + 'base_href' => $scripturl . '?action=admin;area=scheduledtasks', + 'get_items' => array( + 'function' => 'list_getScheduledTasks', + ), + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['scheduled_tasks_name'], + 'style' => 'width: 40%;', + ), + 'data' => array( + 'sprintf' => array( + 'format' => ' + %2$s
%3$s', + 'params' => array( + 'id' => false, + 'name' => false, + 'desc' => false, + ), + ), + ), + ), + 'next_due' => array( + 'header' => array( + 'value' => $txt['scheduled_tasks_next_time'], + ), + 'data' => array( + 'db' => 'next_time', + 'class' => 'smalltext', + ), + ), + 'regularity' => array( + 'header' => array( + 'value' => $txt['scheduled_tasks_regularity'], + ), + 'data' => array( + 'db' => 'regularity', + 'class' => 'smalltext', + ), + ), + 'enabled' => array( + 'header' => array( + 'value' => $txt['scheduled_tasks_enabled'], + 'style' => 'width: 6%;', + ), + 'data' => array( + 'sprintf' => array( + 'format' => + '', + 'params' => array( + 'id' => false, + 'checked_state' => false, + ), + ), + 'style' => 'text-align: center;', + ), + ), + 'run_now' => array( + 'header' => array( + 'value' => $txt['scheduled_tasks_run_now'], + 'style' => 'width: 12%;', + ), + 'data' => array( + 'sprintf' => array( + 'format' => + '', + 'params' => array( + 'id' => false, + ), + ), + 'style' => 'text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=scheduledtasks', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' + + ', + 'class' => 'floatright', + 'style' => 'text-align: right;', + ), + array( + 'position' => 'after_title', + 'value' => ' + ' . $txt['scheduled_tasks_time_offset'] . '', + 'class' => 'windowbg2', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['sub_template'] = 'view_scheduled_tasks'; + + $context['tasks_were_run'] = isset($_GET['done']); +} + +function list_getScheduledTasks($start, $items_per_page, $sort) +{ + global $smcFunc, $txt, $scripturl; + + $request = $smcFunc['db_query']('', ' + SELECT id_task, next_time, time_offset, time_regularity, time_unit, disabled, task + FROM {db_prefix}scheduled_tasks', + array( + ) + ); + $known_tasks = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Find the next for regularity - don't offset as it's always server time! + $offset = sprintf($txt['scheduled_task_reg_starting'], date('H:i', $row['time_offset'])); + $repeating = sprintf($txt['scheduled_task_reg_repeating'], $row['time_regularity'], $txt['scheduled_task_reg_unit_' . $row['time_unit']]); + + $known_tasks[] = array( + 'id' => $row['id_task'], + 'function' => $row['task'], + 'name' => isset($txt['scheduled_task_' . $row['task']]) ? $txt['scheduled_task_' . $row['task']] : $row['task'], + 'desc' => isset($txt['scheduled_task_desc_' . $row['task']]) ? $txt['scheduled_task_desc_' . $row['task']] : '', + 'next_time' => $row['disabled'] ? $txt['scheduled_tasks_na'] : timeformat(($row['next_time'] == 0 ? time() : $row['next_time']), true, 'server'), + 'disabled' => $row['disabled'], + 'checked_state' => $row['disabled'] ? '' : 'checked="checked"', + 'regularity' => $offset . ', ' . $repeating, + ); + } + $smcFunc['db_free_result']($request); + + return $known_tasks; +} + +// Function for editing a task. +function EditTask() +{ + global $context, $txt, $sourcedir, $smcFunc, $user_info, $modSettings; + + // Just set up some lovely context stuff. + $context[$context['admin_menu_name']]['current_subsection'] = 'tasks'; + $context['sub_template'] = 'edit_scheduled_tasks'; + $context['page_title'] = $txt['scheduled_task_edit']; + $context['server_time'] = timeformat(time(), false, 'server'); + + // Cleaning... + if (!isset($_GET['tid'])) + fatal_lang_error('no_access', false); + $_GET['tid'] = (int) $_GET['tid']; + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + // We'll need this for calculating the next event. + require_once($sourcedir . '/ScheduledTasks.php'); + + // Do we have a valid offset? + preg_match('~(\d{1,2}):(\d{1,2})~', $_POST['offset'], $matches); + + // If a half is empty then assume zero offset! + if (!isset($matches[2]) || $matches[2] > 59) + $matches[2] = 0; + if (!isset($matches[1]) || $matches[1] > 23) + $matches[1] = 0; + + // Now the offset is easy; easy peasy - except we need to offset by a few hours... + $offset = $matches[1] * 3600 + $matches[2] * 60 - date('Z'); + + // The other time bits are simple! + $interval = max((int) $_POST['regularity'], 1); + $unit = in_array(substr($_POST['unit'], 0, 1), array('m', 'h', 'd', 'w')) ? substr($_POST['unit'], 0, 1) : 'd'; + + // Don't allow one minute intervals. + if ($interval == 1 && $unit == 'm') + $interval = 2; + + // Is it disabled? + $disabled = !isset($_POST['enabled']) ? 1 : 0; + + // Do the update! + $smcFunc['db_query']('', ' + UPDATE {db_prefix}scheduled_tasks + SET disabled = {int:disabled}, time_offset = {int:time_offset}, time_unit = {string:time_unit}, + time_regularity = {int:time_regularity} + WHERE id_task = {int:id_task}', + array( + 'disabled' => $disabled, + 'time_offset' => $offset, + 'time_regularity' => $interval, + 'id_task' => $_GET['tid'], + 'time_unit' => $unit, + ) + ); + + // Check the next event. + CalculateNextTrigger($_GET['tid'], true); + + // Return to the main list. + redirectexit('action=admin;area=scheduledtasks'); + } + + // Load the task, understand? Que? Que? + $request = $smcFunc['db_query']('', ' + SELECT id_task, next_time, time_offset, time_regularity, time_unit, disabled, task + FROM {db_prefix}scheduled_tasks + WHERE id_task = {int:id_task}', + array( + 'id_task' => $_GET['tid'], + ) + ); + + // Should never, ever, happen! + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['task'] = array( + 'id' => $row['id_task'], + 'function' => $row['task'], + 'name' => isset($txt['scheduled_task_' . $row['task']]) ? $txt['scheduled_task_' . $row['task']] : $row['task'], + 'desc' => isset($txt['scheduled_task_desc_' . $row['task']]) ? $txt['scheduled_task_desc_' . $row['task']] : '', + 'next_time' => $row['disabled'] ? $txt['scheduled_tasks_na'] : timeformat($row['next_time'] == 0 ? time() : $row['next_time'], true, 'server'), + 'disabled' => $row['disabled'], + 'offset' => $row['time_offset'], + 'regularity' => $row['time_regularity'], + 'offset_formatted' => date('H:i', $row['time_offset']), + 'unit' => $row['time_unit'], + ); + } + $smcFunc['db_free_result']($request); +} + +// Show the log of all tasks that have taken place. +function TaskLog() +{ + global $scripturl, $context, $txt, $smcFunc, $sourcedir; + + // Lets load the language just incase we are outside the Scheduled area. + loadLanguage('ManageScheduledTasks'); + + // Empty the log? + if (!empty($_POST['removeAll'])) + { + checkSession(); + + $smcFunc['db_query']('truncate_table', ' + TRUNCATE {db_prefix}log_scheduled_tasks', + array( + ) + ); + } + + // Setup the list. + $listOptions = array( + 'id' => 'task_log', + 'items_per_page' => 30, + 'title' => $txt['scheduled_log'], + 'no_items_label' => $txt['scheduled_log_empty'], + 'base_href' => $context['admin_area'] == 'scheduledtasks' ? $scripturl . '?action=admin;area=scheduledtasks;sa=tasklog' : $scripturl . '?action=admin;area=logs;sa=tasklog', + 'default_sort_col' => 'date', + 'get_items' => array( + 'function' => 'list_getTaskLogEntries', + ), + 'get_count' => array( + 'function' => 'list_getNumTaskLogEntries', + ), + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['scheduled_tasks_name'], + ), + 'data' => array( + 'db' => 'name' + ), + ), + 'date' => array( + 'header' => array( + 'value' => $txt['scheduled_log_time_run'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return timeformat($rowData[\'time_run\'], true); + '), + ), + 'sort' => array( + 'default' => 'lst.id_log DESC', + 'reverse' => 'lst.id_log', + ), + ), + 'time_taken' => array( + 'header' => array( + 'value' => $txt['scheduled_log_time_taken'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => $txt['scheduled_log_time_taken_seconds'], + 'params' => array( + 'time_taken' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'lst.time_taken', + 'reverse' => 'lst.time_taken DESC', + ), + ), + ), + 'form' => array( + 'href' => $context['admin_area'] == 'scheduledtasks' ? $scripturl . '?action=admin;area=scheduledtasks;sa=tasklog' : $scripturl . '?action=admin;area=logs;sa=tasklog', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' + ', + 'style' => 'text-align: right;', + ), + array( + 'position' => 'after_title', + 'value' => $txt['scheduled_tasks_time_offset'], + 'class' => 'smalltext', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'task_log'; + + // Make it all look tify. + $context[$context['admin_menu_name']]['current_subsection'] = 'tasklog'; + $context['page_title'] = $txt['scheduled_log']; +} + +function list_getTaskLogEntries($start, $items_per_page, $sort) +{ + global $smcFunc, $txt; + + $request = $smcFunc['db_query']('', ' + SELECT lst.id_log, lst.id_task, lst.time_run, lst.time_taken, st.task + FROM {db_prefix}log_scheduled_tasks AS lst + INNER JOIN {db_prefix}scheduled_tasks AS st ON (st.id_task = lst.id_task) + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + ) + ); + $log_entries = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $log_entries[] = array( + 'id' => $row['id_log'], + 'name' => isset($txt['scheduled_task_' . $row['task']]) ? $txt['scheduled_task_' . $row['task']] : $row['task'], + 'time_run' => $row['time_run'], + 'time_taken' => $row['time_taken'], + ); + $smcFunc['db_free_result']($request); + + return $log_entries; +} + +function list_getNumTaskLogEntries() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_scheduled_tasks', + array( + ) + ); + list ($num_entries) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $num_entries; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageSearch.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageSearch.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,775 @@ + 'EditSearchSettings', + 'weights' => 'EditWeights', + 'method' => 'EditSearchMethod', + 'createfulltext' => 'EditSearchMethod', + 'removecustom' => 'EditSearchMethod', + 'removefulltext' => 'EditSearchMethod', + 'createmsgindex' => 'CreateMessageIndex', + ); + + // Default the sub-action to 'edit search settings'. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'weights'; + + $context['sub_action'] = $_REQUEST['sa']; + + // Create the tabs for the template. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['manage_search'], + 'help' => 'search', + 'description' => $txt['search_settings_desc'], + 'tabs' => array( + 'weights' => array( + 'description' => $txt['search_weights_desc'], + ), + 'method' => array( + 'description' => $txt['search_method_desc'], + ), + 'settings' => array( + 'description' => $txt['search_settings_desc'], + ), + ), + ); + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']](); +} + +function EditSearchSettings($return_config = false) +{ + global $txt, $context, $scripturl, $sourcedir, $modSettings; + + // What are we editing anyway? + $config_vars = array( + // Permission... + array('permissions', 'search_posts'), + // Some simple settings. + array('check', 'simpleSearch'), + array('int', 'search_results_per_page'), + array('int', 'search_max_results', 'subtext' => $txt['search_max_results_disable']), + '', + // Some limitations. + array('int', 'search_floodcontrol_time', 'subtext' => $txt['search_floodcontrol_time_desc']), + ); + + // Perhaps the search method wants to add some settings? + $modSettings['search_index'] = empty($modSettings['search_index']) ? 'standard' : $modSettings['search_index']; + if (file_exists($sourcedir . '/SearchAPI-' . ucwords($modSettings['search_index']) . '.php')) + { + loadClassFile('SearchAPI-' . ucwords($modSettings['search_index']) . '.php'); + + $method_call = array($modSettings['search_index'] . '_search', 'searchSettings'); + if (is_callable($method_call)) + call_user_func_array($method_call, array(&$config_vars)); + } + + if ($return_config) + return $config_vars; + + $context['page_title'] = $txt['search_settings_title']; + $context['sub_template'] = 'show_settings'; + + // We'll need this for the settings. + require_once($sourcedir . '/ManageServer.php'); + + // A form was submitted. + if (isset($_REQUEST['save'])) + { + checkSession(); + + saveDBSettings($config_vars); + redirectexit('action=admin;area=managesearch;sa=settings;' . $context['session_var'] . '=' . $context['session_id']); + } + + // Prep the template! + $context['post_url'] = $scripturl . '?action=admin;area=managesearch;save;sa=settings'; + $context['settings_title'] = $txt['search_settings_title']; + + prepareDBSettingContext($config_vars); +} + +function EditWeights() +{ + global $txt, $context, $modSettings; + + $context['page_title'] = $txt['search_weights_title']; + $context['sub_template'] = 'modify_weights'; + + $factors = array( + 'search_weight_frequency', + 'search_weight_age', + 'search_weight_length', + 'search_weight_subject', + 'search_weight_first_message', + 'search_weight_sticky', + ); + + // A form was submitted. + if (isset($_POST['save'])) + { + checkSession(); + + $changes = array(); + foreach ($factors as $factor) + $changes[$factor] = (int) $_POST[$factor]; + updateSettings($changes); + } + + $context['relative_weights'] = array('total' => 0); + foreach ($factors as $factor) + $context['relative_weights']['total'] += isset($modSettings[$factor]) ? $modSettings[$factor] : 0; + + foreach ($factors as $factor) + $context['relative_weights'][$factor] = round(100 * (isset($modSettings[$factor]) ? $modSettings[$factor] : 0) / $context['relative_weights']['total'], 1); +} + +function EditSearchMethod() +{ + global $txt, $context, $modSettings, $smcFunc, $db_type, $db_prefix; + + $context[$context['admin_menu_name']]['current_subsection'] = 'method'; + $context['page_title'] = $txt['search_method_title']; + $context['sub_template'] = 'select_search_method'; + $context['supports_fulltext'] = $smcFunc['db_search_support']('fulltext'); + + // Load any apis. + $context['search_apis'] = loadSearchAPIs(); + + // Detect whether a fulltext index is set. + if ($context['supports_fulltext']) + { + $request = $smcFunc['db_query']('', ' + SHOW INDEX + FROM {db_prefix}messages', + array( + ) + ); + $context['fulltext_index'] = ''; + if ($request !== false || $smcFunc['db_num_rows']($request) != 0) + { + while ($row = $smcFunc['db_fetch_assoc']($request)) + if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT')) + $context['fulltext_index'][] = $row['Key_name']; + $smcFunc['db_free_result']($request); + + if (is_array($context['fulltext_index'])) + $context['fulltext_index'] = array_unique($context['fulltext_index']); + } + + $request = $smcFunc['db_query']('', ' + SHOW COLUMNS + FROM {db_prefix}messages', + array( + ) + ); + if ($request !== false) + { + while ($row = $smcFunc['db_fetch_assoc']($request)) + if ($row['Field'] == 'body' && $row['Type'] == 'mediumtext') + $context['cannot_create_fulltext'] = true; + $smcFunc['db_free_result']($request); + } + + if (preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) !== 0) + $request = $smcFunc['db_query']('', ' + SHOW TABLE STATUS + FROM {string:database_name} + LIKE {string:table_name}', + array( + 'database_name' => '`' . strtr($match[1], array('`' => '')) . '`', + 'table_name' => str_replace('_', '\_', $match[2]) . 'messages', + ) + ); + else + $request = $smcFunc['db_query']('', ' + SHOW TABLE STATUS + LIKE {string:table_name}', + array( + 'table_name' => str_replace('_', '\_', $db_prefix) . 'messages', + ) + ); + + if ($request !== false) + { + while ($row = $smcFunc['db_fetch_assoc']($request)) + if ((isset($row['Type']) && strtolower($row['Type']) != 'myisam') || (isset($row['Engine']) && strtolower($row['Engine']) != 'myisam')) + $context['cannot_create_fulltext'] = true; + $smcFunc['db_free_result']($request); + } + } + + if (!empty($_REQUEST['sa']) && $_REQUEST['sa'] == 'createfulltext') + { + checkSession('get'); + + // Make sure it's gone before creating it. + $smcFunc['db_query']('', ' + ALTER TABLE {db_prefix}messages + DROP INDEX body', + array( + 'db_error_skip' => true, + ) + ); + + $smcFunc['db_query']('', ' + ALTER TABLE {db_prefix}messages + ADD FULLTEXT body (body)', + array( + ) + ); + + $context['fulltext_index'] = 'body'; + } + elseif (!empty($_REQUEST['sa']) && $_REQUEST['sa'] == 'removefulltext' && !empty($context['fulltext_index'])) + { + checkSession('get'); + + $smcFunc['db_query']('', ' + ALTER TABLE {db_prefix}messages + DROP INDEX ' . implode(', + DROP INDEX ', $context['fulltext_index']), + array( + 'db_error_skip' => true, + ) + ); + + $context['fulltext_index'] = ''; + + // Go back to the default search method. + if (!empty($modSettings['search_index']) && $modSettings['search_index'] == 'fulltext') + updateSettings(array( + 'search_index' => '', + )); + } + elseif (!empty($_REQUEST['sa']) && $_REQUEST['sa'] == 'removecustom') + { + checkSession('get'); + + db_extend(); + $tables = $smcFunc['db_list_tables'](false, $db_prefix . 'log_search_words'); + if (!empty($tables)) + { + $smcFunc['db_search_query']('drop_words_table', ' + DROP TABLE {db_prefix}log_search_words', + array( + ) + ); + } + + updateSettings(array( + 'search_custom_index_config' => '', + 'search_custom_index_resume' => '', + )); + + // Go back to the default search method. + if (!empty($modSettings['search_index']) && $modSettings['search_index'] == 'custom') + updateSettings(array( + 'search_index' => '', + )); + } + elseif (isset($_POST['save'])) + { + checkSession(); + updateSettings(array( + 'search_index' => empty($_POST['search_index']) || (!in_array($_POST['search_index'], array('fulltext', 'custom')) && !isset($context['search_apis'][$_POST['search_index']])) ? '' : $_POST['search_index'], + 'search_force_index' => isset($_POST['search_force_index']) ? '1' : '0', + 'search_match_words' => isset($_POST['search_match_words']) ? '1' : '0', + )); + } + + $context['table_info'] = array( + 'data_length' => 0, + 'index_length' => 0, + 'fulltext_length' => 0, + 'custom_index_length' => 0, + ); + + // Get some info about the messages table, to show its size and index size. + if ($db_type == 'mysql') + { + if (preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) !== 0) + $request = $smcFunc['db_query']('', ' + SHOW TABLE STATUS + FROM {string:database_name} + LIKE {string:table_name}', + array( + 'database_name' => '`' . strtr($match[1], array('`' => '')) . '`', + 'table_name' => str_replace('_', '\_', $match[2]) . 'messages', + ) + ); + else + $request = $smcFunc['db_query']('', ' + SHOW TABLE STATUS + LIKE {string:table_name}', + array( + 'table_name' => str_replace('_', '\_', $db_prefix) . 'messages', + ) + ); + if ($request !== false && $smcFunc['db_num_rows']($request) == 1) + { + // Only do this if the user has permission to execute this query. + $row = $smcFunc['db_fetch_assoc']($request); + $context['table_info']['data_length'] = $row['Data_length']; + $context['table_info']['index_length'] = $row['Index_length']; + $context['table_info']['fulltext_length'] = $row['Index_length']; + $smcFunc['db_free_result']($request); + } + + // Now check the custom index table, if it exists at all. + if (preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) !== 0) + $request = $smcFunc['db_query']('', ' + SHOW TABLE STATUS + FROM {string:database_name} + LIKE {string:table_name}', + array( + 'database_name' => '`' . strtr($match[1], array('`' => '')) . '`', + 'table_name' => str_replace('_', '\_', $match[2]) . 'log_search_words', + ) + ); + else + $request = $smcFunc['db_query']('', ' + SHOW TABLE STATUS + LIKE {string:table_name}', + array( + 'table_name' => str_replace('_', '\_', $db_prefix) . 'log_search_words', + ) + ); + if ($request !== false && $smcFunc['db_num_rows']($request) == 1) + { + // Only do this if the user has permission to execute this query. + $row = $smcFunc['db_fetch_assoc']($request); + $context['table_info']['index_length'] += $row['Data_length'] + $row['Index_length']; + $context['table_info']['custom_index_length'] = $row['Data_length'] + $row['Index_length']; + $smcFunc['db_free_result']($request); + } + } + elseif ($db_type == 'postgresql') + { + // In order to report the sizes correctly we need to perform vacuum (optimize) on the tables we will be using. + db_extend(); + $temp_tables = $smcFunc['db_list_tables'](); + foreach ($temp_tables as $table) + if ($table == $db_prefix. 'messages' || $table == $db_prefix. 'log_search_words') + $smcFunc['db_optimize_table']($table); + + // PostGreSql has some hidden sizes. + $request = $smcFunc['db_query']('', ' + SELECT relname, relpages * 8 *1024 AS "KB" FROM pg_class + WHERE relname = {string:messages} OR relname = {string:log_search_words} + ORDER BY relpages DESC', + array( + 'messages' => $db_prefix. 'messages', + 'log_search_words' => $db_prefix. 'log_search_words', + ) + ); + + if ($request !== false && $smcFunc['db_num_rows']($request) > 0) + { + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['relname'] == $db_prefix . 'messages') + { + $context['table_info']['data_length'] = (int) $row['KB']; + $context['table_info']['index_length'] = (int) $row['KB']; + // Doesn't support fulltext + $context['table_info']['fulltext_length'] = $txt['not_applicable']; + } + elseif ($row['relname'] == $db_prefix. 'log_search_words') + { + $context['table_info']['index_length'] = (int) $row['KB']; + $context['table_info']['custom_index_length'] = (int) $row['KB']; + } + } + $smcFunc['db_free_result']($request); + } + else + // Didn't work for some reason... + $context['table_info'] = array( + 'data_length' => $txt['not_applicable'], + 'index_length' => $txt['not_applicable'], + 'fulltext_length' => $txt['not_applicable'], + 'custom_index_length' => $txt['not_applicable'], + ); + } + else + $context['table_info'] = array( + 'data_length' => $txt['not_applicable'], + 'index_length' => $txt['not_applicable'], + 'fulltext_length' => $txt['not_applicable'], + 'custom_index_length' => $txt['not_applicable'], + ); + + // Format the data and index length in kilobytes. + foreach ($context['table_info'] as $type => $size) + { + // If it's not numeric then just break. This database engine doesn't support size. + if (!is_numeric($size)) + break; + + $context['table_info'][$type] = comma_format($context['table_info'][$type] / 1024) . ' ' . $txt['search_method_kilobytes']; + } + + $context['custom_index'] = !empty($modSettings['search_custom_index_config']); + $context['partial_custom_index'] = !empty($modSettings['search_custom_index_resume']) && empty($modSettings['search_custom_index_config']); + $context['double_index'] = !empty($context['fulltext_index']) && $context['custom_index']; +} + +function CreateMessageIndex() +{ + global $modSettings, $context, $smcFunc, $db_prefix, $txt; + + // Scotty, we need more time... + @set_time_limit(600); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + $context[$context['admin_menu_name']]['current_subsection'] = 'method'; + $context['page_title'] = $txt['search_index_custom']; + + $messages_per_batch = 50; + + $index_properties = array( + 2 => array( + 'column_definition' => 'small', + 'step_size' => 1000000, + ), + 4 => array( + 'column_definition' => 'medium', + 'step_size' => 1000000, + 'max_size' => 16777215, + ), + 5 => array( + 'column_definition' => 'large', + 'step_size' => 100000000, + 'max_size' => 2000000000, + ), + ); + + if (isset($_REQUEST['resume']) && !empty($modSettings['search_custom_index_resume'])) + { + $context['index_settings'] = unserialize($modSettings['search_custom_index_resume']); + $context['start'] = (int) $context['index_settings']['resume_at']; + unset($context['index_settings']['resume_at']); + $context['step'] = 1; + } + else + { + $context['index_settings'] = array( + 'bytes_per_word' => isset($_REQUEST['bytes_per_word']) && isset($index_properties[$_REQUEST['bytes_per_word']]) ? (int) $_REQUEST['bytes_per_word'] : 2, + ); + $context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; + $context['step'] = isset($_REQUEST['step']) ? (int) $_REQUEST['step'] : 0; + } + + if ($context['step'] !== 0) + checkSession('request'); + + // Step 0: let the user determine how they like their index. + if ($context['step'] === 0) + { + $context['sub_template'] = 'create_index'; + } + + // Step 1: insert all the words. + if ($context['step'] === 1) + { + $context['sub_template'] = 'create_index_progress'; + + if ($context['start'] === 0) + { + db_extend(); + $tables = $smcFunc['db_list_tables'](false, $db_prefix . 'log_search_words'); + if (!empty($tables)) + { + $smcFunc['db_search_query']('drop_words_table', ' + DROP TABLE {db_prefix}log_search_words', + array( + ) + ); + } + + $smcFunc['db_create_word_search']($index_properties[$context['index_settings']['bytes_per_word']]['column_definition']); + + // Temporarily switch back to not using a search index. + if (!empty($modSettings['search_index']) && $modSettings['search_index'] == 'custom') + updateSettings(array('search_index' => '')); + + // Don't let simultanious processes be updating the search index. + if (!empty($modSettings['search_custom_index_config'])) + updateSettings(array('search_custom_index_config' => '')); + } + + $num_messages = array( + 'done' => 0, + 'todo' => 0, + ); + + $request = $smcFunc['db_query']('', ' + SELECT id_msg >= {int:starting_id} AS todo, COUNT(*) AS num_messages + FROM {db_prefix}messages + GROUP BY todo', + array( + 'starting_id' => $context['start'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $num_messages[empty($row['todo']) ? 'done' : 'todo'] = $row['num_messages']; + + if (empty($num_messages['todo'])) + { + $context['step'] = 2; + $context['percentage'] = 80; + $context['start'] = 0; + } + else + { + // Number of seconds before the next step. + $stop = time() + 3; + while (time() < $stop) + { + $inserts = array(); + $request = $smcFunc['db_query']('', ' + SELECT id_msg, body + FROM {db_prefix}messages + WHERE id_msg BETWEEN {int:starting_id} AND {int:ending_id} + LIMIT {int:limit}', + array( + 'starting_id' => $context['start'], + 'ending_id' => $context['start'] + $messages_per_batch - 1, + 'limit' => $messages_per_batch, + ) + ); + $forced_break = false; + $number_processed = 0; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // In theory it's possible for one of these to take friggin ages so add more timeout protection. + if ($stop < time()) + { + $forced_break = true; + break; + } + + $number_processed++; + foreach (text2words($row['body'], $context['index_settings']['bytes_per_word'], true) as $id_word) + { + $inserts[] = array($id_word, $row['id_msg']); + } + } + $num_messages['done'] += $number_processed; + $num_messages['todo'] -= $number_processed; + $smcFunc['db_free_result']($request); + + $context['start'] += $forced_break ? $number_processed : $messages_per_batch; + + if (!empty($inserts)) + $smcFunc['db_insert']('ignore', + '{db_prefix}log_search_words', + array('id_word' => 'int', 'id_msg' => 'int'), + $inserts, + array('id_word', 'id_msg') + ); + if ($num_messages['todo'] === 0) + { + $context['step'] = 2; + $context['start'] = 0; + break; + } + else + updateSettings(array('search_custom_index_resume' => serialize(array_merge($context['index_settings'], array('resume_at' => $context['start']))))); + } + + // Since there are still two steps to go, 90% is the maximum here. + $context['percentage'] = round($num_messages['done'] / ($num_messages['done'] + $num_messages['todo']), 3) * 80; + } + } + + // Step 2: removing the words that occur too often and are of no use. + elseif ($context['step'] === 2) + { + if ($context['index_settings']['bytes_per_word'] < 4) + $context['step'] = 3; + else + { + $stop_words = $context['start'] === 0 || empty($modSettings['search_stopwords']) ? array() : explode(',', $modSettings['search_stopwords']); + $stop = time() + 3; + $context['sub_template'] = 'create_index_progress'; + $max_messages = ceil(60 * $modSettings['totalMessages'] / 100); + + while (time() < $stop) + { + $request = $smcFunc['db_query']('', ' + SELECT id_word, COUNT(id_word) AS num_words + FROM {db_prefix}log_search_words + WHERE id_word BETWEEN {int:starting_id} AND {int:ending_id} + GROUP BY id_word + HAVING COUNT(id_word) > {int:minimum_messages}', + array( + 'starting_id' => $context['start'], + 'ending_id' => $context['start'] + $index_properties[$context['index_settings']['bytes_per_word']]['step_size'] - 1, + 'minimum_messages' => $max_messages, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $stop_words[] = $row['id_word']; + $smcFunc['db_free_result']($request); + + updateSettings(array('search_stopwords' => implode(',', $stop_words))); + + if (!empty($stop_words)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_search_words + WHERE id_word in ({array_int:stop_words})', + array( + 'stop_words' => $stop_words, + ) + ); + + $context['start'] += $index_properties[$context['index_settings']['bytes_per_word']]['step_size']; + if ($context['start'] > $index_properties[$context['index_settings']['bytes_per_word']]['max_size']) + { + $context['step'] = 3; + break; + } + } + $context['percentage'] = 80 + round($context['start'] / $index_properties[$context['index_settings']['bytes_per_word']]['max_size'], 3) * 20; + } + } + + // Step 3: remove words not distinctive enough. + if ($context['step'] === 3) + { + $context['sub_template'] = 'create_index_done'; + + updateSettings(array('search_index' => 'custom', 'search_custom_index_config' => serialize($context['index_settings']))); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}settings + WHERE variable = {string:search_custom_index_resume}', + array( + 'search_custom_index_resume' => 'search_custom_index_resume', + ) + ); + } +} + +// Get the installed APIs. +function loadSearchAPIs() +{ + global $sourcedir, $txt; + + $apis = array(); + if ($dh = opendir($sourcedir)) + { + while (($file = readdir($dh)) !== false) + { + if (is_file($sourcedir . '/' . $file) && preg_match('~SearchAPI-([A-Za-z\d_]+)\.php~', $file, $matches)) + { + // Check this is definitely a valid API! + $fp = fopen($sourcedir . '/' . $file, 'rb'); + $header = fread($fp, 4096); + fclose($fp); + + if (strpos($header, '* SearchAPI-' . $matches[1] . '.php') !== false) + { + loadClassFile($file); + + $index_name = strtolower($matches[1]); + $search_class_name = $index_name . '_search'; + $searchAPI = new $search_class_name(); + + // No Support? NEXT! + if (!$searchAPI->is_supported) + continue; + + $apis[$index_name] = array( + 'filename' => $file, + 'setting_index' => $index_name, + 'has_template' => in_array($index_name, array('custom', 'fulltext', 'standard')), + 'label' => $index_name && isset($txt['search_index_' . $index_name]) ? $txt['search_index_' . $index_name] : '', + 'desc' => $index_name && isset($txt['search_index_' . $index_name . '_desc']) ? $txt['search_index_' . $index_name . '_desc'] : '', + ); + } + } + } + } + closedir($dh); + + return $apis; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageSearchEngines.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageSearchEngines.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1022 @@ + 'EditSpider', + 'logs' => 'SpiderLogs', + 'settings' => 'ManageSearchEngineSettings', + 'spiders' => 'ViewSpiders', + 'stats' => 'SpiderStats', + ); + + // Ensure we have a valid subaction. + $context['sub_action'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'stats'; + + $context['page_title'] = $txt['search_engines']; + + // Some more tab data. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['search_engines'], + 'description' => $txt['search_engines_description'], + ); + + // Call the function! + $subActions[$context['sub_action']](); +} + +// This is really just the settings page. +function ManageSearchEngineSettings($return_config = false) +{ + global $context, $txt, $modSettings, $scripturl, $sourcedir, $smcFunc; + + $config_vars = array( + // How much detail? + array('select', 'spider_mode', array($txt['spider_mode_off'], $txt['spider_mode_standard'], $txt['spider_mode_high'], $txt['spider_mode_vhigh']), 'onchange' => 'disableFields();'), + 'spider_group' => array('select', 'spider_group', array($txt['spider_group_none'], $txt['membergroups_members'])), + array('select', 'show_spider_online', array($txt['show_spider_online_no'], $txt['show_spider_online_summary'], $txt['show_spider_online_detail'], $txt['show_spider_online_detail_admin'])), + ); + + // Set up a message. + $context['settings_message'] = '' . sprintf($txt['spider_settings_desc'], $scripturl . '?action=admin;area=logs;sa=pruning;' . $context['session_var'] . '=' . $context['session_id']) . ''; + + // Do some javascript. + $javascript_function = ' + function disableFields() + { + disabledState = document.getElementById(\'spider_mode\').value == 0;'; + + foreach ($config_vars as $variable) + if ($variable[1] != 'spider_mode') + $javascript_function .= ' + if (document.getElementById(\'' . $variable[1] . '\')) + document.getElementById(\'' . $variable[1] . '\').disabled = disabledState;'; + + $javascript_function .= ' + } + disableFields();'; + + if ($return_config) + return $config_vars; + + // We need to load the groups for the spider group thingy. + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name + FROM {db_prefix}membergroups + WHERE id_group != {int:admin_group} + AND id_group != {int:moderator_group}', + array( + 'admin_group' => 1, + 'moderator_group' => 3, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $config_vars['spider_group'][2][$row['id_group']] = $row['group_name']; + $smcFunc['db_free_result']($request); + + // Make sure it's valid - note that regular members are given id_group = 1 which is reversed in Load.php - no admins here! + if (isset($_POST['spider_group']) && !isset($config_vars['spider_group'][2][$_POST['spider_group']])) + $_POST['spider_group'] = 0; + + // We'll want this for our easy save. + require_once($sourcedir . '/ManageServer.php'); + + // Setup the template. + $context['page_title'] = $txt['settings']; + $context['sub_template'] = 'show_settings'; + + // Are we saving them - are we?? + if (isset($_GET['save'])) + { + checkSession(); + + saveDBSettings($config_vars); + recacheSpiderNames(); + redirectexit('action=admin;area=sengines;sa=settings'); + } + + // Final settings... + $context['post_url'] = $scripturl . '?action=admin;area=sengines;save;sa=settings'; + $context['settings_title'] = $txt['settings']; + $context['settings_insert_below'] = ' + '; + + // Prepare the settings... + prepareDBSettingContext($config_vars); +} + +// View a list of all the spiders we know about. +function ViewSpiders() +{ + global $context, $txt, $sourcedir, $scripturl, $smcFunc; + + if (!isset($_SESSION['spider_stat']) || $_SESSION['spider_stat'] < time() - 60) + { + consolidateSpiderStats(); + $_SESSION['spider_stat'] = time(); + } + + // Are we adding a new one? + if (!empty($_POST['addSpider'])) + return EditSpider(); + // User pressed the 'remove selection button'. + elseif (!empty($_POST['removeSpiders']) && !empty($_POST['remove']) && is_array($_POST['remove'])) + { + checkSession(); + + // Make sure every entry is a proper integer. + foreach ($_POST['remove'] as $index => $spider_id) + $_POST['remove'][(int) $index] = (int) $spider_id; + + // Delete them all! + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}spiders + WHERE id_spider IN ({array_int:remove_list})', + array( + 'remove_list' => $_POST['remove'], + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_spider_hits + WHERE id_spider IN ({array_int:remove_list})', + array( + 'remove_list' => $_POST['remove'], + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_spider_stats + WHERE id_spider IN ({array_int:remove_list})', + array( + 'remove_list' => $_POST['remove'], + ) + ); + + cache_put_data('spider_search', null, 300); + recacheSpiderNames(); + } + + // Get the last seens. + $request = $smcFunc['db_query']('', ' + SELECT id_spider, MAX(last_seen) AS last_seen_time + FROM {db_prefix}log_spider_stats + GROUP BY id_spider', + array( + ) + ); + + $context['spider_last_seen'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['spider_last_seen'][$row['id_spider']] = $row['last_seen_time']; + $smcFunc['db_free_result']($request); + + $listOptions = array( + 'id' => 'spider_list', + 'items_per_page' => 20, + 'base_href' => $scripturl . '?action=admin;area=sengines;sa=spiders', + 'default_sort_col' => 'name', + 'get_items' => array( + 'function' => 'list_getSpiders', + ), + 'get_count' => array( + 'function' => 'list_getNumSpiders', + ), + 'no_items_label' => $txt['spiders_no_entries'], + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['spider_name'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $scripturl; + + return sprintf(\'%3$s\', $scripturl, $rowData[\'id_spider\'], htmlspecialchars($rowData[\'spider_name\'])); + '), + ), + 'sort' => array( + 'default' => 'spider_name', + 'reverse' => 'spider_name DESC', + ), + ), + 'last_seen' => array( + 'header' => array( + 'value' => $txt['spider_last_seen'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt; + + return isset($context[\'spider_last_seen\'][$rowData[\'id_spider\']]) ? timeformat($context[\'spider_last_seen\'][$rowData[\'id_spider\']]) : $txt[\'spider_last_never\']; + '), + ), + ), + 'user_agent' => array( + 'header' => array( + 'value' => $txt['spider_agent'], + ), + 'data' => array( + 'db_htmlsafe' => 'user_agent', + ), + 'sort' => array( + 'default' => 'user_agent', + 'reverse' => 'user_agent DESC', + ), + ), + 'ip_info' => array( + 'header' => array( + 'value' => $txt['spider_ip_info'], + ), + 'data' => array( + 'db_htmlsafe' => 'ip_info', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'ip_info', + 'reverse' => 'ip_info DESC', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id_spider' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=sengines;sa=spiders', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' + + + ', + 'style' => 'text-align: right;', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'spider_list'; +} + +function list_getSpiders($start, $items_per_page, $sort) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT id_spider, spider_name, user_agent, ip_info + FROM {db_prefix}spiders + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + ) + ); + $spiders = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $spiders[$row['id_spider']] = $row; + $smcFunc['db_free_result']($request); + + return $spiders; +} + +function list_getNumSpiders() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS num_spiders + FROM {db_prefix}spiders', + array( + ) + ); + list ($numSpiders) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $numSpiders; +} + +// Here we can add, and edit, spider info! +function EditSpider() +{ + global $context, $smcFunc, $txt; + + // Some standard stuff. + $context['id_spider'] = !empty($_GET['sid']) ? (int) $_GET['sid'] : 0; + $context['page_title'] = $context['id_spider'] ? $txt['spiders_edit'] : $txt['spiders_add']; + $context['sub_template'] = 'spider_edit'; + + // Are we saving? + if (!empty($_POST['save'])) + { + checkSession(); + + $ips = array(); + // Check the IP range is valid. + $ip_sets = explode(',', $_POST['spider_ip']); + foreach ($ip_sets as $set) + { + $test = ip2range(trim($set)); + if (!empty($test)) + $ips[] = $set; + } + $ips = implode(',', $ips); + + // Goes in as it is... + if ($context['id_spider']) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}spiders + SET spider_name = {string:spider_name}, user_agent = {string:spider_agent}, + ip_info = {string:ip_info} + WHERE id_spider = {int:current_spider}', + array( + 'current_spider' => $context['id_spider'], + 'spider_name' => $_POST['spider_name'], + 'spider_agent' => $_POST['spider_agent'], + 'ip_info' => $ips, + ) + ); + else + $smcFunc['db_insert']('insert', + '{db_prefix}spiders', + array( + 'spider_name' => 'string', 'user_agent' => 'string', 'ip_info' => 'string', + ), + array( + $_POST['spider_name'], $_POST['spider_agent'], $ips, + ), + array('id_spider') + ); + + // Order by user agent length. + sortSpiderTable(); + + cache_put_data('spider_search', null, 300); + recacheSpiderNames(); + + redirectexit('action=admin;area=sengines;sa=spiders'); + } + + // The default is new. + $context['spider'] = array( + 'id' => 0, + 'name' => '', + 'agent' => '', + 'ip_info' => '', + ); + + // An edit? + if ($context['id_spider']) + { + $request = $smcFunc['db_query']('', ' + SELECT id_spider, spider_name, user_agent, ip_info + FROM {db_prefix}spiders + WHERE id_spider = {int:current_spider}', + array( + 'current_spider' => $context['id_spider'], + ) + ); + if ($row = $smcFunc['db_fetch_assoc']($request)) + $context['spider'] = array( + 'id' => $row['id_spider'], + 'name' => $row['spider_name'], + 'agent' => $row['user_agent'], + 'ip_info' => $row['ip_info'], + ); + $smcFunc['db_free_result']($request); + } + +} + +//!!! Should this not be... you know... in a different file? +// Do we think the current user is a spider? +function SpiderCheck() +{ + global $modSettings, $smcFunc; + + if (isset($_SESSION['id_robot'])) + unset($_SESSION['id_robot']); + $_SESSION['robot_check'] = time(); + + // We cache the spider data for five minutes if we can. + if (!empty($modSettings['cache_enable'])) + $spider_data = cache_get_data('spider_search', 300); + + if (!isset($spider_data) || $spider_data === NULL) + { + $request = $smcFunc['db_query']('spider_check', ' + SELECT id_spider, user_agent, ip_info + FROM {db_prefix}spiders', + array( + ) + ); + $spider_data = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $spider_data[] = $row; + $smcFunc['db_free_result']($request); + + if (!empty($modSettings['cache_enable'])) + cache_put_data('spider_search', $spider_data, 300); + } + + if (empty($spider_data)) + return false; + + // Only do these bits once. + $ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']); + preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $_SERVER['REMOTE_ADDR'], $ip_parts); + + foreach ($spider_data as $spider) + { + // User agent is easy. + if (!empty($spider['user_agent']) && strpos($ci_user_agent, strtolower($spider['user_agent'])) !== false) + $_SESSION['id_robot'] = $spider['id_spider']; + // IP stuff is harder. + elseif (!empty($ip_parts)) + { + $ips = explode(',', $spider['ip_info']); + foreach ($ips as $ip) + { + $ip = ip2range($ip); + if (!empty($ip)) + { + foreach ($ip as $key => $value) + { + if ($value['low'] > $ip_parts[$key + 1] || $value['high'] < $ip_parts[$key + 1]) + break; + elseif ($key == 3) + $_SESSION['id_robot'] = $spider['id_spider']; + } + } + } + } + + if (isset($_SESSION['id_robot'])) + break; + } + + // If this is low server tracking then log the spider here as oppossed to the main logging function. + if (!empty($modSettings['spider_mode']) && $modSettings['spider_mode'] == 1 && !empty($_SESSION['id_robot'])) + logSpider(); + + return !empty($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0; +} + +// Log the spider presence online. +//!!! Different file? +function logSpider() +{ + global $smcFunc, $modSettings, $context; + + if (empty($modSettings['spider_mode']) || empty($_SESSION['id_robot'])) + return; + + // Attempt to update today's entry. + if ($modSettings['spider_mode'] == 1) + { + $date = strftime('%Y-%m-%d', forum_time(false)); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_spider_stats + SET last_seen = {int:current_time}, page_hits = page_hits + 1 + WHERE id_spider = {int:current_spider} + AND stat_date = {date:current_date}', + array( + 'current_date' => $date, + 'current_time' => time(), + 'current_spider' => $_SESSION['id_robot'], + ) + ); + + // Nothing updated? + if ($smcFunc['db_affected_rows']() == 0) + { + $smcFunc['db_insert']('ignore', + '{db_prefix}log_spider_stats', + array( + 'id_spider' => 'int', 'last_seen' => 'int', 'stat_date' => 'date', 'page_hits' => 'int', + ), + array( + $_SESSION['id_robot'], time(), $date, 1, + ), + array('id_spider', 'stat_date') + ); + } + } + // If we're tracking better stats than track, better stats - we sort out the today thing later. + else + { + if ($modSettings['spider_mode'] > 2) + { + $url = $_GET + array('USER_AGENT' => $_SERVER['HTTP_USER_AGENT']); + unset($url['sesc'], $url[$context['session_var']]); + $url = serialize($url); + } + else + $url = ''; + + $smcFunc['db_insert']('insert', + '{db_prefix}log_spider_hits', + array('id_spider' => 'int', 'log_time' => 'int', 'url' => 'string'), + array($_SESSION['id_robot'], time(), $url), + array() + ); + } +} + +// This function takes any unprocessed hits and turns them into stats. +function consolidateSpiderStats() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('consolidate_spider_stats', ' + SELECT id_spider, MAX(log_time) AS last_seen, COUNT(*) AS num_hits + FROM {db_prefix}log_spider_hits + WHERE processed = {int:not_processed} + GROUP BY id_spider, MONTH(log_time), DAYOFMONTH(log_time)', + array( + 'not_processed' => 0, + ) + ); + $spider_hits = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $spider_hits[] = $row; + $smcFunc['db_free_result']($request); + + if (empty($spider_hits)) + return; + + // Attempt to update the master data. + $stat_inserts = array(); + foreach ($spider_hits as $stat) + { + // We assume the max date is within the right day. + $date = strftime('%Y-%m-%d', $stat['last_seen']); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_spider_stats + SET page_hits = page_hits + ' . $stat['num_hits'] . ', + last_seen = CASE WHEN last_seen > {int:last_seen} THEN last_seen ELSE {int:last_seen} END + WHERE id_spider = {int:current_spider} + AND stat_date = {date:last_seen_date}', + array( + 'last_seen_date' => $date, + 'last_seen' => $stat['last_seen'], + 'current_spider' => $stat['id_spider'], + ) + ); + if ($smcFunc['db_affected_rows']() == 0) + $stat_inserts[] = array($date, $stat['id_spider'], $stat['num_hits'], $stat['last_seen']); + } + + // New stats? + if (!empty($stat_inserts)) + $smcFunc['db_insert']('ignore', + '{db_prefix}log_spider_stats', + array('stat_date' => 'date', 'id_spider' => 'int', 'page_hits' => 'int', 'last_seen' => 'int'), + $stat_inserts, + array('stat_date', 'id_spider') + ); + + // All processed. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_spider_hits + SET processed = {int:is_processed} + WHERE processed = {int:not_processed}', + array( + 'is_processed' => 1, + 'not_processed' => 0, + ) + ); +} + +// See what spiders have been up to. +function SpiderLogs() +{ + global $context, $txt, $sourcedir, $scripturl, $smcFunc, $modSettings; + + // Load the template and language just incase. + loadLanguage('Search'); + loadTemplate('ManageSearch'); + + // Did they want to delete some entries? + if (!empty($_POST['delete_entries']) && isset($_POST['older'])) + { + checkSession(); + + $deleteTime = time() - (((int) $_POST['older']) * 24 * 60 * 60); + + // Delete the entires. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_spider_hits + WHERE log_time < {int:delete_period}', + array( + 'delete_period' => $deleteTime, + ) + ); + } + + $listOptions = array( + 'id' => 'spider_logs', + 'items_per_page' => 20, + 'title' => $txt['spider_logs'], + 'no_items_label' => $txt['spider_logs_empty'], + 'base_href' => $context['admin_area'] == 'sengines' ? $scripturl . '?action=admin;area=sengines;sa=logs' : $scripturl . '?action=admin;area=logs;sa=spiderlog', + 'default_sort_col' => 'log_time', + 'get_items' => array( + 'function' => 'list_getSpiderLogs', + ), + 'get_count' => array( + 'function' => 'list_getNumSpiderLogs', + ), + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['spider'], + ), + 'data' => array( + 'db' => 'spider_name', + ), + 'sort' => array( + 'default' => 's.spider_name', + 'reverse' => 's.spider_name DESC', + ), + ), + 'log_time' => array( + 'header' => array( + 'value' => $txt['spider_time'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return timeformat($rowData[\'log_time\']); + '), + ), + 'sort' => array( + 'default' => 'sl.id_hit DESC', + 'reverse' => 'sl.id_hit', + ), + ), + 'viewing' => array( + 'header' => array( + 'value' => $txt['spider_viewing'], + ), + 'data' => array( + 'db' => 'url', + ), + ), + ), + 'additional_rows' => array( + array( + 'position' => 'after_title', + 'value' => $txt['spider_logs_info'], + 'class' => 'smalltext', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + // Now determine the actions of the URLs. + if (!empty($context['spider_logs']['rows'])) + { + $urls = array(); + // Grab the current /url. + foreach ($context['spider_logs']['rows'] as $k => $row) + { + // Feature disabled? + if (empty($row['viewing']['value']) && isset($modSettings['spider_mode']) && $modSettings['spider_mode'] < 3) + $context['spider_logs']['rows'][$k]['viewing']['value'] = '' . $txt['spider_disabled'] . ''; + else + $urls[$k] = array($row['viewing']['value'], -1); + } + + // Now stick in the new URLs. + require_once($sourcedir . '/Who.php'); + $urls = determineActions($urls, 'whospider_'); + foreach ($urls as $k => $new_url) + { + $context['spider_logs']['rows'][$k]['viewing']['value'] = $new_url; + } + } + + $context['page_title'] = $txt['spider_logs']; + $context['sub_template'] = 'show_spider_logs'; + $context['default_list'] = 'spider_logs'; +} + +function list_getSpiderLogs($start, $items_per_page, $sort) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT sl.id_spider, sl.url, sl.log_time, s.spider_name + FROM {db_prefix}log_spider_hits AS sl + INNER JOIN {db_prefix}spiders AS s ON (s.id_spider = sl.id_spider) + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + ) + ); + $spider_logs = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $spider_logs[] = $row; + $smcFunc['db_free_result']($request); + + return $spider_logs; +} + +function list_getNumSpiderLogs() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS num_logs + FROM {db_prefix}log_spider_hits', + array( + ) + ); + list ($numLogs) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $numLogs; +} + +// Show the spider statistics. +function SpiderStats() +{ + global $context, $txt, $sourcedir, $scripturl, $smcFunc; + + // Force an update of the stats every 60 seconds. + if (!isset($_SESSION['spider_stat']) || $_SESSION['spider_stat'] < time() - 60) + { + consolidateSpiderStats(); + $_SESSION['spider_stat'] = time(); + } + + // Get the earliest and latest dates. + $request = $smcFunc['db_query']('', ' + SELECT MIN(stat_date) AS first_date, MAX(stat_date) AS last_date + FROM {db_prefix}log_spider_stats', + array( + ) + ); + + list ($min_date, $max_date) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $min_year = (int) substr($min_date, 0, 4); + $max_year = (int) substr($max_date, 0, 4); + $min_month = (int) substr($min_date, 5, 2); + $max_month = (int) substr($max_date, 5, 2); + + // Prepare the dates for the drop down. + $date_choices = array(); + for ($y = $min_year; $y <= $max_year; $y++) + for ($m = 1; $m <= 12; $m++) + { + // This doesn't count? + if ($y == $min_year && $m < $min_month) + continue; + if ($y == $max_year && $m > $max_month) + break; + + $date_choices[$y . $m] = $txt['months_short'][$m] . ' ' . $y; + } + + // What are we currently viewing? + $current_date = isset($_REQUEST['new_date']) && isset($date_choices[$_REQUEST['new_date']]) ? $_REQUEST['new_date'] : $max_date; + + // Prepare the HTML. + $date_select = ' + ' . $txt['spider_stats_select_month'] . ': + + '; + + // If we manually jumped to a date work out the offset. + if (isset($_REQUEST['new_date'])) + { + $date_query = sprintf('%04d-%02d-01', substr($current_date, 0, 4), substr($current_date, 4)); + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS offset + FROM {db_prefix}log_spider_stats + WHERE stat_date < {date:date_being_viewed}', + array( + 'date_being_viewed' => $date_query, + ) + ); + list ($_REQUEST['start']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + $listOptions = array( + 'id' => 'spider_stat_list', + 'items_per_page' => 20, + 'base_href' => $scripturl . '?action=admin;area=sengines;sa=stats', + 'default_sort_col' => 'stat_date', + 'get_items' => array( + 'function' => 'list_getSpiderStats', + ), + 'get_count' => array( + 'function' => 'list_getNumSpiderStats', + ), + 'no_items_label' => $txt['spider_stats_no_entries'], + 'columns' => array( + 'stat_date' => array( + 'header' => array( + 'value' => $txt['date'], + ), + 'data' => array( + 'db' => 'stat_date', + ), + 'sort' => array( + 'default' => 'stat_date', + 'reverse' => 'stat_date DESC', + ), + ), + 'name' => array( + 'header' => array( + 'value' => $txt['spider_name'], + ), + 'data' => array( + 'db' => 'spider_name', + ), + 'sort' => array( + 'default' => 's.spider_name', + 'reverse' => 's.spider_name DESC', + ), + ), + 'page_hits' => array( + 'header' => array( + 'value' => $txt['spider_stats_page_hits'], + ), + 'data' => array( + 'db' => 'page_hits', + ), + 'sort' => array( + 'default' => 'ss.page_hits', + 'reverse' => 'ss.page_hits DESC', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=sengines;sa=stats', + 'name' => 'spider_stat_list', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => $date_select, + 'style' => 'text-align: right;', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'spider_stat_list'; +} + +function list_getSpiderStats($start, $items_per_page, $sort) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT ss.id_spider, ss.stat_date, ss.page_hits, s.spider_name + FROM {db_prefix}log_spider_stats AS ss + INNER JOIN {db_prefix}spiders AS s ON (s.id_spider = ss.id_spider) + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + ) + ); + $spider_stats = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $spider_stats[] = $row; + $smcFunc['db_free_result']($request); + + return $spider_stats; +} + +function list_getNumSpiderStats() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS num_stats + FROM {db_prefix}log_spider_stats', + array( + ) + ); + list ($numStats) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $numStats; +} + +// Recache spider names? +function recacheSpiderNames() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT id_spider, spider_name + FROM {db_prefix}spiders', + array( + ) + ); + $spiders = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $spiders[$row['id_spider']] = $row['spider_name']; + $smcFunc['db_free_result']($request); + + updateSettings(array('spider_name_cache' => serialize($spiders))); +} + +// Sort the search engine table by user agent name to avoid misidentification of engine. +function sortSpiderTable() +{ + global $smcFunc; + + db_extend('packages'); + + // Add a sorting column. + $smcFunc['db_add_column']('{db_prefix}spiders', array('name' => 'temp_order', 'size' => 8, 'type' => 'mediumint', 'null' => false)); + + // Set the contents of this column. + $smcFunc['db_query']('set_spider_order', ' + UPDATE {db_prefix}spiders + SET temp_order = LENGTH(user_agent)', + array( + ) + ); + + // Order the table by this column. + $smcFunc['db_query']('alter_table_spiders', ' + ALTER TABLE {db_prefix}spiders + ORDER BY temp_order DESC', + array( + 'db_error_skip' => true, + ) + ); + + // Remove the sorting column. + $smcFunc['db_remove_column']('{db_prefix}spiders', 'temp_order'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageServer.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageServer.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2135 @@ + $txt['displayedValue'])), + Note that just saying array('first', 'second') will put 0 in the SQL for 'first'. + + * A password input box. Used for passwords, no less! + ie. array('password', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'), + + * A permission - for picking groups who have a permission. + ie. array('permissions', 'manage_groups'), + + * A BBC selection box. + ie. array('bbc', 'sig_bbc'), + + For each option: + type (see above), variable name, size/possible values. + OR make type '' for an empty string for a horizontal rule. + SET preinput - to put some HTML prior to the input box. + SET postinput - to put some HTML following the input box. + SET invalid - to mark the data as invalid. + PLUS You can override label and help parameters by forcing their keys in the array, for example: + array('text', 'invalidlabel', 3, 'label' => 'Actual Label') */ + +// This is the main pass through function, it creates tabs and the like. +function ModifySettings() +{ + global $context, $txt, $scripturl, $boarddir; + + // This is just to keep the database password more secure. + isAllowedTo('admin_forum'); + + // Load up all the tabs... + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['admin_server_settings'], + 'help' => 'serversettings', + 'description' => $txt['admin_basic_settings'], + ); + + checkSession('request'); + + // The settings are in here, I swear! + loadLanguage('ManageSettings'); + + $context['page_title'] = $txt['admin_server_settings']; + $context['sub_template'] = 'show_settings'; + + $subActions = array( + 'general' => 'ModifyGeneralSettings', + 'database' => 'ModifyDatabaseSettings', + 'cookie' => 'ModifyCookieSettings', + 'cache' => 'ModifyCacheSettings', + 'loads' => 'ModifyLoadBalancingSettings', + ); + + // By default we're editing the core settings + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'general'; + $context['sub_action'] = $_REQUEST['sa']; + + // Warn the user if there's any relevant information regarding Settings.php. + if ($_REQUEST['sa'] != 'cache') + { + // Warn the user if the backup of Settings.php failed. + $settings_not_writable = !is_writable($boarddir . '/Settings.php'); + $settings_backup_fail = !@is_writable($boarddir . '/Settings_bak.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php'); + + if ($settings_not_writable) + $context['settings_message'] = '
' . $txt['settings_not_writable'] . '

'; + elseif ($settings_backup_fail) + $context['settings_message'] = '
' . $txt['admin_backup_fail'] . '

'; + + $context['settings_not_writable'] = $settings_not_writable; + } + + // Call the right function for this sub-action. + $subActions[$_REQUEST['sa']](); +} + +// General forum settings - forum name, maintenance mode, etc. +function ModifyGeneralSettings($return_config = false) +{ + global $scripturl, $context, $txt; + + /* If you're writing a mod, it's a bad idea to add things here.... + For each option: + variable name, description, type (constant), size/possible values, helptext. + OR an empty string for a horizontal rule. + OR a string for a titled section. */ + $config_vars = array( + array('mbname', $txt['admin_title'], 'file', 'text', 30), + '', + array('maintenance', $txt['admin_maintain'], 'file', 'check'), + array('mtitle', $txt['maintenance_subject'], 'file', 'text', 36), + array('mmessage', $txt['maintenance_message'], 'file', 'text', 36), + '', + array('webmaster_email', $txt['admin_webmaster_email'], 'file', 'text', 30), + '', + array('enableCompressedOutput', $txt['enableCompressedOutput'], 'db', 'check', null, 'enableCompressedOutput'), + array('disableTemplateEval', $txt['disableTemplateEval'], 'db', 'check', null, 'disableTemplateEval'), + array('disableHostnameLookup', $txt['disableHostnameLookup'], 'db', 'check', null, 'disableHostnameLookup'), + ); + + if ($return_config) + return $config_vars; + + // Setup the template stuff. + $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=general;save'; + $context['settings_title'] = $txt['general_settings']; + + // Saving settings? + if (isset($_REQUEST['save'])) + { + saveSettings($config_vars); + redirectexit('action=admin;area=serversettings;sa=general;' . $context['session_var'] . '=' . $context['session_id']); + } + + // Fill the config array. + prepareServerSettingsContext($config_vars); +} + +// Basic database and paths settings - database name, host, etc. +function ModifyDatabaseSettings($return_config = false) +{ + global $scripturl, $context, $settings, $txt, $boarddir; + + /* If you're writing a mod, it's a bad idea to add things here.... + For each option: + variable name, description, type (constant), size/possible values, helptext. + OR an empty string for a horizontal rule. + OR a string for a titled section. */ + $config_vars = array( + array('db_server', $txt['database_server'], 'file', 'text'), + array('db_user', $txt['database_user'], 'file', 'text'), + array('db_passwd', $txt['database_password'], 'file', 'password'), + array('db_name', $txt['database_name'], 'file', 'text'), + array('db_prefix', $txt['database_prefix'], 'file', 'text'), + array('db_persist', $txt['db_persist'], 'file', 'check', null, 'db_persist'), + array('db_error_send', $txt['db_error_send'], 'file', 'check'), + array('ssi_db_user', $txt['ssi_db_user'], 'file', 'text', null, 'ssi_db_user'), + array('ssi_db_passwd', $txt['ssi_db_passwd'], 'file', 'password'), + '', + array('autoFixDatabase', $txt['autoFixDatabase'], 'db', 'check', false, 'autoFixDatabase'), + array('autoOptMaxOnline', $txt['autoOptMaxOnline'], 'db', 'int'), + '', + array('boardurl', $txt['admin_url'], 'file', 'text', 36), + array('boarddir', $txt['boarddir'], 'file', 'text', 36), + array('sourcedir', $txt['sourcesdir'], 'file', 'text', 36), + array('cachedir', $txt['cachedir'], 'file', 'text', 36), + ); + + if ($return_config) + return $config_vars; + + // Setup the template stuff. + $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=database;save'; + $context['settings_title'] = $txt['database_paths_settings']; + $context['save_disabled'] = $context['settings_not_writable']; + + // Saving settings? + if (isset($_REQUEST['save'])) + { + saveSettings($config_vars); + redirectexit('action=admin;area=serversettings;sa=database;' . $context['session_var'] . '=' . $context['session_id']); + } + + // Fill the config array. + prepareServerSettingsContext($config_vars); +} + +// This function basically edits anything which is configuration and stored in the database, except for caching. +function ModifyCookieSettings($return_config = false) +{ + global $context, $scripturl, $txt, $sourcedir, $modSettings, $cookiename, $user_settings; + + // Define the variables we want to edit. + $config_vars = array( + // Cookies... + array('cookiename', $txt['cookie_name'], 'file', 'text', 20), + array('cookieTime', $txt['cookieTime'], 'db', 'int'), + array('localCookies', $txt['localCookies'], 'db', 'check', false, 'localCookies'), + array('globalCookies', $txt['globalCookies'], 'db', 'check', false, 'globalCookies'), + array('secureCookies', $txt['secureCookies'], 'db', 'check', false, 'secureCookies', 'disabled' => !isset($_SERVER['HTTPS']) || !(strtolower($_SERVER['HTTPS']) == 'on' || strtolower($_SERVER['HTTPS']) == '1')), + '', + // Sessions + array('databaseSession_enable', $txt['databaseSession_enable'], 'db', 'check', false, 'databaseSession_enable'), + array('databaseSession_loose', $txt['databaseSession_loose'], 'db', 'check', false, 'databaseSession_loose'), + array('databaseSession_lifetime', $txt['databaseSession_lifetime'], 'db', 'int', false, 'databaseSession_lifetime'), + ); + + if ($return_config) + return $config_vars; + + $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cookie;save'; + $context['settings_title'] = $txt['cookies_sessions_settings']; + + // Saving settings? + if (isset($_REQUEST['save'])) + { + saveSettings($config_vars); + + // If the cookie name was changed, reset the cookie. + if ($cookiename != $_POST['cookiename']) + { + $original_session_id = $context['session_id']; + include_once($sourcedir . '/Subs-Auth.php'); + + // Remove the old cookie. + setLoginCookie(-3600, 0); + + // Set the new one. + $cookiename = $_POST['cookiename']; + setLoginCookie(60 * $modSettings['cookieTime'], $user_settings['id_member'], sha1($user_settings['passwd'] . $user_settings['password_salt'])); + + redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $original_session_id, $context['server']['needs_login_fix']); + } + + redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $context['session_id']); + } + + // Fill the config array. + prepareServerSettingsContext($config_vars); +} + +// Simply modifying cache functions +function ModifyCacheSettings($return_config = false) +{ + global $context, $scripturl, $txt, $helptxt, $modSettings; + + // Define the variables we want to edit. + $config_vars = array( + // Only a couple of settings, but they are important + array('select', 'cache_enable', array($txt['cache_off'], $txt['cache_level1'], $txt['cache_level2'], $txt['cache_level3'])), + array('text', 'cache_memcached'), + ); + + if ($return_config) + return $config_vars; + + // Saving again? + if (isset($_GET['save'])) + { + saveDBSettings($config_vars); + + // We have to manually force the clearing of the cache otherwise the changed settings might not get noticed. + $modSettings['cache_enable'] = 1; + cache_put_data('modSettings', null, 90); + + redirectexit('action=admin;area=serversettings;sa=cache;' . $context['session_var'] . '=' . $context['session_id']); + } + + $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cache;save'; + $context['settings_title'] = $txt['caching_settings']; + $context['settings_message'] = $txt['caching_information']; + + // Detect an optimizer? + if (function_exists('eaccelerator_put')) + $detected = 'eAccelerator'; + elseif (function_exists('mmcache_put')) + $detected = 'MMCache'; + elseif (function_exists('apc_store')) + $detected = 'APC'; + elseif (function_exists('output_cache_put')) + $detected = 'Zend'; + elseif (function_exists('memcache_set')) + $detected = 'Memcached'; + elseif (function_exists('xcache_set')) + $detected = 'XCache'; + else + $detected = 'no_caching'; + + $context['settings_message'] = sprintf($context['settings_message'], $txt['detected_' . $detected]); + + // Prepare the template. + prepareDBSettingContext($config_vars); +} + +function ModifyLoadBalancingSettings($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $modSettings; + + // Setup a warning message, but disabled by default. + $disabled = true; + $context['settings_message'] = $txt['loadavg_disabled_conf']; + + if (strpos(strtolower(PHP_OS), 'win') === 0) + $context['settings_message'] = $txt['loadavg_disabled_windows']; + else + { + $modSettings['load_average'] = @file_get_contents('/proc/loadavg'); + if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) !== 0) + $modSettings['load_average'] = (float) $matches[1]; + elseif (($modSettings['load_average'] = @`uptime`) !== null && preg_match('~load averages?: (\d+\.\d+), (\d+\.\d+), (\d+\.\d+)~i', $modSettings['load_average'], $matches) !== 0) + $modSettings['load_average'] = (float) $matches[1]; + else + unset($modSettings['load_average']); + + if (!empty($modSettings['load_average'])) + { + $context['settings_message'] = sprintf($txt['loadavg_warning'], $modSettings['load_average']); + $disabled = false; + } + } + + // Start with a simple checkbox. + $config_vars = array( + array('check', 'loadavg_enable'), + ); + + // Set the default values for each option. + $default_values = array( + 'loadavg_auto_opt' => '1.0', + 'loadavg_search' => '2.5', + 'loadavg_allunread' => '2.0', + 'loadavg_unreadreplies' => '3.5', + 'loadavg_show_posts' => '2.0', + 'loadavg_forum' => '40.0', + ); + + // Loop through the settings. + foreach ($default_values as $name => $value) + { + // Use the default value if the setting isn't set yet. + $value = !isset($modSettings[$name]) ? $value : $modSettings[$name]; + $config_vars[] = array('text', $name, 'value' => $value, 'disabled' => $disabled); + } + + if ($return_config) + return $config_vars; + + $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=loads;save'; + $context['settings_title'] = $txt['load_balancing_settings']; + + // Saving? + if (isset($_GET['save'])) + { + // Stupidity is not allowed. + foreach ($_POST as $key => $value) + { + if (strpos($key, 'loadavg') === 0 || $key === 'loadavg_enable') + continue; + elseif ($key == 'loadavg_auto_opt' && $value <= 1) + $_POST['loadavg_auto_opt'] = '1.0'; + elseif ($key == 'loadavg_forum' && $value < 10) + $_POST['loadavg_forum'] = '10.0'; + elseif ($value < 2) + $_POST[$key] = '2.0'; + } + + saveDBSettings($config_vars); + redirectexit('action=admin;area=serversettings;sa=loads;' . $context['session_var'] . '=' . $context['session_id']); + } + + prepareDBSettingContext($config_vars); +} + +// This is the main function for the language area. +function ManageLanguages() +{ + global $context, $txt, $scripturl, $modSettings; + + loadLanguage('ManageSettings'); + + $context['page_title'] = $txt['edit_languages']; + $context['sub_template'] = 'show_settings'; + + $subActions = array( + 'edit' => 'ModifyLanguages', + 'add' => 'AddLanguage', + 'settings' => 'ModifyLanguageSettings', + 'downloadlang' => 'DownloadLanguage', + 'editlang' => 'ModifyLanguage', + ); + + // By default we're managing languages. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'edit'; + $context['sub_action'] = $_REQUEST['sa']; + + // Load up all the tabs... + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['language_configuration'], + 'description' => $txt['language_description'], + ); + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']](); +} + +// Interface for adding a new language +function AddLanguage() +{ + global $context, $sourcedir, $forum_version, $boarddir, $txt, $smcFunc, $scripturl; + + // Are we searching for new languages courtesy of Simple Machines? + if (!empty($_POST['smf_add_sub'])) + { + // Need fetch_web_data. + require_once($sourcedir . '/Subs-Package.php'); + + $context['smf_search_term'] = htmlspecialchars(trim($_POST['smf_add'])); + + // We're going to use this URL. + $url = 'http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))); + + // Load the class file and stick it into an array. + loadClassFile('Class-Package.php'); + $language_list = new xmlArray(fetch_web_data($url), true); + + // Check it exists. + if (!$language_list->exists('languages')) + $context['smf_error'] = 'no_response'; + else + { + $language_list = $language_list->path('languages[0]'); + $lang_files = $language_list->set('language'); + $context['smf_languages'] = array(); + foreach ($lang_files as $file) + { + // Were we searching? + if (!empty($context['smf_search_term']) && strpos($file->fetch('name'), $smcFunc['strtolower']($context['smf_search_term'])) === false) + continue; + + $context['smf_languages'][] = array( + 'id' => $file->fetch('id'), + 'name' => $smcFunc['ucwords']($file->fetch('name')), + 'version' => $file->fetch('version'), + 'utf8' => $file->fetch('utf8'), + 'description' => $file->fetch('description'), + 'link' => $scripturl . '?action=admin;area=languages;sa=downloadlang;did=' . $file->fetch('id') . ';' . $context['session_var'] . '=' . $context['session_id'], + ); + } + if (empty($context['smf_languages'])) + $context['smf_error'] = 'no_files'; + } + } + + $context['sub_template'] = 'add_language'; +} + +// Download a language file from the Simple Machines website. +function DownloadLanguage() +{ + global $context, $sourcedir, $forum_version, $boarddir, $txt, $smcFunc, $scripturl, $modSettings; + + loadLanguage('ManageSettings'); + require_once($sourcedir . '/Subs-Package.php'); + + // Clearly we need to know what to request. + if (!isset($_GET['did'])) + fatal_lang_error('no_access', false); + + // Some lovely context. + $context['download_id'] = $_GET['did']; + $context['sub_template'] = 'download_language'; + $context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'add'; + + // Can we actually do the installation - and do they want to? + if (!empty($_POST['do_install']) && !empty($_POST['copy_file'])) + { + checkSession('get'); + + $chmod_files = array(); + $install_files = array(); + // Check writable status. + foreach ($_POST['copy_file'] as $file) + { + // Check it's not very bad. + if (strpos($file, '..') !== false || (substr($file, 0, 6) != 'Themes' && !preg_match('~agreement\.[A-Za-z-_0-9]+\.txt$~', $file))) + fatal_error($txt['languages_download_illegal_paths']); + + $chmod_files[] = $boarddir . '/' . $file; + $install_files[] = $file; + } + + // Call this in case we have work to do. + $file_status = create_chmod_control($chmod_files); + $files_left = $file_status['files']['notwritable']; + + // Something not writable? + if (!empty($files_left)) + $context['error_message'] = $txt['languages_download_not_chmod']; + // Otherwise, go go go! + elseif (!empty($install_files)) + { + $archive_content = read_tgz_file('http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))) . ';fetch=' . urlencode($_GET['did']), $boarddir, false, true, $install_files); + // Make sure the files aren't stuck in the cache. + package_flush_cache(); + $context['install_complete'] = sprintf($txt['languages_download_complete_desc'], $scripturl . '?action=admin;area=languages'); + + return; + } + } + + // Open up the old china. + if (!isset($archive_content)) + $archive_content = read_tgz_file('http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))) . ';fetch=' . urlencode($_GET['did']), null); + + if (empty($archive_content)) + fatal_error($txt['add_language_error_no_response']); + + // Now for each of the files, let's do some *stuff* + $context['files'] = array( + 'lang' => array(), + 'other' => array(), + ); + $context['make_writable'] = array(); + foreach ($archive_content as $file) + { + $dirname = dirname($file['filename']); + $filename = basename($file['filename']); + $extension = substr($filename, strrpos($filename, '.') + 1); + + // Don't do anything with files we don't understand. + if (!in_array($extension, array('php', 'jpg', 'gif', 'jpeg', 'png', 'txt'))) + continue; + + // Basic data. + $context_data = array( + 'name' => $filename, + 'destination' => $boarddir . '/' . $file['filename'], + 'generaldest' => $file['filename'], + 'size' => $file['size'], + // Does chmod status allow the copy? + 'writable' => false, + // Should we suggest they copy this file? + 'default_copy' => true, + // Does the file already exist, if so is it same or different? + 'exists' => false, + ); + + // Does the file exist, is it different and can we overwrite? + if (file_exists($boarddir . '/' . $file['filename'])) + { + if (is_writable($boarddir . '/' . $file['filename'])) + $context_data['writable'] = true; + + // Finally, do we actually think the content has changed? + if ($file['size'] == filesize($boarddir . '/' . $file['filename']) && $file['md5'] == md5_file($boarddir . '/' . $file['filename'])) + { + $context_data['exists'] = 'same'; + $context_data['default_copy'] = false; + } + // Attempt to discover newline character differences. + elseif ($file['md5'] == md5(preg_replace("~[\r]?\n~", "\r\n", file_get_contents($boarddir . '/' . $file['filename'])))) + { + $context_data['exists'] = 'same'; + $context_data['default_copy'] = false; + } + else + $context_data['exists'] = 'different'; + } + // No overwrite? + else + { + // Can we at least stick it in the directory... + if (is_writable($boarddir . '/' . $dirname)) + $context_data['writable'] = true; + } + + // I love PHP files, that's why I'm a developer and not an artistic type spending my time drinking absinth and living a life of sin... + if ($extension == 'php' && preg_match('~\w+\.\w+(?:-utf8)?\.php~', $filename)) + { + $context_data += array( + 'version' => '??', + 'cur_version' => false, + 'version_compare' => 'newer', + ); + + list ($name, $language) = explode('.', $filename); + + // Let's get the new version, I like versions, they tell me that I'm up to date. + if (preg_match('~\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '~i', $file['preview'], $match) == 1) + $context_data['version'] = $match[1]; + + // Now does the old file exist - if so what is it's version? + if (file_exists($boarddir . '/' . $file['filename'])) + { + // OK - what is the current version? + $fp = fopen($boarddir . '/' . $file['filename'], 'rb'); + $header = fread($fp, 768); + fclose($fp); + + // Find the version. + if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1) + { + $context_data['cur_version'] = $match[1]; + + // How does this compare? + if ($context_data['cur_version'] == $context_data['version']) + $context_data['version_compare'] = 'same'; + elseif ($context_data['cur_version'] > $context_data['version']) + $context_data['version_compare'] = 'older'; + + // Don't recommend copying if the version is the same. + if ($context_data['version_compare'] != 'newer') + $context_data['default_copy'] = false; + } + } + + // Add the context data to the main set. + $context['files']['lang'][] = $context_data; + } + else + { + // If we think it's a theme thing, work out what the theme is. + if (substr($dirname, 0, 6) == 'Themes' && preg_match('~Themes[\\/]([^\\/]+)[\\/]~', $dirname, $match)) + $theme_name = $match[1]; + else + $theme_name = 'misc'; + + // Assume it's an image, could be an acceptance note etc but rare. + $context['files']['images'][$theme_name][] = $context_data; + } + + // Collect together all non-writable areas. + if (!$context_data['writable']) + $context['make_writable'][] = $context_data['destination']; + } + + // So, I'm a perfectionist - let's get the theme names. + $theme_indexes = array(); + foreach ($context['files']['images'] as $k => $dummy) + $indexes[] = $k; + + $context['theme_names'] = array(); + if (!empty($indexes)) + { + $value_data = array( + 'query' => array(), + 'params' => array(), + ); + + foreach ($indexes as $k => $index) + { + $value_data['query'][] = 'value LIKE {string:value_' . $k . '}'; + $value_data['params']['value_' . $k] = '%' . $index; + } + + $request = $smcFunc['db_query']('', ' + SELECT id_theme, value + FROM {db_prefix}themes + WHERE id_member = {int:no_member} + AND variable = {string:theme_dir} + AND (' . implode(' OR ', $value_data['query']) . ')', + array_merge($value_data['params'], array( + 'no_member' => 0, + 'theme_dir' => 'theme_dir', + 'index_compare_explode' => 'value LIKE \'%' . implode('\' OR value LIKE \'%', $indexes) . '\'', + )) + ); + $themes = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Find the right one. + foreach ($indexes as $index) + if (strpos($row['value'], $index) !== false) + $themes[$row['id_theme']] = $index; + } + $smcFunc['db_free_result']($request); + + if (!empty($themes)) + { + // Now we have the id_theme we can get the pretty description. + $request = $smcFunc['db_query']('', ' + SELECT id_theme, value + FROM {db_prefix}themes + WHERE id_member = {int:no_member} + AND variable = {string:name} + AND id_theme IN ({array_int:theme_list})', + array( + 'theme_list' => array_keys($themes), + 'no_member' => 0, + 'name' => 'name', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Now we have it... + $context['theme_names'][$themes[$row['id_theme']]] = $row['value']; + } + $smcFunc['db_free_result']($request); + } + } + + // Before we go to far can we make anything writable, eh, eh? + if (!empty($context['make_writable'])) + { + // What is left to be made writable? + $file_status = create_chmod_control($context['make_writable']); + $context['still_not_writable'] = $file_status['files']['notwritable']; + + // Mark those which are now writable as such. + foreach ($context['files'] as $type => $data) + { + if ($type == 'lang') + { + foreach ($data as $k => $file) + if (!$file['writable'] && !in_array($file['destination'], $context['still_not_writable'])) + $context['files'][$type][$k]['writable'] = true; + } + else + { + foreach ($data as $theme => $files) + foreach ($files as $k => $file) + if (!$file['writable'] && !in_array($file['destination'], $context['still_not_writable'])) + $context['files'][$type][$theme][$k]['writable'] = true; + } + } + + // Are we going to need more language stuff? + if (!empty($context['still_not_writable'])) + loadLanguage('Packages'); + } + + // This is the list for the main files. + $listOptions = array( + 'id' => 'lang_main_files_list', + 'title' => $txt['languages_download_main_files'], + 'get_items' => array( + 'function' => create_function('', ' + global $context; + return $context[\'files\'][\'lang\']; + '), + ), + 'columns' => array( + 'name' => array( + 'header' => array( + 'value' => $txt['languages_download_filename'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt; + + return \'\' . $rowData[\'name\'] . \'
\' . $txt[\'languages_download_dest\'] . \': \' . $rowData[\'destination\'] . \'\' . ($rowData[\'version_compare\'] == \'older\' ? \'
\' . $txt[\'languages_download_older\'] : \'\'); + '), + ), + ), + 'writable' => array( + 'header' => array( + 'value' => $txt['languages_download_writable'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + return \'\' . ($rowData[\'writable\'] ? $txt[\'yes\'] : $txt[\'no\']) . \'\'; + '), + 'style' => 'text-align: center', + ), + ), + 'version' => array( + 'header' => array( + 'value' => $txt['languages_download_version'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + return \'\' . $rowData[\'version\'] . \'\'; + '), + ), + ), + 'exists' => array( + 'header' => array( + 'value' => $txt['languages_download_exists'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + return $rowData[\'exists\'] ? ($rowData[\'exists\'] == \'same\' ? $txt[\'languages_download_exists_same\'] : $txt[\'languages_download_exists_different\']) : $txt[\'no\']; + '), + ), + ), + 'copy' => array( + 'header' => array( + 'value' => $txt['languages_download_copy'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return \'\'; + '), + 'style' => 'text-align: center; width: 4%;', + ), + ), + ), + ); + + // Kill the cache, as it is now invalid.. + if (!empty($modSettings['cache_enable'])) + { + cache_put_data('known_languages', null, !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600); + cache_put_data('known_languages_all', null, !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600); + } + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['default_list'] = 'lang_main_files_list'; +} + +// This lists all the current languages and allows editing of them. +function ModifyLanguages() +{ + global $txt, $context, $scripturl; + global $user_info, $smcFunc, $sourcedir, $language, $boarddir, $forum_version; + + // Setting a new default? + if (!empty($_POST['set_default']) && !empty($_POST['def_language'])) + { + checkSession(); + + getLanguages(true, false); + $lang_exists = false; + foreach ($context['languages'] as $lang) + { + if ($_POST['def_language'] == $lang['filename']) + { + $lang_exists = true; + break; + } + } + + if ($_POST['def_language'] != $language && $lang_exists) + { + require_once($sourcedir . '/Subs-Admin.php'); + updateSettingsFile(array('language' => '\'' . $_POST['def_language'] . '\'')); + $language = $_POST['def_language']; + } + } + + $listOptions = array( + 'id' => 'language_list', + 'items_per_page' => 20, + 'base_href' => $scripturl . '?action=admin;area=languages', + 'title' => $txt['edit_languages'], + 'get_items' => array( + 'function' => 'list_getLanguages', + ), + 'get_count' => array( + 'function' => 'list_getNumLanguages', + ), + 'columns' => array( + 'default' => array( + 'header' => array( + 'value' => $txt['languages_default'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return \'\'; + '), + 'style' => 'text-align: center; width: 8%;', + ), + ), + 'name' => array( + 'header' => array( + 'value' => $txt['languages_lang_name'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $scripturl, $context; + + return sprintf(\'%3$s\', $scripturl, $rowData[\'id\'], $rowData[\'name\']); + '), + ), + ), + 'character_set' => array( + 'header' => array( + 'value' => $txt['languages_character_set'], + ), + 'data' => array( + 'db_htmlsafe' => 'char_set', + ), + ), + 'count' => array( + 'header' => array( + 'value' => $txt['languages_users'], + ), + 'data' => array( + 'db_htmlsafe' => 'count', + 'style' => 'text-align: center', + ), + ), + 'locale' => array( + 'header' => array( + 'value' => $txt['languages_locale'], + ), + 'data' => array( + 'db_htmlsafe' => 'locale', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=languages', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '', + 'style' => 'text-align: right;', + ), + ), + // For highlighting the default. + 'javascript' => ' + var prevClass = ""; + var prevDiv = ""; + function highlightSelected(box) + { + if (prevClass != "") + prevDiv.className = prevClass; + + prevDiv = document.getElementById(box); + prevClass = prevDiv.className; + + prevDiv.className = "highlight2"; + } + highlightSelected("list_language_list_' . ($language == '' ? 'english' : $language). '"); + ', + ); + + // Display a warning if we cannot edit the default setting. + if (!is_writable($boarddir . '/Settings.php')) + $listOptions['additional_rows'][] = array( + 'position' => 'after_title', + 'value' => $txt['language_settings_writable'], + 'class' => 'smalltext alert', + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'language_list'; +} + +// How many languages? +function list_getNumLanguages() +{ + global $settings; + + // Return how many we have. + return count(getLanguages(true, false)); +} + +// Fetch the actual language information. +function list_getLanguages() +{ + global $settings, $smcFunc, $language, $context, $txt; + + $languages = array(); + // Keep our old entries. + $old_txt = $txt; + $backup_actual_theme_dir = $settings['actual_theme_dir']; + $backup_base_theme_dir = !empty($settings['base_theme_dir']) ? $settings['base_theme_dir'] : ''; + + // Override these for now. + $settings['actual_theme_dir'] = $settings['base_theme_dir'] = $settings['default_theme_dir']; + getLanguages(true, false); + + // Put them back. + $settings['actual_theme_dir'] = $backup_actual_theme_dir; + if (!empty($backup_base_theme_dir)) + $settings['base_theme_dir'] = $backup_base_theme_dir; + else + unset($settings['base_theme_dir']); + + // Get the language files and data... + foreach ($context['languages'] as $lang) + { + // Load the file to get the character set. + require($settings['default_theme_dir'] . '/languages/index.' . $lang['filename'] . '.php'); + + $languages[$lang['filename']] = array( + 'id' => $lang['filename'], + 'count' => 0, + 'char_set' => $txt['lang_character_set'], + 'default' => $language == $lang['filename'] || ($language == '' && $lang['filename'] == 'english'), + 'locale' => $txt['lang_locale'], + 'name' => $smcFunc['ucwords'](strtr($lang['filename'], array('_' => ' ', '-utf8' => ''))), + ); + } + + // Work out how many people are using each language. + $request = $smcFunc['db_query']('', ' + SELECT lngfile, COUNT(*) AS num_users + FROM {db_prefix}members + GROUP BY lngfile', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Default? + if (empty($row['lngfile']) || !isset($languages[$row['lngfile']])) + $row['lngfile'] = $language; + + if (!isset($languages[$row['lngfile']]) && isset($languages['english'])) + $languages['english']['count'] += $row['num_users']; + elseif (isset($languages[$row['lngfile']])) + $languages[$row['lngfile']]['count'] += $row['num_users']; + } + $smcFunc['db_free_result']($request); + + // Restore the current users language. + $txt = $old_txt; + + // Return how many we have. + return $languages; +} + +// Edit language related settings. +function ModifyLanguageSettings($return_config = false) +{ + global $scripturl, $context, $txt, $boarddir, $settings, $smcFunc; + + // Warn the user if the backup of Settings.php failed. + $settings_not_writable = !is_writable($boarddir . '/Settings.php'); + $settings_backup_fail = !@is_writable($boarddir . '/Settings_bak.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php'); + + /* If you're writing a mod, it's a bad idea to add things here.... + For each option: + variable name, description, type (constant), size/possible values, helptext. + OR an empty string for a horizontal rule. + OR a string for a titled section. */ + $config_vars = array( + 'language' => array('language', $txt['default_language'], 'file', 'select', array(), null, 'disabled' => $settings_not_writable), + array('userLanguage', $txt['userLanguage'], 'db', 'check', null, 'userLanguage'), + ); + + if ($return_config) + return $config_vars; + + // Get our languages. No cache and use utf8. + getLanguages(false, false); + foreach ($context['languages'] as $lang) + $config_vars['language'][4][$lang['filename']] = array($lang['filename'], strtr($lang['name'], array('-utf8' => ' (UTF-8)'))); + + // Saving settings? + if (isset($_REQUEST['save'])) + { + checkSession(); + saveSettings($config_vars); + redirectexit('action=admin;area=languages;sa=settings'); + } + + // Setup the template stuff. + $context['post_url'] = $scripturl . '?action=admin;area=languages;sa=settings;save'; + $context['settings_title'] = $txt['language_settings']; + $context['save_disabled'] = $settings_not_writable; + + if ($settings_not_writable) + $context['settings_message'] = '
' . $txt['settings_not_writable'] . '

'; + elseif ($settings_backup_fail) + $context['settings_message'] = '
' . $txt['admin_backup_fail'] . '

'; + + // Fill the config array. + prepareServerSettingsContext($config_vars); +} + +// Edit a particular set of language entries. +function ModifyLanguage() +{ + global $settings, $context, $smcFunc, $txt, $modSettings, $boarddir, $sourcedir, $language; + + loadLanguage('ManageSettings'); + + // Select the languages tab. + $context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'edit'; + $context['page_title'] = $txt['edit_languages']; + $context['sub_template'] = 'modify_language_entries'; + + $context['lang_id'] = $_GET['lid']; + list($theme_id, $file_id) = empty($_REQUEST['tfid']) || strpos($_REQUEST['tfid'], '+') === false ? array(1, '') : explode('+', $_REQUEST['tfid']); + + // Clean the ID - just in case. + preg_match('~([A-Za-z0-9_-]+)~', $context['lang_id'], $matches); + $context['lang_id'] = $matches[1]; + + // Get all the theme data. + $request = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE id_theme != {int:default_theme} + AND id_member = {int:no_member} + AND variable IN ({string:name}, {string:theme_dir})', + array( + 'default_theme' => 1, + 'no_member' => 0, + 'name' => 'name', + 'theme_dir' => 'theme_dir', + ) + ); + $themes = array( + 1 => array( + 'name' => $txt['dvc_default'], + 'theme_dir' => $settings['default_theme_dir'], + ), + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $themes[$row['id_theme']][$row['variable']] = $row['value']; + $smcFunc['db_free_result']($request); + + // This will be where we look + $lang_dirs = array(); + // Check we have themes with a path and a name - just in case - and add the path. + foreach ($themes as $id => $data) + { + if (count($data) != 2) + unset($themes[$id]); + elseif (is_dir($data['theme_dir'] . '/languages')) + $lang_dirs[$id] = $data['theme_dir'] . '/languages'; + + // How about image directories? + if (is_dir($data['theme_dir'] . '/images/' . $context['lang_id'])) + $images_dirs[$id] = $data['theme_dir'] . '/images/' . $context['lang_id']; + } + + $current_file = $file_id ? $lang_dirs[$theme_id] . '/' . $file_id . '.' . $context['lang_id'] . '.php' : ''; + + // Now for every theme get all the files and stick them in context! + $context['possible_files'] = array(); + foreach ($lang_dirs as $theme => $theme_dir) + { + // Open it up. + $dir = dir($theme_dir); + while ($entry = $dir->read()) + { + // We're only after the files for this language. + if (preg_match('~^([A-Za-z]+)\.' . $context['lang_id'] . '\.php$~', $entry, $matches) == 0) + continue; + + //!!! Temp! + if ($matches[1] == 'EmailTemplates') + continue; + + if (!isset($context['possible_files'][$theme])) + $context['possible_files'][$theme] = array( + 'id' => $theme, + 'name' => $themes[$theme]['name'], + 'files' => array(), + ); + + $context['possible_files'][$theme]['files'][] = array( + 'id' => $matches[1], + 'name' => isset($txt['lang_file_desc_' . $matches[1]]) ? $txt['lang_file_desc_' . $matches[1]] : $matches[1], + 'selected' => $theme_id == $theme && $file_id == $matches[1], + ); + } + $dir->close(); + } + + // We no longer wish to speak this language. + if (!empty($_POST['delete_main']) && $context['lang_id'] != 'english') + { + checkSession(); + + // !!! Todo: FTP Controls? + require_once($sourcedir . '/Subs-Package.php'); + + // First, Make a backup? + if (!empty($modSettings['package_make_backups']) && (!isset($_SESSION['last_backup_for']) || $_SESSION['last_backup_for'] != $context['lang_id'] . '$$$')) + { + $_SESSION['last_backup_for'] = $context['lang_id'] . '$$$'; + package_create_backup('backup_lang_' . $context['lang_id']); + } + + // Second, loop through the array to remove the files. + foreach ($lang_dirs as $curPath) + { + foreach ($context['possible_files'][1]['files'] as $lang) + if (file_exists($curPath . '/' . $lang['id'] . '.' . $context['lang_id'] . '.php')) + unlink($curPath . '/' . $lang['id'] . '.' . $context['lang_id'] . '.php'); + + // Check for the email template. + if (file_exists($curPath . '/EmailTemplates.' . $context['lang_id'] . '.php')) + unlink($curPath . '/EmailTemplates.' . $context['lang_id'] . '.php'); + } + + // Third, the agreement file. + if (file_exists($boarddir . '/agreement.' . $context['lang_id'] . '.txt')) + unlink($boarddir . '/agreement.' . $context['lang_id'] . '.txt'); + + // Fourth, a related images folder? + foreach ($images_dirs as $curPath) + if (is_dir($curPath)) + deltree($curPath); + + // Members can no longer use this language. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET lngfile = {string:empty_string} + WHERE lngfile = {string:current_language}', + array( + 'empty_string' => '', + 'current_language' => $context['lang_id'], + ) + ); + + // Fifth, update getLanguages() cache. + if (!empty($modSettings['cache_enable'])) + { + cache_put_data('known_languages', null, !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600); + cache_put_data('known_languages_all', null, !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600); + } + + // Sixth, if we deleted the default language, set us back to english? + if ($context['lang_id'] == $language) + { + require_once($sourcedir . '/Subs-Admin.php'); + $language = 'english'; + updateSettingsFile(array('language' => '\'' . $language . '\'')); + } + + // Seventh, get out of here. + redirectexit('action=admin;area=languages;sa=edit;' . $context['session_var'] . '=' . $context['session_id']); + } + + // Saving primary settings? + $madeSave = false; + if (!empty($_POST['save_main']) && !$current_file) + { + checkSession(); + + // Read in the current file. + $current_data = implode('', file($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php')); + // These are the replacements. old => new + $replace_array = array( + '~\$txt\[\'lang_character_set\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_character_set\'] = \'' . addslashes($_POST['character_set']) . '\';', + '~\$txt\[\'lang_locale\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_locale\'] = \'' . addslashes($_POST['locale']) . '\';', + '~\$txt\[\'lang_dictionary\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_dictionary\'] = \'' . addslashes($_POST['dictionary']) . '\';', + '~\$txt\[\'lang_spelling\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_spelling\'] = \'' . addslashes($_POST['spelling']) . '\';', + '~\$txt\[\'lang_rtl\'\]\s=\s[A-Za-z0-9]+;~' => '$txt[\'lang_rtl\'] = ' . (!empty($_POST['rtl']) ? 'true' : 'false') . ';', + ); + $current_data = preg_replace(array_keys($replace_array), array_values($replace_array), $current_data); + $fp = fopen($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php', 'w+'); + fwrite($fp, $current_data); + fclose($fp); + + $madeSave = true; + } + + // Quickly load index language entries. + $old_txt = $txt; + require($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php'); + $context['lang_file_not_writable_message'] = is_writable($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php') ? '' : sprintf($txt['lang_file_not_writable'], $settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php'); + // Setup the primary settings context. + $context['primary_settings'] = array( + 'name' => $smcFunc['ucwords'](strtr($context['lang_id'], array('_' => ' ', '-utf8' => ''))), + 'character_set' => $txt['lang_character_set'], + 'locale' => $txt['lang_locale'], + 'dictionary' => $txt['lang_dictionary'], + 'spelling' => $txt['lang_spelling'], + 'rtl' => $txt['lang_rtl'], + ); + + // Restore normal service. + $txt = $old_txt; + + // Are we saving? + $save_strings = array(); + if (isset($_POST['save_entries']) && !empty($_POST['entry'])) + { + checkSession(); + + // Clean each entry! + foreach ($_POST['entry'] as $k => $v) + { + // Only try to save if it's changed! + if ($_POST['entry'][$k] != $_POST['comp'][$k]) + $save_strings[$k] = cleanLangString($v, false); + } + } + + // If we are editing a file work away at that. + if ($current_file) + { + $context['entries_not_writable_message'] = is_writable($current_file) ? '' : sprintf($txt['lang_entries_not_writable'], $current_file); + + $entries = array(); + // We can't just require it I'm afraid - otherwise we pass in all kinds of variables! + $multiline_cache = ''; + foreach (file($current_file) as $line) + { + // Got a new entry? + if ($line[0] == '$' && !empty($multiline_cache)) + { + preg_match('~\$(helptxt|txt)\[\'(.+)\'\]\s=\s(.+);~', strtr($multiline_cache, array("\n" => '', "\t" => '')), $matches); + if (!empty($matches[3])) + { + $entries[$matches[2]] = array( + 'type' => $matches[1], + 'full' => $matches[0], + 'entry' => $matches[3], + ); + $multiline_cache = ''; + } + } + $multiline_cache .= $line . "\n"; + } + // Last entry to add? + if ($multiline_cache) + { + preg_match('~\$(helptxt|txt)\[\'(.+)\'\]\s=\s(.+);~', strtr($multiline_cache, array("\n" => '', "\t" => '')), $matches); + if (!empty($matches[3])) + $entries[$matches[2]] = array( + 'type' => $matches[1], + 'full' => $matches[0], + 'entry' => $matches[3], + ); + } + + // These are the entries we can definitely save. + $final_saves = array(); + + $context['file_entries'] = array(); + foreach ($entries as $entryKey => $entryValue) + { + // Ignore some things we set separately. + $ignore_files = array('lang_character_set', 'lang_locale', 'lang_dictionary', 'lang_spelling', 'lang_rtl'); + if (in_array($entryKey, $ignore_files)) + continue; + + // These are arrays that need breaking out. + $arrays = array('days', 'days_short', 'months', 'months_titles', 'months_short'); + if (in_array($entryKey, $arrays)) + { + // Get off the first bits. + $entryValue['entry'] = substr($entryValue['entry'], strpos($entryValue['entry'], '(') + 1, strrpos($entryValue['entry'], ')') - strpos($entryValue['entry'], '(')); + $entryValue['entry'] = explode(',', strtr($entryValue['entry'], array(' ' => ''))); + + // Now create an entry for each item. + $cur_index = 0; + $save_cache = array( + 'enabled' => false, + 'entries' => array(), + ); + foreach ($entryValue['entry'] as $id => $subValue) + { + // Is this a new index? + if (preg_match('~^(\d+)~', $subValue, $matches)) + { + $cur_index = $matches[1]; + $subValue = substr($subValue, strpos($subValue, '\'')); + } + + // Clean up some bits. + $subValue = strtr($subValue, array('"' => '', '\'' => '', ')' => '')); + + // Can we save? + if (isset($save_strings[$entryKey . '-+- ' . $cur_index])) + { + $save_cache['entries'][$cur_index] = strtr($save_strings[$entryKey . '-+- ' . $cur_index], array('\'' => '')); + $save_cache['enabled'] = true; + } + else + $save_cache['entries'][$cur_index] = $subValue; + + $context['file_entries'][] = array( + 'key' => $entryKey . '-+- ' . $cur_index, + 'value' => $subValue, + 'rows' => 1, + ); + $cur_index++; + } + + // Do we need to save? + if ($save_cache['enabled']) + { + // Format the string, checking the indexes first. + $items = array(); + $cur_index = 0; + foreach ($save_cache['entries'] as $k2 => $v2) + { + // Manually show the custom index. + if ($k2 != $cur_index) + { + $items[] = $k2 . ' => \'' . $v2 . '\''; + $cur_index = $k2; + } + else + $items[] = '\'' . $v2 . '\''; + + $cur_index++; + } + // Now create the string! + $final_saves[$entryKey] = array( + 'find' => $entryValue['full'], + 'replace' => '$' . $entryValue['type'] . '[\'' . $entryKey . '\'] = array(' . implode(', ', $items) . ');', + ); + } + } + else + { + // Saving? + if (isset($save_strings[$entryKey]) && $save_strings[$entryKey] != $entryValue['entry']) + { + // !!! Fix this properly. + if ($save_strings[$entryKey] == '') + $save_strings[$entryKey] = '\'\''; + + // Set the new value. + $entryValue['entry'] = $save_strings[$entryKey]; + // And we know what to save now! + $final_saves[$entryKey] = array( + 'find' => $entryValue['full'], + 'replace' => '$' . $entryValue['type'] . '[\'' . $entryKey . '\'] = ' . $save_strings[$entryKey] . ';', + ); + } + + $editing_string = cleanLangString($entryValue['entry'], true); + $context['file_entries'][] = array( + 'key' => $entryKey, + 'value' => $editing_string, + 'rows' => (int) (strlen($editing_string) / 38) + substr_count($editing_string, "\n") + 1, + ); + } + } + + // Any saves to make? + if (!empty($final_saves)) + { + checkSession(); + + $file_contents = implode('', file($current_file)); + foreach ($final_saves as $save) + $file_contents = strtr($file_contents, array($save['find'] => $save['replace'])); + + // Save the actual changes. + $fp = fopen($current_file, 'w+'); + fwrite($fp, $file_contents); + fclose($fp); + + $madeSave = true; + } + + // Another restore. + $txt = $old_txt; + } + + // If we saved, redirect. + if ($madeSave) + redirectexit('action=admin;area=languages;sa=editlang;lid=' . $context['lang_id']); +} + +// This function could be two functions - either way it cleans language entries to/from display. +function cleanLangString($string, $to_display = true) +{ + global $smcFunc; + + // If going to display we make sure it doesn't have any HTML in it - etc. + $new_string = ''; + if ($to_display) + { + // Are we in a string (0 = no, 1 = single quote, 2 = parsed) + $in_string = 0; + $is_escape = false; + for ($i = 0; $i < strlen($string); $i++) + { + // Handle ecapes first. + if ($string{$i} == '\\') + { + // Toggle the escape. + $is_escape = !$is_escape; + // If we're now escaped don't add this string. + if ($is_escape) + continue; + } + // Special case - parsed string with line break etc? + elseif (($string{$i} == 'n' || $string{$i} == 't') && $in_string == 2 && $is_escape) + { + // Put the escape back... + $new_string .= $string{$i} == 'n' ? "\n" : "\t"; + $is_escape = false; + continue; + } + // Have we got a single quote? + elseif ($string{$i} == '\'') + { + // Already in a parsed string, or escaped in a linear string, means we print it - otherwise something special. + if ($in_string != 2 && ($in_string != 1 || !$is_escape)) + { + // Is it the end of a single quote string? + if ($in_string == 1) + $in_string = 0; + // Otherwise it's the start! + else + $in_string = 1; + + // Don't actually include this character! + continue; + } + } + // Otherwise a double quote? + elseif ($string{$i} == '"') + { + // Already in a single quote string, or escaped in a parsed string, means we print it - otherwise something special. + if ($in_string != 1 && ($in_string != 2 || !$is_escape)) + { + // Is it the end of a double quote string? + if ($in_string == 2) + $in_string = 0; + // Otherwise it's the start! + else + $in_string = 2; + + // Don't actually include this character! + continue; + } + } + // A join/space outside of a string is simply removed. + elseif ($in_string == 0 && (empty($string{$i}) || $string{$i} == '.')) + continue; + // Start of a variable? + elseif ($in_string == 0 && $string{$i} == '$') + { + // Find the whole of it! + preg_match('~([\$A-Za-z0-9\'\[\]_-]+)~', substr($string, $i), $matches); + if (!empty($matches[1])) + { + // Come up with some pseudo thing to indicate this is a var. + //!!! Do better than this, please! + $new_string .= '{%' . $matches[1] . '%}'; + + // We're not going to reparse this. + $i += strlen($matches[1]) - 1; + } + + continue; + } + // Right, if we're outside of a string we have DANGER, DANGER! + elseif ($in_string == 0) + { + continue; + } + + // Actually add the character to the string! + $new_string .= $string{$i}; + // If anything was escaped it ain't any longer! + $is_escape = false; + } + + // Unhtml then rehtml the whole thing! + $new_string = htmlspecialchars(un_htmlspecialchars($new_string)); + } + else + { + // Keep track of what we're doing... + $in_string = 0; + // This is for deciding whether to HTML a quote. + $in_html = false; + for ($i = 0; $i < strlen($string); $i++) + { + // Handle line breaks! + if ($string{$i} == "\n" || $string{$i} == "\t") + { + // Are we in a string? Is it the right type? + if ($in_string == 1) + { + // Change type! + $new_string .= '\' . "\\' . ($string{$i} == "\n" ? 'n' : 't'); + $in_string = 2; + } + elseif ($in_string == 2) + $new_string .= '\\' . ($string{$i} == "\n" ? 'n' : 't'); + // Otherwise start one off - joining if required. + else + $new_string .= ($new_string ? ' . ' : '') . '"\\' . ($string{$i} == "\n" ? 'n' : 't'); + + continue; + } + // We don't do parsed strings apart from for breaks. + elseif ($in_string == 2) + { + $in_string = 0; + $new_string .= '"'; + } + + // Not in a string yet? + if ($in_string != 1) + { + $in_string = 1; + $new_string .= ($new_string ? ' . ' : '') . '\''; + } + + // Is this a variable? + if ($string{$i} == '{' && $string{$i + 1} == '%' && $string{$i + 2} == '$') + { + // Grab the variable. + preg_match('~\{%([\$A-Za-z0-9\'\[\]_-]+)%\}~', substr($string, $i), $matches); + if (!empty($matches[1])) + { + if ($in_string == 1) + $new_string .= '\' . '; + elseif ($new_string) + $new_string .= ' . '; + + $new_string .= $matches[1]; + $i += strlen($matches[1]) + 3; + $in_string = 0; + } + + continue; + } + // Is this a lt sign? + elseif ($string{$i} == '<') + { + // Probably HTML? + if ($string{$i + 1} != ' ') + $in_html = true; + // Assume we need an entity... + else + { + $new_string .= '<'; + continue; + } + } + // What about gt? + elseif ($string{$i} == '>') + { + // Will it be HTML? + if ($in_html) + $in_html = false; + // Otherwise we need an entity... + else + { + $new_string .= '>'; + continue; + } + } + // Is it a slash? If so escape it... + if ($string{$i} == '\\') + $new_string .= '\\'; + // The infamous double quote? + elseif ($string{$i} == '"') + { + // If we're in HTML we leave it as a quote - otherwise we entity it. + if (!$in_html) + { + $new_string .= '"'; + continue; + } + } + // A single quote? + elseif ($string{$i} == '\'') + { + // Must be in a string so escape it. + $new_string .= '\\'; + } + + // Finally add the character to the string! + $new_string .= $string{$i}; + } + + // If we ended as a string then close it off. + if ($in_string == 1) + $new_string .= '\''; + elseif ($in_string == 2) + $new_string .= '"'; + } + + return $new_string; +} + +// Helper function, it sets up the context for the manage server settings. +function prepareServerSettingsContext(&$config_vars) +{ + global $context, $modSettings; + + $context['config_vars'] = array(); + foreach ($config_vars as $identifier => $config_var) + { + if (!is_array($config_var) || !isset($config_var[1])) + $context['config_vars'][] = $config_var; + else + { + $varname = $config_var[0]; + global $$varname; + + $context['config_vars'][] = array( + 'label' => $config_var[1], + 'help' => isset($config_var[5]) ? $config_var[5] : '', + 'type' => $config_var[3], + 'size' => empty($config_var[4]) ? 0 : $config_var[4], + 'data' => isset($config_var[4]) && is_array($config_var[4]) ? $config_var[4] : array(), + 'name' => $config_var[0], + 'value' => $config_var[2] == 'file' ? htmlspecialchars($$varname) : (isset($modSettings[$config_var[0]]) ? htmlspecialchars($modSettings[$config_var[0]]) : (in_array($config_var[3], array('int', 'float')) ? 0 : '')), + 'disabled' => !empty($context['settings_not_writable']) || !empty($config_var['disabled']), + 'invalid' => false, + 'javascript' => '', + 'preinput' => '', + 'postinput' => '', + ); + } + } +} + +// Helper function, it sets up the context for database settings. +function prepareDBSettingContext(&$config_vars) +{ + global $txt, $helptxt, $context, $modSettings, $sourcedir; + + loadLanguage('Help'); + + $context['config_vars'] = array(); + $inlinePermissions = array(); + $bbcChoice = array(); + foreach ($config_vars as $config_var) + { + // HR? + if (!is_array($config_var)) + $context['config_vars'][] = $config_var; + else + { + // If it has no name it doesn't have any purpose! + if (empty($config_var[1])) + continue; + + // Special case for inline permissions + if ($config_var[0] == 'permissions' && allowedTo('manage_permissions')) + $inlinePermissions[] = $config_var[1]; + elseif ($config_var[0] == 'permissions') + continue; + + // Are we showing the BBC selection box? + if ($config_var[0] == 'bbc') + $bbcChoice[] = $config_var[1]; + + $context['config_vars'][$config_var[1]] = array( + 'label' => isset($config_var['text_label']) ? $config_var['text_label'] : (isset($txt[$config_var[1]]) ? $txt[$config_var[1]] : (isset($config_var[3]) && !is_array($config_var[3]) ? $config_var[3] : '')), + 'help' => isset($helptxt[$config_var[1]]) ? $config_var[1] : '', + 'type' => $config_var[0], + 'size' => !empty($config_var[2]) && !is_array($config_var[2]) ? $config_var[2] : (in_array($config_var[0], array('int', 'float')) ? 6 : 0), + 'data' => array(), + 'name' => $config_var[1], + 'value' => isset($modSettings[$config_var[1]]) ? ($config_var[0] == 'select' ? $modSettings[$config_var[1]] : htmlspecialchars($modSettings[$config_var[1]])) : (in_array($config_var[0], array('int', 'float')) ? 0 : ''), + 'disabled' => false, + 'invalid' => !empty($config_var['invalid']), + 'javascript' => '', + 'var_message' => !empty($config_var['message']) && isset($txt[$config_var['message']]) ? $txt[$config_var['message']] : '', + 'preinput' => isset($config_var['preinput']) ? $config_var['preinput'] : '', + 'postinput' => isset($config_var['postinput']) ? $config_var['postinput'] : '', + ); + + // If this is a select box handle any data. + if (!empty($config_var[2]) && is_array($config_var[2])) + { + // If we allow multiple selections, we need to adjust a few things. + if ($config_var[0] == 'select' && !empty($config_var['multiple'])) + { + $context['config_vars'][$config_var[1]]['name'] .= '[]'; + $context['config_vars'][$config_var[1]]['value'] = unserialize($context['config_vars'][$config_var[1]]['value']); + } + + // If it's associative + if (isset($config_var[2][0]) && is_array($config_var[2][0])) + $context['config_vars'][$config_var[1]]['data'] = $config_var[2]; + else + { + foreach ($config_var[2] as $key => $item) + $context['config_vars'][$config_var[1]]['data'][] = array($key, $item); + } + } + + // Finally allow overrides - and some final cleanups. + foreach ($config_var as $k => $v) + { + if (!is_numeric($k)) + { + if (substr($k, 0, 2) == 'on') + $context['config_vars'][$config_var[1]]['javascript'] .= ' ' . $k . '="' . $v . '"'; + else + $context['config_vars'][$config_var[1]][$k] = $v; + } + + // See if there are any other labels that might fit? + if (isset($txt['setting_' . $config_var[1]])) + $context['config_vars'][$config_var[1]]['label'] = $txt['setting_' . $config_var[1]]; + elseif (isset($txt['groups_' . $config_var[1]])) + $context['config_vars'][$config_var[1]]['label'] = $txt['groups_' . $config_var[1]]; + } + + // Set the subtext in case it's part of the label. + // !!! Temporary. Preventing divs inside label tags. + $divPos = strpos($context['config_vars'][$config_var[1]]['label'], ']*>~', '', substr($context['config_vars'][$config_var[1]]['label'], $divPos)); + $context['config_vars'][$config_var[1]]['label'] = substr($context['config_vars'][$config_var[1]]['label'], 0, $divPos); + } + } + } + + // If we have inline permissions we need to prep them. + if (!empty($inlinePermissions) && allowedTo('manage_permissions')) + { + require_once($sourcedir . '/ManagePermissions.php'); + init_inline_permissions($inlinePermissions, isset($context['permissions_excluded']) ? $context['permissions_excluded'] : array()); + } + + // What about any BBC selection boxes? + if (!empty($bbcChoice)) + { + // What are the options, eh? + $temp = parse_bbc(false); + $bbcTags = array(); + foreach ($temp as $tag) + $bbcTags[] = $tag['tag']; + + $bbcTags = array_unique($bbcTags); + $totalTags = count($bbcTags); + + // The number of columns we want to show the BBC tags in. + $numColumns = isset($context['num_bbc_columns']) ? $context['num_bbc_columns'] : 3; + + // Start working out the context stuff. + $context['bbc_columns'] = array(); + $tagsPerColumn = ceil($totalTags / $numColumns); + + $col = 0; $i = 0; + foreach ($bbcTags as $tag) + { + if ($i % $tagsPerColumn == 0 && $i != 0) + $col++; + + $context['bbc_columns'][$col][] = array( + 'tag' => $tag, + // !!! 'tag_' . ? + 'show_help' => isset($helptxt[$tag]), + ); + + $i++; + } + + // Now put whatever BBC options we may have into context too! + $context['bbc_sections'] = array(); + foreach ($bbcChoice as $bbc) + { + $context['bbc_sections'][$bbc] = array( + 'title' => isset($txt['bbc_title_' . $bbc]) ? $txt['bbc_title_' . $bbc] : $txt['bbcTagsToUse_select'], + 'disabled' => empty($modSettings['bbc_disabled_' . $bbc]) ? array() : $modSettings['bbc_disabled_' . $bbc], + 'all_selected' => empty($modSettings['bbc_disabled_' . $bbc]), + ); + } + } +} + +// Helper function. Saves settings by putting them in Settings.php or saving them in the settings table. +function saveSettings(&$config_vars) +{ + global $boarddir, $sc, $cookiename, $modSettings, $user_settings; + global $sourcedir, $context, $cachedir; + + // Fix the darn stupid cookiename! (more may not be allowed, but these for sure!) + if (isset($_POST['cookiename'])) + $_POST['cookiename'] = preg_replace('~[,;\s\.$]+~' . ($context['utf8'] ? 'u' : ''), '', $_POST['cookiename']); + + // Fix the forum's URL if necessary. + if (isset($_POST['boardurl'])) + { + if (substr($_POST['boardurl'], -10) == '/index.php') + $_POST['boardurl'] = substr($_POST['boardurl'], 0, -10); + elseif (substr($_POST['boardurl'], -1) == '/') + $_POST['boardurl'] = substr($_POST['boardurl'], 0, -1); + if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://') + $_POST['boardurl'] = 'http://' . $_POST['boardurl']; + } + + // Any passwords? + $config_passwords = array( + 'db_passwd', + 'ssi_db_passwd', + ); + + // All the strings to write. + $config_strs = array( + 'mtitle', 'mmessage', + 'language', 'mbname', 'boardurl', + 'cookiename', + 'webmaster_email', + 'db_name', 'db_user', 'db_server', 'db_prefix', 'ssi_db_user', + 'boarddir', 'sourcedir', 'cachedir', + ); + // All the numeric variables. + $config_ints = array( + ); + // All the checkboxes. + $config_bools = array( + 'db_persist', 'db_error_send', + 'maintenance', + ); + + // Now sort everything into a big array, and figure out arrays and etc. + $new_settings = array(); + foreach ($config_passwords as $config_var) + { + if (isset($_POST[$config_var][1]) && $_POST[$config_var][0] == $_POST[$config_var][1]) + $new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var][0], '\'\\') . '\''; + } + foreach ($config_strs as $config_var) + { + if (isset($_POST[$config_var])) + $new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var], '\'\\') . '\''; + } + foreach ($config_ints as $config_var) + { + if (isset($_POST[$config_var])) + $new_settings[$config_var] = (int) $_POST[$config_var]; + } + foreach ($config_bools as $key) + { + if (!empty($_POST[$key])) + $new_settings[$key] = '1'; + else + $new_settings[$key] = '0'; + } + + // Save the relevant settings in the Settings.php file. + require_once($sourcedir . '/Subs-Admin.php'); + updateSettingsFile($new_settings); + + // Now loopt through the remaining (database-based) settings. + $new_settings = array(); + foreach ($config_vars as $config_var) + { + // We just saved the file-based settings, so skip their definitions. + if (!is_array($config_var) || $config_var[2] == 'file') + continue; + + // Rewrite the definition a bit. + $new_settings[] = array($config_var[3], $config_var[0]); + } + + // Save the new database-based settings, if any. + if (!empty($new_settings)) + saveDBSettings($new_settings); +} + +// Helper function for saving database settings. +function saveDBSettings(&$config_vars) +{ + global $sourcedir, $context; + + $inlinePermissions = array(); + foreach ($config_vars as $var) + { + if (!isset($var[1]) || (!isset($_POST[$var[1]]) && $var[0] != 'check' && $var[0] != 'permissions' && ($var[0] != 'bbc' || !isset($_POST[$var[1] . '_enabledTags'])))) + continue; + + // Checkboxes! + elseif ($var[0] == 'check') + $setArray[$var[1]] = !empty($_POST[$var[1]]) ? '1' : '0'; + // Select boxes! + elseif ($var[0] == 'select' && in_array($_POST[$var[1]], array_keys($var[2]))) + $setArray[$var[1]] = $_POST[$var[1]]; + elseif ($var[0] == 'select' && !empty($var['multiple']) && array_intersect($_POST[$var[1]], array_keys($var[2])) != array()) + { + // For security purposes we validate this line by line. + $options = array(); + foreach ($_POST[$var[1]] as $invar) + if (in_array($invar, array_keys($var[2]))) + $options[] = $invar; + + $setArray[$var[1]] = serialize($options); + } + // Integers! + elseif ($var[0] == 'int') + $setArray[$var[1]] = (int) $_POST[$var[1]]; + // Floating point! + elseif ($var[0] == 'float') + $setArray[$var[1]] = (float) $_POST[$var[1]]; + // Text! + elseif ($var[0] == 'text' || $var[0] == 'large_text') + $setArray[$var[1]] = $_POST[$var[1]]; + // Passwords! + elseif ($var[0] == 'password') + { + if (isset($_POST[$var[1]][1]) && $_POST[$var[1]][0] == $_POST[$var[1]][1]) + $setArray[$var[1]] = $_POST[$var[1]][0]; + } + // BBC. + elseif ($var[0] == 'bbc') + { + + $bbcTags = array(); + foreach (parse_bbc(false) as $tag) + $bbcTags[] = $tag['tag']; + + if (!isset($_POST[$var[1] . '_enabledTags'])) + $_POST[$var[1] . '_enabledTags'] = array(); + elseif (!is_array($_POST[$var[1] . '_enabledTags'])) + $_POST[$var[1] . '_enabledTags'] = array($_POST[$var[1] . '_enabledTags']); + + $setArray[$var[1]] = implode(',', array_diff($bbcTags, $_POST[$var[1] . '_enabledTags'])); + } + // Permissions? + elseif ($var[0] == 'permissions') + $inlinePermissions[] = $var[1]; + } + + if (!empty($setArray)) + updateSettings($setArray); + + // If we have inline permissions we need to save them. + if (!empty($inlinePermissions) && allowedTo('manage_permissions')) + { + require_once($sourcedir . '/ManagePermissions.php'); + save_inline_permissions($inlinePermissions); + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageSettings.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageSettings.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2059 @@ + 'ModifyBasicSettings', + 'layout' => 'ModifyLayoutSettings', + 'karma' => 'ModifyKarmaSettings', + 'sig' => 'ModifySignatureSettings', + 'profile' => 'ShowCustomProfiles', + 'profileedit' => 'EditCustomProfiles', + ); + + loadGeneralSettingParameters($subActions, 'basic'); + + // Load up all the tabs... + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['modSettings_title'], + 'help' => 'featuresettings', + 'description' => sprintf($txt['modSettings_desc'], $settings['theme_id'], $context['session_id'], $context['session_var']), + 'tabs' => array( + 'basic' => array( + ), + 'layout' => array( + ), + 'karma' => array( + ), + 'sig' => array( + 'description' => $txt['signature_settings_desc'], + ), + 'profile' => array( + 'description' => $txt['custom_profile_desc'], + ), + ), + ); + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']](); +} + +// This function passes control through to the relevant security tab. +function ModifySecuritySettings() +{ + global $context, $txt, $scripturl, $modSettings, $settings; + + $context['page_title'] = $txt['admin_security_moderation']; + + $subActions = array( + 'general' => 'ModifyGeneralSecuritySettings', + 'spam' => 'ModifySpamSettings', + 'moderation' => 'ModifyModerationSettings', + ); + + loadGeneralSettingParameters($subActions, 'general'); + + // Load up all the tabs... + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['admin_security_moderation'], + 'help' => 'securitysettings', + 'description' => $txt['security_settings_desc'], + 'tabs' => array( + 'general' => array( + ), + 'spam' => array( + 'description' => $txt['antispam_Settings_desc'] , + ), + 'moderation' => array( + ), + ), + ); + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']](); +} + +// This my friend, is for all the mod authors out there. They're like builders without the ass crack - with the possible exception of... /cut short +function ModifyModSettings() +{ + global $context, $txt, $scripturl, $modSettings, $settings; + + $context['page_title'] = $txt['admin_modifications']; + + $subActions = array( + 'general' => 'ModifyGeneralModSettings', + // Mod authors, once again, if you have a whole section to add do it AFTER this line, and keep a comma at the end. + ); + + // Make it easier for mods to add new areas. + call_integration_hook('integrate_modify_modifications', array(&$subActions)); + + loadGeneralSettingParameters($subActions, 'general'); + + // Load up all the tabs... + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['admin_modifications'], + 'help' => 'modsettings', + 'description' => $txt['modification_settings_desc'], + 'tabs' => array( + 'general' => array( + ), + ), + ); + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']](); +} + +// This is an overall control panel enabling/disabling lots of SMF's key feature components. +function ModifyCoreFeatures($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $sc, $modSettings; + + /* This is an array of all the features that can be enabled/disabled - each option can have the following: + title - Text title of this item (If standard string does not exist). + desc - Description of this feature (If standard string does not exist). + image - Custom image to show next to feature. + settings - Array of settings to change (For each name => value) on enable - reverse is done for disable. If > 1 will not change value if set. + setting_callback- Function that returns an array of settings to save - takes one parameter which is value for this feature. + save_callback - Function called on save, takes state as parameter. + */ + $core_features = array( + // cd = calendar. + 'cd' => array( + 'url' => 'action=admin;area=managecalendar', + 'settings' => array( + 'cal_enabled' => 1, + ), + ), + // cp = custom profile fields. + 'cp' => array( + 'url' => 'action=admin;area=featuresettings;sa=profile', + 'save_callback' => create_function('$value', ' + global $smcFunc; + if (!$value) + { + $smcFunc[\'db_query\'](\'\', \' + UPDATE {db_prefix}custom_fields + SET active = 0\'); + } + '), + 'setting_callback' => create_function('$value', ' + if (!$value) + return array( + \'disabled_profile_fields\' => \'\', + \'registration_fields\' => \'\', + \'displayFields\' => \'\', + ); + else + return array(); + '), + ), + // k = karma. + 'k' => array( + 'url' => 'action=admin;area=featuresettings;sa=karma', + 'settings' => array( + 'karmaMode' => 2, + ), + ), + // ml = moderation log. + 'ml' => array( + 'url' => 'action=admin;area=logs;sa=modlog', + 'settings' => array( + 'modlog_enabled' => 1, + ), + ), + // pm = post moderation. + 'pm' => array( + 'url' => 'action=admin;area=permissions;sa=postmod', + 'setting_callback' => create_function('$value', ' + global $sourcedir; + + // Cant use warning post moderation if disabled! + if (!$value) + { + require_once($sourcedir . \'/PostModeration.php\'); + approveAllData(); + + return array(\'warning_moderate\' => 0); + } + else + return array(); + '), + ), + // ps = Paid Subscriptions. + 'ps' => array( + 'url' => 'action=admin;area=paidsubscribe', + 'settings' => array( + 'paid_enabled' => 1, + ), + 'setting_callback' => create_function('$value', ' + global $smcFunc, $sourcedir; + + // Set the correct disabled value for scheduled task. + $smcFunc[\'db_query\'](\'\', \' + UPDATE {db_prefix}scheduled_tasks + SET disabled = {int:disabled} + WHERE task = {string:task}\', + array( + \'disabled\' => $value ? 0 : 1, + \'task\' => \'paid_subscriptions\', + ) + ); + + // Should we calculate next trigger? + if ($value) + { + require_once($sourcedir . \'/ScheduledTasks.php\'); + CalculateNextTrigger(\'paid_subscriptions\'); + } + '), + ), + // rg = report generator. + 'rg' => array( + 'url' => 'action=admin;area=reports', + ), + // w = warning. + 'w' => array( + 'url' => 'action=admin;area=securitysettings;sa=moderation', + 'setting_callback' => create_function('$value', ' + global $modSettings; + list ($modSettings[\'warning_enable\'], $modSettings[\'user_limit\'], $modSettings[\'warning_decrement\']) = explode(\',\', $modSettings[\'warning_settings\']); + $warning_settings = ($value ? 1 : 0) . \',\' . $modSettings[\'user_limit\'] . \',\' . $modSettings[\'warning_decrement\']; + if (!$value) + { + $returnSettings = array( + \'warning_watch\' => 0, + \'warning_moderate\' => 0, + \'warning_mute\' => 0, + ); + } + elseif (empty($modSettings[\'warning_enable\']) && $value) + { + $returnSettings = array( + \'warning_watch\' => 10, + \'warning_moderate\' => 35, + \'warning_mute\' => 60, + ); + } + else + $returnSettings = array(); + + $returnSettings[\'warning_settings\'] = $warning_settings; + return $returnSettings; + '), + ), + // Search engines + 'sp' => array( + 'url' => 'action=admin;area=sengines', + 'settings' => array( + 'spider_mode' => 1, + ), + 'setting_callback' => create_function('$value', ' + // Turn off the spider group if disabling. + if (!$value) + return array(\'spider_group\' => 0, \'show_spider_online\' => 0); + '), + 'on_save' => create_function('', ' + global $sourcedir, $modSettings; + require_once($sourcedir . \'/ManageSearchEngines.php\'); + recacheSpiderNames(); + '), + ), + ); + + // Anyone who would like to add a core feature? + call_integration_hook('integrate_core_features', array(&$core_features)); + + // Are we getting info for the help section. + if ($return_config) + { + $return_data = array(); + foreach ($core_features as $id => $data) + $return_data[] = array('switch', isset($data['title']) ? $data['title'] : $txt['core_settings_item_' . $id]); + return $return_data; + } + + loadGeneralSettingParameters(); + + // Are we saving? + if (isset($_POST['save'])) + { + checkSession(); + + $setting_changes = array('admin_features' => array()); + + // Are we using the javascript stuff or radios to submit? + $post_var_prefix = empty($_POST['js_worked']) ? 'feature_plain_' : 'feature_'; + + // Cycle each feature and change things as required! + foreach ($core_features as $id => $feature) + { + // Enabled? + if (!empty($_POST[$post_var_prefix . $id])) + $setting_changes['admin_features'][] = $id; + + // Setting values to change? + if (isset($feature['settings'])) + { + foreach ($feature['settings'] as $key => $value) + { + if (empty($_POST[$post_var_prefix . $id]) || (!empty($_POST[$post_var_prefix . $id]) && ($value < 2 || empty($modSettings[$key])))) + $setting_changes[$key] = !empty($_POST[$post_var_prefix . $id]) ? $value : !$value; + } + } + // Is there a call back for settings? + if (isset($feature['setting_callback'])) + { + $returned_settings = $feature['setting_callback'](!empty($_POST[$post_var_prefix . $id])); + if (!empty($returned_settings)) + $setting_changes = array_merge($setting_changes, $returned_settings); + } + + // Standard save callback? + if (isset($feature['on_save'])) + $feature['on_save'](); + } + + // Make sure this one setting is a string! + $setting_changes['admin_features'] = implode(',', $setting_changes['admin_features']); + + // Make any setting changes! + updateSettings($setting_changes); + + // Any post save things? + foreach ($core_features as $id => $feature) + { + // Standard save callback? + if (isset($feature['save_callback'])) + $feature['save_callback'](!empty($_POST[$post_var_prefix . $id])); + } + + redirectexit('action=admin;area=corefeatures;' . $context['session_var'] . '=' . $context['session_id']); + } + + // Put them in context. + $context['features'] = array(); + foreach ($core_features as $id => $feature) + $context['features'][$id] = array( + 'title' => isset($feature['title']) ? $feature['title'] : $txt['core_settings_item_' . $id], + 'desc' => isset($feature['desc']) ? $feature['desc'] : $txt['core_settings_item_' . $id . '_desc'], + 'enabled' => in_array($id, $context['admin_features']), + 'url' => !empty($feature['url']) ? $scripturl . '?' . $feature['url'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '', + ); + + // Are they a new user? + $context['is_new_install'] = !isset($modSettings['admin_features']); + $context['force_disable_tabs'] = $context['is_new_install']; + // Don't show them this twice! + if ($context['is_new_install']) + updateSettings(array('admin_features' => '')); + + $context['sub_template'] = 'core_features'; + $context['page_title'] = $txt['core_settings_title']; +} + +function ModifyBasicSettings($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $sc, $modSettings; + + $config_vars = array( + // Big Options... polls, sticky, bbc.... + array('select', 'pollMode', array($txt['disable_polls'], $txt['enable_polls'], $txt['polls_as_topics'])), + '', + // Basic stuff, titles, flash, permissions... + array('check', 'allow_guestAccess'), + array('check', 'enable_buddylist'), + array('check', 'allow_editDisplayName'), + array('check', 'allow_hideOnline'), + array('check', 'titlesEnable'), + array('text', 'default_personal_text'), + '', + // SEO stuff + array('check', 'queryless_urls'), + array('text', 'meta_keywords', 'size' => 50), + '', + // Number formatting, timezones. + array('text', 'time_format'), + array('select', 'number_format', array('1234.00' => '1234.00', '1,234.00' => '1,234.00', '1.234,00' => '1.234,00', '1 234,00' => '1 234,00', '1234,00' => '1234,00')), + array('float', 'time_offset'), + 'default_timezone' => array('select', 'default_timezone', array()), + '', + // Who's online? + array('check', 'who_enabled'), + array('int', 'lastActive'), + '', + // Statistics. + array('check', 'trackStats'), + array('check', 'hitStats'), + '', + // Option-ish things... miscellaneous sorta. + array('check', 'allow_disableAnnounce'), + array('check', 'disallow_sendBody'), + ); + + // Get all the time zones. + if (function_exists('timezone_identifiers_list') && function_exists('date_default_timezone_set')) + { + $all_zones = timezone_identifiers_list(); + // Make sure we set the value to the same as the printed value. + foreach ($all_zones as $zone) + $config_vars['default_timezone'][2][$zone] = $zone; + } + else + unset($config_vars['default_timezone']); + + if ($return_config) + return $config_vars; + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + // Prevent absurd boundaries here - make it a day tops. + if (isset($_POST['lastActive'])) + $_POST['lastActive'] = min((int) $_POST['lastActive'], 1440); + + saveDBSettings($config_vars); + + writeLog(); + redirectexit('action=admin;area=featuresettings;sa=basic'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=basic'; + $context['settings_title'] = $txt['mods_cat_features']; + + prepareDBSettingContext($config_vars); +} + +// Settings really associated with general security aspects. +function ModifyGeneralSecuritySettings($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $sc, $modSettings; + + $config_vars = array( + array('check', 'guest_hideContacts'), + array('check', 'make_email_viewable'), + '', + array('int', 'failed_login_threshold'), + '', + array('check', 'enableErrorLogging'), + array('check', 'enableErrorQueryLogging'), + array('check', 'securityDisable'), + '', + // Reactive on email, and approve on delete + array('check', 'send_validation_onChange'), + array('check', 'approveAccountDeletion'), + '', + // Password strength. + array('select', 'password_strength', array($txt['setting_password_strength_low'], $txt['setting_password_strength_medium'], $txt['setting_password_strength_high'])), + '', + // Reporting of personal messages? + array('check', 'enableReportPM'), + ); + + if ($return_config) + return $config_vars; + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + saveDBSettings($config_vars); + + writeLog(); + redirectexit('action=admin;area=securitysettings;sa=general'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=securitysettings;save;sa=general'; + $context['settings_title'] = $txt['mods_cat_security_general']; + + prepareDBSettingContext($config_vars); +} + +function ModifyLayoutSettings($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $sc; + + $config_vars = array( + // Pagination stuff. + array('check', 'compactTopicPagesEnable'), + array('int', 'compactTopicPagesContiguous', null, $txt['contiguous_page_display'] . '
' . str_replace(' ', ' ', '"3" ' . $txt['to_display'] . ': 1 ... 4 [5] 6 ... 9') . '
' . str_replace(' ', ' ', '"5" ' . $txt['to_display'] . ': 1 ... 3 4 [5] 6 7 ... 9') . '
'), + array('int', 'defaultMaxMembers'), + '', + // Stuff that just is everywhere - today, search, online, etc. + array('select', 'todayMod', array($txt['today_disabled'], $txt['today_only'], $txt['yesterday_today'])), + array('check', 'topbottomEnable'), + array('check', 'onlineEnable'), + array('check', 'enableVBStyleLogin'), + '', + // Automagic image resizing. + array('int', 'max_image_width'), + array('int', 'max_image_height'), + '', + // This is like debugging sorta. + array('check', 'timeLoadPageEnable'), + ); + + if ($return_config) + return $config_vars; + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + saveDBSettings($config_vars); + writeLog(); + + redirectexit('action=admin;area=featuresettings;sa=layout'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=layout'; + $context['settings_title'] = $txt['mods_cat_layout']; + + prepareDBSettingContext($config_vars); +} + +function ModifyKarmaSettings($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $sc; + + $config_vars = array( + // Karma - On or off? + array('select', 'karmaMode', explode('|', $txt['karma_options'])), + '', + // Who can do it.... and who is restricted by time limits? + array('int', 'karmaMinPosts'), + array('float', 'karmaWaitTime'), + array('check', 'karmaTimeRestrictAdmins'), + '', + // What does it look like? [smite]? + array('text', 'karmaLabel'), + array('text', 'karmaApplaudLabel'), + array('text', 'karmaSmiteLabel'), + ); + + if ($return_config) + return $config_vars; + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + saveDBSettings($config_vars); + redirectexit('action=admin;area=featuresettings;sa=karma'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=karma'; + $context['settings_title'] = $txt['karma']; + + prepareDBSettingContext($config_vars); +} + +// Moderation type settings - although there are fewer than we have you believe ;) +function ModifyModerationSettings($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $sc, $modSettings; + + $config_vars = array( + // Warning system? + array('int', 'warning_watch', 'help' => 'warning_enable'), + 'moderate' => array('int', 'warning_moderate'), + array('int', 'warning_mute'), + 'rem1' => array('int', 'user_limit'), + 'rem2' => array('int', 'warning_decrement'), + array('select', 'warning_show', array($txt['setting_warning_show_mods'], $txt['setting_warning_show_user'], $txt['setting_warning_show_all'])), + ); + + if ($return_config) + return $config_vars; + + // Cannot use moderation if post moderation is not enabled. + if (!$modSettings['postmod_active']) + unset($config_vars['moderate']); + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + // Make sure these don't have an effect. + if (substr($modSettings['warning_settings'], 0, 1) != 1) + { + $_POST['warning_watch'] = 0; + $_POST['warning_moderate'] = 0; + $_POST['warning_mute'] = 0; + } + else + { + $_POST['warning_watch'] = min($_POST['warning_watch'], 100); + $_POST['warning_moderate'] = $modSettings['postmod_active'] ? min($_POST['warning_moderate'], 100) : 0; + $_POST['warning_mute'] = min($_POST['warning_mute'], 100); + } + + // Fix the warning setting array! + $_POST['warning_settings'] = '1,' . min(100, (int) $_POST['user_limit']) . ',' . min(100, (int) $_POST['warning_decrement']); + $save_vars = $config_vars; + $save_vars[] = array('text', 'warning_settings'); + unset($save_vars['rem1'], $save_vars['rem2']); + + saveDBSettings($save_vars); + redirectexit('action=admin;area=securitysettings;sa=moderation'); + } + + // We actually store lots of these together - for efficiency. + list ($modSettings['warning_enable'], $modSettings['user_limit'], $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']); + + $context['post_url'] = $scripturl . '?action=admin;area=securitysettings;save;sa=moderation'; + $context['settings_title'] = $txt['moderation_settings']; + + prepareDBSettingContext($config_vars); +} + +// Let's try keep the spam to a minimum ah Thantos? +function ModifySpamSettings($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $sc, $modSettings, $smcFunc; + + // Generate a sample registration image. + $context['use_graphic_library'] = in_array('gd', get_loaded_extensions()); + $context['verification_image_href'] = $scripturl . '?action=verificationcode;rand=' . md5(mt_rand()); + + $config_vars = array( + array('check', 'reg_verification'), + array('check', 'search_enable_captcha'), + // This, my friend, is a cheat :p + 'guest_verify' => array('check', 'guests_require_captcha', 'subtext' => $txt['setting_guests_require_captcha_desc']), + array('int', 'posts_require_captcha', 'subtext' => $txt['posts_require_captcha_desc'], 'onchange' => 'if (this.value > 0){ document.getElementById(\'guests_require_captcha\').checked = true; document.getElementById(\'guests_require_captcha\').disabled = true;} else {document.getElementById(\'guests_require_captcha\').disabled = false;}'), + array('check', 'guests_report_require_captcha'), + '', + // PM Settings + 'pm1' => array('int', 'max_pm_recipients'), + 'pm2' => array('int', 'pm_posts_verification'), + 'pm3' => array('int', 'pm_posts_per_hour'), + // Visual verification. + array('title', 'configure_verification_means'), + array('desc', 'configure_verification_means_desc'), + 'vv' => array('select', 'visual_verification_type', array($txt['setting_image_verification_off'], $txt['setting_image_verification_vsimple'], $txt['setting_image_verification_simple'], $txt['setting_image_verification_medium'], $txt['setting_image_verification_high'], $txt['setting_image_verification_extreme']), 'subtext'=> $txt['setting_visual_verification_type_desc'], 'onchange' => $context['use_graphic_library'] ? 'refreshImages();' : ''), + array('int', 'qa_verification_number', 'subtext' => $txt['setting_qa_verification_number_desc']), + // Clever Thomas, who is looking sheepy now? Not I, the mighty sword swinger did say. + array('title', 'setup_verification_questions'), + array('desc', 'setup_verification_questions_desc'), + array('callback', 'question_answer_list'), + ); + + if ($return_config) + return $config_vars; + + // Load any question and answers! + $context['question_answers'] = array(); + $request = $smcFunc['db_query']('', ' + SELECT id_comment, body AS question, recipient_name AS answer + FROM {db_prefix}log_comments + WHERE comment_type = {string:ver_test}', + array( + 'ver_test' => 'ver_test', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['question_answers'][$row['id_comment']] = array( + 'id' => $row['id_comment'], + 'question' => $row['question'], + 'answer' => $row['answer'], + ); + } + $smcFunc['db_free_result']($request); + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + // Fix PM settings. + $_POST['pm_spam_settings'] = (int) $_POST['max_pm_recipients'] . ',' . (int) $_POST['pm_posts_verification'] . ',' . (int) $_POST['pm_posts_per_hour']; + + // Hack in guest requiring verification! + if (empty($_POST['posts_require_captcha']) && !empty($_POST['guests_require_captcha'])) + $_POST['posts_require_captcha'] = -1; + + $save_vars = $config_vars; + unset($save_vars['pm1'], $save_vars['pm2'], $save_vars['pm3'], $save_vars['guest_verify']); + + $save_vars[] = array('text', 'pm_spam_settings'); + + // Handle verification questions. + $questionInserts = array(); + $count_questions = 0; + foreach ($_POST['question'] as $id => $question) + { + $question = trim($smcFunc['htmlspecialchars']($question, ENT_COMPAT, $context['character_set'])); + $answer = trim($smcFunc['strtolower']($smcFunc['htmlspecialchars']($_POST['answer'][$id], ENT_COMPAT, $context['character_set']))); + + // Already existed? + if (isset($context['question_answers'][$id])) + { + $count_questions++; + // Changed? + if ($context['question_answers'][$id]['question'] != $question || $context['question_answers'][$id]['answer'] != $answer) + { + if ($question == '' || $answer == '') + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_comments + WHERE comment_type = {string:ver_test} + AND id_comment = {int:id}', + array( + 'id' => $id, + 'ver_test' => 'ver_test', + ) + ); + $count_questions--; + } + else + $request = $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_comments + SET body = {string:question}, recipient_name = {string:answer} + WHERE comment_type = {string:ver_test} + AND id_comment = {int:id}', + array( + 'id' => $id, + 'ver_test' => 'ver_test', + 'question' => $question, + 'answer' => $answer, + ) + ); + } + } + // It's so shiney and new! + elseif ($question != '' && $answer != '') + { + $questionInserts[] = array( + 'comment_type' => 'ver_test', + 'body' => $question, + 'recipient_name' => $answer, + ); + } + } + + // Any questions to insert? + if (!empty($questionInserts)) + { + $smcFunc['db_insert']('', + '{db_prefix}log_comments', + array('comment_type' => 'string', 'body' => 'string-65535', 'recipient_name' => 'string-80'), + $questionInserts, + array('id_comment') + ); + $count_questions++; + } + + if (empty($count_questions) || $_POST['qa_verification_number'] > $count_questions) + $_POST['qa_verification_number'] = $count_questions; + + // Now save. + saveDBSettings($save_vars); + + cache_put_data('verificationQuestionIds', null, 300); + + redirectexit('action=admin;area=securitysettings;sa=spam'); + } + + $character_range = array_merge(range('A', 'H'), array('K', 'M', 'N', 'P', 'R'), range('T', 'Y')); + $_SESSION['visual_verification_code'] = ''; + for ($i = 0; $i < 6; $i++) + $_SESSION['visual_verification_code'] .= $character_range[array_rand($character_range)]; + + // Some javascript for CAPTCHA. + $context['settings_post_javascript'] = ''; + if ($context['use_graphic_library']) + $context['settings_post_javascript'] .= ' + function refreshImages() + { + var imageType = document.getElementById(\'visual_verification_type\').value; + document.getElementById(\'verification_image\').src = \'' . $context['verification_image_href'] . ';type=\' + imageType; + }'; + + // Show the image itself, or text saying we can't. + if ($context['use_graphic_library']) + $config_vars['vv']['postinput'] = '
' . $txt['setting_image_verification_sample'] . '
'; + else + $config_vars['vv']['postinput'] = '
' . $txt['setting_image_verification_nogd'] . ''; + + // Hack for PM spam settings. + list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']); + + // Hack for guests requiring verification. + $modSettings['guests_require_captcha'] = !empty($modSettings['posts_require_captcha']); + $modSettings['posts_require_captcha'] = !isset($modSettings['posts_require_captcha']) || $modSettings['posts_require_captcha'] == -1 ? 0 : $modSettings['posts_require_captcha']; + + // Some minor javascript for the guest post setting. + if ($modSettings['posts_require_captcha']) + $context['settings_post_javascript'] .= ' + document.getElementById(\'guests_require_captcha\').disabled = true;'; + + $context['post_url'] = $scripturl . '?action=admin;area=securitysettings;save;sa=spam'; + $context['settings_title'] = $txt['antispam_Settings']; + + prepareDBSettingContext($config_vars); +} + +// You'll never guess what this function does... +function ModifySignatureSettings($return_config = false) +{ + global $context, $txt, $modSettings, $sig_start, $smcFunc, $helptxt, $scripturl; + + $config_vars = array( + // Are signatures even enabled? + array('check', 'signature_enable'), + '', + // Tweaking settings! + array('int', 'signature_max_length'), + array('int', 'signature_max_lines'), + array('int', 'signature_max_font_size'), + array('check', 'signature_allow_smileys', 'onclick' => 'document.getElementById(\'signature_max_smileys\').disabled = !this.checked;'), + array('int', 'signature_max_smileys'), + '', + // Image settings. + array('int', 'signature_max_images'), + array('int', 'signature_max_image_width'), + array('int', 'signature_max_image_height'), + '', + array('bbc', 'signature_bbc'), + ); + + if ($return_config) + return $config_vars; + + // Setup the template. + $context['page_title'] = $txt['signature_settings']; + $context['sub_template'] = 'show_settings'; + + // Disable the max smileys option if we don't allow smileys at all! + $context['settings_post_javascript'] = 'document.getElementById(\'signature_max_smileys\').disabled = !document.getElementById(\'signature_allow_smileys\').checked;'; + + // Load all the signature settings. + list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']); + $sig_limits = explode(',', $sig_limits); + $disabledTags = !empty($sig_bbc) ? explode(',', $sig_bbc) : array(); + + // Applying to ALL signatures?!! + if (isset($_GET['apply'])) + { + // Security! + checkSession('get'); + + $sig_start = time(); + // This is horrid - but I suppose some people will want the option to do it. + $_GET['step'] = isset($_GET['step']) ? (int) $_GET['step'] : 0; + $done = false; + + $request = $smcFunc['db_query']('', ' + SELECT MAX(id_member) + FROM {db_prefix}members', + array( + ) + ); + list ($context['max_member']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + while (!$done) + { + $changes = array(); + + $request = $smcFunc['db_query']('', ' + SELECT id_member, signature + FROM {db_prefix}members + WHERE id_member BETWEEN ' . $_GET['step'] . ' AND ' . $_GET['step'] . ' + 49 + AND id_group != {int:admin_group} + AND FIND_IN_SET({int:admin_group}, additional_groups) = 0', + array( + 'admin_group' => 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Apply all the rules we can realistically do. + $sig = strtr($row['signature'], array('
' => "\n")); + + // Max characters... + if (!empty($sig_limits[1])) + $sig = $smcFunc['substr']($sig, 0, $sig_limits[1]); + // Max lines... + if (!empty($sig_limits[2])) + { + $count = 0; + for ($i = 0; $i < strlen($sig); $i++) + { + if ($sig[$i] == "\n") + { + $count++; + if ($count >= $sig_limits[2]) + $sig = substr($sig, 0, $i) . strtr(substr($sig, $i), array("\n" => ' ')); + } + } + } + + if (!empty($sig_limits[7]) && preg_match_all('~\[size=([\d\.]+)?(px|pt|em|x-large|larger)~i', $sig, $matches) !== false && isset($matches[2])) + { + foreach ($matches[1] as $ind => $size) + { + $limit_broke = 0; + // Attempt to allow all sizes of abuse, so to speak. + if ($matches[2][$ind] == 'px' && $size > $sig_limits[7]) + $limit_broke = $sig_limits[7] . 'px'; + elseif ($matches[2][$ind] == 'pt' && $size > ($sig_limits[7] * 0.75)) + $limit_broke = ((int) $sig_limits[7] * 0.75) . 'pt'; + elseif ($matches[2][$ind] == 'em' && $size > ((float) $sig_limits[7] / 16)) + $limit_broke = ((float) $sig_limits[7] / 16) . 'em'; + elseif ($matches[2][$ind] != 'px' && $matches[2][$ind] != 'pt' && $matches[2][$ind] != 'em' && $sig_limits[7] < 18) + $limit_broke = 'large'; + + if ($limit_broke) + $sig = str_replace($matches[0][$ind], '[size=' . $sig_limits[7] . 'px', $sig); + } + } + + // Stupid images - this is stupidly, stupidly challenging. + if ((!empty($sig_limits[3]) || !empty($sig_limits[5]) || !empty($sig_limits[6]))) + { + $replaces = array(); + $img_count = 0; + // Get all BBC tags... + preg_match_all('~\[img(\s+width=([\d]+))?(\s+height=([\d]+))?(\s+width=([\d]+))?\s*\](?:
)*([^<">]+?)(?:
)*\[/img\]~i', $sig, $matches); + // ... and all HTML ones. + preg_match_all('~<img\s+src=(?:")?((?:http://|ftp://|https://|ftps://).+?)(?:")?(?:\s+alt=(?:")?(.*?)(?:")?)?(?:\s?/)?>~i', $sig, $matches2, PREG_PATTERN_ORDER); + // And stick the HTML in the BBC. + if (!empty($matches2)) + { + foreach ($matches2[0] as $ind => $dummy) + { + $matches[0][] = $matches2[0][$ind]; + $matches[1][] = ''; + $matches[2][] = ''; + $matches[3][] = ''; + $matches[4][] = ''; + $matches[5][] = ''; + $matches[6][] = ''; + $matches[7][] = $matches2[1][$ind]; + } + } + // Try to find all the images! + if (!empty($matches)) + { + $image_count_holder = array(); + foreach ($matches[0] as $key => $image) + { + $width = -1; $height = -1; + $img_count++; + // Too many images? + if (!empty($sig_limits[3]) && $img_count > $sig_limits[3]) + { + // If we've already had this before we only want to remove the excess. + if (isset($image_count_holder[$image])) + { + $img_offset = -1; + $rep_img_count = 0; + while ($img_offset !== false) + { + $img_offset = strpos($sig, $image, $img_offset + 1); + $rep_img_count++; + if ($rep_img_count > $image_count_holder[$image]) + { + // Only replace the excess. + $sig = substr($sig, 0, $img_offset) . str_replace($image, '', substr($sig, $img_offset)); + // Stop looping. + $img_offset = false; + } + } + } + else + $replaces[$image] = ''; + + continue; + } + + // Does it have predefined restraints? Width first. + if ($matches[6][$key]) + $matches[2][$key] = $matches[6][$key]; + if ($matches[2][$key] && $sig_limits[5] && $matches[2][$key] > $sig_limits[5]) + { + $width = $sig_limits[5]; + $matches[4][$key] = $matches[4][$key] * ($width / $matches[2][$key]); + } + elseif ($matches[2][$key]) + $width = $matches[2][$key]; + // ... and height. + if ($matches[4][$key] && $sig_limits[6] && $matches[4][$key] > $sig_limits[6]) + { + $height = $sig_limits[6]; + if ($width != -1) + $width = $width * ($height / $matches[4][$key]); + } + elseif ($matches[4][$key]) + $height = $matches[4][$key]; + + // If the dimensions are still not fixed - we need to check the actual image. + if (($width == -1 && $sig_limits[5]) || ($height == -1 && $sig_limits[6])) + { + $sizes = url_image_size($matches[7][$key]); + if (is_array($sizes)) + { + // Too wide? + if ($sizes[0] > $sig_limits[5] && $sig_limits[5]) + { + $width = $sig_limits[5]; + $sizes[1] = $sizes[1] * ($width / $sizes[0]); + } + // Too high? + if ($sizes[1] > $sig_limits[6] && $sig_limits[6]) + { + $height = $sig_limits[6]; + if ($width == -1) + $width = $sizes[0]; + $width = $width * ($height / $sizes[1]); + } + elseif ($width != -1) + $height = $sizes[1]; + } + } + + // Did we come up with some changes? If so remake the string. + if ($width != -1 || $height != -1) + { + $replaces[$image] = '[img' . ($width != -1 ? ' width=' . round($width) : '') . ($height != -1 ? ' height=' . round($height) : '') . ']' . $matches[7][$key] . '[/img]'; + } + + // Record that we got one. + $image_count_holder[$image] = isset($image_count_holder[$image]) ? $image_count_holder[$image] + 1 : 1; + } + if (!empty($replaces)) + $sig = str_replace(array_keys($replaces), array_values($replaces), $sig); + } + } + // Try to fix disabled tags. + if (!empty($disabledTags)) + { + $sig = preg_replace('~\[(?:' . implode('|', $disabledTags) . ').+?\]~i', '', $sig); + $sig = preg_replace('~\[/(?:' . implode('|', $disabledTags) . ')\]~i', '', $sig); + } + + $sig = strtr($sig, array("\n" => '
')); + if ($sig != $row['signature']) + $changes[$row['id_member']] = $sig; + } + if ($smcFunc['db_num_rows']($request) == 0) + $done = true; + $smcFunc['db_free_result']($request); + + // Do we need to delete what we have? + if (!empty($changes)) + { + foreach ($changes as $id => $sig) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET signature = {string:signature} + WHERE id_member = {int:id_member}', + array( + 'id_member' => $id, + 'signature' => $sig, + ) + ); + } + + $_GET['step'] += 50; + if (!$done) + pauseSignatureApplySettings(); + } + } + + $context['signature_settings'] = array( + 'enable' => isset($sig_limits[0]) ? $sig_limits[0] : 0, + 'max_length' => isset($sig_limits[1]) ? $sig_limits[1] : 0, + 'max_lines' => isset($sig_limits[2]) ? $sig_limits[2] : 0, + 'max_images' => isset($sig_limits[3]) ? $sig_limits[3] : 0, + 'allow_smileys' => isset($sig_limits[4]) && $sig_limits[4] == -1 ? 0 : 1, + 'max_smileys' => isset($sig_limits[4]) && $sig_limits[4] != -1 ? $sig_limits[4] : 0, + 'max_image_width' => isset($sig_limits[5]) ? $sig_limits[5] : 0, + 'max_image_height' => isset($sig_limits[6]) ? $sig_limits[6] : 0, + 'max_font_size' => isset($sig_limits[7]) ? $sig_limits[7] : 0, + ); + + // Temporarily make each setting a modSetting! + foreach ($context['signature_settings'] as $key => $value) + $modSettings['signature_' . $key] = $value; + + // Make sure we check the right tags! + $modSettings['bbc_disabled_signature_bbc'] = $disabledTags; + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + // Clean up the tag stuff! + $bbcTags = array(); + foreach (parse_bbc(false) as $tag) + $bbcTags[] = $tag['tag']; + + if (!isset($_POST['signature_bbc_enabledTags'])) + $_POST['signature_bbc_enabledTags'] = array(); + elseif (!is_array($_POST['signature_bbc_enabledTags'])) + $_POST['signature_bbc_enabledTags'] = array($_POST['signature_bbc_enabledTags']); + + $sig_limits = array(); + foreach ($context['signature_settings'] as $key => $value) + { + if ($key == 'allow_smileys') + continue; + elseif ($key == 'max_smileys' && empty($_POST['signature_allow_smileys'])) + $sig_limits[] = -1; + else + $sig_limits[] = !empty($_POST['signature_' . $key]) ? max(1, (int) $_POST['signature_' . $key]) : 0; + } + + $_POST['signature_settings'] = implode(',', $sig_limits) . ':' . implode(',', array_diff($bbcTags, $_POST['signature_bbc_enabledTags'])); + + // Even though we have practically no settings let's keep the convention going! + $save_vars = array(); + $save_vars[] = array('text', 'signature_settings'); + + saveDBSettings($save_vars); + redirectexit('action=admin;area=featuresettings;sa=sig'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=sig'; + $context['settings_title'] = $txt['signature_settings']; + + $context['settings_message'] = '

' . sprintf($txt['signature_settings_warning'], $context['session_id'], $context['session_var']) . '

'; + + prepareDBSettingContext($config_vars); +} + +// Just pause the signature applying thing. +function pauseSignatureApplySettings() +{ + global $context, $txt, $sig_start; + + // Try get more time... + @set_time_limit(600); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + // Have we exhausted all the time we allowed? + if (time() - array_sum(explode(' ', $sig_start)) < 3) + return; + + $context['continue_get_data'] = '?action=admin;area=featuresettings;sa=sig;apply;step=' . $_GET['step'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['page_title'] = $txt['not_done_title']; + $context['continue_post_data'] = ''; + $context['continue_countdown'] = '2'; + $context['sub_template'] = 'not_done'; + + // Specific stuff to not break this template! + $context[$context['admin_menu_name']]['current_subsection'] = 'sig'; + + // Get the right percent. + $context['continue_percent'] = round(($_GET['step'] / $context['max_member']) * 100); + + // Never more than 100%! + $context['continue_percent'] = min($context['continue_percent'], 100); + + obExit(); +} + +// Show all the custom profile fields available to the user. +function ShowCustomProfiles() +{ + global $txt, $scripturl, $context, $settings, $sc, $smcFunc; + global $modSettings, $sourcedir; + + $context['page_title'] = $txt['custom_profile_title']; + $context['sub_template'] = 'show_custom_profile'; + + // What about standard fields they can tweak? + $standard_fields = array('icq', 'msn', 'aim', 'yim', 'location', 'gender', 'website', 'posts', 'warning_status'); + // What fields can't you put on the registration page? + $context['fields_no_registration'] = array('posts', 'warning_status'); + + // Are we saving any standard field changes? + if (isset($_POST['save'])) + { + checkSession(); + + // Do the active ones first. + $disable_fields = array_flip($standard_fields); + if (!empty($_POST['active'])) + { + foreach ($_POST['active'] as $value) + if (isset($disable_fields[$value])) + unset($disable_fields[$value]); + } + // What we have left! + $changes['disabled_profile_fields'] = empty($disable_fields) ? '' : implode(',', array_keys($disable_fields)); + + // Things we want to show on registration? + $reg_fields = array(); + if (!empty($_POST['reg'])) + { + foreach ($_POST['reg'] as $value) + if (in_array($value, $standard_fields) && !isset($disable_fields[$value])) + $reg_fields[] = $value; + } + // What we have left! + $changes['registration_fields'] = empty($reg_fields) ? '' : implode(',', $reg_fields); + + if (!empty($changes)) + updateSettings($changes); + } + + require_once($sourcedir . '/Subs-List.php'); + + $listOptions = array( + 'id' => 'standard_profile_fields', + 'title' => $txt['standard_profile_title'], + 'base_href' => $scripturl . '?action=admin;area=featuresettings;sa=profile', + 'get_items' => array( + 'function' => 'list_getProfileFields', + 'params' => array( + true, + ), + ), + 'columns' => array( + 'field' => array( + 'header' => array( + 'value' => $txt['standard_profile_field'], + 'style' => 'text-align: left;', + ), + 'data' => array( + 'db' => 'label', + 'style' => 'width: 60%;', + ), + ), + 'active' => array( + 'header' => array( + 'value' => $txt['custom_edit_active'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + $isChecked = $rowData[\'disabled\'] ? \'\' : \' checked="checked"\'; + $onClickHandler = $rowData[\'can_show_register\'] ? sprintf(\'onclick="document.getElementById(\\\'reg_%1$s\\\').disabled = !this.checked;"\', $rowData[\'id\']) : \'\'; + return sprintf(\'\', $rowData[\'id\'], $isChecked, $onClickHandler); + '), + 'style' => 'width: 20%; text-align: center;', + ), + ), + 'show_on_registration' => array( + 'header' => array( + 'value' => $txt['custom_edit_registration'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + $isChecked = $rowData[\'on_register\'] && !$rowData[\'disabled\'] ? \' checked="checked"\' : \'\'; + $isDisabled = $rowData[\'can_show_register\'] ? \'\' : \' disabled="disabled"\'; + return sprintf(\'\', $rowData[\'id\'], $isChecked, $isDisabled); + '), + 'style' => 'width: 20%; text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=featuresettings;sa=profile', + 'name' => 'standardProfileFields', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '', + 'style' => 'text-align: right;', + ), + ), + ); + createList($listOptions); + + $listOptions = array( + 'id' => 'custom_profile_fields', + 'title' => $txt['custom_profile_title'], + 'base_href' => $scripturl . '?action=admin;area=featuresettings;sa=profile', + 'default_sort_col' => 'field_name', + 'no_items_label' => $txt['custom_profile_none'], + 'items_per_page' => 25, + 'get_items' => array( + 'function' => 'list_getProfileFields', + 'params' => array( + false, + ), + ), + 'get_count' => array( + 'function' => 'list_getProfileFieldSize', + ), + 'columns' => array( + 'field_name' => array( + 'header' => array( + 'value' => $txt['custom_profile_fieldname'], + 'style' => 'text-align: left;', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $scripturl; + + return sprintf(\'%3$s
%4$s
\', $scripturl, $rowData[\'id_field\'], $rowData[\'field_name\'], $rowData[\'field_desc\']); + '), + 'style' => 'width: 62%;', + ), + 'sort' => array( + 'default' => 'field_name', + 'reverse' => 'field_name DESC', + ), + ), + 'field_type' => array( + 'header' => array( + 'value' => $txt['custom_profile_fieldtype'], + 'style' => 'text-align: left;', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + $textKey = sprintf(\'custom_profile_type_%1$s\', $rowData[\'field_type\']); + return isset($txt[$textKey]) ? $txt[$textKey] : $textKey; + '), + 'style' => 'width: 15%;', + ), + 'sort' => array( + 'default' => 'field_type', + 'reverse' => 'field_type DESC', + ), + ), + 'active' => array( + 'header' => array( + 'value' => $txt['custom_profile_active'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + return $rowData[\'active\'] ? $txt[\'yes\'] : $txt[\'no\']; + '), + 'style' => 'width: 8%; text-align: center;', + ), + 'sort' => array( + 'default' => 'active DESC', + 'reverse' => 'active', + ), + ), + 'placement' => array( + 'header' => array( + 'value' => $txt['custom_profile_placement'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + return $txt[\'custom_profile_placement_\' . (empty($rowData[\'placement\']) ? \'standard\' : ($rowData[\'placement\'] == 1 ? \'withicons\' : \'abovesignature\'))]; + '), + 'style' => 'width: 8%; text-align: center;', + ), + 'sort' => array( + 'default' => 'placement DESC', + 'reverse' => 'placement', + ), + ), + 'show_on_registration' => array( + 'header' => array( + 'value' => $txt['modify'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '' . $txt['modify'] . '', + 'params' => array( + 'id_field' => false, + ), + ), + 'style' => 'width: 15%; text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=featuresettings;sa=profileedit', + 'name' => 'customProfileFields', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '', + 'style' => 'text-align: right;', + ), + ), + ); + createList($listOptions); +} + +function list_getProfileFields($start, $items_per_page, $sort, $standardFields) +{ + global $txt, $modSettings, $smcFunc; + + $list = array(); + + if ($standardFields) + { + $standard_fields = array('icq', 'msn', 'aim', 'yim', 'location', 'gender', 'website', 'posts', 'warning_status'); + $fields_no_registration = array('posts', 'warning_status'); + $disabled_fields = isset($modSettings['disabled_profile_fields']) ? explode(',', $modSettings['disabled_profile_fields']) : array(); + $registration_fields = isset($modSettings['registration_fields']) ? explode(',', $modSettings['registration_fields']) : array(); + + foreach ($standard_fields as $field) + $list[] = array( + 'id' => $field, + 'label' => isset($txt['standard_profile_field_' . $field]) ? $txt['standard_profile_field_' . $field] : (isset($txt[$field]) ? $txt[$field] : $field), + 'disabled' => in_array($field, $disabled_fields), + 'on_register' => in_array($field, $registration_fields) && !in_array($field, $fields_no_registration), + 'can_show_register' => !in_array($field, $fields_no_registration), + ); + } + else + { + // Load all the fields. + $request = $smcFunc['db_query']('', ' + SELECT id_field, col_name, field_name, field_desc, field_type, active, placement + FROM {db_prefix}custom_fields + ORDER BY {raw:sort} + LIMIT {int:start}, {int:items_per_page}', + array( + 'sort' => $sort, + 'start' => $start, + 'items_per_page' => $items_per_page, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $list[] = $row; + $smcFunc['db_free_result']($request); + } + + return $list; +} + +function list_getProfileFieldSize() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}custom_fields', + array( + ) + ); + + list ($numProfileFields) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $numProfileFields; +} + +// Edit some profile fields? +function EditCustomProfiles() +{ + global $txt, $scripturl, $context, $settings, $sc, $smcFunc; + + // Sort out the context! + $context['fid'] = isset($_GET['fid']) ? (int) $_GET['fid'] : 0; + $context[$context['admin_menu_name']]['current_subsection'] = 'profile'; + $context['page_title'] = $context['fid'] ? $txt['custom_edit_title'] : $txt['custom_add_title']; + $context['sub_template'] = 'edit_profile_field'; + + // Load the profile language for section names. + loadLanguage('Profile'); + + if ($context['fid']) + { + $request = $smcFunc['db_query']('', ' + SELECT + id_field, col_name, field_name, field_desc, field_type, field_length, field_options, + show_reg, show_display, show_profile, private, active, default_value, can_search, + bbc, mask, enclose, placement + FROM {db_prefix}custom_fields + WHERE id_field = {int:current_field}', + array( + 'current_field' => $context['fid'], + ) + ); + $context['field'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['field_type'] == 'textarea') + @list ($rows, $cols) = @explode(',', $row['default_value']); + else + { + $rows = 3; + $cols = 30; + } + + $context['field'] = array( + 'name' => $row['field_name'], + 'desc' => $row['field_desc'], + 'colname' => $row['col_name'], + 'profile_area' => $row['show_profile'], + 'reg' => $row['show_reg'], + 'display' => $row['show_display'], + 'type' => $row['field_type'], + 'max_length' => $row['field_length'], + 'rows' => $rows, + 'cols' => $cols, + 'bbc' => $row['bbc'] ? true : false, + 'default_check' => $row['field_type'] == 'check' && $row['default_value'] ? true : false, + 'default_select' => $row['field_type'] == 'select' || $row['field_type'] == 'radio' ? $row['default_value'] : '', + 'options' => strlen($row['field_options']) > 1 ? explode(',', $row['field_options']) : array('', '', ''), + 'active' => $row['active'], + 'private' => $row['private'], + 'can_search' => $row['can_search'], + 'mask' => $row['mask'], + 'regex' => substr($row['mask'], 0, 5) == 'regex' ? substr($row['mask'], 5) : '', + 'enclose' => $row['enclose'], + 'placement' => $row['placement'], + ); + } + $smcFunc['db_free_result']($request); + } + + // Setup the default values as needed. + if (empty($context['field'])) + $context['field'] = array( + 'name' => '', + 'colname' => '???', + 'desc' => '', + 'profile_area' => 'forumprofile', + 'reg' => false, + 'display' => false, + 'type' => 'text', + 'max_length' => 255, + 'rows' => 4, + 'cols' => 30, + 'bbc' => false, + 'default_check' => false, + 'default_select' => '', + 'options' => array('', '', ''), + 'active' => true, + 'private' => false, + 'can_search' => false, + 'mask' => 'nohtml', + 'regex' => '', + 'enclose' => '', + 'placement' => 0, + ); + + // Are we saving? + if (isset($_POST['save'])) + { + checkSession(); + + // Everyone needs a name - even the (bracket) unknown... + if (trim($_POST['field_name']) == '') + fatal_lang_error('custom_option_need_name'); + $_POST['field_name'] = $smcFunc['htmlspecialchars']($_POST['field_name']); + $_POST['field_desc'] = $smcFunc['htmlspecialchars']($_POST['field_desc']); + + // Checkboxes... + $show_reg = isset($_POST['reg']) ? (int) $_POST['reg'] : 0; + $show_display = isset($_POST['display']) ? 1 : 0; + $bbc = isset($_POST['bbc']) ? 1 : 0; + $show_profile = $_POST['profile_area']; + $active = isset($_POST['active']) ? 1 : 0; + $private = isset($_POST['private']) ? (int) $_POST['private'] : 0; + $can_search = isset($_POST['can_search']) ? 1 : 0; + + // Some masking stuff... + $mask = isset($_POST['mask']) ? $_POST['mask'] : ''; + if ($mask == 'regex' && isset($_POST['regex'])) + $mask .= $_POST['regex']; + + $field_length = isset($_POST['max_length']) ? (int) $_POST['max_length'] : 255; + $enclose = isset($_POST['enclose']) ? $_POST['enclose'] : ''; + $placement = isset($_POST['placement']) ? (int) $_POST['placement'] : 0; + + // Select options? + $field_options = ''; + $newOptions = array(); + $default = isset($_POST['default_check']) && $_POST['field_type'] == 'check' ? 1 : ''; + if (!empty($_POST['select_option']) && ($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio')) + { + foreach ($_POST['select_option'] as $k => $v) + { + // Clean, clean, clean... + $v = $smcFunc['htmlspecialchars']($v); + $v = strtr($v, array(',' => '')); + + // Nada, zip, etc... + if (trim($v) == '') + continue; + + // Otherwise, save it boy. + $field_options .= $v . ','; + // This is just for working out what happened with old options... + $newOptions[$k] = $v; + + // Is it default? + if (isset($_POST['default_select']) && $_POST['default_select'] == $k) + $default = $v; + } + $field_options = substr($field_options, 0, -1); + } + + // Text area has default has dimensions + if ($_POST['field_type'] == 'textarea') + $default = (int) $_POST['rows'] . ',' . (int) $_POST['cols']; + + // Come up with the unique name? + if (empty($context['fid'])) + { + $colname = $smcFunc['substr'](strtr($_POST['field_name'], array(' ' => '')), 0, 6); + preg_match('~([\w\d_-]+)~', $colname, $matches); + + // If there is nothing to the name, then let's start out own - for foreign languages etc. + if (isset($matches[1])) + $colname = $initial_colname = 'cust_' . strtolower($matches[1]); + else + $colname = $initial_colname = 'cust_' . mt_rand(1, 999); + + // Make sure this is unique. + // !!! This may not be the most efficient way to do this. + $unique = false; + for ($i = 0; !$unique && $i < 9; $i ++) + { + $request = $smcFunc['db_query']('', ' + SELECT id_field + FROM {db_prefix}custom_fields + WHERE col_name = {string:current_column}', + array( + 'current_column' => $colname, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + $unique = true; + else + $colname = $initial_colname . $i; + $smcFunc['db_free_result']($request); + } + + // Still not a unique colum name? Leave it up to the user, then. + if (!$unique) + fatal_lang_error('custom_option_not_unique'); + } + // Work out what to do with the user data otherwise... + else + { + // Anything going to check or select is pointless keeping - as is anything coming from check! + if (($_POST['field_type'] == 'check' && $context['field']['type'] != 'check') + || (($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') && $context['field']['type'] != 'select' && $context['field']['type'] != 'radio') + || ($context['field']['type'] == 'check' && $_POST['field_type'] != 'check')) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE variable = {string:current_column} + AND id_member > {int:no_member}', + array( + 'no_member' => 0, + 'current_column' => $context['field']['colname'], + ) + ); + } + // Otherwise - if the select is edited may need to adjust! + elseif ($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') + { + $optionChanges = array(); + $takenKeys = array(); + // Work out what's changed! + foreach ($context['field']['options'] as $k => $option) + { + if (trim($option) == '') + continue; + + // Still exists? + if (in_array($option, $newOptions)) + { + $takenKeys[] = $k; + continue; + } + } + + // Finally - have we renamed it - or is it really gone? + foreach ($optionChanges as $k => $option) + { + // Just been renamed? + if (!in_array($k, $takenKeys) && !empty($newOptions[$k])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}themes + SET value = {string:new_value} + WHERE variable = {string:current_column} + AND value = {string:old_value} + AND id_member > {int:no_member}', + array( + 'no_member' => 0, + 'new_value' => $newOptions[$k], + 'current_column' => $context['field']['colname'], + 'old_value' => $option, + ) + ); + } + } + //!!! Maybe we should adjust based on new text length limits? + } + + // Do the insertion/updates. + if ($context['fid']) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}custom_fields + SET + field_name = {string:field_name}, field_desc = {string:field_desc}, + field_type = {string:field_type}, field_length = {int:field_length}, + field_options = {string:field_options}, show_reg = {int:show_reg}, + show_display = {int:show_display}, show_profile = {string:show_profile}, + private = {int:private}, active = {int:active}, default_value = {string:default_value}, + can_search = {int:can_search}, bbc = {int:bbc}, mask = {string:mask}, + enclose = {string:enclose}, placement = {int:placement} + WHERE id_field = {int:current_field}', + array( + 'field_length' => $field_length, + 'show_reg' => $show_reg, + 'show_display' => $show_display, + 'private' => $private, + 'active' => $active, + 'can_search' => $can_search, + 'bbc' => $bbc, + 'current_field' => $context['fid'], + 'field_name' => $_POST['field_name'], + 'field_desc' => $_POST['field_desc'], + 'field_type' => $_POST['field_type'], + 'field_options' => $field_options, + 'show_profile' => $show_profile, + 'default_value' => $default, + 'mask' => $mask, + 'enclose' => $enclose, + 'placement' => $placement, + ) + ); + + // Just clean up any old selects - these are a pain! + if (($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') && !empty($newOptions)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE variable = {string:current_column} + AND value NOT IN ({array_string:new_option_values}) + AND id_member > {int:no_member}', + array( + 'no_member' => 0, + 'new_option_values' => $newOptions, + 'current_column' => $context['field']['colname'], + ) + ); + } + else + { + $smcFunc['db_insert']('', + '{db_prefix}custom_fields', + array( + 'col_name' => 'string', 'field_name' => 'string', 'field_desc' => 'string', + 'field_type' => 'string', 'field_length' => 'string', 'field_options' => 'string', + 'show_reg' => 'int', 'show_display' => 'int', 'show_profile' => 'string', + 'private' => 'int', 'active' => 'int', 'default_value' => 'string', 'can_search' => 'int', + 'bbc' => 'int', 'mask' => 'string', 'enclose' => 'string', 'placement' => 'int', + ), + array( + $colname, $_POST['field_name'], $_POST['field_desc'], + $_POST['field_type'], $field_length, $field_options, + $show_reg, $show_display, $show_profile, + $private, $active, $default, $can_search, + $bbc, $mask, $enclose, $placement, + ), + array('id_field') + ); + } + + // As there's currently no option to priorize certain fields over others, let's order them alphabetically. + $smcFunc['db_query']('alter_table_boards', ' + ALTER TABLE {db_prefix}custom_fields + ORDER BY field_name', + array( + 'db_error_skip' => true, + ) + ); + } + // Deleting? + elseif (isset($_POST['delete']) && $context['field']['colname']) + { + checkSession(); + + // Delete the user data first. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE variable = {string:current_column} + AND id_member > {int:no_member}', + array( + 'no_member' => 0, + 'current_column' => $context['field']['colname'], + ) + ); + // Finally - the field itself is gone! + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}custom_fields + WHERE id_field = {int:current_field}', + array( + 'current_field' => $context['fid'], + ) + ); + } + + // Rebuild display cache etc. + if (isset($_POST['delete']) || isset($_POST['save'])) + { + checkSession(); + + $request = $smcFunc['db_query']('', ' + SELECT col_name, field_name, field_type, bbc, enclose, placement + FROM {db_prefix}custom_fields + WHERE show_display = {int:is_displayed} + AND active = {int:active} + AND private != {int:not_owner_only} + AND private != {int:not_admin_only}', + array( + 'is_displayed' => 1, + 'active' => 1, + 'not_owner_only' => 2, + 'not_admin_only' => 3, + ) + ); + + $fields = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $fields[] = array( + 'colname' => strtr($row['col_name'], array('|' => '', ';' => '')), + 'title' => strtr($row['field_name'], array('|' => '', ';' => '')), + 'type' => $row['field_type'], + 'bbc' => $row['bbc'] ? '1' : '0', + 'placement' => !empty($row['placement']) ? $row['placement'] : '0', + 'enclose' => !empty($row['enclose']) ? $row['enclose'] : '', + ); + } + $smcFunc['db_free_result']($request); + + updateSettings(array('displayFields' => serialize($fields))); + redirectexit('action=admin;area=featuresettings;sa=profile'); + } +} + +function ModifyPruningSettings($return_config = false) +{ + global $txt, $scripturl, $sourcedir, $context, $settings, $sc, $modSettings; + + // Make sure we understand what's going on. + loadLanguage('ManageSettings'); + + $context['page_title'] = $txt['pruning_title']; + + $config_vars = array( + // Even do the pruning? + // The array indexes are there so we can remove/change them before saving. + 'pruningOptions' => array('check', 'pruningOptions'), + '', + // Various logs that could be pruned. + array('int', 'pruneErrorLog', 'postinput' => $txt['days_word']), // Error log. + array('int', 'pruneModLog', 'postinput' => $txt['days_word']), // Moderation log. + array('int', 'pruneBanLog', 'postinput' => $txt['days_word']), // Ban hit log. + array('int', 'pruneReportLog', 'postinput' => $txt['days_word']), // Report to moderator log. + array('int', 'pruneScheduledTaskLog', 'postinput' => $txt['days_word']), // Log of the scheduled tasks and how long they ran. + array('int', 'pruneSpiderHitLog', 'postinput' => $txt['days_word']), // Log of the scheduled tasks and how long they ran. + // If you add any additional logs make sure to add them after this point. Additionally, make sure you add them to the weekly scheduled task. + // Mod Developers: Do NOT use the pruningOptions master variable for this as SMF Core may overwrite your setting in the future! + ); + + if ($return_config) + return $config_vars; + + // We'll need this in a bit. + require_once($sourcedir . '/ManageServer.php'); + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + $savevar = array( + array('text', 'pruningOptions') + ); + + if (!empty($_POST['pruningOptions'])) + { + $vals = array(); + foreach ($config_vars as $index => $dummy) + { + if (!is_array($dummy) || $index == 'pruningOptions') + continue; + + $vals[] = empty($_POST[$dummy[1]]) || $_POST[$dummy[1]] < 0 ? 0 : (int) $_POST[$dummy[1]]; + } + $_POST['pruningOptions'] = implode(',', $vals); + } + else + $_POST['pruningOptions'] = ''; + + saveDBSettings($savevar); + redirectexit('action=admin;area=logs;sa=pruning'); + } + + $context['post_url'] = $scripturl . '?action=admin;area=logs;save;sa=pruning'; + $context['settings_title'] = $txt['pruning_title']; + $context['sub_template'] = 'show_settings'; + + // Get the actual values + if (!empty($modSettings['pruningOptions'])) + @list ($modSettings['pruneErrorLog'], $modSettings['pruneModLog'], $modSettings['pruneBanLog'], $modSettings['pruneReportLog'], $modSettings['pruneScheduledTaskLog'], $modSettings['pruneSpiderHitLog']) = explode(',', $modSettings['pruningOptions']); + else + $modSettings['pruneErrorLog'] = $modSettings['pruneModLog'] = $modSettings['pruneBanLog'] = $modSettings['pruneReportLog'] = $modSettings['pruneScheduledTaskLog'] = $modSettings['pruneSpiderHitLog'] = 0; + + prepareDBSettingContext($config_vars); +} + +// If you have a general mod setting to add stick it here. +function ModifyGeneralModSettings($return_config = false) +{ + global $txt, $scripturl, $context, $settings, $sc, $modSettings; + + $config_vars = array( + // Mod authors, add any settings UNDER this line. Include a comma at the end of the line and don't remove this statement!! + ); + + // Make it even easier to add new settings. + call_integration_hook('integrate_general_mod_settings', array(&$config_vars)); + + if ($return_config) + return $config_vars; + + $context['post_url'] = $scripturl . '?action=admin;area=modsettings;save;sa=general'; + $context['settings_title'] = $txt['mods_cat_modifications_misc']; + + // No removing this line you, dirty unwashed mod authors. :p + if (empty($config_vars)) + { + $context['settings_save_dont_show'] = true; + $context['settings_message'] = '
' . $txt['modification_no_misc_settings'] . '
'; + + return prepareDBSettingContext($config_vars); + } + + // Saving? + if (isset($_GET['save'])) + { + checkSession(); + + $save_vars = $config_vars; + + // This line is to help mod authors do a search/add after if you want to add something here. Keyword: FOOT TAPPING SUCKS! + saveDBSettings($save_vars); + + // This line is to help mod authors do a search/add after if you want to add something here. Keyword: I LOVE TEA! + redirectexit('action=admin;area=modsettings;sa=general'); + } + + // This line is to help mod authors do a search/add after if you want to add something here. Keyword: RED INK IS FOR TEACHERS AND THOSE WHO LIKE PAIN! + prepareDBSettingContext($config_vars); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ManageSmileys.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ManageSmileys.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1776 @@ + 'AddSmiley', + 'editicon' => 'EditMessageIcons', + 'editicons' => 'EditMessageIcons', + 'editsets' => 'EditSmileySets', + 'editsmileys' => 'EditSmileys', + 'import' => 'EditSmileySets', + 'modifyset' => 'EditSmileySets', + 'modifysmiley' => 'EditSmileys', + 'setorder' => 'EditSmileyOrder', + 'settings' => 'EditSmileySettings', + 'install' => 'InstallSmileySet' + ); + + // Default the sub-action to 'edit smiley settings'. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'editsets'; + + $context['page_title'] = $txt['smileys_manage']; + $context['sub_action'] = $_REQUEST['sa']; + $context['sub_template'] = $context['sub_action']; + + // Load up all the tabs... + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['smileys_manage'], + 'help' => 'smileys', + 'description' => $txt['smiley_settings_explain'], + 'tabs' => array( + 'editsets' => array( + 'description' => $txt['smiley_editsets_explain'], + ), + 'addsmiley' => array( + 'description' => $txt['smiley_addsmiley_explain'], + ), + 'editsmileys' => array( + 'description' => $txt['smiley_editsmileys_explain'], + ), + 'setorder' => array( + 'description' => $txt['smiley_setorder_explain'], + ), + 'editicons' => array( + 'description' => $txt['icons_edit_icons_explain'], + ), + 'settings' => array( + 'description' => $txt['smiley_settings_explain'], + ), + ), + ); + + // Some settings may not be enabled, disallow these from the tabs as appropriate. + if (empty($modSettings['messageIcons_enable'])) + $context[$context['admin_menu_name']]['tab_data']['tabs']['editicons']['disabled'] = true; + if (empty($modSettings['smiley_enable'])) + { + $context[$context['admin_menu_name']]['tab_data']['tabs']['addsmiley']['disabled'] = true; + $context[$context['admin_menu_name']]['tab_data']['tabs']['editsmileys']['disabled'] = true; + $context[$context['admin_menu_name']]['tab_data']['tabs']['setorder']['disabled'] = true; + } + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']](); +} + +function EditSmileySettings($return_config = false) +{ + global $modSettings, $context, $settings, $txt, $boarddir, $sourcedir, $scripturl; + + // The directories... + $context['smileys_dir'] = empty($modSettings['smileys_dir']) ? $boarddir . '/Smileys' : $modSettings['smileys_dir']; + $context['smileys_dir_found'] = is_dir($context['smileys_dir']); + + // Get the names of the smiley sets. + $smiley_sets = explode(',', $modSettings['smiley_sets_known']); + $set_names = explode("\n", $modSettings['smiley_sets_names']); + + $smiley_context = array(); + foreach ($smiley_sets as $i => $set) + $smiley_context[$set] = $set_names[$i]; + + // All the settings for the page... + $config_vars = array( + array('title', 'settings'), + // Inline permissions. + array('permissions', 'manage_smileys'), + '', + array('select', 'smiley_sets_default', $smiley_context), + array('check', 'smiley_sets_enable'), + array('check', 'smiley_enable', 'subtext' => $txt['smileys_enable_note']), + array('text', 'smileys_url'), + array('text', 'smileys_dir', 'invalid' => !$context['smileys_dir_found']), + '', + // Message icons. + array('check', 'messageIcons_enable', 'subtext' => $txt['setting_messageIcons_enable_note']), + ); + + if ($return_config) + return $config_vars; + + // Setup the basics of the settings template. + require_once($sourcedir . '/ManageServer.php'); + $context['sub_template'] = 'show_settings'; + + // Finish up the form... + $context['post_url'] = $scripturl . '?action=admin;area=smileys;save;sa=settings'; + $context['permissions_excluded'] = array(-1); + + // Saving the settings? + if (isset($_GET['save'])) + { + checkSession(); + + // Validate the smiley set name. + $_POST['smiley_sets_default'] = empty($smiley_context[$_POST['smiley_sets_default']]) ? 'default' : $_POST['smiley_sets_default']; + + // Make sure that the smileys are in the right order after enabling them. + if (isset($_POST['smiley_enable'])) + sortSmileyTable(); + + saveDBSettings($config_vars); + + cache_put_data('parsing_smileys', null, 480); + cache_put_data('posting_smileys', null, 480); + + redirectexit('action=admin;area=smileys;sa=settings'); + } + + prepareDBSettingContext($config_vars); +} + +function EditSmileySets() +{ + global $modSettings, $context, $settings, $txt, $boarddir; + global $smcFunc, $scripturl, $sourcedir; + + // Set the right tab to be selected. + $context[$context['admin_menu_name']]['current_subsection'] = 'editsets'; + + // They must've been submitted a form. + if (isset($_POST[$context['session_var']])) + { + checkSession(); + + // Delete selected smiley sets. + if (!empty($_POST['delete']) && !empty($_POST['smiley_set'])) + { + $set_paths = explode(',', $modSettings['smiley_sets_known']); + $set_names = explode("\n", $modSettings['smiley_sets_names']); + foreach ($_POST['smiley_set'] as $id => $val) + if (isset($set_paths[$id], $set_names[$id]) && !empty($id)) + unset($set_paths[$id], $set_names[$id]); + + updateSettings(array( + 'smiley_sets_known' => implode(',', $set_paths), + 'smiley_sets_names' => implode("\n", $set_names), + 'smiley_sets_default' => in_array($modSettings['smiley_sets_default'], $set_paths) ? $modSettings['smiley_sets_default'] : $set_paths[0], + )); + + cache_put_data('parsing_smileys', null, 480); + cache_put_data('posting_smileys', null, 480); + } + // Add a new smiley set. + elseif (!empty($_POST['add'])) + $context['sub_action'] = 'modifyset'; + // Create or modify a smiley set. + elseif (isset($_POST['set'])) + { + $set_paths = explode(',', $modSettings['smiley_sets_known']); + $set_names = explode("\n", $modSettings['smiley_sets_names']); + + // Create a new smiley set. + if ($_POST['set'] == -1 && isset($_POST['smiley_sets_path'])) + { + if (in_array($_POST['smiley_sets_path'], $set_paths)) + fatal_lang_error('smiley_set_already_exists'); + + updateSettings(array( + 'smiley_sets_known' => $modSettings['smiley_sets_known'] . ',' . $_POST['smiley_sets_path'], + 'smiley_sets_names' => $modSettings['smiley_sets_names'] . "\n" . $_POST['smiley_sets_name'], + 'smiley_sets_default' => empty($_POST['smiley_sets_default']) ? $modSettings['smiley_sets_default'] : $_POST['smiley_sets_path'], + )); + } + // Modify an existing smiley set. + else + { + // Make sure the smiley set exists. + if (!isset($set_paths[$_POST['set']]) || !isset($set_names[$_POST['set']])) + fatal_lang_error('smiley_set_not_found'); + + // Make sure the path is not yet used by another smileyset. + if (in_array($_POST['smiley_sets_path'], $set_paths) && $_POST['smiley_sets_path'] != $set_paths[$_POST['set']]) + fatal_lang_error('smiley_set_path_already_used'); + + $set_paths[$_POST['set']] = $_POST['smiley_sets_path']; + $set_names[$_POST['set']] = $_POST['smiley_sets_name']; + updateSettings(array( + 'smiley_sets_known' => implode(',', $set_paths), + 'smiley_sets_names' => implode("\n", $set_names), + 'smiley_sets_default' => empty($_POST['smiley_sets_default']) ? $modSettings['smiley_sets_default'] : $_POST['smiley_sets_path'] + )); + } + + // The user might have checked to also import smileys. + if (!empty($_POST['smiley_sets_import'])) + ImportSmileys($_POST['smiley_sets_path']); + + cache_put_data('parsing_smileys', null, 480); + cache_put_data('posting_smileys', null, 480); + } + } + + // Load all available smileysets... + $context['smiley_sets'] = explode(',', $modSettings['smiley_sets_known']); + $set_names = explode("\n", $modSettings['smiley_sets_names']); + foreach ($context['smiley_sets'] as $i => $set) + $context['smiley_sets'][$i] = array( + 'id' => $i, + 'path' => htmlspecialchars($set), + 'name' => htmlspecialchars($set_names[$i]), + 'selected' => $set == $modSettings['smiley_sets_default'] + ); + + // Importing any smileys from an existing set? + if ($context['sub_action'] == 'import') + { + checkSession('get'); + $_GET['set'] = (int) $_GET['set']; + + // Sanity check - then import. + if (isset($context['smiley_sets'][$_GET['set']])) + ImportSmileys(un_htmlspecialchars($context['smiley_sets'][$_GET['set']]['path'])); + + // Force the process to continue. + $context['sub_action'] = 'modifyset'; + $context['sub_template'] = 'modifyset'; + } + // If we're modifying or adding a smileyset, some context info needs to be set. + if ($context['sub_action'] == 'modifyset') + { + $_GET['set'] = !isset($_GET['set']) ? -1 : (int) $_GET['set']; + if ($_GET['set'] == -1 || !isset($context['smiley_sets'][$_GET['set']])) + $context['current_set'] = array( + 'id' => '-1', + 'path' => '', + 'name' => '', + 'selected' => false, + 'is_new' => true, + ); + else + { + $context['current_set'] = &$context['smiley_sets'][$_GET['set']]; + $context['current_set']['is_new'] = false; + + // Calculate whether there are any smileys in the directory that can be imported. + if (!empty($modSettings['smiley_enable']) && !empty($modSettings['smileys_dir']) && is_dir($modSettings['smileys_dir'] . '/' . $context['current_set']['path'])) + { + $smileys = array(); + $dir = dir($modSettings['smileys_dir'] . '/' . $context['current_set']['path']); + while ($entry = $dir->read()) + { + if (in_array(strrchr($entry, '.'), array('.jpg', '.gif', '.jpeg', '.png'))) + $smileys[strtolower($entry)] = $entry; + } + $dir->close(); + + // Exclude the smileys that are already in the database. + $request = $smcFunc['db_query']('', ' + SELECT filename + FROM {db_prefix}smileys + WHERE filename IN ({array_string:smiley_list})', + array( + 'smiley_list' => $smileys, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + if (isset($smileys[strtolower($row['filename'])])) + unset($smileys[strtolower($row['filename'])]); + $smcFunc['db_free_result']($request); + + $context['current_set']['can_import'] = count($smileys); + // Setup this string to look nice. + $txt['smiley_set_import_multiple'] = sprintf($txt['smiley_set_import_multiple'], $context['current_set']['can_import']); + } + } + + // Retrieve all potential smiley set directories. + $context['smiley_set_dirs'] = array(); + if (!empty($modSettings['smileys_dir']) && is_dir($modSettings['smileys_dir'])) + { + $dir = dir($modSettings['smileys_dir']); + while ($entry = $dir->read()) + { + if (!in_array($entry, array('.', '..')) && is_dir($modSettings['smileys_dir'] . '/' . $entry)) + $context['smiley_set_dirs'][] = array( + 'id' => $entry, + 'path' => $modSettings['smileys_dir'] . '/' . $entry, + 'selectable' => $entry == $context['current_set']['path'] || !in_array($entry, explode(',', $modSettings['smiley_sets_known'])), + 'current' => $entry == $context['current_set']['path'], + ); + } + $dir->close(); + } + } + + $listOptions = array( + 'id' => 'smiley_set_list', + 'base_href' => $scripturl . '?action=admin;area=smileys;sa=editsets', + 'default_sort_col' => 'default', + 'get_items' => array( + 'function' => 'list_getSmileySets', + ), + 'get_count' => array( + 'function' => 'list_getNumSmileySets', + ), + 'columns' => array( + 'default' => array( + 'header' => array( + 'value' => $txt['smiley_sets_default'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return $rowData[\'selected\'] ? \'*\' : \'\'; + '), + 'style' => 'text-align: center;', + ), + 'sort' => array( + 'default' => 'selected DESC', + ), + ), + 'name' => array( + 'header' => array( + 'value' => $txt['smiley_sets_name'], + ), + 'data' => array( + 'db_htmlsafe' => 'name', + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'name', + 'reverse' => 'name DESC', + ), + ), + 'url' => array( + 'header' => array( + 'value' => $txt['smiley_sets_url'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => $modSettings['smileys_url'] . '/%1$s/...', + 'params' => array( + 'path' => true, + ), + ), + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'path', + 'reverse' => 'path DESC', + ), + ), + 'modify' => array( + 'header' => array( + 'value' => $txt['smiley_set_modify'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '' . $txt['smiley_set_modify'] . '', + 'params' => array( + 'id' => true, + ), + ), + 'style' => 'text-align: center;', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + return $rowData[\'id\'] == 0 ? \'\' : sprintf(\'\', $rowData[\'id\']); + '), + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=smileys;sa=editsets', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' [' . $txt['smiley_sets_add'] . ']', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); +} + +// !!! to be moved to Subs-Smileys. +function list_getSmileySets($start, $items_per_page, $sort) +{ + global $modSettings; + + $known_sets = explode(',', $modSettings['smiley_sets_known']); + $set_names = explode("\n", $modSettings['smiley_sets_names']); + $cols = array( + 'id' => array(), + 'selected' => array(), + 'path' => array(), + 'name' => array(), + ); + foreach ($known_sets as $i => $set) + { + $cols['id'][] = $i; + $cols['selected'][] = $i; + $cols['path'][] = $set; + $cols['name'][] = $set_names[$i]; + } + $sort_flag = strpos($sort, 'DESC') === false ? SORT_ASC : SORT_DESC; + if (substr($sort, 0, 4) === 'name') + array_multisort($cols['name'], $sort_flag, SORT_REGULAR, $cols['path'], $cols['selected'], $cols['id']); + elseif (substr($sort, 0, 4) === 'path') + array_multisort($cols['path'], $sort_flag, SORT_REGULAR, $cols['name'], $cols['selected'], $cols['id']); + else + array_multisort($cols['selected'], $sort_flag, SORT_REGULAR, $cols['path'], $cols['name'], $cols['id']); + + $smiley_sets = array(); + foreach ($cols['id'] as $i => $id) + $smiley_sets[] = array( + 'id' => $id, + 'path' => $cols['path'][$i], + 'name' => $cols['name'][$i], + 'selected' => $cols['path'][$i] == $modSettings['smiley_sets_default'] + ); + + return $smiley_sets; +} + +// !!! to be moved to Subs-Smileys. +function list_getNumSmileySets() +{ + global $modSettings; + + return count(explode(',', $modSettings['smiley_sets_known'])); +} + +function AddSmiley() +{ + global $modSettings, $context, $settings, $txt, $boarddir, $smcFunc; + + // Get a list of all known smiley sets. + $context['smileys_dir'] = empty($modSettings['smileys_dir']) ? $boarddir . '/Smileys' : $modSettings['smileys_dir']; + $context['smileys_dir_found'] = is_dir($context['smileys_dir']); + $context['smiley_sets'] = explode(',', $modSettings['smiley_sets_known']); + $set_names = explode("\n", $modSettings['smiley_sets_names']); + foreach ($context['smiley_sets'] as $i => $set) + $context['smiley_sets'][$i] = array( + 'id' => $i, + 'path' => htmlspecialchars($set), + 'name' => htmlspecialchars($set_names[$i]), + 'selected' => $set == $modSettings['smiley_sets_default'] + ); + + // Submitting a form? + if (isset($_POST[$context['session_var']], $_POST['smiley_code'])) + { + checkSession(); + + // Some useful arrays... types we allow - and ports we don't! + $allowedTypes = array('jpeg', 'jpg', 'gif', 'png', 'bmp'); + $disabledFiles = array('con', 'com1', 'com2', 'com3', 'com4', 'prn', 'aux', 'lpt1', '.htaccess', 'index.php'); + + $_POST['smiley_code'] = htmltrim__recursive($_POST['smiley_code']); + $_POST['smiley_location'] = empty($_POST['smiley_location']) || $_POST['smiley_location'] > 2 || $_POST['smiley_location'] < 0 ? 0 : (int) $_POST['smiley_location']; + $_POST['smiley_filename'] = htmltrim__recursive($_POST['smiley_filename']); + + // Make sure some code was entered. + if (empty($_POST['smiley_code'])) + fatal_lang_error('smiley_has_no_code'); + + // Check whether the new code has duplicates. It should be unique. + $request = $smcFunc['db_query']('', ' + SELECT id_smiley + FROM {db_prefix}smileys + WHERE code = {raw:mysql_binary_statement} {string:smiley_code}', + array( + 'mysql_binary_statement' => $smcFunc['db_title'] == 'MySQL' ? 'BINARY' : '', + 'smiley_code' => $_POST['smiley_code'], + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + fatal_lang_error('smiley_not_unique'); + $smcFunc['db_free_result']($request); + + // If we are uploading - check all the smiley sets are writable! + if ($_POST['method'] != 'existing') + { + $writeErrors = array(); + foreach ($context['smiley_sets'] as $set) + { + if (!is_writable($context['smileys_dir'] . '/' . un_htmlspecialchars($set['path']))) + $writeErrors[] = $set['path']; + } + if (!empty($writeErrors)) + fatal_lang_error('smileys_upload_error_notwritable', true, array(implode(', ', $writeErrors))); + } + + // Uploading just one smiley for all of them? + if (isset($_POST['sameall']) && isset($_FILES['uploadSmiley']['name']) && $_FILES['uploadSmiley']['name'] != '') + { + if (!is_uploaded_file($_FILES['uploadSmiley']['tmp_name']) || (@ini_get('open_basedir') == '' && !file_exists($_FILES['uploadSmiley']['tmp_name']))) + fatal_lang_error('smileys_upload_error'); + + // Sorry, no spaces, dots, or anything else but letters allowed. + $_FILES['uploadSmiley']['name'] = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $_FILES['uploadSmiley']['name']); + + // We only allow image files - it's THAT simple - no messing around here... + if (!in_array(strtolower(substr(strrchr($_FILES['uploadSmiley']['name'], '.'), 1)), $allowedTypes)) + fatal_lang_error('smileys_upload_error_types', false, array(implode(', ', $allowedTypes))); + + // We only need the filename... + $destName = basename($_FILES['uploadSmiley']['name']); + + // Make sure they aren't trying to upload a nasty file - for their own good here! + if (in_array(strtolower($destName), $disabledFiles)) + fatal_lang_error('smileys_upload_error_illegal'); + + // Check if the file already exists... and if not move it to EVERY smiley set directory. + $i = 0; + // Keep going until we find a set the file doesn't exist in. (or maybe it exists in all of them?) + while (isset($context['smiley_sets'][$i]) && file_exists($context['smileys_dir'] . '/' . un_htmlspecialchars($context['smiley_sets'][$i]['path']) . '/' . $destName)) + $i++; + + // Okay, we're going to put the smiley right here, since it's not there yet! + if (isset($context['smiley_sets'][$i]['path'])) + { + $smileyLocation = $context['smileys_dir'] . '/' . un_htmlspecialchars($context['smiley_sets'][$i]['path']) . '/' . $destName; + move_uploaded_file($_FILES['uploadSmiley']['tmp_name'], $smileyLocation); + @chmod($smileyLocation, 0644); + + // Now, we want to move it from there to all the other sets. + for ($n = count($context['smiley_sets']); $i < $n; $i++) + { + $currentPath = $context['smileys_dir'] . '/' . un_htmlspecialchars($context['smiley_sets'][$i]['path']) . '/' . $destName; + + // The file is already there! Don't overwrite it! + if (file_exists($currentPath)) + continue; + + // Okay, so copy the first one we made to here. + copy($smileyLocation, $currentPath); + @chmod($currentPath, 0644); + } + } + + // Finally make sure it's saved correctly! + $_POST['smiley_filename'] = $destName; + } + // What about uploading several files? + elseif ($_POST['method'] != 'existing') + { + foreach ($_FILES as $name => $data) + { + if ($_FILES[$name]['name'] == '') + fatal_lang_error('smileys_upload_error_blank'); + + if (empty($newName)) + $newName = basename($_FILES[$name]['name']); + elseif (basename($_FILES[$name]['name']) != $newName) + fatal_lang_error('smileys_upload_error_name'); + } + + foreach ($context['smiley_sets'] as $i => $set) + { + $set['name'] = un_htmlspecialchars($set['name']); + $set['path'] = un_htmlspecialchars($set['path']); + + if (!isset($_FILES['individual_' . $set['name']]['name']) || $_FILES['individual_' . $set['name']]['name'] == '') + continue; + + // Got one... + if (!is_uploaded_file($_FILES['individual_' . $set['name']]['tmp_name']) || (@ini_get('open_basedir') == '' && !file_exists($_FILES['individual_' . $set['name']]['tmp_name']))) + fatal_lang_error('smileys_upload_error'); + + // Sorry, no spaces, dots, or anything else but letters allowed. + $_FILES['individual_' . $set['name']]['name'] = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $_FILES['individual_' . $set['name']]['name']); + + // We only allow image files - it's THAT simple - no messing around here... + if (!in_array(strtolower(substr(strrchr($_FILES['individual_' . $set['name']]['name'], '.'), 1)), $allowedTypes)) + fatal_lang_error('smileys_upload_error_types', false, array(implode(', ', $allowedTypes))); + + // We only need the filename... + $destName = basename($_FILES['individual_' . $set['name']]['name']); + + // Make sure they aren't trying to upload a nasty file - for their own good here! + if (in_array(strtolower($destName), $disabledFiles)) + fatal_lang_error('smileys_upload_error_illegal'); + + // If the file exists - ignore it. + $smileyLocation = $context['smileys_dir'] . '/' . $set['path'] . '/' . $destName; + if (file_exists($smileyLocation)) + continue; + + // Finally - move the image! + move_uploaded_file($_FILES['individual_' . $set['name']]['tmp_name'], $smileyLocation); + @chmod($smileyLocation, 0644); + + // Should always be saved correctly! + $_POST['smiley_filename'] = $destName; + } + } + + // Also make sure a filename was given. + if (empty($_POST['smiley_filename'])) + fatal_lang_error('smiley_has_no_filename'); + + // Find the position on the right. + $smiley_order = '0'; + if ($_POST['smiley_location'] != 1) + { + $request = $smcFunc['db_query']('', ' + SELECT MAX(smiley_order) + 1 + FROM {db_prefix}smileys + WHERE hidden = {int:smiley_location} + AND smiley_row = {int:first_row}', + array( + 'smiley_location' => $_POST['smiley_location'], + 'first_row' => 0, + ) + ); + list ($smiley_order) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (empty($smiley_order)) + $smiley_order = '0'; + } + $smcFunc['db_insert']('', + '{db_prefix}smileys', + array( + 'code' => 'string-30', 'filename' => 'string-48', 'description' => 'string-80', 'hidden' => 'int', 'smiley_order' => 'int', + ), + array( + $_POST['smiley_code'], $_POST['smiley_filename'], $_POST['smiley_description'], $_POST['smiley_location'], $smiley_order, + ), + array('id_smiley') + ); + + cache_put_data('parsing_smileys', null, 480); + cache_put_data('posting_smileys', null, 480); + + // No errors? Out of here! + redirectexit('action=admin;area=smileys;sa=editsmileys'); + } + + $context['selected_set'] = $modSettings['smiley_sets_default']; + + // Get all possible filenames for the smileys. + $context['filenames'] = array(); + if ($context['smileys_dir_found']) + { + foreach ($context['smiley_sets'] as $smiley_set) + { + if (!file_exists($context['smileys_dir'] . '/' . un_htmlspecialchars($smiley_set['path']))) + continue; + + $dir = dir($context['smileys_dir'] . '/' . un_htmlspecialchars($smiley_set['path'])); + while ($entry = $dir->read()) + { + if (!in_array($entry, $context['filenames']) && in_array(strrchr($entry, '.'), array('.jpg', '.gif', '.jpeg', '.png'))) + $context['filenames'][strtolower($entry)] = array( + 'id' => htmlspecialchars($entry), + 'selected' => false, + ); + } + $dir->close(); + } + ksort($context['filenames']); + } + + // Create a new smiley from scratch. + $context['filenames'] = array_values($context['filenames']); + $context['current_smiley'] = array( + 'id' => 0, + 'code' => '', + 'filename' => $context['filenames'][0]['id'], + 'description' => $txt['smileys_default_description'], + 'location' => 0, + 'is_new' => true, + ); +} + +function EditSmileys() +{ + global $modSettings, $context, $settings, $txt, $boarddir; + global $smcFunc, $scripturl, $sourcedir; + + // Force the correct tab to be displayed. + $context[$context['admin_menu_name']]['current_subsection'] = 'editsmileys'; + + // Submitting a form? + if (isset($_POST[$context['session_var']])) + { + checkSession(); + + // Changing the selected smileys? + if (isset($_POST['smiley_action']) && !empty($_POST['checked_smileys'])) + { + foreach ($_POST['checked_smileys'] as $id => $smiley_id) + $_POST['checked_smileys'][$id] = (int) $smiley_id; + + if ($_POST['smiley_action'] == 'delete') + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}smileys + WHERE id_smiley IN ({array_int:checked_smileys})', + array( + 'checked_smileys' => $_POST['checked_smileys'], + ) + ); + // Changing the status of the smiley? + else + { + // Check it's a valid type. + $displayTypes = array( + 'post' => 0, + 'hidden' => 1, + 'popup' => 2 + ); + if (isset($displayTypes[$_POST['smiley_action']])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}smileys + SET hidden = {int:display_type} + WHERE id_smiley IN ({array_int:checked_smileys})', + array( + 'checked_smileys' => $_POST['checked_smileys'], + 'display_type' => $displayTypes[$_POST['smiley_action']], + ) + ); + } + } + // Create/modify a smiley. + elseif (isset($_POST['smiley'])) + { + // Is it a delete? + if (!empty($_POST['deletesmiley'])) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}smileys + WHERE id_smiley = {int:current_smiley}', + array( + 'current_smiley' => $_POST['smiley'], + ) + ); + } + // Otherwise an edit. + else + { + $_POST['smiley'] = (int) $_POST['smiley']; + $_POST['smiley_code'] = htmltrim__recursive($_POST['smiley_code']); + $_POST['smiley_filename'] = htmltrim__recursive($_POST['smiley_filename']); + $_POST['smiley_location'] = empty($_POST['smiley_location']) || $_POST['smiley_location'] > 2 || $_POST['smiley_location'] < 0 ? 0 : (int) $_POST['smiley_location']; + + // Make sure some code was entered. + if (empty($_POST['smiley_code'])) + fatal_lang_error('smiley_has_no_code'); + + // Also make sure a filename was given. + if (empty($_POST['smiley_filename'])) + fatal_lang_error('smiley_has_no_filename'); + + // Check whether the new code has duplicates. It should be unique. + $request = $smcFunc['db_query']('', ' + SELECT id_smiley + FROM {db_prefix}smileys + WHERE code = {raw:mysql_binary_type} {string:smiley_code}' . (empty($_POST['smiley']) ? '' : ' + AND id_smiley != {int:current_smiley}'), + array( + 'current_smiley' => $_POST['smiley'], + 'mysql_binary_type' => $smcFunc['db_title'] == 'MySQL' ? 'BINARY' : '', + 'smiley_code' => $_POST['smiley_code'], + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + fatal_lang_error('smiley_not_unique'); + $smcFunc['db_free_result']($request); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}smileys + SET + code = {string:smiley_code}, + filename = {string:smiley_filename}, + description = {string:smiley_description}, + hidden = {int:smiley_location} + WHERE id_smiley = {int:current_smiley}', + array( + 'smiley_location' => $_POST['smiley_location'], + 'current_smiley' => $_POST['smiley'], + 'smiley_code' => $_POST['smiley_code'], + 'smiley_filename' => $_POST['smiley_filename'], + 'smiley_description' => $_POST['smiley_description'], + ) + ); + } + + // Sort all smiley codes for more accurate parsing (longest code first). + sortSmileyTable(); + } + + cache_put_data('parsing_smileys', null, 480); + cache_put_data('posting_smileys', null, 480); + } + + // Load all known smiley sets. + $context['smiley_sets'] = explode(',', $modSettings['smiley_sets_known']); + $set_names = explode("\n", $modSettings['smiley_sets_names']); + foreach ($context['smiley_sets'] as $i => $set) + $context['smiley_sets'][$i] = array( + 'id' => $i, + 'path' => htmlspecialchars($set), + 'name' => htmlspecialchars($set_names[$i]), + 'selected' => $set == $modSettings['smiley_sets_default'] + ); + + // Prepare overview of all (custom) smileys. + if ($context['sub_action'] == 'editsmileys') + { + // Determine the language specific sort order of smiley locations. + $smiley_locations = array( + $txt['smileys_location_form'], + $txt['smileys_location_hidden'], + $txt['smileys_location_popup'], + ); + asort($smiley_locations); + + // Create a list of options for selecting smiley sets. + $smileyset_option_list = ' + '; + + $listOptions = array( + 'id' => 'smiley_list', + 'items_per_page' => 40, + 'base_href' => $scripturl . '?action=admin;area=smileys;sa=editsmileys', + 'default_sort_col' => 'filename', + 'get_items' => array( + 'function' => 'list_getSmileys', + ), + 'get_count' => array( + 'function' => 'list_getNumSmileys', + ), + 'no_items_label' => $txt['smileys_no_entries'], + 'columns' => array( + 'picture' => array( + 'data' => array( + 'sprintf' => array( + 'format' => '%3$s', + 'params' => array( + 'id_smiley' => false, + 'filename' => true, + 'description' => true, + ), + ), + 'style' => 'text-align: center;', + ), + ), + 'code' => array( + 'header' => array( + 'value' => $txt['smileys_code'], + ), + 'data' => array( + 'db_htmlsafe' => 'code', + ), + 'sort' => array( + 'default' => 'code', + 'reverse' => 'code DESC', + ), + ), + 'filename' => array( + 'header' => array( + 'value' => $txt['smileys_filename'], + ), + 'data' => array( + 'db_htmlsafe' => 'filename', + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'filename', + 'reverse' => 'filename DESC', + ), + ), + 'location' => array( + 'header' => array( + 'value' => $txt['smileys_location'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + if (empty($rowData[\'hidden\'])) + return $txt[\'smileys_location_form\']; + elseif ($rowData[\'hidden\'] == 1) + return $txt[\'smileys_location_hidden\']; + else + return $txt[\'smileys_location_popup\']; + '), + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'FIND_IN_SET(hidden, \'' . implode(',', array_keys($smiley_locations)) . '\')', + 'reverse' => 'FIND_IN_SET(hidden, \'' . implode(',', array_keys($smiley_locations)) . '\') DESC', + ), + ), + 'tooltip' => array( + 'header' => array( + 'value' => $txt['smileys_description'], + ), + 'data' => array( + 'function' => create_function('$rowData', empty($modSettings['smileys_dir']) || !is_dir($modSettings['smileys_dir']) ? ' + return htmlspecialchars($rowData[\'description\']); + ' : ' + global $context, $txt, $modSettings; + + // Check if there are smileys missing in some sets. + $missing_sets = array(); + foreach ($context[\'smiley_sets\'] as $smiley_set) + if (!file_exists(sprintf(\'%1$s/%2$s/%3$s\', $modSettings[\'smileys_dir\'], $smiley_set[\'path\'], $rowData[\'filename\']))) + $missing_sets[] = $smiley_set[\'path\']; + + $description = htmlspecialchars($rowData[\'description\']); + + if (!empty($missing_sets)) + $description .= sprintf(\'
%1$s: %2$s\', $txt[\'smileys_not_found_in_set\'], implode(\', \', $missing_sets)); + + return $description; + '), + 'class' => 'windowbg', + ), + 'sort' => array( + 'default' => 'description', + 'reverse' => 'description DESC', + ), + ), + 'modify' => array( + 'header' => array( + 'value' => $txt['smileys_modify'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '' . $txt['smileys_modify'] . '', + 'params' => array( + 'id_smiley' => false, + ), + ), + 'style' => 'text-align: center;', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id_smiley' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=smileys;sa=editsmileys', + 'name' => 'smileyForm', + ), + 'additional_rows' => array( + array( + 'position' => 'above_column_headers', + 'value' => $smileyset_option_list, + 'style' => 'text-align: right;', + ), + array( + 'position' => 'below_table_data', + 'value' => ' + + ', + 'style' => 'text-align: right;', + ), + ), + 'javascript' => ' + function makeChanges(action) + { + if (action == \'-1\') + return false; + else if (action == \'delete\') + { + if (confirm(\'' . $txt['smileys_confirm'] . '\')) + document.forms.smileyForm.submit(); + } + else + document.forms.smileyForm.submit(); + return true; + } + function changeSet(newSet) + { + var currentImage, i, knownSmileys = []; + + if (knownSmileys.length == 0) + { + for (var i = 0, n = document.images.length; i < n; i++) + if (document.images[i].id.substr(0, 6) == \'smiley\') + knownSmileys[knownSmileys.length] = document.images[i].id.substr(6); + } + + for (i = 0; i < knownSmileys.length; i++) + { + currentImage = document.getElementById("smiley" + knownSmileys[i]); + currentImage.src = "' . $modSettings['smileys_url'] . '/" + newSet + "/" + document.forms.smileyForm["smileys[" + knownSmileys[i] + "][filename]"].value; + } + }', + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + // The list is the only thing to show, so make it the main template. + $context['default_list'] = 'smiley_list'; + $context['sub_template'] = 'show_list'; + } + // Modifying smileys. + elseif ($context['sub_action'] == 'modifysmiley') + { + // Get a list of all known smiley sets. + $context['smileys_dir'] = empty($modSettings['smileys_dir']) ? $boarddir . '/Smileys' : $modSettings['smileys_dir']; + $context['smileys_dir_found'] = is_dir($context['smileys_dir']); + $context['smiley_sets'] = explode(',', $modSettings['smiley_sets_known']); + $set_names = explode("\n", $modSettings['smiley_sets_names']); + foreach ($context['smiley_sets'] as $i => $set) + $context['smiley_sets'][$i] = array( + 'id' => $i, + 'path' => htmlspecialchars($set), + 'name' => htmlspecialchars($set_names[$i]), + 'selected' => $set == $modSettings['smiley_sets_default'] + ); + + $context['selected_set'] = $modSettings['smiley_sets_default']; + + // Get all possible filenames for the smileys. + $context['filenames'] = array(); + if ($context['smileys_dir_found']) + { + foreach ($context['smiley_sets'] as $smiley_set) + { + if (!file_exists($context['smileys_dir'] . '/' . un_htmlspecialchars($smiley_set['path']))) + continue; + + $dir = dir($context['smileys_dir'] . '/' . un_htmlspecialchars($smiley_set['path'])); + while ($entry = $dir->read()) + { + if (!in_array($entry, $context['filenames']) && in_array(strrchr($entry, '.'), array('.jpg', '.gif', '.jpeg', '.png'))) + $context['filenames'][strtolower($entry)] = array( + 'id' => htmlspecialchars($entry), + 'selected' => false, + ); + } + $dir->close(); + } + ksort($context['filenames']); + } + + $request = $smcFunc['db_query']('', ' + SELECT id_smiley AS id, code, filename, description, hidden AS location, 0 AS is_new + FROM {db_prefix}smileys + WHERE id_smiley = {int:current_smiley}', + array( + 'current_smiley' => (int) $_REQUEST['smiley'], + ) + ); + if ($smcFunc['db_num_rows']($request) != 1) + fatal_lang_error('smiley_not_found'); + $context['current_smiley'] = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $context['current_smiley']['code'] = htmlspecialchars($context['current_smiley']['code']); + $context['current_smiley']['filename'] = htmlspecialchars($context['current_smiley']['filename']); + $context['current_smiley']['description'] = htmlspecialchars($context['current_smiley']['description']); + + if (isset($context['filenames'][strtolower($context['current_smiley']['filename'])])) + $context['filenames'][strtolower($context['current_smiley']['filename'])]['selected'] = true; + } +} + +function list_getSmileys($start, $items_per_page, $sort) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT id_smiley, code, filename, description, smiley_row, smiley_order, hidden + FROM {db_prefix}smileys + ORDER BY ' . $sort, + array( + ) + ); + $smileys = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $smileys[] = $row; + $smcFunc['db_free_result']($request); + + return $smileys; +} + +function list_getNumSmileys() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}smileys', + array( + ) + ); + list($numSmileys) = $smcFunc['db_fetch_row']; + $smcFunc['db_free_result']($request); + + return $numSmileys; +} + +function EditSmileyOrder() +{ + global $modSettings, $context, $settings, $txt, $boarddir, $smcFunc; + + // Move smileys to another position. + if (isset($_REQUEST['reorder'])) + { + checkSession('get'); + + $_GET['location'] = empty($_GET['location']) || $_GET['location'] != 'popup' ? 0 : 2; + $_GET['source'] = empty($_GET['source']) ? 0 : (int) $_GET['source']; + + if (empty($_GET['source'])) + fatal_lang_error('smiley_not_found'); + + if (!empty($_GET['after'])) + { + $_GET['after'] = (int) $_GET['after']; + + $request = $smcFunc['db_query']('', ' + SELECT smiley_row, smiley_order, hidden + FROM {db_prefix}smileys + WHERE hidden = {int:location} + AND id_smiley = {int:after_smiley}', + array( + 'location' => $_GET['location'], + 'after_smiley' => $_GET['after'], + ) + ); + if ($smcFunc['db_num_rows']($request) != 1) + fatal_lang_error('smiley_not_found'); + list ($smiley_row, $smiley_order, $smileyLocation) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + { + $smiley_row = (int) $_GET['row']; + $smiley_order = -1; + $smileyLocation = (int) $_GET['location']; + } + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}smileys + SET smiley_order = smiley_order + 1 + WHERE hidden = {int:new_location} + AND smiley_row = {int:smiley_row} + AND smiley_order > {int:smiley_order}', + array( + 'new_location' => $_GET['location'], + 'smiley_row' => $smiley_row, + 'smiley_order' => $smiley_order, + ) + ); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}smileys + SET + smiley_order = {int:smiley_order} + 1, + smiley_row = {int:smiley_row}, + hidden = {int:new_location} + WHERE id_smiley = {int:current_smiley}', + array( + 'smiley_order' => $smiley_order, + 'smiley_row' => $smiley_row, + 'new_location' => $smileyLocation, + 'current_smiley' => $_GET['source'], + ) + ); + + cache_put_data('parsing_smileys', null, 480); + cache_put_data('posting_smileys', null, 480); + } + + $request = $smcFunc['db_query']('', ' + SELECT id_smiley, code, filename, description, smiley_row, smiley_order, hidden + FROM {db_prefix}smileys + WHERE hidden != {int:popup} + ORDER BY smiley_order, smiley_row', + array( + 'popup' => 1, + ) + ); + $context['smileys'] = array( + 'postform' => array( + 'rows' => array(), + ), + 'popup' => array( + 'rows' => array(), + ), + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $location = empty($row['hidden']) ? 'postform' : 'popup'; + $context['smileys'][$location]['rows'][$row['smiley_row']][] = array( + 'id' => $row['id_smiley'], + 'code' => htmlspecialchars($row['code']), + 'filename' => htmlspecialchars($row['filename']), + 'description' => htmlspecialchars($row['description']), + 'row' => $row['smiley_row'], + 'order' => $row['smiley_order'], + 'selected' => !empty($_REQUEST['move']) && $_REQUEST['move'] == $row['id_smiley'], + ); + } + $smcFunc['db_free_result']($request); + + $context['move_smiley'] = empty($_REQUEST['move']) ? 0 : (int) $_REQUEST['move']; + + // Make sure all rows are sequential. + foreach (array_keys($context['smileys']) as $location) + $context['smileys'][$location] = array( + 'id' => $location, + 'title' => $location == 'postform' ? $txt['smileys_location_form'] : $txt['smileys_location_popup'], + 'description' => $location == 'postform' ? $txt['smileys_location_form_description'] : $txt['smileys_location_popup_description'], + 'last_row' => count($context['smileys'][$location]['rows']), + 'rows' => array_values($context['smileys'][$location]['rows']), + ); + + // Check & fix smileys that are not ordered properly in the database. + foreach (array_keys($context['smileys']) as $location) + { + foreach ($context['smileys'][$location]['rows'] as $id => $smiley_row) + { + // Fix empty rows if any. + if ($id != $smiley_row[0]['row']) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}smileys + SET smiley_row = {int:new_row} + WHERE smiley_row = {int:current_row} + AND hidden = {int:location}', + array( + 'new_row' => $id, + 'current_row' => $smiley_row[0]['row'], + 'location' => $location == 'postform' ? '0' : '2', + ) + ); + // Only change the first row value of the first smiley (we don't need the others :P). + $context['smileys'][$location]['rows'][$id][0]['row'] = $id; + } + // Make sure the smiley order is always sequential. + foreach ($smiley_row as $order_id => $smiley) + if ($order_id != $smiley['order']) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}smileys + SET smiley_order = {int:new_order} + WHERE id_smiley = {int:current_smiley}', + array( + 'new_order' => $order_id, + 'current_smiley' => $smiley['id'], + ) + ); + } + } + + cache_put_data('parsing_smileys', null, 480); + cache_put_data('posting_smileys', null, 480); +} + +function InstallSmileySet() +{ + global $sourcedir, $boarddir, $modSettings, $smcFunc; + + isAllowedTo('manage_smileys'); + checkSession('request'); + + require_once($sourcedir . '/Subs-Package.php'); + + $name = strtok(basename(isset($_FILES['set_gz']) ? $_FILES['set_gz']['name'] : $_REQUEST['set_gz']), '.'); + $name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $name); + + // !!! Decide: overwrite or not? + if (isset($_FILES['set_gz']) && is_uploaded_file($_FILES['set_gz']['tmp_name']) && (@ini_get('open_basedir') != '' || file_exists($_FILES['set_gz']['tmp_name']))) + $extracted = read_tgz_file($_FILES['set_gz']['tmp_name'], $boarddir . '/Smileys/' . $name); + elseif (isset($_REQUEST['set_gz'])) + { + // Check that the smiley is from simplemachines.org, for now... maybe add mirroring later. + if (preg_match('~^http://[\w_\-]+\.simplemachines\.org/~', $_REQUEST['set_gz']) == 0 || strpos($_REQUEST['set_gz'], 'dlattach') !== false) + fatal_lang_error('not_on_simplemachines'); + + $extracted = read_tgz_file($_REQUEST['set_gz'], $boarddir . '/Smileys/' . $name); + } + else + redirectexit('action=admin;area=smileys'); + + updateSettings(array( + 'smiley_sets_known' => $modSettings['smiley_sets_known'] . ',' . $name, + 'smiley_sets_names' => $modSettings['smiley_sets_names'] . "\n" . strtok(basename(isset($_FILES['set_gz']) ? $_FILES['set_gz']['name'] : $_REQUEST['set_gz']), '.'), + )); + + cache_put_data('parsing_smileys', null, 480); + cache_put_data('posting_smileys', null, 480); + + // !!! Add some confirmation? + redirectexit('action=admin;area=smileys'); +} + +// A function to import new smileys from an existing directory into the database. +function ImportSmileys($smileyPath) +{ + global $modSettings, $smcFunc; + + if (empty($modSettings['smileys_dir']) || !is_dir($modSettings['smileys_dir'] . '/' . $smileyPath)) + fatal_lang_error('smiley_set_unable_to_import'); + + $smileys = array(); + $dir = dir($modSettings['smileys_dir'] . '/' . $smileyPath); + while ($entry = $dir->read()) + { + if (in_array(strrchr($entry, '.'), array('.jpg', '.gif', '.jpeg', '.png'))) + $smileys[strtolower($entry)] = $entry; + } + $dir->close(); + + // Exclude the smileys that are already in the database. + $request = $smcFunc['db_query']('', ' + SELECT filename + FROM {db_prefix}smileys + WHERE filename IN ({array_string:smiley_list})', + array( + 'smiley_list' => $smileys, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + if (isset($smileys[strtolower($row['filename'])])) + unset($smileys[strtolower($row['filename'])]); + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT MAX(smiley_order) + FROM {db_prefix}smileys + WHERE hidden = {int:postform} + AND smiley_row = {int:first_row}', + array( + 'postform' => 0, + 'first_row' => 0, + ) + ); + list ($smiley_order) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $new_smileys = array(); + foreach ($smileys as $smiley) + if (strlen($smiley) <= 48) + $new_smileys[] = array(':' . strtok($smiley, '.') . ':', $smiley, strtok($smiley, '.'), 0, ++$smiley_order); + + if (!empty($new_smileys)) + { + $smcFunc['db_insert']('', + '{db_prefix}smileys', + array( + 'code' => 'string-30', 'filename' => 'string-48', 'description' => 'string-80', 'smiley_row' => 'int', 'smiley_order' => 'int', + ), + $new_smileys, + array('id_smiley') + ); + + // Make sure the smiley codes are still in the right order. + sortSmileyTable(); + + cache_put_data('parsing_smileys', null, 480); + cache_put_data('posting_smileys', null, 480); + } +} + +function EditMessageIcons() +{ + global $user_info, $modSettings, $context, $settings, $txt; + global $boarddir, $smcFunc, $scripturl, $sourcedir; + + // Get a list of icons. + $context['icons'] = array(); + $request = $smcFunc['db_query']('', ' + SELECT m.id_icon, m.title, m.filename, m.icon_order, m.id_board, b.name AS board_name + FROM {db_prefix}message_icons AS m + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE ({query_see_board} OR b.id_board IS NULL)', + array( + ) + ); + $last_icon = 0; + $trueOrder = 0; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['icons'][$row['id_icon']] = array( + 'id' => $row['id_icon'], + 'title' => $row['title'], + 'filename' => $row['filename'], + 'image_url' => $settings[file_exists($settings['theme_dir'] . '/images/post/' . $row['filename'] . '.gif') ? 'actual_images_url' : 'default_images_url'] . '/post/' . $row['filename'] . '.gif', + 'board_id' => $row['id_board'], + 'board' => empty($row['board_name']) ? $txt['icons_edit_icons_all_boards'] : $row['board_name'], + 'order' => $row['icon_order'], + 'true_order' => $trueOrder++, + 'after' => $last_icon, + ); + $last_icon = $row['id_icon']; + } + $smcFunc['db_free_result']($request); + + // Submitting a form? + if (isset($_POST[$context['session_var']])) + { + checkSession(); + + // Deleting icons? + if (isset($_POST['delete']) && !empty($_POST['checked_icons'])) + { + $deleteIcons = array(); + foreach ($_POST['checked_icons'] as $icon) + $deleteIcons[] = (int) $icon; + + // Do the actual delete! + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}message_icons + WHERE id_icon IN ({array_int:icon_list})', + array( + 'icon_list' => $deleteIcons, + ) + ); + } + // Editing/Adding an icon? + elseif ($context['sub_action'] == 'editicon' && isset($_GET['icon'])) + { + $_GET['icon'] = (int) $_GET['icon']; + + // Do some preperation with the data... like check the icon exists *somewhere* + if (strpos($_POST['icon_filename'], '.gif') !== false) + $_POST['icon_filename'] = substr($_POST['icon_filename'], 0, -4); + if (!file_exists($settings['default_theme_dir'] . '/images/post/' . $_POST['icon_filename'] . '.gif')) + fatal_lang_error('icon_not_found'); + // There is a 16 character limit on message icons... + elseif (strlen($_POST['icon_filename']) > 16) + fatal_lang_error('icon_name_too_long'); + elseif ($_POST['icon_location'] == $_GET['icon'] && !empty($_GET['icon'])) + fatal_lang_error('icon_after_itself'); + + // First do the sorting... if this is an edit reduce the order of everything after it by one ;) + if ($_GET['icon'] != 0) + { + $oldOrder = $context['icons'][$_GET['icon']]['true_order']; + foreach ($context['icons'] as $id => $data) + if ($data['true_order'] > $oldOrder) + $context['icons'][$id]['true_order']--; + } + + // If there are no existing icons and this is a new one, set the id to 1 (mainly for non-mysql) + if (empty($_GET['icon']) && empty($context['icons'])) + $_GET['icon'] = 1; + + // Get the new order. + $newOrder = $_POST['icon_location'] == 0 ? 0 : $context['icons'][$_POST['icon_location']]['true_order'] + 1; + // Do the same, but with the one that used to be after this icon, done to avoid conflict. + foreach ($context['icons'] as $id => $data) + if ($data['true_order'] >= $newOrder) + $context['icons'][$id]['true_order']++; + + // Finally set the current icon's position! + $context['icons'][$_GET['icon']]['true_order'] = $newOrder; + + // Simply replace the existing data for the other bits. + $context['icons'][$_GET['icon']]['title'] = $_POST['icon_description']; + $context['icons'][$_GET['icon']]['filename'] = $_POST['icon_filename']; + $context['icons'][$_GET['icon']]['board_id'] = (int) $_POST['icon_board']; + + // Do a huge replace ;) + $iconInsert = array(); + $iconInsert_new = array(); + foreach ($context['icons'] as $id => $icon) + { + if ($id != 0) + { + $iconInsert[] = array($id, $icon['board_id'], $icon['title'], $icon['filename'], $icon['true_order']); + } + else + { + $iconInsert_new[] = array($icon['board_id'], $icon['title'], $icon['filename'], $icon['true_order']); + } + } + + $smcFunc['db_insert']('replace', + '{db_prefix}message_icons', + array('id_icon' => 'int', 'id_board' => 'int', 'title' => 'string-80', 'filename' => 'string-80', 'icon_order' => 'int'), + $iconInsert, + array('id_icon') + ); + + if (!empty($iconInsert_new)) + { + $smcFunc['db_insert']('replace', + '{db_prefix}message_icons', + array('id_board' => 'int', 'title' => 'string-80', 'filename' => 'string-80', 'icon_order' => 'int'), + $iconInsert_new, + array('id_icon') + ); + } + } + + // Sort by order, so it is quicker :) + $smcFunc['db_query']('alter_table_icons', ' + ALTER TABLE {db_prefix}message_icons + ORDER BY icon_order', + array( + 'db_error_skip' => true, + ) + ); + + // Unless we're adding a new thing, we'll escape + if (!isset($_POST['add'])) + redirectexit('action=admin;area=smileys;sa=editicons'); + } + + $context[$context['admin_menu_name']]['current_subsection'] = 'editicons'; + + $listOptions = array( + 'id' => 'message_icon_list', + 'base_href' => $scripturl . '?action=admin;area=smileys;sa=editicons', + 'get_items' => array( + 'function' => 'list_getMessageIcons', + ), + 'no_items_label' => $txt['icons_no_entries'], + 'columns' => array( + 'icon' => array( + 'data' => array( + 'function' => create_function('$rowData', ' + global $settings; + + $images_url = $settings[file_exists(sprintf(\'%1$s/images/post/%2$s.gif\', $settings[\'theme_dir\'], $rowData[\'filename\'])) ? \'actual_images_url\' : \'default_images_url\']; + return sprintf(\'%3$s\', $images_url, $rowData[\'filename\'], htmlspecialchars($rowData[\'title\'])); + '), + ), + 'style' => 'text-align: center;', + ), + 'filename' => array( + 'header' => array( + 'value' => $txt['smileys_filename'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s.gif', + 'params' => array( + 'filename' => true, + ), + ), + ), + ), + 'tooltip' => array( + 'header' => array( + 'value' => $txt['smileys_description'], + ), + 'data' => array( + 'db_htmlsafe' => 'title', + 'class' => 'windowbg', + ), + ), + 'board' => array( + 'header' => array( + 'value' => $txt['icons_board'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + return empty($rowData[\'board_name\']) ? $txt[\'icons_edit_icons_all_boards\'] : $rowData[\'board_name\']; + '), + ), + ), + 'modify' => array( + 'header' => array( + 'value' => $txt['smileys_modify'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '' . $txt['smileys_modify'] . '', + 'params' => array( + 'id_icon' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id_icon' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=admin;area=smileys;sa=editicons', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '[' . $txt['icons_add_new'] . ']', + ), + ), + ); + + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + // If we're adding/editing an icon we'll need a list of boards + if ($context['sub_action'] == 'editicon' || isset($_POST['add'])) + { + // Force the sub_template just in case. + $context['sub_template'] = 'editicon'; + + $context['new_icon'] = !isset($_GET['icon']); + + // Get the properties of the current icon from the icon list. + if (!$context['new_icon']) + $context['icon'] = $context['icons'][$_GET['icon']]; + + // Get a list of boards needed for assigning this icon to a specific board. + $boardListOptions = array( + 'use_permissions' => true, + 'selected_board' => isset($context['icon']['board_id']) ? $context['icon']['board_id'] : 0, + ); + require_once($sourcedir . '/Subs-MessageIndex.php'); + $context['categories'] = getBoardList($boardListOptions); + } +} + +function list_getMessageIcons($start, $items_per_page, $sort) +{ + global $smcFunc, $user_info; + + $request = $smcFunc['db_query']('', ' + SELECT m.id_icon, m.title, m.filename, m.icon_order, m.id_board, b.name AS board_name + FROM {db_prefix}message_icons AS m + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE ({query_see_board} OR b.id_board IS NULL)', + array( + ) + ); + + $message_icons = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $message_icons[] = $row; + $smcFunc['db_free_result']($request); + + return $message_icons; +} + +// This function sorts the smiley table by code length, it is needed as MySQL withdrew support for functions in order by. +function sortSmileyTable() +{ + global $smcFunc; + + db_extend('packages'); + + // Add a sorting column. + $smcFunc['db_add_column']('{db_prefix}smileys', array('name' => 'temp_order', 'size' => 8, 'type' => 'mediumint', 'null' => false)); + + // Set the contents of this column. + $smcFunc['db_query']('set_smiley_order', ' + UPDATE {db_prefix}smileys + SET temp_order = LENGTH(code)', + array( + ) + ); + + // Order the table by this column. + $smcFunc['db_query']('alter_table_smileys', ' + ALTER TABLE {db_prefix}smileys + ORDER BY temp_order DESC', + array( + 'db_error_skip' => true, + ) + ); + + // Remove the sorting column. + $smcFunc['db_remove_column']('{db_prefix}smileys', 'temp_order'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Memberlist.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Memberlist.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,592 @@ + array('label', 'function', 'is_selected') + $subActions = array( + 'all' => array($txt['view_all_members'], 'MLAll', $context['listing_by'] == 'all'), + 'search' => array($txt['mlist_search'], '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 columns... + $context['columns'] = array( + 'is_online' => array( + 'label' => $txt['status'], + 'width' => '60', + 'class' => 'first_th', + ), + 'real_name' => array( + 'label' => $txt['username'] + ), + 'email_address' => array( + 'label' => $txt['email'], + 'width' => '25' + ), + 'website_url' => array( + 'label' => $txt['website'], + 'width' => '70', + 'link_with' => 'website', + ), + 'icq' => array( + 'label' => $txt['icq'], + 'width' => '30' + ), + 'aim' => array( + 'label' => $txt['aim'], + 'width' => '30' + ), + 'yim' => array( + 'label' => $txt['yim'], + 'width' => '30' + ), + 'msn' => array( + 'label' => $txt['msn'], + 'width' => '30' + ), + 'id_group' => array( + 'label' => $txt['position'] + ), + 'registered' => array( + 'label' => $txt['date_registered'] + ), + 'posts' => array( + 'label' => $txt['posts'], + 'width' => '115', + 'colspan' => '2', + 'default_sort_rev' => true, + ) + ); + + $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; + } + + // Aesthetic stuff. + end($context['columns']); + $context['columns'][key($context['columns'])]['class'] = 'last_th'; + + $context['linktree'][] = array( + 'url' => $scripturl . '?action=mlist', + 'name' => $txt['members_list'] + ); + + $context['can_send_pm'] = allowedTo('pm_send'); + + // Jump to the sub action. + if (isset($subActions[$context['listing_by']])) + $subActions[$context['listing_by']][1](); + else + $subActions['all'][1](); +} + +// List all members, page by page. +function MLAll() +{ + global $txt, $scripturl, $user_info; + global $modSettings, $context, $smcFunc; + + // The chunk size for the cached index. + $cache_step_size = 500; + + // Only use caching if: + // 1. there are at least 2k members, + // 2. the default sorting method (real_name) is being used, + // 3. the page shown is high enough to make a DB filesort unprofitable. + $use_cache = $modSettings['totalMembers'] > 2000 && (!isset($_REQUEST['sort']) || $_REQUEST['sort'] === 'real_name') && isset($_REQUEST['start']) && $_REQUEST['start'] > $cache_step_size; + + if ($use_cache) + { + // Maybe there's something cached already. + if (!empty($modSettings['memberlist_cache'])) + $memberlist_cache = @unserialize($modSettings['memberlist_cache']); + + // The chunk size for the cached index. + $cache_step_size = 500; + + // Only update the cache if something changed or no cache existed yet. + if (empty($memberlist_cache) || empty($modSettings['memberlist_updated']) || $memberlist_cache['last_update'] < $modSettings['memberlist_updated']) + { + $request = $smcFunc['db_query']('', ' + SELECT real_name + FROM {db_prefix}members + WHERE is_activated = {int:is_activated} + ORDER BY real_name', + array( + 'is_activated' => 1, + ) + ); + + $memberlist_cache = array( + 'last_update' => time(), + 'num_members' => $smcFunc['db_num_rows']($request), + 'index' => array(), + ); + + for ($i = 0, $n = $smcFunc['db_num_rows']($request); $i < $n; $i += $cache_step_size) + { + $smcFunc['db_data_seek']($request, $i); + list($memberlist_cache['index'][$i]) = $smcFunc['db_fetch_row']($request); + } + $smcFunc['db_data_seek']($request, $memberlist_cache['num_members'] - 1); + list ($memberlist_cache['index'][$i]) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Now we've got the cache...store it. + updateSettings(array('memberlist_cache' => serialize($memberlist_cache))); + } + + $context['num_members'] = $memberlist_cache['num_members']; + } + + // Without cache we need an extra query to get the amount of members. + else + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}members + WHERE is_activated = {int:is_activated}', + array( + 'is_activated' => 1, + ) + ); + list ($context['num_members']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // Set defaults for sort (real_name) and start. (0) + if (!isset($_REQUEST['sort']) || !isset($context['columns'][$_REQUEST['sort']])) + $_REQUEST['sort'] = 'real_name'; + + if (!is_numeric($_REQUEST['start'])) + { + if (preg_match('~^[^\'\\\\/]~' . ($context['utf8'] ? 'u' : ''), $smcFunc['strtolower']($_REQUEST['start']), $match) === 0) + fatal_error('Hacker?', false); + + $_REQUEST['start'] = $match[0]; + + $request = $smcFunc['db_query']('substring', ' + SELECT COUNT(*) + FROM {db_prefix}members + WHERE LOWER(SUBSTRING(real_name, 1, 1)) < {string:first_letter} + AND is_activated = {int:is_activated}', + array( + 'is_activated' => 1, + 'first_letter' => $_REQUEST['start'], + ) + ); + list ($_REQUEST['start']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + $context['letter_links'] = ''; + for ($i = 97; $i < 123; $i++) + $context['letter_links'] .= '' . strtoupper(chr($i)) . ' '; + + // Sort out the column information. + foreach ($context['columns'] as $col => $column_details) + { + $context['columns'][$col]['href'] = $scripturl . '?action=mlist;sort=' . $col . ';start=0'; + + if ((!isset($_REQUEST['desc']) && $col == $_REQUEST['sort']) || ($col != $_REQUEST['sort'] && !empty($column_details['default_sort_rev']))) + $context['columns'][$col]['href'] .= ';desc'; + + $context['columns'][$col]['link'] = '' . $context['columns'][$col]['label'] . ''; + $context['columns'][$col]['selected'] = $_REQUEST['sort'] == $col; + } + + $context['sort_by'] = $_REQUEST['sort']; + $context['sort_direction'] = !isset($_REQUEST['desc']) ? 'up' : 'down'; + + // Construct the page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=mlist;sort=' . $_REQUEST['sort'] . (isset($_REQUEST['desc']) ? ';desc' : ''), $_REQUEST['start'], $context['num_members'], $modSettings['defaultMaxMembers']); + + // Send the data to the template. + $context['start'] = $_REQUEST['start'] + 1; + $context['end'] = min($_REQUEST['start'] + $modSettings['defaultMaxMembers'], $context['num_members']); + + $context['can_moderate_forum'] = allowedTo('moderate_forum'); + $context['page_title'] = sprintf($txt['viewing_members'], $context['start'], $context['end']); + $context['linktree'][] = array( + 'url' => $scripturl . '?action=mlist;sort=' . $_REQUEST['sort'] . ';start=' . $_REQUEST['start'], + 'name' => &$context['page_title'], + 'extra_after' => ' (' . sprintf($txt['of_total_members'], $context['num_members']) . ')' + ); + + // List out the different sorting methods... + $sort_methods = array( + 'is_online' => 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( + 'down' => 'mem.real_name DESC', + 'up' => 'mem.real_name ASC' + ), + 'email_address' => 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( + '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' + ), + 'icq' => array( + 'down' => 'LENGTH(mem.icq) > 0 ASC, mem.icq = 0 DESC, mem.icq DESC', + 'up' => 'LENGTH(mem.icq) > 0 DESC, mem.icq = 0 ASC, mem.icq ASC' + ), + 'aim' => array( + 'down' => 'LENGTH(mem.aim) > 0 ASC, IFNULL(mem.aim, 1=1) DESC, mem.aim DESC', + 'up' => 'LENGTH(mem.aim) > 0 DESC, IFNULL(mem.aim, 1=1) ASC, mem.aim ASC' + ), + 'yim' => array( + 'down' => 'LENGTH(mem.yim) > 0 ASC, IFNULL(mem.yim, 1=1) DESC, mem.yim DESC', + 'up' => 'LENGTH(mem.yim) > 0 DESC, IFNULL(mem.yim, 1=1) ASC, mem.yim ASC' + ), + 'msn' => array( + 'down' => 'LENGTH(mem.msn) > 0 ASC, IFNULL(mem.msn, 1=1) DESC, mem.msn DESC', + 'up' => 'LENGTH(mem.msn) > 0 DESC, IFNULL(mem.msn, 1=1) ASC, mem.msn ASC' + ), + 'registered' => array( + 'down' => 'mem.date_registered DESC', + 'up' => 'mem.date_registered ASC' + ), + 'id_group' => 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' + ), + 'posts' => array( + 'down' => 'mem.posts DESC', + 'up' => 'mem.posts ASC' + ) + ); + + $limit = $_REQUEST['start']; + $query_parameters = array( + 'regular_id_group' => 0, + 'is_activated' => 1, + 'sort' => $sort_methods[$_REQUEST['sort']][$context['sort_direction']], + ); + + // Using cache allows to narrow down the list to be retrieved. + if ($use_cache && $_REQUEST['sort'] === 'real_name' && !isset($_REQUEST['desc'])) + { + $first_offset = $_REQUEST['start'] - ($_REQUEST['start'] % $cache_step_size); + $second_offset = ceil(($_REQUEST['start'] + $modSettings['defaultMaxMembers']) / $cache_step_size) * $cache_step_size; + + $where = 'mem.real_name BETWEEN {string:real_name_low} AND {string:real_name_high}'; + $query_parameters['real_name_low'] = $memberlist_cache['index'][$first_offset]; + $query_parameters['real_name_high'] = $memberlist_cache['index'][$second_offset]; + $limit -= $first_offset; + } + + // Reverse sorting is a bit more complicated... + elseif ($use_cache && $_REQUEST['sort'] === 'real_name') + { + $first_offset = floor(($memberlist_cache['num_members'] - $modSettings['defaultMaxMembers'] - $_REQUEST['start']) / $cache_step_size) * $cache_step_size; + if ($first_offset < 0) + $first_offset = 0; + $second_offset = ceil(($memberlist_cache['num_members'] - $_REQUEST['start']) / $cache_step_size) * $cache_step_size; + + $where = 'mem.real_name BETWEEN {string:real_name_low} AND {string:real_name_high}'; + $query_parameters['real_name_low'] = $memberlist_cache['index'][$first_offset]; + $query_parameters['real_name_high'] = $memberlist_cache['index'][$second_offset]; + $limit = $second_offset - ($memberlist_cache['num_members'] - $_REQUEST['start']) - ($second_offset > $memberlist_cache['num_members'] ? $cache_step_size - ($memberlist_cache['num_members'] % $cache_step_size) : 0); + } + + // Select the members from the database. + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member + FROM {db_prefix}members AS mem' . ($_REQUEST['sort'] === 'is_online' ? ' + LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member)' : '') . ($_REQUEST['sort'] === 'id_group' ? ' + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:regular_id_group} THEN mem.id_post_group ELSE mem.id_group END)' : '') . ' + WHERE mem.is_activated = {int:is_activated}' . (empty($where) ? '' : ' + AND ' . $where) . ' + ORDER BY {raw:sort} + LIMIT ' . $limit . ', ' . $modSettings['defaultMaxMembers'], + $query_parameters + ); + printMemberListRows($request); + $smcFunc['db_free_result']($request); + + // Add anchors at the start of each letter. + if ($_REQUEST['sort'] == 'real_name') + { + $last_letter = ''; + foreach ($context['members'] as $i => $dummy) + { + $this_letter = $smcFunc['strtolower']($smcFunc['substr']($context['members'][$i]['name'], 0, 1)); + + if ($this_letter != $last_letter && preg_match('~[a-z]~', $this_letter) === 1) + { + $context['members'][$i]['sort_letter'] = htmlspecialchars($this_letter); + $last_letter = $this_letter; + } + } + } +} + +// Search for members... +function MLSearch() +{ + global $txt, $scripturl, $context, $user_info, $modSettings, $smcFunc; + + $context['page_title'] = $txt['mlist_search']; + $context['can_moderate_forum'] = allowedTo('moderate_forum'); + + // Can they search custom fields? + $request = $smcFunc['db_query']('', ' + SELECT col_name, field_name, field_desc + FROM {db_prefix}custom_fields + WHERE active = {int:active} + ' . (allowedTo('admin_forum') ? '' : ' AND private < {int:private_level}') . ' + AND can_search = {int:can_search} + AND (field_type = {string:field_type_text} OR field_type = {string:field_type_textarea})', + array( + 'active' => 1, + 'can_search' => 1, + 'private_level' => 2, + 'field_type_text' => 'text', + 'field_type_textarea' => 'textarea', + ) + ); + $context['custom_search_fields'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['custom_search_fields'][$row['col_name']] = array( + 'colname' => $row['col_name'], + 'name' => $row['field_name'], + 'desc' => $row['field_desc'], + ); + $smcFunc['db_free_result']($request); + + // They're searching.. + if (isset($_REQUEST['search']) && isset($_REQUEST['fields'])) + { + $_POST['search'] = trim(isset($_GET['search']) ? $_GET['search'] : $_POST['search']); + $_POST['fields'] = isset($_GET['fields']) ? explode(',', $_GET['fields']) : $_POST['fields']; + + $context['old_search'] = $_REQUEST['search']; + $context['old_search_value'] = urlencode($_REQUEST['search']); + + // No fields? Use default... + if (empty($_POST['fields'])) + $_POST['fields'] = array('name'); + + $query_parameters = array( + 'regular_id_group' => 0, + 'is_activated' => 1, + 'blank_string' => '', + 'search' => '%' . strtr($smcFunc['htmlspecialchars']($_POST['search'], ENT_QUOTES), array('_' => '\\_', '%' => '\\%', '*' => '%')) . '%', + ); + + // Search for a name? + if (in_array('name', $_POST['fields'])) + $fields = array('member_name', 'real_name'); + else + $fields = array(); + // Search for messengers... + if (in_array('messenger', $_POST['fields']) && (!$user_info['is_guest'] || empty($modSettings['guest_hideContacts']))) + $fields += array(3 => 'msn', 'aim', 'icq', 'yim'); + // Search for websites. + if (in_array('website', $_POST['fields'])) + $fields += array(7 => 'website_title', 'website_url'); + // Search for groups. + if (in_array('group', $_POST['fields'])) + $fields += array(9 => 'IFNULL(group_name, {string:blank_string})'); + // Search for an email address? + if (in_array('email', $_POST['fields'])) + { + $fields += array(2 => allowedTo('moderate_forum') ? 'email_address' : '(hide_email = 0 AND email_address'); + $condition = allowedTo('moderate_forum') ? '' : ')'; + } + else + $condition = ''; + + $customJoin = array(); + $customCount = 10; + // Any custom fields to search for - these being tricky? + foreach ($_POST['fields'] as $field) + { + $curField = substr($field, 5); + if (substr($field, 0, 5) == 'cust_' && isset($context['custom_search_fields'][$curField])) + { + $customJoin[] = 'LEFT JOIN {db_prefix}themes AS t' . $curField . ' ON (t' . $curField . '.variable = {string:t' . $curField . '} AND t' . $curField . '.id_theme = 1 AND t' . $curField . '.id_member = mem.id_member)'; + $query_parameters['t' . $curField] = $curField; + $fields += array($customCount++ => 'IFNULL(t' . $curField . '.value, {string:blank_string})'); + } + } + + $query = $_POST['search'] == '' ? '= {string:blank_string}' : 'LIKE {string:search}'; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}members AS mem + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:regular_id_group} THEN mem.id_post_group ELSE mem.id_group END)' . + (empty($customJoin) ? '' : implode(' + ', $customJoin)) . ' + WHERE (' . implode( ' ' . $query . ' OR ', $fields) . ' ' . $query . $condition . ') + AND mem.is_activated = {int:is_activated}', + $query_parameters + ); + list ($numResults) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['page_index'] = constructPageIndex($scripturl . '?action=mlist;sa=search;search=' . $_POST['search'] . ';fields=' . implode(',', $_POST['fields']), $_REQUEST['start'], $numResults, $modSettings['defaultMaxMembers']); + + // Find the members from the database. + // !!!SLOW This query is slow. + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member + FROM {db_prefix}members AS mem + LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member) + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:regular_id_group} THEN mem.id_post_group ELSE mem.id_group END)' . + (empty($customJoin) ? '' : implode(' + ', $customJoin)) . ' + WHERE (' . implode( ' ' . $query . ' OR ', $fields) . ' ' . $query . $condition . ') + AND mem.is_activated = {int:is_activated} + LIMIT ' . $_REQUEST['start'] . ', ' . $modSettings['defaultMaxMembers'], + $query_parameters + ); + printMemberListRows($request); + $smcFunc['db_free_result']($request); + } + else + { + // These are all the possible fields. + $context['search_fields'] = array( + 'name' => $txt['mlist_search_name'], + 'email' => $txt['mlist_search_email'], + 'messenger' => $txt['mlist_search_messenger'], + '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'); + + $context['sub_template'] = 'search'; + $context['old_search'] = isset($_GET['search']) ? $_GET['search'] : (isset($_POST['search']) ? htmlspecialchars($_POST['search']) : ''); + } + + $context['linktree'][] = array( + 'url' => $scripturl . '?action=mlist;sa=search', + 'name' => &$context['page_title'] + ); +} + +function printMemberListRows($request) +{ + global $scripturl, $txt, $user_info, $modSettings; + global $context, $settings, $memberContext, $smcFunc; + + // Get the most posts. + $result = $smcFunc['db_query']('', ' + SELECT MAX(posts) + FROM {db_prefix}members', + array( + ) + ); + list ($MOST_POSTS) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Avoid division by zero... + if ($MOST_POSTS == 0) + $MOST_POSTS = 1; + + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[] = $row['id_member']; + + // Load all the members for display. + loadMemberData($members); + + $context['members'] = array(); + foreach ($members as $member) + { + if (!loadMemberContext($member)) + continue; + + $context['members'][$member] = $memberContext[$member]; + $context['members'][$member]['post_percent'] = round(($context['members'][$member]['real_posts'] * 100) / $MOST_POSTS); + $context['members'][$member]['registered_date'] = strftime('%Y-%m-%d', $context['members'][$member]['registered_timestamp']); + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/MessageIndex.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/MessageIndex.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1172 @@ + $board, + ) + ); + + redirectexit($board_info['redirect']); + } + + if (WIRELESS) + $context['sub_template'] = WIRELESS_PROTOCOL . '_messageindex'; + else + loadTemplate('MessageIndex'); + + $context['name'] = $board_info['name']; + $context['description'] = $board_info['description']; + // How many topics do we have in total? + $board_info['total_topics'] = allowedTo('approve_posts') ? $board_info['num_topics'] + $board_info['unapproved_topics'] : $board_info['num_topics'] + $board_info['unapproved_user_topics']; + + // View all the topics, or just a few? + $context['topics_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) && !WIRELESS ? $options['topics_per_page'] : $modSettings['defaultMaxTopics']; + $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) && !WIRELESS ? $options['messages_per_page'] : $modSettings['defaultMaxMessages']; + $maxindex = isset($_REQUEST['all']) && !empty($modSettings['enableAllMessages']) ? $board_info['total_topics'] : $context['topics_per_page']; + + // Right, let's only index normal stuff! + if (count($_GET) > 1) + { + $session_name = session_name(); + foreach ($_GET as $k => $v) + { + if (!in_array($k, array('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; + + // If we can view unapproved messages and there are some build up a list. + if (allowedTo('approve_posts') && ($board_info['unapproved_topics'] || $board_info['unapproved_posts'])) + { + $untopics = $board_info['unapproved_topics'] ? '' . $board_info['unapproved_topics'] . '' : 0; + $unposts = $board_info['unapproved_posts'] ? '' . ($board_info['unapproved_posts'] - $board_info['unapproved_topics']) . '' : 0; + $context['unapproved_posts_message'] = sprintf($txt['there_are_unapproved_topics'], $untopics, $unposts, $scripturl . '?action=moderate;area=postmod;sa=' . ($board_info['unapproved_topics'] ? 'topics' : 'posts') . ';brd=' . $board); + } + + // We only know these. + if (isset($_REQUEST['sort']) && !in_array($_REQUEST['sort'], array('subject', 'starter', 'last_poster', 'replies', 'views', 'first_post', 'last_post'))) + $_REQUEST['sort'] = 'last_post'; + + // Make sure the starting place makes sense and construct the page index. + if (isset($_REQUEST['sort'])) + $context['page_index'] = constructPageIndex($scripturl . '?board=' . $board . '.%1$d;sort=' . $_REQUEST['sort'] . (isset($_REQUEST['desc']) ? ';desc' : ''), $_REQUEST['start'], $board_info['total_topics'], $maxindex, true); + else + $context['page_index'] = constructPageIndex($scripturl . '?board=' . $board . '.%1$d', $_REQUEST['start'], $board_info['total_topics'], $maxindex, true); + $context['start'] = &$_REQUEST['start']; + + // Set a canonical URL for this page. + $context['canonical_url'] = $scripturl . '?board=' . $board . '.' . $context['start']; + + $context['links'] = array( + 'first' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?board=' . $board . '.0' : '', + 'prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?board=' . $board . '.' . ($_REQUEST['start'] - $context['topics_per_page']) : '', + 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $board_info['total_topics'] ? $scripturl . '?board=' . $board . '.' . ($_REQUEST['start'] + $context['topics_per_page']) : '', + 'last' => $_REQUEST['start'] + $context['topics_per_page'] < $board_info['total_topics'] ? $scripturl . '?board=' . $board . '.' . (floor(($board_info['total_topics'] - 1) / $context['topics_per_page']) * $context['topics_per_page']) : '', + 'up' => $board_info['parent'] == 0 ? $scripturl . '?' : $scripturl . '?board=' . $board_info['parent'] . '.0' + ); + + $context['page_info'] = array( + 'current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, + 'num_pages' => floor(($board_info['total_topics'] - 1) / $context['topics_per_page']) + 1 + ); + + if (isset($_REQUEST['all']) && !empty($modSettings['enableAllMessages']) && $maxindex > $modSettings['enableAllMessages']) + { + $maxindex = $modSettings['enableAllMessages']; + $_REQUEST['start'] = 0; + } + + // Build a list of the board's moderators. + $context['moderators'] = &$board_info['moderators']; + $context['link_moderators'] = array(); + if (!empty($board_info['moderators'])) + { + foreach ($board_info['moderators'] as $mod) + $context['link_moderators'][] ='' . $mod['name'] . ''; + + $context['linktree'][count($context['linktree']) - 1]['extra_after'] = ' (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')'; + } + + // Mark current and parent boards as seen. + if (!$user_info['is_guest']) + { + // We can't know they read it if we allow prefetches. + if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') + { + ob_end_clean(); + header('HTTP/1.1 403 Prefetch Forbidden'); + die; + } + + $smcFunc['db_insert']('replace', + '{db_prefix}log_boards', + array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'), + array($modSettings['maxMsgID'], $user_info['id'], $board), + array('id_member', 'id_board') + ); + + if (!empty($board_info['parent_boards'])) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_boards + SET id_msg = {int:id_msg} + WHERE id_member = {int:current_member} + AND id_board IN ({array_int:board_list})', + array( + 'current_member' => $user_info['id'], + 'board_list' => array_keys($board_info['parent_boards']), + 'id_msg' => $modSettings['maxMsgID'], + ) + ); + + // We've seen all these boards now! + foreach ($board_info['parent_boards'] as $k => $dummy) + if (isset($_SESSION['topicseen_cache'][$k])) + unset($_SESSION['topicseen_cache'][$k]); + } + + if (isset($_SESSION['topicseen_cache'][$board])) + unset($_SESSION['topicseen_cache'][$board]); + + $request = $smcFunc['db_query']('', ' + SELECT sent + FROM {db_prefix}log_notify + WHERE id_board = {int:current_board} + AND id_member = {int:current_member} + LIMIT 1', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + ) + ); + $context['is_marked_notify'] = $smcFunc['db_num_rows']($request) != 0; + if ($context['is_marked_notify']) + { + list ($sent) = $smcFunc['db_fetch_row']($request); + if (!empty($sent)) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_notify + SET sent = {int:is_sent} + WHERE id_board = {int:current_board} + AND id_member = {int:current_member}', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'is_sent' => 0, + ) + ); + } + } + $smcFunc['db_free_result']($request); + } + else + $context['is_marked_notify'] = false; + + // 'Print' the header and board info. + $context['page_title'] = strip_tags($board_info['name']); + + // Set the variables up for the template. + $context['can_mark_notify'] = allowedTo('mark_notify') && !$user_info['is_guest']; + $context['can_post_new'] = allowedTo('post_new') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_topics')); + $context['can_post_poll'] = $modSettings['pollMode'] == '1' && allowedTo('poll_post') && $context['can_post_new']; + $context['can_moderate_forum'] = allowedTo('moderate_forum'); + $context['can_approve_posts'] = allowedTo('approve_posts'); + + require_once($sourcedir . '/Subs-BoardIndex.php'); + $boardIndexOptions = array( + 'include_categories' => false, + 'base_level' => $board_info['child_level'] + 1, + 'parent_id' => $board_info['id'], + 'set_latest_post' => false, + 'countChildPosts' => !empty($modSettings['countChildPosts']), + ); + $context['boards'] = getBoardIndex($boardIndexOptions); + + // Nosey, nosey - who's viewing this topic? + if (!empty($settings['display_who_viewing'])) + { + $context['view_members'] = array(); + $context['view_members_list'] = array(); + $context['view_num_hidden'] = 0; + + $request = $smcFunc['db_query']('', ' + SELECT + lo.id_member, lo.log_time, mem.real_name, mem.member_name, mem.show_online, + mg.online_color, mg.id_group, mg.group_name + FROM {db_prefix}log_online AS lo + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lo.id_member) + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_member_group} THEN mem.id_post_group ELSE mem.id_group END) + WHERE INSTR(lo.url, {string:in_url_string}) > 0 OR lo.session = {string:session}', + array( + 'reg_member_group' => 0, + 'in_url_string' => 's:5:"board";i:' . $board . ';', + 'session' => $user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($row['id_member'])) + continue; + + if (!empty($row['online_color'])) + $link = '' . $row['real_name'] . ''; + else + $link = '' . $row['real_name'] . ''; + + $is_buddy = in_array($row['id_member'], $user_info['buddies']); + if ($is_buddy) + $link = '' . $link . ''; + + if (!empty($row['show_online']) || allowedTo('moderate_forum')) + $context['view_members_list'][$row['log_time'] . $row['member_name']] = empty($row['show_online']) ? '' . $link . '' : $link; + $context['view_members'][$row['log_time'] . $row['member_name']] = array( + 'id' => $row['id_member'], + 'username' => $row['member_name'], + 'name' => $row['real_name'], + 'group' => $row['id_group'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => $link, + 'is_buddy' => $is_buddy, + 'hidden' => empty($row['show_online']), + ); + + if (empty($row['show_online'])) + $context['view_num_hidden']++; + } + $context['view_num_guests'] = $smcFunc['db_num_rows']($request) - count($context['view_members']); + $smcFunc['db_free_result']($request); + + // Put them in "last clicked" order. + krsort($context['view_members_list']); + krsort($context['view_members']); + } + + // Default sort methods. + $sort_methods = array( + 'subject' => 'mf.subject', + 'starter' => 'IFNULL(memf.real_name, mf.poster_name)', + 'last_poster' => 'IFNULL(meml.real_name, ml.poster_name)', + 'replies' => 't.num_replies', + 'views' => 't.num_views', + 'first_post' => 't.id_topic', + 'last_post' => 't.id_last_msg' + ); + + // They didn't pick one, default to by last post descending. + if (!isset($_REQUEST['sort']) || !isset($sort_methods[$_REQUEST['sort']])) + { + $context['sort_by'] = 'last_post'; + $_REQUEST['sort'] = 'id_last_msg'; + $ascending = isset($_REQUEST['asc']); + } + // Otherwise default to ascending. + else + { + $context['sort_by'] = $_REQUEST['sort']; + $_REQUEST['sort'] = $sort_methods[$_REQUEST['sort']]; + $ascending = !isset($_REQUEST['desc']); + } + + $context['sort_direction'] = $ascending ? 'up' : 'down'; + + // Calculate the fastest way to get the topics. + $start = (int) $_REQUEST['start']; + if ($start > ($board_info['total_topics'] - 1) / 2) + { + $ascending = !$ascending; + $fake_ascending = true; + $maxindex = $board_info['total_topics'] < $start + $maxindex + 1 ? $board_info['total_topics'] - $start : $maxindex; + $start = $board_info['total_topics'] < $start + $maxindex + 1 ? 0 : $board_info['total_topics'] - $start - $maxindex; + } + else + $fake_ascending = false; + + // Setup the default topic icons... + $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless', 'clip'); + $context['icon_sources'] = array(); + foreach ($stable_icons as $icon) + $context['icon_sources'][$icon] = 'images_url'; + + $topic_ids = array(); + $context['topics'] = array(); + + // Sequential pages are often not optimized, so we add an additional query. + $pre_query = $start > 0; + if ($pre_query && $maxindex > 0) + { + $request = $smcFunc['db_query']('', ' + SELECT t.id_topic + FROM {db_prefix}topics AS t' . ($context['sort_by'] === 'last_poster' ? ' + INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)' : (in_array($context['sort_by'], array('starter', 'subject')) ? ' + INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)' : '')) . ($context['sort_by'] === 'starter' ? ' + LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)' : '') . ($context['sort_by'] === 'last_poster' ? ' + LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)' : '') . ' + WHERE t.id_board = {int:current_board}' . (!$modSettings['postmod_active'] || $context['can_approve_posts'] ? '' : ' + AND (t.approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR t.id_member_started = {int:current_member}') . ')') . ' + ORDER BY ' . (!empty($modSettings['enableStickyTopics']) ? 'is_sticky' . ($fake_ascending ? '' : ' DESC') . ', ' : '') . $_REQUEST['sort'] . ($ascending ? '' : ' DESC') . ' + LIMIT {int:start}, {int:maxindex}', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'is_approved' => 1, + 'id_member_guest' => 0, + 'start' => $start, + 'maxindex' => $maxindex, + ) + ); + $topic_ids = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topic_ids[] = $row['id_topic']; + } + + // Grab the appropriate topic information... + if (!$pre_query || !empty($topic_ids)) + { + // For search engine effectiveness we'll link guests differently. + $context['pageindex_multiplier'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) && !WIRELESS ? $options['messages_per_page'] : $modSettings['defaultMaxMessages']; + + $result = $smcFunc['db_query']('substring', ' + SELECT + t.id_topic, t.num_replies, t.locked, t.num_views, t.is_sticky, t.id_poll, t.id_previous_board, + ' . ($user_info['is_guest'] ? '0' : 'IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1') . ' AS new_from, + t.id_last_msg, t.approved, t.unapproved_posts, ml.poster_time AS last_poster_time, + ml.id_msg_modified, ml.subject AS last_subject, ml.icon AS last_icon, + ml.poster_name AS last_member_name, ml.id_member AS last_id_member, + IFNULL(meml.real_name, ml.poster_name) AS last_display_name, t.id_first_msg, + mf.poster_time AS first_poster_time, mf.subject AS first_subject, mf.icon AS first_icon, + mf.poster_name AS first_member_name, mf.id_member AS first_id_member, + IFNULL(memf.real_name, mf.poster_name) AS first_display_name, SUBSTRING(ml.body, 1, 385) AS last_body, + SUBSTRING(mf.body, 1, 385) AS first_body, ml.smileys_enabled AS last_smileys, mf.smileys_enabled AS first_smileys + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg) + INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member) + LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)' . ($user_info['is_guest'] ? '' : ' + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})'). ' + WHERE ' . ($pre_query ? 't.id_topic IN ({array_int:topic_list})' : 't.id_board = {int:current_board}') . (!$modSettings['postmod_active'] || $context['can_approve_posts'] ? '' : ' + AND (t.approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR t.id_member_started = {int:current_member}') . ')') . ' + ORDER BY ' . ($pre_query ? 'FIND_IN_SET(t.id_topic, {string:find_set_topics})' : (!empty($modSettings['enableStickyTopics']) ? 'is_sticky' . ($fake_ascending ? '' : ' DESC') . ', ' : '') . $_REQUEST['sort'] . ($ascending ? '' : ' DESC')) . ' + LIMIT ' . ($pre_query ? '' : '{int:start}, ') . '{int:maxindex}', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'topic_list' => $topic_ids, + 'is_approved' => 1, + 'find_set_topics' => implode(',', $topic_ids), + 'start' => $start, + 'maxindex' => $maxindex, + ) + ); + + // Begin 'printing' the message index for current board. + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if ($row['id_poll'] > 0 && $modSettings['pollMode'] == '0') + continue; + + if (!$pre_query) + $topic_ids[] = $row['id_topic']; + + if (!empty($settings['message_index_preview'])) + { + // Limit them to 128 characters - do this FIRST because it's a lot of wasted censoring otherwise. + $row['first_body'] = strip_tags(strtr(parse_bbc($row['first_body'], $row['first_smileys'], $row['id_first_msg']), array('
' => ' '))); + if ($smcFunc['strlen']($row['first_body']) > 128) + $row['first_body'] = $smcFunc['substr']($row['first_body'], 0, 128) . '...'; + $row['last_body'] = strip_tags(strtr(parse_bbc($row['last_body'], $row['last_smileys'], $row['id_last_msg']), array('
' => ' '))); + if ($smcFunc['strlen']($row['last_body']) > 128) + $row['last_body'] = $smcFunc['substr']($row['last_body'], 0, 128) . '...'; + + // Censor the subject and message preview. + censorText($row['first_subject']); + censorText($row['first_body']); + + // Don't censor them twice! + if ($row['id_first_msg'] == $row['id_last_msg']) + { + $row['last_subject'] = $row['first_subject']; + $row['last_body'] = $row['first_body']; + } + else + { + censorText($row['last_subject']); + censorText($row['last_body']); + } + } + else + { + $row['first_body'] = ''; + $row['last_body'] = ''; + censorText($row['first_subject']); + + if ($row['id_first_msg'] == $row['id_last_msg']) + $row['last_subject'] = $row['first_subject']; + else + censorText($row['last_subject']); + } + + // Decide how many pages the topic should have. + if ($row['num_replies'] + 1 > $context['messages_per_page']) + { + $pages = '« '; + + // We can't pass start by reference. + $start = -1; + $pages .= constructPageIndex($scripturl . '?topic=' . $row['id_topic'] . '.%1$d', $start, $row['num_replies'] + 1, $context['messages_per_page'], true); + + // If we can use all, show all. + if (!empty($modSettings['enableAllMessages']) && $row['num_replies'] + 1 < $modSettings['enableAllMessages']) + $pages .= '  ' . $txt['all'] . ''; + $pages .= ' »'; + } + else + $pages = ''; + + // We need to check the topic icons exist... + if (empty($modSettings['messageIconChecks_disable'])) + { + if (!isset($context['icon_sources'][$row['first_icon']])) + $context['icon_sources'][$row['first_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['first_icon'] . '.gif') ? 'images_url' : 'default_images_url'; + if (!isset($context['icon_sources'][$row['last_icon']])) + $context['icon_sources'][$row['last_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['last_icon'] . '.gif') ? 'images_url' : 'default_images_url'; + } + else + { + if (!isset($context['icon_sources'][$row['first_icon']])) + $context['icon_sources'][$row['first_icon']] = 'images_url'; + if (!isset($context['icon_sources'][$row['last_icon']])) + $context['icon_sources'][$row['last_icon']] = 'images_url'; + } + + // 'Print' the topic info. + $context['topics'][$row['id_topic']] = array( + 'id' => $row['id_topic'], + 'first_post' => array( + 'id' => $row['id_first_msg'], + 'member' => array( + 'username' => $row['first_member_name'], + 'name' => $row['first_display_name'], + 'id' => $row['first_id_member'], + 'href' => !empty($row['first_id_member']) ? $scripturl . '?action=profile;u=' . $row['first_id_member'] : '', + 'link' => !empty($row['first_id_member']) ? '' . $row['first_display_name'] . '' : $row['first_display_name'] + ), + 'time' => timeformat($row['first_poster_time']), + 'timestamp' => forum_time(true, $row['first_poster_time']), + 'subject' => $row['first_subject'], + 'preview' => $row['first_body'], + 'icon' => $row['first_icon'], + 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.gif', + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'link' => '' . $row['first_subject'] . '' + ), + 'last_post' => array( + 'id' => $row['id_last_msg'], + 'member' => array( + 'username' => $row['last_member_name'], + 'name' => $row['last_display_name'], + 'id' => $row['last_id_member'], + 'href' => !empty($row['last_id_member']) ? $scripturl . '?action=profile;u=' . $row['last_id_member'] : '', + 'link' => !empty($row['last_id_member']) ? '' . $row['last_display_name'] . '' : $row['last_display_name'] + ), + 'time' => timeformat($row['last_poster_time']), + 'timestamp' => forum_time(true, $row['last_poster_time']), + 'subject' => $row['last_subject'], + 'preview' => $row['last_body'], + 'icon' => $row['last_icon'], + 'icon_url' => $settings[$context['icon_sources'][$row['last_icon']]] . '/post/' . $row['last_icon'] . '.gif', + 'href' => $scripturl . '?topic=' . $row['id_topic'] . ($user_info['is_guest'] ? ('.' . (!empty($options['view_newest_first']) ? 0 : ((int) (($row['num_replies']) / $context['pageindex_multiplier'])) * $context['pageindex_multiplier']) . '#msg' . $row['id_last_msg']) : (($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . '#new')), + 'link' => '' . $row['last_subject'] . '' + ), + 'is_sticky' => !empty($modSettings['enableStickyTopics']) && !empty($row['is_sticky']), + 'is_locked' => !empty($row['locked']), + 'is_poll' => $modSettings['pollMode'] == '1' && $row['id_poll'] > 0, + 'is_hot' => $row['num_replies'] >= $modSettings['hotTopicPosts'], + 'is_very_hot' => $row['num_replies'] >= $modSettings['hotTopicVeryPosts'], + 'is_posted_in' => false, + 'icon' => $row['first_icon'], + 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.gif', + 'subject' => $row['first_subject'], + 'new' => $row['new_from'] <= $row['id_msg_modified'], + 'new_from' => $row['new_from'], + 'newtime' => $row['new_from'], + 'new_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['new_from'] . '#new', + 'pages' => $pages, + 'replies' => comma_format($row['num_replies']), + 'views' => comma_format($row['num_views']), + 'approved' => $row['approved'], + 'unapproved_posts' => $row['unapproved_posts'], + ); + + determineTopicClass($context['topics'][$row['id_topic']]); + } + $smcFunc['db_free_result']($result); + + // Fix the sequence of topics if they were retrieved in the wrong order. (for speed reasons...) + if ($fake_ascending) + $context['topics'] = array_reverse($context['topics'], true); + + if (!empty($modSettings['enableParticipation']) && !$user_info['is_guest'] && !empty($topic_ids)) + { + $result = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topic_list}) + AND id_member = {int:current_member} + GROUP BY id_topic + LIMIT ' . count($topic_ids), + array( + 'current_member' => $user_info['id'], + 'topic_list' => $topic_ids, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $context['topics'][$row['id_topic']]['is_posted_in'] = true; + $context['topics'][$row['id_topic']]['class'] = 'my_' . $context['topics'][$row['id_topic']]['class']; + } + $smcFunc['db_free_result']($result); + } + } + + $context['jump_to'] = array( + 'label' => addslashes(un_htmlspecialchars($txt['jump_to'])), + 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), array('&' => '&'))), + 'child_level' => $board_info['child_level'], + ); + + // Is Quick Moderation active/needed? + if (!empty($options['display_quick_mod']) && !empty($context['topics'])) + { + $context['can_lock'] = allowedTo('lock_any'); + $context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']); + $context['can_move'] = allowedTo('move_any'); + $context['can_remove'] = allowedTo('remove_any'); + $context['can_merge'] = allowedTo('merge_any'); + // Ignore approving own topics as it's unlikely to come up... + $context['can_approve'] = $modSettings['postmod_active'] && allowedTo('approve_posts') && !empty($board_info['unapproved_topics']); + // Can we restore topics? + $context['can_restore'] = allowedTo('move_any') && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board; + + // Set permissions for all the topics. + foreach ($context['topics'] as $t => $topic) + { + $started = $topic['first_post']['member']['id'] == $user_info['id']; + $context['topics'][$t]['quick_mod'] = array( + 'lock' => allowedTo('lock_any') || ($started && allowedTo('lock_own')), + 'sticky' => allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']), + 'move' => allowedTo('move_any') || ($started && allowedTo('move_own')), + 'modify' => allowedTo('modify_any') || ($started && allowedTo('modify_own')), + 'remove' => allowedTo('remove_any') || ($started && allowedTo('remove_own')), + 'approve' => $context['can_approve'] && $topic['unapproved_posts'] + ); + $context['can_lock'] |= ($started && allowedTo('lock_own')); + $context['can_move'] |= ($started && allowedTo('move_own')); + $context['can_remove'] |= ($started && allowedTo('remove_own')); + } + + // Find the boards/cateogories they can move their topic to. + if ($options['display_quick_mod'] == 1 && $context['can_move'] && !empty($context['topics'])) + { + require_once($sourcedir . '/Subs-MessageIndex.php'); + $boardListOptions = array( + 'excluded_boards' => array($board), + 'not_redirection' => true, + 'use_permissions' => true, + 'selected_board' => empty($_SESSION['move_to_topic']) ? null : $_SESSION['move_to_topic'], + ); + $context['move_to_boards'] = getBoardList($boardListOptions); + + // Make the boards safe for display. + foreach ($context['move_to_boards'] as $id_cat => $cat) + { + $context['move_to_boards'][$id_cat]['name'] = strip_tags($cat['name']); + foreach ($cat['boards'] as $id_board => $board) + $context['move_to_boards'][$id_cat]['boards'][$id_board]['name'] = strip_tags($board['name']); + } + + // With no other boards to see, it's useless to move. + if (empty($context['move_to_boards'])) + $context['can_move'] = false; + } + // Can we use quick moderation checkboxes? + if ($options['display_quick_mod'] == 1) + $context['can_quick_mod'] = $context['user']['is_logged'] || $context['can_approve'] || $context['can_remove'] || $context['can_lock'] || $context['can_sticky'] || $context['can_move'] || $context['can_merge'] || $context['can_restore']; + // Or the icons? + else + $context['can_quick_mod'] = $context['can_remove'] || $context['can_lock'] || $context['can_sticky'] || $context['can_move']; + } + + // If there are children, but no topics and no ability to post topics... + $context['no_topic_listing'] = !empty($context['boards']) && empty($context['topics']) && !$context['can_post_new']; +} + +// Allows for moderation from the message index. +function QuickModeration() +{ + global $sourcedir, $board, $user_info, $modSettings, $sourcedir, $smcFunc, $context; + + // Check the session = get or post. + checkSession('request'); + + // Lets go straight to the restore area. + if (isset($_REQUEST['qaction']) && $_REQUEST['qaction'] == 'restore' && !empty($_REQUEST['topics'])) + redirectexit('action=restoretopic;topics=' . implode(',', $_REQUEST['topics']) . ';' . $context['session_var'] . '=' . $context['session_id']); + + if (isset($_SESSION['topicseen_cache'])) + $_SESSION['topicseen_cache'] = array(); + + // This is going to be needed to send off the notifications and for updateLastMessages(). + require_once($sourcedir . '/Subs-Post.php'); + + // Remember the last board they moved things to. + if (isset($_REQUEST['move_to'])) + $_SESSION['move_to_topic'] = $_REQUEST['move_to']; + + // Only a few possible actions. + $possibleActions = array(); + + if (!empty($board)) + { + $boards_can = array( + 'make_sticky' => allowedTo('make_sticky') ? array($board) : array(), + 'move_any' => allowedTo('move_any') ? array($board) : array(), + 'move_own' => allowedTo('move_own') ? array($board) : array(), + 'remove_any' => allowedTo('remove_any') ? array($board) : array(), + 'remove_own' => allowedTo('remove_own') ? array($board) : array(), + 'lock_any' => allowedTo('lock_any') ? array($board) : array(), + 'lock_own' => allowedTo('lock_own') ? array($board) : array(), + 'merge_any' => allowedTo('merge_any') ? array($board) : array(), + 'approve_posts' => allowedTo('approve_posts') ? array($board) : array(), + ); + + $redirect_url = 'board=' . $board . '.' . $_REQUEST['start']; + } + else + { + // !!! Ugly. There's no getting around this, is there? + // !!! Maybe just do this on the actions people want to use? + $boards_can = array( + 'make_sticky' => boardsAllowedTo('make_sticky'), + 'move_any' => boardsAllowedTo('move_any'), + 'move_own' => boardsAllowedTo('move_own'), + 'remove_any' => boardsAllowedTo('remove_any'), + 'remove_own' => boardsAllowedTo('remove_own'), + 'lock_any' => boardsAllowedTo('lock_any'), + 'lock_own' => boardsAllowedTo('lock_own'), + 'merge_any' => boardsAllowedTo('merge_any'), + 'approve_posts' => boardsAllowedTo('approve_posts'), + ); + + $redirect_url = isset($_POST['redirect_url']) ? $_POST['redirect_url'] : (isset($_SESSION['old_url']) ? $_SESSION['old_url'] : ''); + } + + if (!$user_info['is_guest']) + $possibleActions[] = 'markread'; + if (!empty($boards_can['make_sticky']) && !empty($modSettings['enableStickyTopics'])) + $possibleActions[] = 'sticky'; + if (!empty($boards_can['move_any']) || !empty($boards_can['move_own'])) + $possibleActions[] = 'move'; + if (!empty($boards_can['remove_any']) || !empty($boards_can['remove_own'])) + $possibleActions[] = 'remove'; + if (!empty($boards_can['lock_any']) || !empty($boards_can['lock_own'])) + $possibleActions[] = 'lock'; + if (!empty($boards_can['merge_any'])) + $possibleActions[] = 'merge'; + if (!empty($boards_can['approve_posts'])) + $possibleActions[] = 'approve'; + + // Two methods: $_REQUEST['actions'] (id_topic => action), and $_REQUEST['topics'] and $_REQUEST['qaction']. + // (if action is 'move', $_REQUEST['move_to'] or $_REQUEST['move_tos'][$topic] is used.) + if (!empty($_REQUEST['topics'])) + { + // If the action isn't valid, just quit now. + if (empty($_REQUEST['qaction']) || !in_array($_REQUEST['qaction'], $possibleActions)) + redirectexit($redirect_url); + + // Merge requires all topics as one parameter and can be done at once. + if ($_REQUEST['qaction'] == 'merge') + { + // Merge requires at least two topics. + if (empty($_REQUEST['topics']) || count($_REQUEST['topics']) < 2) + redirectexit($redirect_url); + + require_once($sourcedir . '/SplitTopics.php'); + return MergeExecute($_REQUEST['topics']); + } + + // Just convert to the other method, to make it easier. + foreach ($_REQUEST['topics'] as $topic) + $_REQUEST['actions'][(int) $topic] = $_REQUEST['qaction']; + } + + // Weird... how'd you get here? + if (empty($_REQUEST['actions'])) + redirectexit($redirect_url); + + // Validate each action. + $temp = array(); + foreach ($_REQUEST['actions'] as $topic => $action) + { + if (in_array($action, $possibleActions)) + $temp[(int) $topic] = $action; + } + $_REQUEST['actions'] = $temp; + + if (!empty($_REQUEST['actions'])) + { + // Find all topics... + $request = $smcFunc['db_query']('', ' + SELECT id_topic, id_member_started, id_board, locked, approved, unapproved_posts + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:action_topic_ids}) + LIMIT ' . count($_REQUEST['actions']), + array( + 'action_topic_ids' => array_keys($_REQUEST['actions']), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!empty($board)) + { + if ($row['id_board'] != $board || ($modSettings['postmod_active'] && !$row['approved'] && !allowedTo('approve_posts'))) + unset($_REQUEST['actions'][$row['id_topic']]); + } + else + { + // Don't allow them to act on unapproved posts they can't see... + if ($modSettings['postmod_active'] && !$row['approved'] && !in_array(0, $boards_can['approve_posts']) && !in_array($row['id_board'], $boards_can['approve_posts'])) + unset($_REQUEST['actions'][$row['id_topic']]); + // Goodness, this is fun. We need to validate the action. + elseif ($_REQUEST['actions'][$row['id_topic']] == 'sticky' && !in_array(0, $boards_can['make_sticky']) && !in_array($row['id_board'], $boards_can['make_sticky'])) + unset($_REQUEST['actions'][$row['id_topic']]); + elseif ($_REQUEST['actions'][$row['id_topic']] == 'move' && !in_array(0, $boards_can['move_any']) && !in_array($row['id_board'], $boards_can['move_any']) && ($row['id_member_started'] != $user_info['id'] || (!in_array(0, $boards_can['move_own']) && !in_array($row['id_board'], $boards_can['move_own'])))) + unset($_REQUEST['actions'][$row['id_topic']]); + elseif ($_REQUEST['actions'][$row['id_topic']] == 'remove' && !in_array(0, $boards_can['remove_any']) && !in_array($row['id_board'], $boards_can['remove_any']) && ($row['id_member_started'] != $user_info['id'] || (!in_array(0, $boards_can['remove_own']) && !in_array($row['id_board'], $boards_can['remove_own'])))) + unset($_REQUEST['actions'][$row['id_topic']]); + elseif ($_REQUEST['actions'][$row['id_topic']] == 'lock' && !in_array(0, $boards_can['lock_any']) && !in_array($row['id_board'], $boards_can['lock_any']) && ($row['id_member_started'] != $user_info['id'] || $locked == 1 || (!in_array(0, $boards_can['lock_own']) && !in_array($row['id_board'], $boards_can['lock_own'])))) + unset($_REQUEST['actions'][$row['id_topic']]); + // If the topic is approved then you need permission to approve the posts within. + elseif ($_REQUEST['actions'][$row['id_topic']] == 'approve' && (!$row['unapproved_posts'] || (!in_array(0, $boards_can['approve_posts']) && !in_array($row['id_board'], $boards_can['approve_posts'])))) + unset($_REQUEST['actions'][$row['id_topic']]); + } + } + $smcFunc['db_free_result']($request); + } + + $stickyCache = array(); + $moveCache = array(0 => array(), 1 => array()); + $removeCache = array(); + $lockCache = array(); + $markCache = array(); + $approveCache = array(); + + // Separate the actions. + foreach ($_REQUEST['actions'] as $topic => $action) + { + $topic = (int) $topic; + + if ($action == 'markread') + $markCache[] = $topic; + elseif ($action == 'sticky') + $stickyCache[] = $topic; + elseif ($action == 'move') + { + // $moveCache[0] is the topic, $moveCache[1] is the board to move to. + $moveCache[1][$topic] = (int) (isset($_REQUEST['move_tos'][$topic]) ? $_REQUEST['move_tos'][$topic] : $_REQUEST['move_to']); + + if (empty($moveCache[1][$topic])) + continue; + + $moveCache[0][] = $topic; + } + elseif ($action == 'remove') + $removeCache[] = $topic; + elseif ($action == 'lock') + $lockCache[] = $topic; + elseif ($action == 'approve') + $approveCache[] = $topic; + } + + if (empty($board)) + $affectedBoards = array(); + else + $affectedBoards = array($board => array(0, 0)); + + // Do all the stickies... + if (!empty($stickyCache)) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET is_sticky = CASE WHEN is_sticky = {int:is_sticky} THEN 0 ELSE 1 END + WHERE id_topic IN ({array_int:sticky_topic_ids})', + array( + 'sticky_topic_ids' => $stickyCache, + 'is_sticky' => 1, + ) + ); + + // Get the board IDs and Sticky status + $request = $smcFunc['db_query']('', ' + SELECT id_topic, id_board, is_sticky + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:sticky_topic_ids}) + LIMIT ' . count($stickyCache), + array( + 'sticky_topic_ids' => $stickyCache, + ) + ); + $stickyCacheBoards = array(); + $stickyCacheStatus = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $stickyCacheBoards[$row['id_topic']] = $row['id_board']; + $stickyCacheStatus[$row['id_topic']] = empty($row['is_sticky']); + } + $smcFunc['db_free_result']($request); + } + + // Move sucka! (this is, by the by, probably the most complicated part....) + if (!empty($moveCache[0])) + { + // I know - I just KNOW you're trying to beat the system. Too bad for you... we CHECK :P. + $request = $smcFunc['db_query']('', ' + SELECT t.id_topic, t.id_board, b.count_posts + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}boards AS b ON (t.id_board = b.id_board) + WHERE t.id_topic IN ({array_int:move_topic_ids})' . (!empty($board) && !allowedTo('move_any') ? ' + AND t.id_member_started = {int:current_member}' : '') . ' + LIMIT ' . count($moveCache[0]), + array( + 'current_member' => $user_info['id'], + 'move_topic_ids' => $moveCache[0], + ) + ); + $moveTos = array(); + $moveCache2 = array(); + $countPosts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $to = $moveCache[1][$row['id_topic']]; + + if (empty($to)) + continue; + + // Does this topic's board count the posts or not? + $countPosts[$row['id_topic']] = empty($row['count_posts']); + + if (!isset($moveTos[$to])) + $moveTos[$to] = array(); + + $moveTos[$to][] = $row['id_topic']; + + // For reporting... + $moveCache2[] = array($row['id_topic'], $row['id_board'], $to); + } + $smcFunc['db_free_result']($request); + + $moveCache = $moveCache2; + + require_once($sourcedir . '/MoveTopic.php'); + + // Do the actual moves... + foreach ($moveTos as $to => $topics) + moveTopics($topics, $to); + + // Does the post counts need to be updated? + if (!empty($moveTos)) + { + $topicRecounts = array(); + $request = $smcFunc['db_query']('', ' + SELECT id_board, count_posts + FROM {db_prefix}boards + WHERE id_board IN ({array_int:move_boards})', + array( + 'move_boards' => array_keys($moveTos), + ) + ); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $cp = empty($row['count_posts']); + + // Go through all the topics that are being moved to this board. + foreach ($moveTos[$row['id_board']] as $topic) + { + // If both boards have the same value for post counting then no adjustment needs to be made. + if ($countPosts[$topic] != $cp) + { + // If the board being moved to does count the posts then the other one doesn't so add to their post count. + $topicRecounts[$topic] = $cp ? '+' : '-'; + } + } + } + + $smcFunc['db_free_result']($request); + + if (!empty($topicRecounts)) + { + $members = array(); + + // Get all the members who have posted in the moved topics. + $request = $smcFunc['db_query']('', ' + SELECT id_member, id_topic + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:moved_topic_ids})', + array( + 'moved_topic_ids' => array_keys($topicRecounts), + ) + ); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($members[$row['id_member']])) + $members[$row['id_member']] = 0; + + if ($topicRecounts[$row['id_topic']] === '+') + $members[$row['id_member']] += 1; + else + $members[$row['id_member']] -= 1; + } + + $smcFunc['db_free_result']($request); + + // And now update them member's post counts + foreach ($members as $id_member => $post_adj) + updateMemberData($id_member, array('posts' => 'posts + ' . $post_adj)); + + } + } + } + + // Now delete the topics... + if (!empty($removeCache)) + { + // They can only delete their own topics. (we wouldn't be here if they couldn't do that..) + $result = $smcFunc['db_query']('', ' + SELECT id_topic, id_board + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:removed_topic_ids})' . (!empty($board) && !allowedTo('remove_any') ? ' + AND id_member_started = {int:current_member}' : '') . ' + LIMIT ' . count($removeCache), + array( + 'current_member' => $user_info['id'], + 'removed_topic_ids' => $removeCache, + ) + ); + + $removeCache = array(); + $removeCacheBoards = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $removeCache[] = $row['id_topic']; + $removeCacheBoards[$row['id_topic']] = $row['id_board']; + } + $smcFunc['db_free_result']($result); + + // Maybe *none* were their own topics. + if (!empty($removeCache)) + { + // Gotta send the notifications *first*! + foreach ($removeCache as $topic) + { + // Only log the topic ID if it's not in the recycle board. + logAction('remove', array((empty($modSettings['recycle_enable']) || $modSettings['recycle_board'] != $removeCacheBoards[$topic] ? 'topic' : 'old_topic_id') => $topic, 'board' => $removeCacheBoards[$topic])); + sendNotifications($topic, 'remove'); + } + + require_once($sourcedir . '/RemoveTopic.php'); + removeTopics($removeCache); + } + } + + // Approve the topics... + if (!empty($approveCache)) + { + // We need unapproved topic ids and their authors! + $request = $smcFunc['db_query']('', ' + SELECT id_topic, id_member_started + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:approve_topic_ids}) + AND approved = {int:not_approved} + LIMIT ' . count($approveCache), + array( + 'approve_topic_ids' => $approveCache, + 'not_approved' => 0, + ) + ); + $approveCache = array(); + $approveCacheMembers = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $approveCache[] = $row['id_topic']; + $approveCacheMembers[$row['id_topic']] = $row['id_member_started']; + } + $smcFunc['db_free_result']($request); + + // Any topics to approve? + if (!empty($approveCache)) + { + // Handle the approval part... + approveTopics($approveCache); + + // Time for some logging! + foreach ($approveCache as $topic) + logAction('approve_topic', array('topic' => $topic, 'member' => $approveCacheMembers[$topic])); + } + } + + // And (almost) lastly, lock the topics... + if (!empty($lockCache)) + { + $lockStatus = array(); + + // Gotta make sure they CAN lock/unlock these topics... + if (!empty($board) && !allowedTo('lock_any')) + { + // Make sure they started the topic AND it isn't already locked by someone with higher priv's. + $result = $smcFunc['db_query']('', ' + SELECT id_topic, locked, id_board + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:locked_topic_ids}) + AND id_member_started = {int:current_member} + AND locked IN (2, 0) + LIMIT ' . count($lockCache), + array( + 'current_member' => $user_info['id'], + 'locked_topic_ids' => $lockCache, + ) + ); + $lockCache = array(); + $lockCacheBoards = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $lockCache[] = $row['id_topic']; + $lockCacheBoards[$row['id_topic']] = $row['id_board']; + $lockStatus[$row['id_topic']] = empty($row['locked']); + } + $smcFunc['db_free_result']($result); + } + else + { + $result = $smcFunc['db_query']('', ' + SELECT id_topic, locked, id_board + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:locked_topic_ids}) + LIMIT ' . count($lockCache), + array( + 'locked_topic_ids' => $lockCache, + ) + ); + $lockCacheBoards = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $lockStatus[$row['id_topic']] = empty($row['locked']); + $lockCacheBoards[$row['id_topic']] = $row['id_board']; + } + $smcFunc['db_free_result']($result); + } + + // It could just be that *none* were their own topics... + if (!empty($lockCache)) + { + // Alternate the locked value. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET locked = CASE WHEN locked = {int:is_locked} THEN ' . (allowedTo('lock_any') ? '1' : '2') . ' ELSE 0 END + WHERE id_topic IN ({array_int:locked_topic_ids})', + array( + 'locked_topic_ids' => $lockCache, + 'is_locked' => 0, + ) + ); + } + } + + if (!empty($markCache)) + { + $markArray = array(); + foreach ($markCache as $topic) + $markArray[] = array($modSettings['maxMsgID'], $user_info['id'], $topic); + + $smcFunc['db_insert']('replace', + '{db_prefix}log_topics', + array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int'), + $markArray, + array('id_member', 'id_topic') + ); + } + + foreach ($moveCache as $topic) + { + // Didn't actually move anything! + if (!isset($topic[0])) + break; + + logAction('move', array('topic' => $topic[0], 'board_from' => $topic[1], 'board_to' => $topic[2])); + sendNotifications($topic[0], 'move'); + } + foreach ($lockCache as $topic) + { + logAction($lockStatus[$topic] ? 'lock' : 'unlock', array('topic' => $topic, 'board' => $lockCacheBoards[$topic])); + sendNotifications($topic, $lockStatus[$topic] ? 'lock' : 'unlock'); + } + foreach ($stickyCache as $topic) + { + logAction($stickyCacheStatus[$topic] ? 'unsticky' : 'sticky', array('topic' => $topic, 'board' => $stickyCacheBoards[$topic])); + sendNotifications($topic, 'sticky'); + } + + updateStats('topic'); + updateStats('message'); + updateSettings(array( + 'calendar_updated' => time(), + )); + + if (!empty($affectedBoards)) + updateLastMessages(array_keys($affectedBoards)); + + redirectexit($redirect_url); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ModSettings.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ModSettings.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,330 @@ + &$txt['displayedValue']), + &$txt['descriptionOfTheOption'], 'OptionalReferenceToHelpAdmin'), + Note that just saying array('first', 'second') will put 0 in the SQL for 'first'. + + * A password input box. Used for passwords, no less! + ie. array('password', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth', + &$txt['descriptionOfTheOption'], 'OptionalReferenceToHelpAdmin'), + + For each option: + type (see above), variable name, size/possible values, description, helptext. + OR make type 'rule' for an empty string for a horizontal rule. + OR make type 'heading' with a string for a titled section. */ + +// This function passes control through to the relevant tab. +function ModifyFeatureSettings() +{ + global $context, $txt, $scripturl, $modSettings, $sourcedir; + + // You need to be an admin to edit settings! + isAllowedTo('admin_forum'); + + // All the admin bar, to make it right. + adminIndex('edit_mods_settings'); + loadLanguage('Help'); + loadLanguage('ModSettings'); + + // Will need the utility functions from here. + require_once($sourcedir . '/ManageServer.php'); + + $context['page_title'] = $txt['modSettings_title']; + $context['sub_template'] = 'show_settings'; + + $subActions = array( + 'basic' => 'ModifyBasicSettings', + 'layout' => 'ModifyLayoutSettings', + 'karma' => 'ModifyKarmaSettings', + ); + + // By default do the basic settings. + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'basic'; + $context['sub_action'] = $_REQUEST['sa']; + + // Load up all the tabs... + $context['admin_tabs'] = array( + 'title' => &$txt['modSettings_title'], + 'help' => 'modsettings', + 'description' => $txt['smf3'], + 'tabs' => array( + 'basic' => array( + 'title' => $txt['mods_cat_features'], + 'href' => $scripturl . '?action=featuresettings;sa=basic;sesc=' . $context['session_id'], + ), + 'layout' => array( + 'title' => $txt['mods_cat_layout'], + 'href' => $scripturl . '?action=featuresettings;sa=layout;sesc=' . $context['session_id'], + ), + 'karma' => array( + 'title' => $txt['smf293'], + 'href' => $scripturl . '?action=featuresettings;sa=karma;sesc=' . $context['session_id'], + 'is_last' => true, + ), + ), + ); + + // Select the right tab based on the sub action. + if (isset($context['admin_tabs']['tabs'][$context['sub_action']])) + $context['admin_tabs']['tabs'][$context['sub_action']]['is_selected'] = true; + + // Call the right function for this sub-acton. + $subActions[$_REQUEST['sa']](); +} + +// This function basically just redirects to the right save function. +function ModifyFeatureSettings2() +{ + global $context, $txt, $scripturl, $modSettings, $sourcedir; + + isAllowedTo('admin_forum'); + loadLanguage('ModSettings'); + + // Quick session check... + checkSession(); + + require_once($sourcedir . '/ManageServer.php'); + + $subActions = array( + 'basic' => 'ModifyBasicSettings', + 'layout' => 'ModifyLayoutSettings', + 'karma' => 'ModifyKarmaSettings', + ); + + // Default to core (I assume) + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'basic'; + + // Actually call the saving function. + $subActions[$_REQUEST['sa']](); +} + +function ModifyBasicSettings() +{ + global $txt, $scripturl, $context, $settings, $sc, $modSettings; + + $config_vars = array( + // Big Options... polls, sticky, bbc.... + array('select', 'pollMode', array(&$txt['smf34'], &$txt['smf32'], &$txt['smf33'])), + '', + // Basic stuff, user languages, titles, flash, permissions... + array('check', 'allow_guestAccess'), + array('check', 'userLanguage'), + array('check', 'allow_editDisplayName'), + array('check', 'allow_hideOnline'), + array('check', 'allow_hideEmail'), + array('check', 'guest_hideContacts'), + array('check', 'titlesEnable'), + array('check', 'enable_buddylist'), + array('text', 'default_personalText'), + array('int', 'max_signatureLength'), + '', + // Stats, compression, cookies.... server type stuff. + array('text', 'time_format'), + array('select', 'number_format', array('1234.00' => '1234.00', '1,234.00' => '1,234.00', '1.234,00' => '1.234,00', '1 234,00' => '1 234,00', '1234,00' => '1234,00')), + array('float', 'time_offset'), + array('int', 'failed_login_threshold'), + array('int', 'lastActive'), + array('check', 'trackStats'), + array('check', 'hitStats'), + array('check', 'enableErrorLogging'), + array('check', 'securityDisable'), + '', + // Reactive on email, and approve on delete + array('check', 'send_validation_onChange'), + array('check', 'approveAccountDeletion'), + '', + // Option-ish things... miscellaneous sorta. + array('check', 'allow_disableAnnounce'), + array('check', 'disallow_sendBody'), + array('check', 'modlog_enabled'), + array('check', 'queryless_urls'), + '', + // Width/Height image reduction. + array('int', 'max_image_width'), + array('int', 'max_image_height'), + '', + // Reporting of personal messages? + array('check', 'enableReportPM'), + ); + + // Saving? + if (isset($_GET['save'])) + { + // Fix PM settings. + $_POST['pm_spam_settings'] = (int) $_POST['max_pm_recipients'] . ',' . (int) $_POST['pm_posts_verification'] . ',' . (int) $_POST['pm_posts_per_hour']; + $save_vars = $config_vars; + $save_vars[] = array('text', 'pm_spam_settings'); + + saveDBSettings($save_vars); + + writeLog(); + redirectexit('action=featuresettings;sa=basic'); + } + + // Hack for PM spam settings. + list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']); + $config_vars[] = array('int', 'max_pm_recipients'); + $config_vars[] = array('int', 'pm_posts_verification'); + $config_vars[] = array('int', 'pm_posts_per_hour'); + + $context['post_url'] = $scripturl . '?action=featuresettings2;save;sa=basic'; + $context['settings_title'] = $txt['mods_cat_features']; + + prepareDBSettingContext($config_vars); +} + +function ModifyLayoutSettings() +{ + global $txt, $scripturl, $context, $settings, $sc; + + $config_vars = array( + // Compact pages? + array('check', 'compactTopicPagesEnable'), + array('int', 'compactTopicPagesContiguous', null, $txt['smf235'] . '
' . str_replace(' ', ' ', '"3" ' . $txt['smf236'] . ': 1 ... 4 [5] 6 ... 9') . '
' . str_replace(' ', ' ', '"5" ' . $txt['smf236'] . ': 1 ... 3 4 [5] 6 7 ... 9') . '
'), + '', + // Stuff that just is everywhere - today, search, online, etc. + array('select', 'todayMod', array(&$txt['smf290'], &$txt['smf291'], &$txt['smf292'])), + array('check', 'topbottomEnable'), + array('check', 'onlineEnable'), + array('check', 'enableVBStyleLogin'), + '', + // Pagination stuff. + array('int', 'defaultMaxMembers'), + '', + // This is like debugging sorta. + array('check', 'timeLoadPageEnable'), + array('check', 'disableHostnameLookup'), + '', + // Who's online. + array('check', 'who_enabled'), + ); + + // Saving? + if (isset($_GET['save'])) + { + saveDBSettings($config_vars); + redirectexit('action=featuresettings;sa=layout'); + + loadUserSettings(); + writeLog(); + } + + $context['post_url'] = $scripturl . '?action=featuresettings2;save;sa=layout'; + $context['settings_title'] = $txt['mods_cat_layout']; + + prepareDBSettingContext($config_vars); +} + +function ModifyKarmaSettings() +{ + global $txt, $scripturl, $context, $settings, $sc; + + $config_vars = array( + // Karma - On or off? + array('select', 'karmaMode', explode('|', $txt['smf64'])), + '', + // Who can do it.... and who is restricted by time limits? + array('int', 'karmaMinPosts'), + array('float', 'karmaWaitTime'), + array('check', 'karmaTimeRestrictAdmins'), + '', + // What does it look like? [smite]? + array('text', 'karmaLabel'), + array('text', 'karmaApplaudLabel'), + array('text', 'karmaSmiteLabel'), + ); + + // Saving? + if (isset($_GET['save'])) + { + saveDBSettings($config_vars); + redirectexit('action=featuresettings;sa=karma'); + } + + $context['post_url'] = $scripturl . '?action=featuresettings2;save;sa=karma'; + $context['settings_title'] = $txt['smf293']; + + prepareDBSettingContext($config_vars); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ModerationCenter.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ModerationCenter.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2022 @@ + array( + 'title' => $txt['mc_main'], + 'areas' => array( + 'index' => array( + 'label' => $txt['moderation_center'], + 'function' => 'ModerationHome', + ), + 'modlog' => array( + 'label' => $txt['modlog_view'], + 'enabled' => !empty($modSettings['modlog_enabled']) && $context['can_moderate_boards'], + 'file' => 'Modlog.php', + 'function' => 'ViewModlog', + ), + 'notice' => array( + 'file' => 'ModerationCenter.php', + 'function' => 'ShowNotice', + 'select' => 'index' + ), + 'warnings' => array( + 'label' => $txt['mc_warnings'], + 'enabled' => in_array('w', $context['admin_features']) && $modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards'], + 'function' => 'ViewWarnings', + 'subsections' => array( + 'log' => array($txt['mc_warning_log']), + 'templates' => array($txt['mc_warning_templates'], 'issue_warning'), + ), + ), + 'userwatch' => array( + 'label' => $txt['mc_watched_users_title'], + 'enabled' => in_array('w', $context['admin_features']) && $modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards'], + 'function' => 'ViewWatchedUsers', + 'subsections' => array( + 'member' => array($txt['mc_watched_users_member']), + 'post' => array($txt['mc_watched_users_post']), + ), + ), + ), + ), + 'posts' => array( + 'title' => $txt['mc_posts'], + 'enabled' => $context['can_moderate_boards'] || $context['can_moderate_approvals'], + 'areas' => array( + 'postmod' => array( + 'label' => $txt['mc_unapproved_posts'], + 'enabled' => $context['can_moderate_approvals'], + 'file' => 'PostModeration.php', + 'function' => 'PostModerationMain', + 'custom_url' => $scripturl . '?action=moderate;area=postmod', + 'subsections' => array( + 'posts' => array($txt['mc_unapproved_replies']), + 'topics' => array($txt['mc_unapproved_topics']), + ), + ), + 'attachmod' => array( + 'label' => $txt['mc_unapproved_attachments'], + 'enabled' => $context['can_moderate_approvals'], + 'file' => 'PostModeration.php', + 'function' => 'PostModerationMain', + 'custom_url' => $scripturl . '?action=moderate;area=attachmod;sa=attachments', + ), + 'reports' => array( + 'label' => $txt['mc_reported_posts'], + 'enabled' => $context['can_moderate_boards'], + 'file' => 'ModerationCenter.php', + 'function' => 'ReportedPosts', + 'subsections' => array( + 'open' => array($txt['mc_reportedp_active']), + 'closed' => array($txt['mc_reportedp_closed']), + ), + ), + ), + ), + 'groups' => array( + 'title' => $txt['mc_groups'], + 'enabled' => $context['can_moderate_groups'], + 'areas' => array( + 'groups' => array( + 'label' => $txt['mc_group_requests'], + 'file' => 'Groups.php', + 'function' => 'Groups', + 'custom_url' => $scripturl . '?action=moderate;area=groups;sa=requests', + ), + 'viewgroups' => array( + 'label' => $txt['mc_view_groups'], + 'file' => 'Groups.php', + 'function' => 'Groups', + ), + ), + ), + 'prefs' => array( + 'title' => $txt['mc_prefs'], + 'areas' => array( + 'settings' => array( + 'label' => $txt['mc_settings'], + 'function' => 'ModerationSettings', + ), + ), + ), + ); + + // I don't know where we're going - I don't know where we've been... + $menuOptions = array( + 'action' => 'moderate', + 'disable_url_session_check' => true, + ); + $mod_include_data = createMenu($moderation_areas, $menuOptions); + unset($moderation_areas); + + // We got something - didn't we? DIDN'T WE! + if ($mod_include_data == false) + fatal_lang_error('no_access', false); + + // Retain the ID information in case required by a subaction. + $context['moderation_menu_id'] = $context['max_menu_id']; + $context['moderation_menu_name'] = 'menu_data_' . $context['moderation_menu_id']; + + // What a pleasant shortcut - even tho we're not *really* on the admin screen who cares... + $context['admin_area'] = $mod_include_data['current_area']; + + // Build the link tree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=moderate', + 'name' => $txt['moderation_center'], + ); + if (isset($mod_include_data['current_area']) && $mod_include_data['current_area'] != 'index') + $context['linktree'][] = array( + 'url' => $scripturl . '?action=moderate;area=' . $mod_include_data['current_area'], + 'name' => $mod_include_data['label'], + ); + if (!empty($mod_include_data['current_subsection']) && $mod_include_data['subsections'][$mod_include_data['current_subsection']][0] != $mod_include_data['label']) + $context['linktree'][] = array( + 'url' => $scripturl . '?action=moderate;area=' . $mod_include_data['current_area'] . ';sa=' . $mod_include_data['current_subsection'], + 'name' => $mod_include_data['subsections'][$mod_include_data['current_subsection']][0], + ); + + // Now - finally - the bit before the encore - the main performance of course! + if (!$dont_call) + { + if (isset($mod_include_data['file'])) + require_once($sourcedir . '/' . $mod_include_data['file']); + + $mod_include_data['function'](); + } +} + +// This function basically is the home page of the moderation center. +function ModerationHome() +{ + global $txt, $context, $scripturl, $modSettings, $user_info, $user_settings; + + loadTemplate('ModerationCenter'); + + $context['page_title'] = $txt['moderation_center']; + $context['sub_template'] = 'moderation_center'; + + // Load what blocks the user actually can see... + $valid_blocks = array( + 'n' => 'LatestNews', + 'p' => 'Notes', + ); + if ($context['can_moderate_groups']) + $valid_blocks['g'] = 'GroupRequests'; + if ($context['can_moderate_boards']) + { + $valid_blocks['r'] = 'ReportedPosts'; + $valid_blocks['w'] = 'WatchedUsers'; + } + + if (empty($user_settings['mod_prefs'])) + $user_blocks = 'n' . ($context['can_moderate_boards'] ? 'wr' : '') . ($context['can_moderate_groups'] ? 'g' : ''); + else + list (, $user_blocks) = explode('|', $user_settings['mod_prefs']); + + $user_blocks = str_split($user_blocks); + + $context['mod_blocks'] = array(); + foreach ($valid_blocks as $k => $block) + { + if (in_array($k, $user_blocks)) + { + $block = 'ModBlock' . $block; + if (function_exists($block)) + $context['mod_blocks'][] = $block(); + } + } +} + +// Just prepares the time stuff for the simple machines latest news. +function ModBlockLatestNews() +{ + global $context, $user_info; + + $context['time_format'] = urlencode($user_info['time_format']); + + // Return the template to use. + return 'latest_news'; +} + +// Show a list of the most active watched users. +function ModBlockWatchedUsers() +{ + global $context, $smcFunc, $scripturl, $modSettings; + + if (($watched_users = cache_get_data('recent_user_watches', 240)) === null) + { + $modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 1 : $modSettings['warning_watch']; + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, last_login + FROM {db_prefix}members + WHERE warning >= {int:warning_watch} + ORDER BY last_login DESC + LIMIT 10', + array( + 'warning_watch' => $modSettings['warning_watch'], + ) + ); + $watched_users = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $watched_users[] = $row; + $smcFunc['db_free_result']($request); + + cache_put_data('recent_user_watches', $watched_users, 240); + } + + $context['watched_users'] = array(); + foreach ($watched_users as $user) + { + $context['watched_users'][] = array( + 'id' => $user['id_member'], + 'name' => $user['real_name'], + 'link' => '' . $user['real_name'] . '', + 'href' => $scripturl . '?action=profile;u=' . $user['id_member'], + 'last_login' => !empty($user['last_login']) ? timeformat($user['last_login']) : '', + ); + } + + return 'watched_users'; +} + +// Show an area for the moderator to type into. +function ModBlockNotes() +{ + global $context, $smcFunc, $scripturl, $txt, $user_info; + + // Are we saving a note? + if (isset($_POST['makenote']) && isset($_POST['new_note'])) + { + checkSession(); + + $_POST['new_note'] = $smcFunc['htmlspecialchars'](trim($_POST['new_note'])); + // Make sure they actually entered something. + if (!empty($_POST['new_note']) && $_POST['new_note'] !== $txt['mc_click_add_note']) + { + // Insert it into the database then! + $smcFunc['db_insert']('', + '{db_prefix}log_comments', + array( + 'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'recipient_name' => 'string', + 'body' => 'string', 'log_time' => 'int', + ), + array( + $user_info['id'], $user_info['name'], 'modnote', '', $_POST['new_note'], time(), + ), + array('id_comment') + ); + + // Clear the cache. + cache_put_data('moderator_notes', null, 240); + cache_put_data('moderator_notes_total', null, 240); + } + + // Redirect otherwise people can resubmit. + redirectexit('action=moderate'); + } + + // Bye... bye... + if (isset($_GET['notes']) && isset($_GET['delete']) && is_numeric($_GET['delete'])) + { + checkSession('get'); + + // Lets delete it. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_comments + WHERE id_comment = {int:note} + AND comment_type = {string:type}', + array( + 'note' => $_GET['delete'], + 'type' => 'modnote', + ) + ); + + // Clear the cache. + cache_put_data('moderator_notes', null, 240); + cache_put_data('moderator_notes_total', null, 240); + + redirectexit('action=moderate'); + } + + // How many notes in total? + if (($moderator_notes_total = cache_get_data('moderator_notes_total', 240)) === null) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_comments AS lc + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member) + WHERE lc.comment_type = {string:modnote}', + array( + 'modnote' => 'modnote', + ) + ); + list ($moderator_notes_total) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + cache_put_data('moderator_notes_total', $moderator_notes_total, 240); + } + + // Grab the current notes. We can only use the cache for the first page of notes. + $offset = isset($_GET['notes']) && isset($_GET['start']) ? $_GET['start'] : 0; + if ($offset != 0 || ($moderator_notes = cache_get_data('moderator_notes', 240)) === null) + { + $request = $smcFunc['db_query']('', ' + SELECT IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lc.member_name) AS member_name, + lc.log_time, lc.body, lc.id_comment AS id_note + FROM {db_prefix}log_comments AS lc + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member) + WHERE lc.comment_type = {string:modnote} + ORDER BY id_comment DESC + LIMIT {int:offset}, 10', + array( + 'modnote' => 'modnote', + 'offset' => $offset, + ) + ); + $moderator_notes = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $moderator_notes[] = $row; + $smcFunc['db_free_result']($request); + + if ($offset == 0) + cache_put_data('moderator_notes', $moderator_notes, 240); + } + + // Lets construct a page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=index;notes', $_GET['start'], $moderator_notes_total, 10); + $context['start'] = $_GET['start']; + + $context['notes'] = array(); + foreach ($moderator_notes as $note) + { + $context['notes'][] = array( + 'author' => array( + 'id' => $note['id_member'], + 'link' => $note['id_member'] ? ('' . $note['member_name'] . '') : $note['member_name'], + ), + 'time' => timeformat($note['log_time']), + 'text' => parse_bbc($note['body']), + 'delete_href' => $scripturl . '?action=moderate;area=index;notes;delete=' . $note['id_note'] . ';' . $context['session_var'] . '=' . $context['session_id'], + ); + } + + return 'notes'; +} + +// Show a list of the most recent reported posts. +function ModBlockReportedPosts() +{ + global $context, $user_info, $scripturl, $smcFunc; + + // Got the info already? + $cachekey = md5(serialize($user_info['mod_cache']['bq'])); + $context['reported_posts'] = array(); + if ($user_info['mod_cache']['bq'] == '0=1') + return 'reported_posts_block'; + + if (($reported_posts = cache_get_data('reported_posts_' . $cachekey, 90)) === null) + { + // By George, that means we in a position to get the reports, jolly good. + $request = $smcFunc['db_query']('', ' + SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, + lr.num_reports, IFNULL(mem.real_name, lr.membername) AS author_name, + IFNULL(mem.id_member, 0) AS id_author + FROM {db_prefix}log_reported AS lr + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member) + WHERE ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . ' + AND lr.closed = {int:not_closed} + AND lr.ignore_all = {int:not_ignored} + ORDER BY lr.time_updated DESC + LIMIT 10', + array( + 'not_closed' => 0, + 'not_ignored' => 0, + ) + ); + $reported_posts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $reported_posts[] = $row; + $smcFunc['db_free_result']($request); + + // Cache it. + cache_put_data('reported_posts_' . $cachekey, $reported_posts, 90); + } + + $context['reported_posts'] = array(); + foreach ($reported_posts as $i => $row) + { + $context['reported_posts'][] = array( + 'id' => $row['id_report'], + 'alternate' => $i % 2, + 'topic_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + 'report_href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'], + 'author' => array( + 'id' => $row['id_author'], + 'name' => $row['author_name'], + 'link' => $row['id_author'] ? '' . $row['author_name'] . '' : $row['author_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_author'], + ), + 'comments' => array(), + 'subject' => $row['subject'], + 'num_reports' => $row['num_reports'], + ); + } + + return 'reported_posts_block'; +} + +// Show a list of all the group requests they can see. +function ModBlockGroupRequests() +{ + global $context, $user_info, $scripturl, $smcFunc; + + $context['group_requests'] = array(); + // Make sure they can even moderate someone! + if ($user_info['mod_cache']['gq'] == '0=1') + return 'group_requests_block'; + + // What requests are outstanding? + $request = $smcFunc['db_query']('', ' + SELECT lgr.id_request, lgr.id_member, lgr.id_group, lgr.time_applied, mem.member_name, mg.group_name, mem.real_name + FROM {db_prefix}log_group_requests AS lgr + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member) + INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group) + WHERE ' . ($user_info['mod_cache']['gq'] == '1=1' || $user_info['mod_cache']['gq'] == '0=1' ? $user_info['mod_cache']['gq'] : 'lgr.' . $user_info['mod_cache']['gq']) . ' + ORDER BY lgr.id_request DESC + LIMIT 10', + array( + ) + ); + for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i ++) + { + $context['group_requests'][] = array( + 'id' => $row['id_request'], + 'alternate' => $i % 2, + 'request_href' => $scripturl . '?action=groups;sa=requests;gid=' . $row['id_group'], + 'member' => array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + 'link' => '' . $row['real_name'] . '', + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + ), + 'group' => array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + ), + 'time_submitted' => timeformat($row['time_applied']), + ); + } + $smcFunc['db_free_result']($request); + + return 'group_requests_block'; +} + +//!!! This needs to be given its own file. +// Browse all the reported posts... +function ReportedPosts() +{ + global $txt, $context, $scripturl, $modSettings, $user_info, $smcFunc; + + loadTemplate('ModerationCenter'); + + // Put the open and closed options into tabs, because we can... + $context[$context['moderation_menu_name']]['tab_data'] = array( + 'title' => $txt['mc_reported_posts'], + 'help' => '', + 'description' => $txt['mc_reported_posts_desc'], + ); + + // This comes under the umbrella of moderating posts. + if ($user_info['mod_cache']['bq'] == '0=1') + isAllowedTo('moderate_forum'); + + // Are they wanting to view a particular report? + if (!empty($_REQUEST['report'])) + return ModReport(); + + // Set up the comforting bits... + $context['page_title'] = $txt['mc_reported_posts']; + $context['sub_template'] = 'reported_posts'; + + // Are we viewing open or closed reports? + $context['view_closed'] = isset($_GET['sa']) && $_GET['sa'] == 'closed' ? 1 : 0; + + // Are we doing any work? + if ((isset($_GET['ignore']) || isset($_GET['close'])) && isset($_GET['rid'])) + { + checkSession('get'); + $_GET['rid'] = (int) $_GET['rid']; + + // Update the report... + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_reported + SET ' . (isset($_GET['ignore']) ? 'ignore_all = {int:ignore_all}' : 'closed = {int:closed}') . ' + WHERE id_report = {int:id_report} + AND ' . $user_info['mod_cache']['bq'], + array( + 'ignore_all' => isset($_GET['ignore']) ? (int) $_GET['ignore'] : 0, + 'closed' => isset($_GET['close']) ? (int) $_GET['close'] : 0, + 'id_report' => $_GET['rid'], + ) + ); + + // Time to update. + updateSettings(array('last_mod_report_action' => time())); + recountOpenReports(); + } + elseif (isset($_POST['close']) && isset($_POST['close_selected'])) + { + checkSession('post'); + + // All the ones to update... + $toClose = array(); + foreach ($_POST['close'] as $rid) + $toClose[] = (int) $rid; + + if (!empty($toClose)) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_reported + SET closed = {int:is_closed} + WHERE id_report IN ({array_int:report_list}) + AND ' . $user_info['mod_cache']['bq'], + array( + 'report_list' => $toClose, + 'is_closed' => 1, + ) + ); + + // Time to update. + updateSettings(array('last_mod_report_action' => time())); + recountOpenReports(); + } + } + + // How many entries are we viewing? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_reported AS lr + WHERE lr.closed = {int:view_closed} + AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']), + array( + 'view_closed' => $context['view_closed'], + ) + ); + list ($context['total_reports']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // So, that means we can page index, yes? + $context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=reports' . ($context['view_closed'] ? ';sa=closed' : ''), $_GET['start'], $context['total_reports'], 10); + $context['start'] = $_GET['start']; + + // By George, that means we in a position to get the reports, golly good. + $request = $smcFunc['db_query']('', ' + SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.body, + lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all, + IFNULL(mem.real_name, lr.membername) AS author_name, IFNULL(mem.id_member, 0) AS id_author + FROM {db_prefix}log_reported AS lr + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member) + WHERE lr.closed = {int:view_closed} + AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . ' + ORDER BY lr.time_updated DESC + LIMIT ' . $context['start'] . ', 10', + array( + 'view_closed' => $context['view_closed'], + ) + ); + $context['reports'] = array(); + $report_ids = array(); + for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i++) + { + $report_ids[] = $row['id_report']; + $context['reports'][$row['id_report']] = array( + 'id' => $row['id_report'], + 'alternate' => $i % 2, + 'topic_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + 'report_href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'], + 'author' => array( + 'id' => $row['id_author'], + 'name' => $row['author_name'], + 'link' => $row['id_author'] ? '' . $row['author_name'] . '' : $row['author_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_author'], + ), + 'comments' => array(), + 'time_started' => timeformat($row['time_started']), + 'last_updated' => timeformat($row['time_updated']), + 'subject' => $row['subject'], + 'body' => parse_bbc($row['body']), + 'num_reports' => $row['num_reports'], + 'closed' => $row['closed'], + 'ignore' => $row['ignore_all'] + ); + } + $smcFunc['db_free_result']($request); + + // Now get all the people who reported it. + if (!empty($report_ids)) + { + $request = $smcFunc['db_query']('', ' + SELECT lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment, + IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lrc.membername) AS reporter + FROM {db_prefix}log_reported_comments AS lrc + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member) + WHERE lrc.id_report IN ({array_int:report_list})', + array( + 'report_list' => $report_ids, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['reports'][$row['id_report']]['comments'][] = array( + 'id' => $row['id_comment'], + 'message' => $row['comment'], + 'time' => timeformat($row['time_sent']), + 'member' => array( + 'id' => $row['id_member'], + 'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'], + 'link' => $row['id_member'] ? '' . $row['reporter'] . '' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']), + 'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '', + ), + ); + } + $smcFunc['db_free_result']($request); + } +} + +// Act as an entrace for all group related activity. +//!!! As for most things in this file, this needs to be moved somewhere appropriate. +function ModerateGroups() +{ + global $txt, $context, $scripturl, $modSettings, $user_info; + + // You need to be allowed to moderate groups... + if ($user_info['mod_cache']['gq'] == '0=1') + isAllowedTo('manage_membergroups'); + + // Load the group templates. + loadTemplate('ModerationCenter'); + + // Setup the subactions... + $subactions = array( + 'requests' => 'GroupRequests', + 'view' => 'ViewGroups', + ); + + if (!isset($_GET['sa']) || !isset($subactions[$_GET['sa']])) + $_GET['sa'] = 'view'; + $context['sub_action'] = $_GET['sa']; + + // Call the relevant function. + $subactions[$context['sub_action']](); +} + +// How many open reports do we have? +function recountOpenReports() +{ + global $user_info, $context, $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_reported + WHERE ' . $user_info['mod_cache']['bq'] . ' + AND closed = {int:not_closed} + AND ignore_all = {int:not_ignored}', + array( + 'not_closed' => 0, + 'not_ignored' => 0, + ) + ); + list ($open_reports) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $_SESSION['rc'] = array( + 'id' => $user_info['id'], + 'time' => time(), + 'reports' => $open_reports, + ); + + $context['open_mod_reports'] = $open_reports; +} + +function ModReport() +{ + global $user_info, $context, $sourcedir, $scripturl, $txt, $smcFunc; + + // Have to at least give us something + if (empty($_REQUEST['report'])) + fatal_lang_error('mc_no_modreport_specified'); + + // Integers only please + $_REQUEST['report'] = (int) $_REQUEST['report']; + + // Get the report details, need this so we can limit access to a particular board + $request = $smcFunc['db_query']('', ' + SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.body, + lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all, + IFNULL(mem.real_name, lr.membername) AS author_name, IFNULL(mem.id_member, 0) AS id_author + FROM {db_prefix}log_reported AS lr + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member) + WHERE lr.id_report = {int:id_report} + AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . ' + LIMIT 1', + array( + 'id_report' => $_REQUEST['report'], + ) + ); + + // So did we find anything? + if (!$smcFunc['db_num_rows']($request)) + fatal_lang_error('mc_no_modreport_found'); + + // Woohoo we found a report and they can see it! Bad news is we have more work to do + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // If they are adding a comment then... add a comment. + if (isset($_POST['add_comment']) && !empty($_POST['mod_comment'])) + { + checkSession(); + + $newComment = trim($smcFunc['htmlspecialchars']($_POST['mod_comment'])); + + // In it goes. + if (!empty($newComment)) + { + $smcFunc['db_insert']('', + '{db_prefix}log_comments', + array( + 'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'recipient_name' => 'string', + 'id_notice' => 'int', 'body' => 'string', 'log_time' => 'int', + ), + array( + $user_info['id'], $user_info['name'], 'reportc', '', + $_REQUEST['report'], $newComment, time(), + ), + array('id_comment') + ); + + // Redirect to prevent double submittion. + redirectexit($scripturl . '?action=moderate;area=reports;report=' . $_REQUEST['report']); + } + } + + $context['report'] = array( + 'id' => $row['id_report'], + 'topic_id' => $row['id_topic'], + 'board_id' => $row['id_board'], + 'message_id' => $row['id_msg'], + 'message_href' => $scripturl . '?msg=' . $row['id_msg'], + 'message_link' => '' . $row['subject'] . '', + 'report_href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'], + 'author' => array( + 'id' => $row['id_author'], + 'name' => $row['author_name'], + 'link' => $row['id_author'] ? '' . $row['author_name'] . '' : $row['author_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_author'], + ), + 'comments' => array(), + 'mod_comments' => array(), + 'time_started' => timeformat($row['time_started']), + 'last_updated' => timeformat($row['time_updated']), + 'subject' => $row['subject'], + 'body' => parse_bbc($row['body']), + 'num_reports' => $row['num_reports'], + 'closed' => $row['closed'], + 'ignore' => $row['ignore_all'] + ); + + // So what bad things do the reporters have to say about it? + $request = $smcFunc['db_query']('', ' + SELECT lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment, lrc.member_ip, + IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lrc.membername) AS reporter + FROM {db_prefix}log_reported_comments AS lrc + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member) + WHERE lrc.id_report = {int:id_report}', + array( + 'id_report' => $context['report']['id'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['report']['comments'][] = array( + 'id' => $row['id_comment'], + 'message' => $row['comment'], + 'time' => timeformat($row['time_sent']), + 'member' => array( + 'id' => $row['id_member'], + 'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'], + 'link' => $row['id_member'] ? '' . $row['reporter'] . '' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']), + 'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '', + 'ip' => !empty($row['member_ip']) && allowedTo('moderate_forum') ? '' . $row['member_ip'] . '' : '', + ), + ); + } + $smcFunc['db_free_result']($request); + + // Hang about old chap, any comments from moderators on this one? + $request = $smcFunc['db_query']('', ' + SELECT lc.id_comment, lc.id_notice, lc.log_time, lc.body, + IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lc.member_name) AS moderator + FROM {db_prefix}log_comments AS lc + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member) + WHERE lc.id_notice = {int:id_report} + AND lc.comment_type = {string:reportc}', + array( + 'id_report' => $context['report']['id'], + 'reportc' => 'reportc', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['report']['mod_comments'][] = array( + 'id' => $row['id_comment'], + 'message' => parse_bbc($row['body']), + 'time' => timeformat($row['log_time']), + 'member' => array( + 'id' => $row['id_member'], + 'name' => $row['moderator'], + 'link' => $row['id_member'] ? '' . $row['moderator'] . '' : $row['moderator'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + ), + ); + } + $smcFunc['db_free_result']($request); + + // What have the other moderators done to this message? + require_once($sourcedir . '/Modlog.php'); + require_once($sourcedir . '/Subs-List.php'); + loadLanguage('Modlog'); + + // This is all the information from the moderation log. + $listOptions = array( + 'id' => 'moderation_actions_list', + 'title' => $txt['mc_modreport_modactions'], + 'items_per_page' => 15, + 'no_items_label' => $txt['modlog_no_entries_found'], + 'base_href' => $scripturl . '?action=moderate;area=reports;report=' . $context['report']['id'], + 'default_sort_col' => 'time', + 'get_items' => array( + 'function' => 'list_getModLogEntries', + 'params' => array( + 'lm.id_topic = {int:id_topic}', + array('id_topic' => $context['report']['topic_id']), + 1, + ), + ), + 'get_count' => array( + 'function' => 'list_getModLogEntryCount', + 'params' => array( + 'lm.id_topic = {int:id_topic}', + array('id_topic' => $context['report']['topic_id']), + 1, + ), + ), + // This assumes we are viewing by user. + 'columns' => array( + 'action' => array( + 'header' => array( + 'value' => $txt['modlog_action'], + ), + 'data' => array( + 'db' => 'action_text', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'lm.action', + 'reverse' => 'lm.action DESC', + ), + ), + 'time' => array( + 'header' => array( + 'value' => $txt['modlog_date'], + ), + 'data' => array( + 'db' => 'time', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'lm.log_time', + 'reverse' => 'lm.log_time DESC', + ), + ), + 'moderator' => array( + 'header' => array( + 'value' => $txt['modlog_member'], + ), + 'data' => array( + 'db' => 'moderator_link', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'mem.real_name', + 'reverse' => 'mem.real_name DESC', + ), + ), + 'position' => array( + 'header' => array( + 'value' => $txt['modlog_position'], + ), + 'data' => array( + 'db' => 'position', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'mg.group_name', + 'reverse' => 'mg.group_name DESC', + ), + ), + 'ip' => array( + 'header' => array( + 'value' => $txt['modlog_ip'], + ), + 'data' => array( + 'db' => 'ip', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'lm.ip', + 'reverse' => 'lm.ip DESC', + ), + ), + ), + ); + + // Create the watched user list. + createList($listOptions); + + // Make sure to get the correct tab selected. + if ($context['report']['closed']) + $context[$context['moderation_menu_name']]['current_subsection'] = 'closed'; + + // Finally we are done :P + loadTemplate('ModerationCenter'); + $context['page_title'] = sprintf($txt['mc_viewmodreport'], $context['report']['subject'], $context['report']['author']['name']); + $context['sub_template'] = 'viewmodreport'; +} + +// Show a notice sent to a user. +function ShowNotice() +{ + global $smcFunc, $txt, $context; + + $context['page_title'] = $txt['show_notice']; + $context['sub_template'] = 'show_notice'; + $context['template_layers'] = array(); + + loadTemplate('ModerationCenter'); + + //!!! Assumes nothing needs permission more than accessing moderation center! + $id_notice = (int) $_GET['nid']; + $request = $smcFunc['db_query']('', ' + SELECT body, subject + FROM {db_prefix}log_member_notices + WHERE id_notice = {int:id_notice}', + array( + 'id_notice' => $id_notice, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + list ($context['notice_body'], $context['notice_subject']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['notice_body'] = parse_bbc($context['notice_body'], false); +} + +// View watched users. +function ViewWatchedUsers() +{ + global $smcFunc, $modSettings, $context, $txt, $scripturl, $user_info, $sourcedir; + + // Some important context! + $context['page_title'] = $txt['mc_watched_users_title']; + $context['view_posts'] = isset($_GET['sa']) && $_GET['sa'] == 'post'; + $context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; + + loadTemplate('ModerationCenter'); + + // Get some key settings! + $modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 1 : $modSettings['warning_watch']; + + // Put some pretty tabs on cause we're gonna be doing hot stuff here... + $context[$context['moderation_menu_name']]['tab_data'] = array( + 'title' => $txt['mc_watched_users_title'], + 'help' => '', + 'description' => $txt['mc_watched_users_desc'], + ); + + // First off - are we deleting? + if (!empty($_REQUEST['delete'])) + { + checkSession(!is_array($_REQUEST['delete']) ? 'get' : 'post'); + + $toDelete = array(); + if (!is_array($_REQUEST['delete'])) + $toDelete[] = (int) $_REQUEST['delete']; + else + foreach ($_REQUEST['delete'] as $did) + $toDelete[] = (int) $did; + + if (!empty($toDelete)) + { + require_once($sourcedir . '/RemoveTopic.php'); + // If they don't have permission we'll let it error - either way no chance of a security slip here! + foreach ($toDelete as $did) + removeMessage($did); + } + } + + // Start preparing the list by grabbing relevant permissions. + if (!$context['view_posts']) + { + $approve_query = ''; + $delete_boards = array(); + } + else + { + // Still obey permissions! + $approve_boards = boardsAllowedTo('approve_posts'); + $delete_boards = boardsAllowedTo('delete_any'); + + if ($approve_boards == array(0)) + $approve_query = ''; + elseif (!empty($approve_boards)) + $approve_query = ' AND m.id_board IN (' . implode(',', $approve_boards) . ')'; + // Nada, zip, etc... + else + $approve_query = ' AND 0'; + } + + require_once($sourcedir . '/Subs-List.php'); + + // This is all the information required for a watched user listing. + $listOptions = array( + 'id' => 'watch_user_list', + 'title' => $txt['mc_watched_users_title'] . ' - ' . ($context['view_posts'] ? $txt['mc_watched_users_post'] : $txt['mc_watched_users_member']), + 'width' => '100%', + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $context['view_posts'] ? $txt['mc_watched_users_no_posts'] : $txt['mc_watched_users_none'], + 'base_href' => $scripturl . '?action=moderate;area=userwatch;sa=' . ($context['view_posts'] ? 'post' : 'member'), + 'default_sort_col' => $context['view_posts'] ? '' : 'member', + 'get_items' => array( + 'function' => $context['view_posts'] ? 'list_getWatchedUserPosts' : 'list_getWatchedUsers', + 'params' => array( + $approve_query, + $delete_boards, + ), + ), + 'get_count' => array( + 'function' => $context['view_posts'] ? 'list_getWatchedUserPostsCount' : 'list_getWatchedUserCount', + 'params' => array( + $approve_query, + ), + ), + // This assumes we are viewing by user. + 'columns' => array( + 'member' => array( + 'header' => array( + 'value' => $txt['mc_watched_users_member'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id' => false, + 'name' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'real_name', + 'reverse' => 'real_name DESC', + ), + ), + 'warning' => array( + 'header' => array( + 'value' => $txt['mc_watched_users_warning'], + ), + 'data' => array( + 'function' => create_function('$member', ' + global $scripturl; + + return allowedTo(\'issue_warning\') ? \'\' . $member[\'warning\'] . \'%\' : $member[\'warning\'] . \'%\'; + '), + ), + 'sort' => array( + 'default' => 'warning', + 'reverse' => 'warning DESC', + ), + ), + 'posts' => array( + 'header' => array( + 'value' => $txt['posts'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id' => false, + 'posts' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'posts', + 'reverse' => 'posts DESC', + ), + ), + 'last_login' => array( + 'header' => array( + 'value' => $txt['mc_watched_users_last_login'], + ), + 'data' => array( + 'db' => 'last_login', + ), + 'sort' => array( + 'default' => 'last_login', + 'reverse' => 'last_login DESC', + ), + ), + 'last_post' => array( + 'header' => array( + 'value' => $txt['mc_watched_users_last_post'], + ), + 'data' => array( + 'function' => create_function('$member', ' + global $scripturl; + + if ($member[\'last_post_id\']) + return \'\' . $member[\'last_post\'] . \'\'; + else + return $member[\'last_post\']; + '), + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=moderate;area=userwatch;sa=post', + 'include_sort' => true, + 'include_start' => true, + 'hidden_fields' => array( + $context['session_var'] => $context['session_id'], + ), + ), + 'additional_rows' => array( + $context['view_posts'] ? + array( + 'position' => 'bottom_of_list', + 'value' => ' + ', + 'align' => 'right', + ) : array(), + ), + ); + + // If this is being viewed by posts we actually change the columns to call a template each time. + if ($context['view_posts']) + { + $listOptions['columns'] = array( + 'posts' => array( + 'data' => array( + 'function' => create_function('$post', ' + return template_user_watch_post_callback($post); + '), + ), + ), + ); + } + + // Create the watched user list. + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'watch_user_list'; +} + +function list_getWatchedUserCount($approve_query) +{ + global $smcFunc, $modSettings; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}members + WHERE warning >= {int:warning_watch}', + array( + 'warning_watch' => $modSettings['warning_watch'], + ) + ); + list ($totalMembers) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $totalMembers; +} + +function list_getWatchedUsers($start, $items_per_page, $sort, $approve_query, $dummy) +{ + global $smcFunc, $txt, $scripturl, $modSettings, $user_info, $context; + + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, last_login, posts, warning + FROM {db_prefix}members + WHERE warning >= {int:warning_watch} + ORDER BY {raw:sort} + LIMIT ' . $start . ', ' . $items_per_page, + array( + 'warning_watch' => $modSettings['warning_watch'], + 'sort' => $sort, + ) + ); + $watched_users = array(); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $watched_users[$row['id_member']] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + 'last_login' => $row['last_login'] ? timeformat($row['last_login']) : $txt['never'], + 'last_post' => $txt['not_applicable'], + 'last_post_id' => 0, + 'warning' => $row['warning'], + 'posts' => $row['posts'], + ); + $members[] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + + if (!empty($members)) + { + // First get the latest messages from these users. + $request = $smcFunc['db_query']('', ' + SELECT m.id_member, MAX(m.id_msg) AS last_post_id + FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : ' + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . ' + WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND m.approved = {int:is_approved}') . ' + GROUP BY m.id_member', + array( + 'member_list' => $members, + 'is_approved' => 1, + ) + ); + $latest_posts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $latest_posts[$row['id_member']] = $row['last_post_id']; + + if (!empty($latest_posts)) + { + // Now get the time those messages were posted. + $request = $smcFunc['db_query']('', ' + SELECT id_member, poster_time + FROM {db_prefix}messages + WHERE id_msg IN ({array_int:message_list})', + array( + 'message_list' => $latest_posts, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $watched_users[$row['id_member']]['last_post'] = timeformat($row['poster_time']); + $watched_users[$row['id_member']]['last_post_id'] = $latest_posts[$row['id_member']]; + } + + $smcFunc['db_free_result']($request); + } + + $request = $smcFunc['db_query']('', ' + SELECT MAX(m.poster_time) AS last_post, MAX(m.id_msg) AS last_post_id, m.id_member + FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : ' + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . ' + WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND m.approved = {int:is_approved}') . ' + GROUP BY m.id_member', + array( + 'member_list' => $members, + 'is_approved' => 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $watched_users[$row['id_member']]['last_post'] = timeformat($row['last_post']); + $watched_users[$row['id_member']]['last_post_id'] = $row['last_post_id']; + } + $smcFunc['db_free_result']($request); + } + + return $watched_users; +} + +function list_getWatchedUserPostsCount($approve_query) +{ + global $smcFunc, $modSettings, $user_info; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE mem.warning >= {int:warning_watch} + AND {query_see_board} + ' . $approve_query, + array( + 'warning_watch' => $modSettings['warning_watch'], + ) + ); + list ($totalMemberPosts) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $totalMemberPosts; +} + +function list_getWatchedUserPosts($start, $items_per_page, $sort, $approve_query, $delete_boards) +{ + global $smcFunc, $txt, $scripturl, $modSettings, $user_info; + + $request = $smcFunc['db_query']('', ' + SELECT m.id_msg, m.id_topic, m.id_board, m.id_member, m.subject, m.body, m.poster_time, + m.approved, mem.real_name, m.smileys_enabled + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE mem.warning >= {int:warning_watch} + AND {query_see_board} + ' . $approve_query . ' + ORDER BY m.id_msg DESC + LIMIT ' . $start . ', ' . $items_per_page, + array( + 'warning_watch' => $modSettings['warning_watch'], + ) + ); + $member_posts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $row['subject'] = censorText($row['subject']); + $row['body'] = censorText($row['body']); + + $member_posts[$row['id_msg']] = array( + 'id' => $row['id_msg'], + 'id_topic' => $row['id_topic'], + 'author_link' => '' . $row['real_name'] . '', + 'subject' => $row['subject'], + 'body' => parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']), + 'poster_time' => timeformat($row['poster_time']), + 'approved' => $row['approved'], + 'can_delete' => $delete_boards == array(0) || in_array($row['id_board'], $delete_boards), + ); + } + $smcFunc['db_free_result']($request); + + return $member_posts; +} + +// Entry point for viewing warning related stuff. +function ViewWarnings() +{ + global $context, $txt; + + $subActions = array( + 'log' => array('ViewWarningLog'), + 'templateedit' => array('ModifyWarningTemplate', 'issue_warning'), + 'templates' => array('ViewWarningTemplates', 'issue_warning'), + ); + + $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) && (empty($subActions[$_REQUEST['sa']][1]) || allowedTo($subActions[$_REQUEST['sa']]))? $_REQUEST['sa'] : 'log'; + + // Some of this stuff is overseas, so to speak. + loadTemplate('ModerationCenter'); + loadLanguage('Profile'); + + // Setup the admin tabs. + $context[$context['moderation_menu_name']]['tab_data'] = array( + 'title' => $txt['mc_warnings'], + 'description' => $txt['mc_warnings_description'], + ); + + // Call the right function. + $subActions[$_REQUEST['sa']][0](); +} + +// Simply put, look at the warning log! +function ViewWarningLog() +{ + global $smcFunc, $modSettings, $context, $txt, $scripturl, $sourcedir; + + // Setup context as always. + $context['page_title'] = $txt['mc_warning_log_title']; + + require_once($sourcedir . '/Subs-List.php'); + + // This is all the information required for a watched user listing. + $listOptions = array( + 'id' => 'warning_list', + 'title' => $txt['mc_warning_log_title'], + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $txt['mc_warnings_none'], + 'base_href' => $scripturl . '?action=moderate;area=warnings;sa=log;' . $context['session_var'] . '=' . $context['session_id'], + 'default_sort_col' => 'time', + 'get_items' => array( + 'function' => 'list_getWarnings', + ), + 'get_count' => array( + 'function' => 'list_getWarningCount', + ), + // This assumes we are viewing by user. + 'columns' => array( + 'issuer' => array( + 'header' => array( + 'value' => $txt['profile_warning_previous_issued'], + ), + 'data' => array( + 'db' => 'issuer_link', + ), + 'sort' => array( + 'default' => 'member_name_col', + 'reverse' => 'member_name_col DESC', + ), + ), + 'recipient' => array( + 'header' => array( + 'value' => $txt['mc_warnings_recipient'], + ), + 'data' => array( + 'db' => 'recipient_link', + ), + 'sort' => array( + 'default' => 'recipient_name', + 'reverse' => 'recipient_name DESC', + ), + ), + 'time' => array( + 'header' => array( + 'value' => $txt['profile_warning_previous_time'], + ), + 'data' => array( + 'db' => 'time', + ), + 'sort' => array( + 'default' => 'lc.log_time DESC', + 'reverse' => 'lc.log_time', + ), + ), + 'reason' => array( + 'header' => array( + 'value' => $txt['profile_warning_previous_reason'], + ), + 'data' => array( + 'function' => create_function('$warning', ' + global $scripturl, $settings, $txt; + + $output = \' +
+ \' . $warning[\'reason\'] . \' +
\'; + + if (!empty($warning[\'id_notice\'])) + $output .= \' +
+ \' . $txt[\'profile_warning_previous_notice\'] . \' +
\'; + + return $output; + '), + ), + ), + 'points' => array( + 'header' => array( + 'value' => $txt['profile_warning_previous_level'], + ), + 'data' => array( + 'db' => 'counter', + ), + ), + ), + ); + + // Create the watched user list. + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'warning_list'; +} + +function list_getWarningCount() +{ + global $smcFunc, $modSettings; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_comments + WHERE comment_type = {string:warning}', + array( + 'warning' => 'warning', + ) + ); + list ($totalWarns) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $totalWarns; +} + +function list_getWarnings($start, $items_per_page, $sort) +{ + global $smcFunc, $txt, $scripturl, $modSettings, $user_info; + + $request = $smcFunc['db_query']('', ' + SELECT IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lc.member_name) AS member_name_col, + IFNULL(mem2.id_member, 0) AS id_recipient, IFNULL(mem2.real_name, lc.recipient_name) AS recipient_name, + lc.log_time, lc.body, lc.id_notice, lc.counter + FROM {db_prefix}log_comments AS lc + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member) + LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = lc.id_recipient) + WHERE lc.comment_type = {string:warning} + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + 'warning' => 'warning', + ) + ); + $warnings = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $warnings[] = array( + 'issuer_link' => $row['id_member'] ? ('' . $row['member_name_col'] . '') : $row['member_name_col'], + 'recipient_link' => $row['id_recipient'] ? ('' . $row['recipient_name'] . '') : $row['recipient_name'], + 'time' => timeformat($row['log_time']), + 'reason' => $row['body'], + 'counter' => $row['counter'] > 0 ? '+' . $row['counter'] : $row['counter'], + 'id_notice' => $row['id_notice'], + ); + } + $smcFunc['db_free_result']($request); + + return $warnings; +} + +// Load all the warning templates. +function ViewWarningTemplates() +{ + global $smcFunc, $modSettings, $context, $txt, $scripturl, $sourcedir, $user_info; + + // Submitting a new one? + if (isset($_POST['add'])) + return ModifyWarningTemplate(); + elseif (isset($_POST['delete']) && !empty($_POST['deltpl'])) + { + checkSession('post'); + + // Log the actions. + $request = $smcFunc['db_query']('', ' + SELECT recipient_name + FROM {db_prefix}log_comments + WHERE id_comment IN ({array_int:delete_ids}) + AND comment_type = {string:warntpl} + AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})', + array( + 'delete_ids' => $_POST['deltpl'], + 'warntpl' => 'warntpl', + 'generic' => 0, + 'current_member' => $user_info['id'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + logAction('delete_warn_template', array('template' => $row['recipient_name'])); + $smcFunc['db_free_result']($request); + + // Do the deletes. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_comments + WHERE id_comment IN ({array_int:delete_ids}) + AND comment_type = {string:warntpl} + AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})', + array( + 'delete_ids' => $_POST['deltpl'], + 'warntpl' => 'warntpl', + 'generic' => 0, + 'current_member' => $user_info['id'], + ) + ); + } + + // Setup context as always. + $context['page_title'] = $txt['mc_warning_templates_title']; + + require_once($sourcedir . '/Subs-List.php'); + + // This is all the information required for a watched user listing. + $listOptions = array( + 'id' => 'warning_template_list', + 'title' => $txt['mc_warning_templates_title'], + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $txt['mc_warning_templates_none'], + 'base_href' => $scripturl . '?action=moderate;area=warnings;sa=templates;' . $context['session_var'] . '=' . $context['session_id'], + 'default_sort_col' => 'title', + 'get_items' => array( + 'function' => 'list_getWarningTemplates', + ), + 'get_count' => array( + 'function' => 'list_getWarningTemplateCount', + ), + // This assumes we are viewing by user. + 'columns' => array( + 'title' => array( + 'header' => array( + 'value' => $txt['mc_warning_templates_name'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%2$s', + 'params' => array( + 'id_comment' => false, + 'title' => false, + 'body' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'template_title', + 'reverse' => 'template_title DESC', + ), + ), + 'creator' => array( + 'header' => array( + 'value' => $txt['mc_warning_templates_creator'], + ), + 'data' => array( + 'db' => 'creator', + ), + 'sort' => array( + 'default' => 'creator_name', + 'reverse' => 'creator_name DESC', + ), + ), + 'time' => array( + 'header' => array( + 'value' => $txt['mc_warning_templates_time'], + ), + 'data' => array( + 'db' => 'time', + ), + 'sort' => array( + 'default' => 'lc.log_time DESC', + 'reverse' => 'lc.log_time', + ), + ), + 'delete' => array( + 'header' => array( + 'style' => 'width: 4%;', + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $context, $txt, $scripturl; + + return \'\'; + '), + 'style' => 'text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=moderate;area=warnings;sa=templates', + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => ' + + ', + 'style' => 'text-align: right;', + ), + ), + ); + + // Create the watched user list. + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'warning_template_list'; +} + +function list_getWarningTemplateCount() +{ + global $smcFunc, $modSettings, $user_info; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_comments + WHERE comment_type = {string:warntpl} + AND (id_recipient = {string:generic} OR id_recipient = {int:current_member})', + array( + 'warntpl' => 'warntpl', + 'generic' => 0, + 'current_member' => $user_info['id'], + ) + ); + list ($totalWarns) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $totalWarns; +} + +function list_getWarningTemplates($start, $items_per_page, $sort) +{ + global $smcFunc, $txt, $scripturl, $modSettings, $user_info; + + $request = $smcFunc['db_query']('', ' + SELECT lc.id_comment, IFNULL(mem.id_member, 0) AS id_member, + IFNULL(mem.real_name, lc.member_name) AS creator_name, recipient_name AS template_title, + lc.log_time, lc.body + FROM {db_prefix}log_comments AS lc + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member) + WHERE lc.comment_type = {string:warntpl} + AND (id_recipient = {string:generic} OR id_recipient = {int:current_member}) + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + 'warntpl' => 'warntpl', + 'generic' => 0, + 'current_member' => $user_info['id'], + ) + ); + $templates = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $templates[] = array( + 'id_comment' => $row['id_comment'], + 'creator' => $row['id_member'] ? ('' . $row['creator_name'] . '') : $row['creator_name'], + 'time' => timeformat($row['log_time']), + 'title' => $row['template_title'], + 'body' => $smcFunc['htmlspecialchars']($row['body']), + ); + } + $smcFunc['db_free_result']($request); + + return $templates; +} + +// Edit a warning template. +function ModifyWarningTemplate() +{ + global $smcFunc, $context, $txt, $user_info, $sourcedir; + + $context['id_template'] = isset($_REQUEST['tid']) ? (int) $_REQUEST['tid'] : 0; + $context['is_edit'] = $context['id_template']; + + // Standard template things. + $context['page_title'] = $context['is_edit'] ? $txt['mc_warning_template_modify'] : $txt['mc_warning_template_add']; + $context['sub_template'] = 'warn_template'; + $context[$context['moderation_menu_name']]['current_subsection'] = 'templates'; + + // Defaults. + $context['template_data'] = array( + 'title' => '', + 'body' => $txt['mc_warning_template_body_default'], + 'personal' => false, + 'can_edit_personal' => true, + ); + + // If it's an edit load it. + if ($context['is_edit']) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, id_recipient, recipient_name AS template_title, body + FROM {db_prefix}log_comments + WHERE id_comment = {int:id} + AND comment_type = {string:warntpl} + AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})', + array( + 'id' => $context['id_template'], + 'warntpl' => 'warntpl', + 'generic' => 0, + 'current_member' => $user_info['id'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['template_data'] = array( + 'title' => $row['template_title'], + 'body' => $smcFunc['htmlspecialchars']($row['body']), + 'personal' => $row['id_recipient'], + 'can_edit_personal' => $row['id_member'] == $user_info['id'], + ); + } + $smcFunc['db_free_result']($request); + } + + // Wait, we are saving? + if (isset($_POST['save'])) + { + checkSession('post'); + + // To check the BBC is pretty good... + require_once($sourcedir . '/Subs-Post.php'); + + // Bit of cleaning! + $_POST['template_body'] = trim($_POST['template_body']); + $_POST['template_title'] = trim($_POST['template_title']); + + // Need something in both boxes. + if (empty($_POST['template_body']) || empty($_POST['template_title'])) + fatal_error($txt['mc_warning_template_error_empty']); + + // Safety first. + $_POST['template_title'] = $smcFunc['htmlspecialchars']($_POST['template_title']); + + // Clean up BBC. + preparsecode($_POST['template_body']); + // But put line breaks back! + $_POST['template_body'] = strtr($_POST['template_body'], array('
' => "\n")); + + // Is this personal? + $recipient_id = !empty($_POST['make_personal']) ? $user_info['id'] : 0; + + // If we are this far it's save time. + if ($context['is_edit']) + { + // Simple update... + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_comments + SET id_recipient = {int:personal}, recipient_name = {string:title}, body = {string:body} + WHERE id_comment = {int:id} + AND comment_type = {string:warntpl} + AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})'. + ($recipient_id ? ' AND id_member = {int:current_member}' : ''), + array( + 'personal' => $recipient_id, + 'title' => $_POST['template_title'], + 'body' => $_POST['template_body'], + 'id' => $context['id_template'], + 'warntpl' => 'warntpl', + 'generic' => 0, + 'current_member' => $user_info['id'], + ) + ); + + // If it wasn't visible and now is they've effectively added it. + if ($context['template_data']['personal'] && !$recipient_id) + logAction('add_warn_template', array('template' => $_POST['template_title'])); + // Conversely if they made it personal it's a delete. + elseif (!$context['template_data']['personal'] && $recipient_id) + logAction('delete_warn_template', array('template' => $_POST['template_title'])); + // Otherwise just an edit. + else + logAction('modify_warn_template', array('template' => $_POST['template_title'])); + } + else + { + $smcFunc['db_insert']('', + '{db_prefix}log_comments', + array( + 'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int', + 'recipient_name' => 'string-255', 'body' => 'string-65535', 'log_time' => 'int', + ), + array( + $user_info['id'], $user_info['name'], 'warntpl', $recipient_id, + $_POST['template_title'], $_POST['template_body'], time(), + ), + array('id_comment') + ); + + logAction('add_warn_template', array('template' => $_POST['template_title'])); + } + + // Get out of town... + redirectexit('action=moderate;area=warnings;sa=templates'); + } +} + +// Change moderation preferences. +function ModerationSettings() +{ + global $context, $smcFunc, $txt, $sourcedir, $scripturl, $user_settings, $user_info; + + // Some useful context stuff. + loadTemplate('ModerationCenter'); + $context['page_title'] = $txt['mc_settings']; + $context['sub_template'] = 'moderation_settings'; + + // What blocks can this user see? + $context['homepage_blocks'] = array( + 'n' => $txt['mc_prefs_latest_news'], + 'p' => $txt['mc_notes'], + ); + if ($context['can_moderate_groups']) + $context['homepage_blocks']['g'] = $txt['mc_group_requests']; + if ($context['can_moderate_boards']) + { + $context['homepage_blocks']['r'] = $txt['mc_reported_posts']; + $context['homepage_blocks']['w'] = $txt['mc_watched_users']; + } + + // Does the user have any settings yet? + if (empty($user_settings['mod_prefs'])) + { + $mod_blocks = 'n' . ($context['can_moderate_boards'] ? 'wr' : '') . ($context['can_moderate_groups'] ? 'g' : ''); + $pref_binary = 5; + $show_reports = 1; + } + else + { + list ($show_reports, $mod_blocks, $pref_binary) = explode('|', $user_settings['mod_prefs']); + } + + // Are we saving? + if (isset($_POST['save'])) + { + checkSession('post'); + /* Current format of mod_prefs is: + x|ABCD|yyy + + WHERE: + x = Show report count on forum header. + ABCD = Block indexes to show on moderation main page. + yyy = Integer with the following bit status: + - yyy & 1 = Always notify on reports. + - yyy & 2 = Notify on reports for moderators only. + - yyy & 4 = Notify about posts awaiting approval. + */ + + // Do blocks first! + $mod_blocks = ''; + if (!empty($_POST['mod_homepage'])) + foreach ($_POST['mod_homepage'] as $k => $v) + { + // Make sure they can add this... + if (isset($context['homepage_blocks'][$k])) + $mod_blocks .= $k; + } + + // Now check other options! + $pref_binary = 0; + + if ($context['can_moderate_approvals'] && !empty($_POST['mod_notify_approval'])) + $pref_binary |= 4; + + if ($context['can_moderate_boards']) + { + if (!empty($_POST['mod_notify_report'])) + $pref_binary |= ($_POST['mod_notify_report'] == 2 ? 1 : 2); + + $show_reports = !empty($_POST['mod_show_reports']) ? 1 : 0; + } + + // Put it all together. + $mod_prefs = $show_reports . '|' . $mod_blocks . '|' . $pref_binary; + updateMemberData($user_info['id'], array('mod_prefs' => $mod_prefs)); + } + + // What blocks does the user currently have selected? + $context['mod_settings'] = array( + 'show_reports' => $show_reports, + 'notify_report' => $pref_binary & 2 ? 1 : ($pref_binary & 1 ? 2 : 0), + 'notify_approval' => $pref_binary & 4, + 'user_blocks' => str_split($mod_blocks), + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Modlog.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Modlog.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,611 @@ + time() - $context['hoursdisable'] * 3600, + 'moderate_log' => $context['log_type'], + ) + ); + } + elseif (!empty($_POST['remove']) && isset($_POST['delete']) && $context['can_delete']) + { + checkSession(); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_actions + WHERE id_log = {int:moderate_log} + AND id_action IN ({array_string:delete_actions}) + AND log_time < {int:twenty_four_hours_wait}', + array( + 'twenty_four_hours_wait' => time() - $context['hoursdisable'] * 3600, + 'delete_actions' => array_unique($_POST['delete']), + 'moderate_log' => $context['log_type'], + ) + ); + } + + // Do the column stuff! + $sort_types = array( + 'action' =>'lm.action', + 'time' => 'lm.log_time', + 'member' => 'mem.real_name', + 'group' => 'mg.group_name', + 'ip' => 'lm.ip', + ); + + // Setup the direction stuff... + $context['order'] = isset($_REQUEST['sort']) && isset($sort_types[$_REQUEST['sort']]) ? $_REQUEST['sort'] : 'time'; + + // If we're coming from a search, get the variables. + if (!empty($_REQUEST['params']) && empty($_REQUEST['is_search'])) + { + $search_params = base64_decode(strtr($_REQUEST['params'], array(' ' => '+'))); + $search_params = @unserialize($search_params); + } + + // This array houses all the valid search types. + $searchTypes = array( + 'action' => array('sql' => 'lm.action', 'label' => $txt['modlog_action']), + 'member' => array('sql' => 'mem.real_name', 'label' => $txt['modlog_member']), + 'group' => array('sql' => 'mg.group_name', 'label' => $txt['modlog_position']), + 'ip' => array('sql' => 'lm.ip', 'label' => $txt['modlog_ip']) + ); + + if (!isset($search_params['string']) || (!empty($_REQUEST['search']) && $search_params['string'] != $_REQUEST['search'])) + $search_params_string = empty($_REQUEST['search']) ? '' : $_REQUEST['search']; + else + $search_params_string = $search_params['string']; + + if (isset($_REQUEST['search_type']) || empty($search_params['type']) || !isset($searchTypes[$search_params['type']])) + $search_params_type = isset($_REQUEST['search_type']) && isset($searchTypes[$_REQUEST['search_type']]) ? $_REQUEST['search_type'] : (isset($searchTypes[$context['order']]) ? $context['order'] : 'member'); + else + $search_params_type = $search_params['type']; + + $search_params_column = $searchTypes[$search_params_type]['sql']; + $search_params = array( + 'string' => $search_params_string, + 'type' => $search_params_type, + ); + + // Setup the search context. + $context['search_params'] = empty($search_params['string']) ? '' : base64_encode(serialize($search_params)); + $context['search'] = array( + 'string' => $search_params['string'], + 'type' => $search_params['type'], + 'label' => $searchTypes[$search_params_type]['label'], + ); + + // If they are searching by action, then we must do some manual intervention to search in their language! + if ($search_params['type'] == 'action' && !empty($search_params['string'])) + { + // For the moment they can only search for ONE action! + foreach ($txt as $key => $text) + { + if (substr($key, 0, 10) == 'modlog_ac_' && strpos($text, $search_params['string']) !== false) + { + $search_params['string'] = substr($key, 10); + break; + } + } + } + + require_once($sourcedir . '/Subs-List.php'); + + // This is all the information required for a watched user listing. + $listOptions = array( + 'id' => 'moderation_log_list', + 'title' => '' . $txt['help'] . ' ' . $txt['modlog_' . ($context['log_type'] == 3 ? 'admin' : 'moderation') . '_log'], + 'width' => '100%', + 'items_per_page' => $context['displaypage'], + 'no_items_label' => $txt['modlog_' . ($context['log_type'] == 3 ? 'admin_log_' : '') . 'no_entries_found'], + 'base_href' => $scripturl . $context['url_start'] . (!empty($context['search_params']) ? ';params=' . $context['search_params'] : ''), + 'default_sort_col' => 'time', + 'get_items' => array( + 'function' => 'list_getModLogEntries', + 'params' => array( + (!empty($search_params['string']) ? ' INSTR({raw:sql_type}, {string:search_string})' : ''), + array('sql_type' => $search_params_column, 'search_string' => $search_params['string']), + $context['log_type'], + ), + ), + 'get_count' => array( + 'function' => 'list_getModLogEntryCount', + 'params' => array( + (!empty($search_params['string']) ? ' INSTR({raw:sql_type}, {string:search_string})' : ''), + array('sql_type' => $search_params_column, 'search_string' => $search_params['string']), + $context['log_type'], + ), + ), + // This assumes we are viewing by user. + 'columns' => array( + 'action' => array( + 'header' => array( + 'value' => $txt['modlog_action'], + 'class' => 'lefttext first_th', + ), + 'data' => array( + 'db' => 'action_text', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'lm.action', + 'reverse' => 'lm.action DESC', + ), + ), + 'time' => array( + 'header' => array( + 'value' => $txt['modlog_date'], + 'class' => 'lefttext', + ), + 'data' => array( + 'db' => 'time', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'lm.log_time DESC', + 'reverse' => 'lm.log_time', + ), + ), + 'moderator' => array( + 'header' => array( + 'value' => $txt['modlog_member'], + 'class' => 'lefttext', + ), + 'data' => array( + 'db' => 'moderator_link', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'mem.real_name', + 'reverse' => 'mem.real_name DESC', + ), + ), + 'position' => array( + 'header' => array( + 'value' => $txt['modlog_position'], + 'class' => 'lefttext', + ), + 'data' => array( + 'db' => 'position', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'mg.group_name', + 'reverse' => 'mg.group_name DESC', + ), + ), + 'ip' => array( + 'header' => array( + 'value' => $txt['modlog_ip'], + 'class' => 'lefttext', + ), + 'data' => array( + 'db' => 'ip', + 'class' => 'smalltext', + ), + 'sort' => array( + 'default' => 'lm.ip', + 'reverse' => 'lm.ip DESC', + ), + ), + 'delete' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'function' => create_function('$entry', ' + return \'\'; + '), + 'style' => 'text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . $context['url_start'], + 'include_sort' => true, + 'include_start' => true, + 'hidden_fields' => array( + $context['session_var'] => $context['session_id'], + 'params' => $context['search_params'] + ), + ), + 'additional_rows' => array( + array( + 'position' => 'after_title', + 'value' => $txt['modlog_' . ($context['log_type'] == 3 ? 'admin' : 'moderation') . '_log_desc'], + 'class' => 'smalltext', + 'style' => 'padding: 2ex;', + ), + array( + 'position' => 'below_table_data', + 'value' => ' + ' . $txt['modlog_search'] . ' (' . $txt['modlog_by'] . ': ' . $context['search']['label'] . '): + + ' . ($context['can_delete'] ? ' | + + ' : ''), + ), + ), + ); + + // Create the watched user list. + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'moderation_log_list'; +} + +// Get the number of mod log entries. +function list_getModLogEntryCount($query_string = '', $query_params = array(), $log_type = 1) +{ + global $smcFunc, $user_info; + + $modlog_query = allowedTo('admin_forum') || $user_info['mod_cache']['bq'] == '1=1' ? '1=1' : ($user_info['mod_cache']['bq'] == '0=1' ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr($user_info['mod_cache']['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr($user_info['mod_cache']['bq'], array('id_board' => 't.id_board')))); + + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_actions AS lm + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lm.id_member) + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END) + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lm.id_board) + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lm.id_topic) + WHERE id_log = {int:log_type} + AND {raw:modlog_query}' + . (!empty($query_string) ? ' + AND ' . $query_string : ''), + array_merge($query_params, array( + 'reg_group_id' => 0, + 'log_type' => $log_type, + 'modlog_query' => $modlog_query, + )) + ); + list ($entry_count) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + return $entry_count; +} + +function list_getModLogEntries($start, $items_per_page, $sort, $query_string = '', $query_params = array(), $log_type = 1) +{ + global $context, $scripturl, $txt, $smcFunc, $user_info; + + $modlog_query = allowedTo('admin_forum') || $user_info['mod_cache']['bq'] == '1=1' ? '1=1' : ($user_info['mod_cache']['bq'] == '0=1' ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr($user_info['mod_cache']['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr($user_info['mod_cache']['bq'], array('id_board' => 't.id_board')))); + + // Do a little bit of self protection. + if (!isset($context['hoursdisable'])) + $context['hoursdisable'] = 24; + + // Can they see the IP address? + $seeIP = allowedTo('moderate_forum'); + + // Here we have the query getting the log details. + $result = $smcFunc['db_query']('', ' + SELECT + lm.id_action, lm.id_member, lm.ip, lm.log_time, lm.action, lm.id_board, lm.id_topic, lm.id_msg, lm.extra, + mem.real_name, mg.group_name + FROM {db_prefix}log_actions AS lm + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lm.id_member) + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END) + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lm.id_board) + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lm.id_topic) + WHERE id_log = {int:log_type} + AND {raw:modlog_query}' + . (!empty($query_string) ? ' + AND ' . $query_string : '') . ' + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array_merge($query_params, array( + 'reg_group_id' => 0, + 'log_type' => $log_type, + 'modlog_query' => $modlog_query, + )) + ); + + // Arrays for decoding objects into. + $topics = array(); + $boards = array(); + $members = array(); + $messages = array(); + $entries = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $row['extra'] = @unserialize($row['extra']); + + // Corrupt? + $row['extra'] = is_array($row['extra']) ? $row['extra'] : array(); + + // Add on some of the column stuff info + if (!empty($row['id_board'])) + { + if ($row['action'] == 'move') + $row['extra']['board_to'] = $row['id_board']; + else + $row['extra']['board'] = $row['id_board']; + } + + if (!empty($row['id_topic'])) + $row['extra']['topic'] = $row['id_topic']; + if (!empty($row['id_msg'])) + $row['extra']['message'] = $row['id_msg']; + + // Is this associated with a topic? + if (isset($row['extra']['topic'])) + $topics[(int) $row['extra']['topic']][] = $row['id_action']; + if (isset($row['extra']['new_topic'])) + $topics[(int) $row['extra']['new_topic']][] = $row['id_action']; + + // How about a member? + if (isset($row['extra']['member'])) + { + // Guests don't have names! + if (empty($row['extra']['member'])) + $row['extra']['member'] = $txt['modlog_parameter_guest']; + else + { + // Try to find it... + $members[(int) $row['extra']['member']][] = $row['id_action']; + } + } + + // Associated with a board? + if (isset($row['extra']['board_to'])) + $boards[(int) $row['extra']['board_to']][] = $row['id_action']; + if (isset($row['extra']['board_from'])) + $boards[(int) $row['extra']['board_from']][] = $row['id_action']; + if (isset($row['extra']['board'])) + $boards[(int) $row['extra']['board']][] = $row['id_action']; + + // A message? + if (isset($row['extra']['message'])) + $messages[(int) $row['extra']['message']][] = $row['id_action']; + + // IP Info? + if (isset($row['extra']['ip_range'])) + if ($seeIP) + $row['extra']['ip_range'] = '' . $row['extra']['ip_range'] . ''; + else + $row['extra']['ip_range'] = $txt['logged']; + + // Email? + if (isset($row['extra']['email'])) + $row['extra']['email'] = '' . $row['extra']['email'] . ''; + + // Bans are complex. + if ($row['action'] == 'ban') + { + $row['action_text'] = $txt['modlog_ac_ban']; + foreach (array('member', 'email', 'ip_range', 'hostname') as $type) + if (isset($row['extra'][$type])) + $row['action_text'] .= $txt['modlog_ac_ban_trigger_' . $type]; + } + + // The array to go to the template. Note here that action is set to a "default" value of the action doesn't match anything in the descriptions. Allows easy adding of logging events with basic details. + $entries[$row['id_action']] = array( + 'id' => $row['id_action'], + 'ip' => $seeIP ? $row['ip'] : $txt['logged'], + 'position' => empty($row['real_name']) && empty($row['group_name']) ? $txt['guest'] : $row['group_name'], + 'moderator_link' => $row['id_member'] ? '' . $row['real_name'] . '' : (empty($row['real_name']) ? ($txt['guest'] . (!empty($row['extra']['member_acted']) ? ' (' . $row['extra']['member_acted'] . ')' : '')) : $row['real_name']), + 'time' => timeformat($row['log_time']), + 'timestamp' => forum_time(true, $row['log_time']), + 'editable' => time() > $row['log_time'] + $context['hoursdisable'] * 3600, + 'extra' => $row['extra'], + 'action' => $row['action'], + 'action_text' => isset($row['action_text']) ? $row['action_text'] : '', + ); + } + $smcFunc['db_free_result']($result); + + if (!empty($boards)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_board, name + FROM {db_prefix}boards + WHERE id_board IN ({array_int:board_list}) + LIMIT ' . count(array_keys($boards)), + array( + 'board_list' => array_keys($boards), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + foreach ($boards[$row['id_board']] as $action) + { + // Make the board number into a link - dealing with moving too. + if (isset($entries[$action]['extra']['board_to']) && $entries[$action]['extra']['board_to'] == $row['id_board']) + $entries[$action]['extra']['board_to'] = '' . $row['name'] . ''; + elseif (isset($entries[$action]['extra']['board_from']) && $entries[$action]['extra']['board_from'] == $row['id_board']) + $entries[$action]['extra']['board_from'] = '' . $row['name'] . ''; + elseif (isset($entries[$action]['extra']['board']) && $entries[$action]['extra']['board'] == $row['id_board']) + $entries[$action]['extra']['board'] = '' . $row['name'] . ''; + } + } + $smcFunc['db_free_result']($request); + } + + if (!empty($topics)) + { + $request = $smcFunc['db_query']('', ' + SELECT ms.subject, t.id_topic + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg) + WHERE t.id_topic IN ({array_int:topic_list}) + LIMIT ' . count(array_keys($topics)), + array( + 'topic_list' => array_keys($topics), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + foreach ($topics[$row['id_topic']] as $action) + { + $this_action = &$entries[$action]; + + // This isn't used in the current theme. + $this_action['topic'] = array( + 'id' => $row['id_topic'], + 'subject' => $row['subject'], + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'link' => '' . $row['subject'] . '' + ); + + // Make the topic number into a link - dealing with splitting too. + if (isset($this_action['extra']['topic']) && $this_action['extra']['topic'] == $row['id_topic']) + $this_action['extra']['topic'] = '' . $row['subject'] . ''; + elseif (isset($this_action['extra']['new_topic']) && $this_action['extra']['new_topic'] == $row['id_topic']) + $this_action['extra']['new_topic'] = '' . $row['subject'] . ''; + } + } + $smcFunc['db_free_result']($request); + } + + if (!empty($messages)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_msg, subject + FROM {db_prefix}messages + WHERE id_msg IN ({array_int:message_list}) + LIMIT ' . count(array_keys($messages)), + array( + 'message_list' => array_keys($messages), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + foreach ($messages[$row['id_msg']] as $action) + { + $this_action = &$entries[$action]; + + // This isn't used in the current theme. + $this_action['message'] = array( + 'id' => $row['id_msg'], + 'subject' => $row['subject'], + 'href' => $scripturl . '?msg=' . $row['id_msg'], + 'link' => '' . $row['subject'] . '', + ); + + // Make the message number into a link. + if (isset($this_action['extra']['message']) && $this_action['extra']['message'] == $row['id_msg']) + $this_action['extra']['message'] = '' . $row['subject'] . ''; + } + } + $smcFunc['db_free_result']($request); + } + + if (!empty($members)) + { + $request = $smcFunc['db_query']('', ' + SELECT real_name, id_member + FROM {db_prefix}members + WHERE id_member IN ({array_int:member_list}) + LIMIT ' . count(array_keys($members)), + array( + 'member_list' => array_keys($members), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + foreach ($members[$row['id_member']] as $action) + { + // Not used currently. + $entries[$action]['member'] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => '' . $row['real_name'] . '' + ); + // Make the member number into a name. + $entries[$action]['extra']['member'] = '' . $row['real_name'] . ''; + } + } + $smcFunc['db_free_result']($request); + } + + // Do some formatting of the action string. + foreach ($entries as $k => $entry) + { + // Make any message info links so its easier to go find that message. + if (isset($entry['extra']['message']) && (empty($entry['message']) || empty($entry['message']['id']))) + $entries[$k]['extra']['message'] = '' . $entry['extra']['message'] . ''; + + // Mark up any deleted members, topics and boards. + foreach (array('board', 'board_from', 'board_to', 'member', 'topic', 'new_topic') as $type) + if (!empty($entry['extra'][$type]) && is_numeric($entry['extra'][$type])) + $entries[$k]['extra'][$type] = sprintf($txt['modlog_id'], $entry['extra'][$type]); + + if (empty($entries[$k]['action_text'])) + $entries[$k]['action_text'] = isset($txt['modlog_ac_' . $entry['action']]) ? $txt['modlog_ac_' . $entry['action']] : $entry['action']; + $entries[$k]['action_text'] = preg_replace('~\{([A-Za-z\d_]+)\}~ie', 'isset($entries[$k][\'extra\'][\'$1\']) ? $entries[$k][\'extra\'][\'$1\'] : \'\'', $entries[$k]['action_text']); + } + + // Back we go! + return $entries; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/MoveTopic.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/MoveTopic.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,699 @@ + $topic, + ) + ); + list ($id_member_started, $context['subject'], $context['is_approved']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Can they see it - if not approved? + if ($modSettings['postmod_active'] && !$context['is_approved']) + isAllowedTo('approve_posts'); + + // Permission check! + // !!! + if (!allowedTo('move_any')) + { + if ($id_member_started == $user_info['id']) + { + isAllowedTo('move_own'); + //$boards = array_merge(boardsAllowedTo('move_own'), boardsAllowedTo('move_any')); + } + else + isAllowedTo('move_any'); + } + //else + //$boards = boardsAllowedTo('move_any'); + + loadTemplate('MoveTopic'); + + // Get a list of boards this moderator can move to. + $request = $smcFunc['db_query']('order_by_board_order', ' + SELECT b.id_board, b.name, b.child_level, c.name AS cat_name, c.id_cat + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE {query_see_board} + AND b.redirect = {string:blank_redirect} + AND b.id_board != {int:current_board}', + array( + 'blank_redirect' => '', + 'current_board' => $board, + ) + ); + $context['boards'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($context['categories'][$row['id_cat']])) + $context['categories'][$row['id_cat']] = array ( + 'name' => strip_tags($row['cat_name']), + 'boards' => array(), + ); + + $context['categories'][$row['id_cat']]['boards'][] = array( + 'id' => $row['id_board'], + 'name' => strip_tags($row['name']), + 'category' => strip_tags($row['cat_name']), + 'child_level' => $row['child_level'], + 'selected' => !empty($_SESSION['move_to_topic']) && $_SESSION['move_to_topic'] == $row['id_board'] && $row['id_board'] != $board, + ); + } + $smcFunc['db_free_result']($request); + + if (empty($context['categories'])) + fatal_lang_error('moveto_noboards', false); + + $context['page_title'] = $txt['move_topic']; + + $context['linktree'][] = array( + 'url' => $scripturl . '?topic=' . $topic . '.0', + 'name' => $context['subject'], + 'extra_before' => $settings['linktree_inline'] ? $txt['topic'] . ': ' : '', + ); + + $context['linktree'][] = array( + 'name' => $txt['move_topic'], + ); + + $context['back_to_topic'] = isset($_REQUEST['goback']); + + if ($user_info['language'] != $language) + { + loadLanguage('index', $language); + $temp = $txt['movetopic_default']; + loadLanguage('index'); + + $txt['movetopic_default'] = $temp; + } + + // Register this form and get a sequence number in $context. + checkSubmitOnce('register'); +} + +// Execute the move. +function MoveTopic2() +{ + global $txt, $board, $topic, $scripturl, $sourcedir, $modSettings, $context; + global $board, $language, $user_info, $smcFunc; + + if (empty($topic)) + fatal_lang_error('no_access', false); + + // You can't choose to have a redirection topic and use an empty reason. + if (isset($_POST['postRedirect']) && (!isset($_POST['reason']) || trim($_POST['reason']) == '')) + fatal_lang_error('movetopic_no_reason', false); + + // Make sure this form hasn't been submitted before. + checkSubmitOnce('check'); + + $request = $smcFunc['db_query']('', ' + SELECT id_member_started, id_first_msg, approved + FROM {db_prefix}topics + WHERE id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + list ($id_member_started, $id_first_msg, $context['is_approved']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Can they see it? + if (!$context['is_approved']) + isAllowedTo('approve_posts'); + + // Can they move topics on this board? + if (!allowedTo('move_any')) + { + if ($id_member_started == $user_info['id']) + { + isAllowedTo('move_own'); + $boards = array_merge(boardsAllowedTo('move_own'), boardsAllowedTo('move_any')); + } + else + isAllowedTo('move_any'); + } + else + $boards = boardsAllowedTo('move_any'); + + // If this topic isn't approved don't let them move it if they can't approve it! + if ($modSettings['postmod_active'] && !$context['is_approved'] && !allowedTo('approve_posts')) + { + // Only allow them to move it to other boards they can't approve it in. + $can_approve = boardsAllowedTo('approve_posts'); + $boards = array_intersect($boards, $can_approve); + } + + checkSession(); + require_once($sourcedir . '/Subs-Post.php'); + + // The destination board must be numeric. + $_POST['toboard'] = (int) $_POST['toboard']; + + // Make sure they can see the board they are trying to move to (and get whether posts count in the target board). + $request = $smcFunc['db_query']('', ' + SELECT b.count_posts, b.name, m.subject + FROM {db_prefix}boards AS b + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic}) + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE {query_see_board} + AND b.id_board = {int:to_board} + AND b.redirect = {string:blank_redirect} + LIMIT 1', + array( + 'current_topic' => $topic, + 'to_board' => $_POST['toboard'], + 'blank_redirect' => '', + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_board'); + list ($pcounter, $board_name, $subject) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Remember this for later. + $_SESSION['move_to_topic'] = $_POST['toboard']; + + // Rename the topic... + if (isset($_POST['reset_subject'], $_POST['custom_subject']) && $_POST['custom_subject'] != '') + { + $_POST['custom_subject'] = strtr($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => '')); + // Keep checking the length. + if ($smcFunc['strlen']($_POST['custom_subject']) > 100) + $_POST['custom_subject'] = $smcFunc['substr']($_POST['custom_subject'], 0, 100); + + // If it's still valid move onwards and upwards. + if ($_POST['custom_subject'] != '') + { + if (isset($_POST['enforce_subject'])) + { + // Get a response prefix, but in the forum's default language. + if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix'))) + { + if ($language === $user_info['language']) + $context['response_prefix'] = $txt['response_prefix']; + else + { + loadLanguage('index', $language, false); + $context['response_prefix'] = $txt['response_prefix']; + loadLanguage('index'); + } + cache_put_data('response_prefix', $context['response_prefix'], 600); + } + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET subject = {string:subject} + WHERE id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + 'subject' => $context['response_prefix'] . $_POST['custom_subject'], + ) + ); + } + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET subject = {string:custom_subject} + WHERE id_msg = {int:id_first_msg}', + array( + 'id_first_msg' => $id_first_msg, + 'custom_subject' => $_POST['custom_subject'], + ) + ); + + // Fix the subject cache. + updateStats('subject', $topic, $_POST['custom_subject']); + } + } + + // Create a link to this in the old board. + //!!! Does this make sense if the topic was unapproved before? I'd just about say so. + if (isset($_POST['postRedirect'])) + { + // Should be in the boardwide language. + if ($user_info['language'] != $language) + loadLanguage('index', $language); + + $_POST['reason'] = $smcFunc['htmlspecialchars']($_POST['reason'], ENT_QUOTES); + preparsecode($_POST['reason']); + + // Add a URL onto the message. + $_POST['reason'] = strtr($_POST['reason'], array( + $txt['movetopic_auto_board'] => '[url=' . $scripturl . '?board=' . $_POST['toboard'] . '.0]' . $board_name . '[/url]', + $txt['movetopic_auto_topic'] => '[iurl]' . $scripturl . '?topic=' . $topic . '.0[/iurl]' + )); + + $msgOptions = array( + 'subject' => $txt['moved'] . ': ' . $subject, + 'body' => $_POST['reason'], + 'icon' => 'moved', + 'smileys_enabled' => 1, + ); + $topicOptions = array( + 'board' => $board, + 'lock_mode' => 1, + 'mark_as_read' => true, + ); + $posterOptions = array( + 'id' => $user_info['id'], + 'update_post_count' => empty($pcounter), + ); + createPost($msgOptions, $topicOptions, $posterOptions); + } + + $request = $smcFunc['db_query']('', ' + SELECT count_posts + FROM {db_prefix}boards + WHERE id_board = {int:current_board} + LIMIT 1', + array( + 'current_board' => $board, + ) + ); + list ($pcounter_from) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if ($pcounter_from != $pcounter) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic} + AND approved = {int:is_approved}', + array( + 'current_topic' => $topic, + 'is_approved' => 1, + ) + ); + $posters = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($posters[$row['id_member']])) + $posters[$row['id_member']] = 0; + + $posters[$row['id_member']]++; + } + $smcFunc['db_free_result']($request); + + foreach ($posters as $id_member => $posts) + { + // The board we're moving from counted posts, but not to. + if (empty($pcounter_from)) + updateMemberData($id_member, array('posts' => 'posts - ' . $posts)); + // The reverse: from didn't, to did. + else + updateMemberData($id_member, array('posts' => 'posts + ' . $posts)); + } + } + + // Do the move (includes statistics update needed for the redirect topic). + moveTopics($topic, $_POST['toboard']); + + // Log that they moved this topic. + if (!allowedTo('move_own') || $id_member_started != $user_info['id']) + logAction('move', array('topic' => $topic, 'board_from' => $board, 'board_to' => $_POST['toboard'])); + // Notify people that this topic has been moved? + sendNotifications($topic, 'move'); + + // Why not go back to the original board in case they want to keep moving? + if (!isset($_REQUEST['goback'])) + redirectexit('board=' . $board . '.0'); + else + redirectexit('topic=' . $topic . '.0'); +} + +// Moves one or more topics to a specific board. (doesn't check permissions.) +function moveTopics($topics, $toBoard) +{ + global $sourcedir, $user_info, $modSettings, $smcFunc; + + // Empty array? + if (empty($topics)) + return; + // Only a single topic. + elseif (is_numeric($topics)) + $topics = array($topics); + $num_topics = count($topics); + $fromBoards = array(); + + // Destination board empty or equal to 0? + if (empty($toBoard)) + return; + + // Are we moving to the recycle board? + $isRecycleDest = !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $toBoard; + + // Determine the source boards... + $request = $smcFunc['db_query']('', ' + SELECT id_board, approved, COUNT(*) AS num_topics, SUM(unapproved_posts) AS unapproved_posts, + SUM(num_replies) AS num_replies + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:topics}) + GROUP BY id_board, approved', + array( + 'topics' => $topics, + ) + ); + // Num of rows = 0 -> no topics found. Num of rows > 1 -> topics are on multiple boards. + if ($smcFunc['db_num_rows']($request) == 0) + return; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($fromBoards[$row['id_board']]['num_posts'])) + { + $fromBoards[$row['id_board']] = array( + 'num_posts' => 0, + 'num_topics' => 0, + 'unapproved_posts' => 0, + 'unapproved_topics' => 0, + 'id_board' => $row['id_board'] + ); + } + // Posts = (num_replies + 1) for each approved topic. + $fromBoards[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? $row['num_topics'] : 0); + $fromBoards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts']; + + // Add the topics to the right type. + if ($row['approved']) + $fromBoards[$row['id_board']]['num_topics'] += $row['num_topics']; + else + $fromBoards[$row['id_board']]['unapproved_topics'] += $row['num_topics']; + } + $smcFunc['db_free_result']($request); + + // Move over the mark_read data. (because it may be read and now not by some!) + $SaveAServer = max(0, $modSettings['maxMsgID'] - 50000); + $request = $smcFunc['db_query']('', ' + SELECT lmr.id_member, lmr.id_msg, t.id_topic + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board + AND lmr.id_msg > t.id_first_msg AND lmr.id_msg > {int:protect_lmr_msg}) + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = lmr.id_member) + WHERE t.id_topic IN ({array_int:topics}) + AND lmr.id_msg > IFNULL(lt.id_msg, 0)', + array( + 'protect_lmr_msg' => $SaveAServer, + 'topics' => $topics, + ) + ); + $log_topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $log_topics[] = array($row['id_topic'], $row['id_member'], $row['id_msg']); + + // Prevent queries from getting too big. Taking some steam off. + if (count($log_topics) > 500) + { + $smcFunc['db_insert']('replace', + '{db_prefix}log_topics', + array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), + $log_topics, + array('id_topic', 'id_member') + ); + + $log_topics = array(); + } + } + $smcFunc['db_free_result']($request); + + // Now that we have all the topics that *should* be marked read, and by which members... + if (!empty($log_topics)) + { + // Insert that information into the database! + $smcFunc['db_insert']('replace', + '{db_prefix}log_topics', + array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), + $log_topics, + array('id_topic', 'id_member') + ); + } + + // Update the number of posts on each board. + $totalTopics = 0; + $totalPosts = 0; + $totalUnapprovedTopics = 0; + $totalUnapprovedPosts = 0; + foreach ($fromBoards as $stats) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET + num_posts = CASE WHEN {int:num_posts} > num_posts THEN 0 ELSE num_posts - {int:num_posts} END, + num_topics = CASE WHEN {int:num_topics} > num_topics THEN 0 ELSE num_topics - {int:num_topics} END, + unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END, + unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END + WHERE id_board = {int:id_board}', + array( + 'id_board' => $stats['id_board'], + 'num_posts' => $stats['num_posts'], + 'num_topics' => $stats['num_topics'], + 'unapproved_posts' => $stats['unapproved_posts'], + 'unapproved_topics' => $stats['unapproved_topics'], + ) + ); + $totalTopics += $stats['num_topics']; + $totalPosts += $stats['num_posts']; + $totalUnapprovedTopics += $stats['unapproved_topics']; + $totalUnapprovedPosts += $stats['unapproved_posts']; + } + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET + num_topics = num_topics + {int:total_topics}, + num_posts = num_posts + {int:total_posts},' . ($isRecycleDest ? ' + unapproved_posts = {int:no_unapproved}, unapproved_topics = {int:no_unapproved}' : ' + unapproved_posts = unapproved_posts + {int:total_unapproved_posts}, + unapproved_topics = unapproved_topics + {int:total_unapproved_topics}') . ' + WHERE id_board = {int:id_board}', + array( + 'id_board' => $toBoard, + 'total_topics' => $totalTopics, + 'total_posts' => $totalPosts, + 'total_unapproved_topics' => $totalUnapprovedTopics, + 'total_unapproved_posts' => $totalUnapprovedPosts, + 'no_unapproved' => 0, + ) + ); + + // Move the topic. Done. :P + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET id_board = {int:id_board}' . ($isRecycleDest ? ', + unapproved_posts = {int:no_unapproved}, approved = {int:is_approved}' : '') . ' + WHERE id_topic IN ({array_int:topics})', + array( + 'id_board' => $toBoard, + 'topics' => $topics, + 'is_approved' => 1, + 'no_unapproved' => 0, + ) + ); + + // If this was going to the recycle bin, check what messages are being recycled, and remove them from the queue. + if ($isRecycleDest && ($totalUnapprovedTopics || $totalUnapprovedPosts)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_msg + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topics}) + and approved = {int:not_approved}', + array( + 'topics' => $topics, + 'not_approved' => 0, + ) + ); + $approval_msgs = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $approval_msgs[] = $row['id_msg']; + $smcFunc['db_free_result']($request); + + // Empty the approval queue for these, as we're going to approve them next. + if (!empty($approval_msgs)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}approval_queue + WHERE id_msg IN ({array_int:message_list}) + AND id_attach = {int:id_attach}', + array( + 'message_list' => $approval_msgs, + 'id_attach' => 0, + ) + ); + + // Get all the current max and mins. + $request = $smcFunc['db_query']('', ' + SELECT id_topic, id_first_msg, id_last_msg + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:topics})', + array( + 'topics' => $topics, + ) + ); + $topicMaxMin = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $topicMaxMin[$row['id_topic']] = array( + 'min' => $row['id_first_msg'], + 'max' => $row['id_last_msg'], + ); + } + $smcFunc['db_free_result']($request); + + // Check the MAX and MIN are correct. + $request = $smcFunc['db_query']('', ' + SELECT id_topic, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topics}) + GROUP BY id_topic', + array( + 'topics' => $topics, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If not, update. + if ($row['first_msg'] != $topicMaxMin[$row['id_topic']]['min'] || $row['last_msg'] != $topicMaxMin[$row['id_topic']]['max']) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET id_first_msg = {int:first_msg}, id_last_msg = {int:last_msg} + WHERE id_topic = {int:selected_topic}', + array( + 'first_msg' => $row['first_msg'], + 'last_msg' => $row['last_msg'], + 'selected_topic' => $row['id_topic'], + ) + ); + } + $smcFunc['db_free_result']($request); + } + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET id_board = {int:id_board}' . ($isRecycleDest ? ',approved = {int:is_approved}' : '') . ' + WHERE id_topic IN ({array_int:topics})', + array( + 'id_board' => $toBoard, + 'topics' => $topics, + 'is_approved' => 1, + ) + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_reported + SET id_board = {int:id_board} + WHERE id_topic IN ({array_int:topics})', + array( + 'id_board' => $toBoard, + 'topics' => $topics, + ) + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}calendar + SET id_board = {int:id_board} + WHERE id_topic IN ({array_int:topics})', + array( + 'id_board' => $toBoard, + 'topics' => $topics, + ) + ); + + // Mark target board as seen, if it was already marked as seen before. + $request = $smcFunc['db_query']('', ' + SELECT (IFNULL(lb.id_msg, 0) >= b.id_msg_updated) AS isSeen + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member}) + WHERE b.id_board = {int:id_board}', + array( + 'current_member' => $user_info['id'], + 'id_board' => $toBoard, + ) + ); + list ($isSeen) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (!empty($isSeen) && !$user_info['is_guest']) + { + $smcFunc['db_insert']('replace', + '{db_prefix}log_boards', + array('id_board' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), + array($toBoard, $user_info['id'], $modSettings['maxMsgID']), + array('id_board', 'id_member') + ); + } + + // Update the cache? + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3) + foreach ($topics as $topic_id) + cache_put_data('topic_board-' . $topic_id, null, 120); + + require_once($sourcedir . '/Subs-Post.php'); + + $updates = array_keys($fromBoards); + $updates[] = $toBoard; + + updateLastMessages(array_unique($updates)); + + // Update 'em pesky stats. + updateStats('topic'); + updateStats('message'); + updateSettings(array( + 'calendar_updated' => time(), + )); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/News.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/News.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,972 @@ + 'm.id_msg <= b.id_last_msg', + ); + if (!empty($_REQUEST['c']) && empty($board)) + { + $_REQUEST['c'] = explode(',', $_REQUEST['c']); + foreach ($_REQUEST['c'] as $i => $c) + $_REQUEST['c'][$i] = (int) $c; + + if (count($_REQUEST['c']) == 1) + { + $request = $smcFunc['db_query']('', ' + SELECT name + FROM {db_prefix}categories + WHERE id_cat = {int:current_category}', + array( + 'current_category' => (int) $_REQUEST['c'][0], + ) + ); + list ($feed_title) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $feed_title = ' - ' . strip_tags($feed_title); + } + + $request = $smcFunc['db_query']('', ' + SELECT b.id_board, b.num_posts + FROM {db_prefix}boards AS b + WHERE b.id_cat IN ({array_int:current_category_list}) + AND {query_see_board}', + array( + 'current_category_list' => $_REQUEST['c'], + ) + ); + $total_cat_posts = 0; + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $boards[] = $row['id_board']; + $total_cat_posts += $row['num_posts']; + } + $smcFunc['db_free_result']($request); + + if (!empty($boards)) + $query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; + + // Try to limit the number of messages we look through. + if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15) + $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 400 - $_GET['limit'] * 5); + } + elseif (!empty($_REQUEST['boards'])) + { + $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); + foreach ($_REQUEST['boards'] as $i => $b) + $_REQUEST['boards'][$i] = (int) $b; + + $request = $smcFunc['db_query']('', ' + SELECT b.id_board, b.num_posts, b.name + FROM {db_prefix}boards AS b + WHERE b.id_board IN ({array_int:board_list}) + AND {query_see_board} + LIMIT ' . count($_REQUEST['boards']), + array( + 'board_list' => $_REQUEST['boards'], + ) + ); + + // Either the board specified doesn't exist or you have no access. + $num_boards = $smcFunc['db_num_rows']($request); + if ($num_boards == 0) + fatal_lang_error('no_board'); + + $total_posts = 0; + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($num_boards == 1) + $feed_title = ' - ' . strip_tags($row['name']); + + $boards[] = $row['id_board']; + $total_posts += $row['num_posts']; + } + $smcFunc['db_free_result']($request); + + if (!empty($boards)) + $query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; + + // The more boards, the more we're going to look through... + if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12) + $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 500 - $_GET['limit'] * 5); + } + elseif (!empty($board)) + { + $request = $smcFunc['db_query']('', ' + SELECT num_posts + FROM {db_prefix}boards + WHERE id_board = {int:current_board} + LIMIT 1', + array( + 'current_board' => $board, + ) + ); + list ($total_posts) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $feed_title = ' - ' . strip_tags($board_info['name']); + + $query_this_board = 'b.id_board = ' . $board; + + // Try to look through just a few messages, if at all possible. + if ($total_posts > 80 && $total_posts > $modSettings['totalMessages'] / 10) + $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 600 - $_GET['limit'] * 5); + } + else + { + $query_this_board = '{query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != ' . $modSettings['recycle_board'] : ''); + $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 100 - $_GET['limit'] * 5); + } + + // Show in rss or proprietary format? + $xml_format = isset($_GET['type']) && in_array($_GET['type'], array('smf', 'rss', 'rss2', 'atom', 'rdf', 'webslice')) ? $_GET['type'] : 'smf'; + + // !!! Birthdays? + + // List all the different types of data they can pull. + $subActions = array( + 'recent' => array('getXmlRecent', 'recent-post'), + 'news' => array('getXmlNews', 'article'), + 'members' => array('getXmlMembers', 'member'), + 'profile' => array('getXmlProfile', null), + ); + if (empty($_GET['sa']) || !isset($subActions[$_GET['sa']])) + $_GET['sa'] = 'recent'; + + //!!! Temp - webslices doesn't do everything yet. + if ($xml_format == 'webslice' && $_GET['sa'] != 'recent') + $xml_format = 'rss2'; + // If this is webslices we kinda cheat - we allow a template that we call direct for the HTML, and we override the CDATA. + elseif ($xml_format == 'webslice') + { + $context['user'] += $user_info; + $cdata_override = true; + loadTemplate('Xml'); + } + + // We only want some information, not all of it. + $cachekey = array($xml_format, $_GET['action'], $_GET['limit'], $_GET['sa']); + foreach (array('board', 'boards', 'c') as $var) + if (isset($_REQUEST[$var])) + $cachekey[] = $_REQUEST[$var]; + $cachekey = md5(serialize($cachekey) . (!empty($query_this_board) ? $query_this_board : '')); + $cache_t = microtime(); + + // Get the associative array representing the xml. + if (!empty($modSettings['cache_enable']) && (!$user_info['is_guest'] || $modSettings['cache_enable'] >= 3)) + $xml = cache_get_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, 240); + if (empty($xml)) + { + $xml = $subActions[$_GET['sa']][0]($xml_format); + + if (!empty($modSettings['cache_enable']) && (($user_info['is_guest'] && $modSettings['cache_enable'] >= 3) + || (!$user_info['is_guest'] && (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $cache_t)) > 0.2)))) + cache_put_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, $xml, 240); + } + + $feed_title = htmlspecialchars(strip_tags($context['forum_name'])) . (isset($feed_title) ? $feed_title : ''); + + // This is an xml file.... + ob_end_clean(); + if (!empty($modSettings['enableCompressedOutput'])) + @ob_start('ob_gzhandler'); + else + ob_start(); + + if ($xml_format == 'smf' || isset($_REQUEST['debug'])) + header('Content-Type: text/xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); + elseif ($xml_format == 'rss' || $xml_format == 'rss2' || $xml_format == 'webslice') + header('Content-Type: application/rss+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); + elseif ($xml_format == 'atom') + header('Content-Type: application/atom+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); + elseif ($xml_format == 'rdf') + header('Content-Type: ' . ($context['browser']['is_ie'] ? 'text/xml' : 'application/rdf+xml') . '; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); + + // First, output the xml header. + echo ''; + + // Are we outputting an rss feed or one with more information? + if ($xml_format == 'rss' || $xml_format == 'rss2') + { + // Start with an RSS 2.0 header. + echo ' + + + ', $feed_title, ' + ', $scripturl, ' + '; + + // Output all of the associative array, start indenting with 2 tabs, and name everything "item". + dumpTags($xml, 2, 'item', $xml_format); + + // Output the footer of the xml. + echo ' + +'; + } + elseif ($xml_format == 'webslice') + { + $context['recent_posts_data'] = $xml; + + // This always has RSS 2 + echo ' + + + ', $feed_title, ' - ', $txt['recent_posts'], ' + ', $scripturl, '?action=recent + + + ', $feed_title, ' - ', $txt['recent_posts'], ' + ', $scripturl, '?action=recent + + + +'; + } + elseif ($xml_format == 'atom') + { + echo ' + + ', $feed_title, ' + + + ', gmstrftime('%Y-%m-%dT%H:%M:%SZ'), ' + + SMF + + ', strip_tags($context['forum_name']), ' + '; + + dumpTags($xml, 2, 'entry', $xml_format); + + echo ' +'; + } + elseif ($xml_format == 'rdf') + { + echo ' + + + ', $feed_title, ' + ', $scripturl, ' + + + '; + + foreach ($xml as $item) + echo ' + '; + + echo ' + + + +'; + + dumpTags($xml, 1, 'item', $xml_format); + + echo ' +'; + } + // Otherwise, we're using our proprietary formats - they give more data, though. + else + { + echo ' +'; + + // Dump out that associative array. Indent properly.... and use the right names for the base elements. + dumpTags($xml, 1, $subActions[$_GET['sa']][1], $xml_format); + + echo ' +'; +} + + obExit(false); +} + +function fix_possible_url($val) +{ + global $modSettings, $context, $scripturl; + + if (substr($val, 0, strlen($scripturl)) != $scripturl) + return $val; + + call_integration_hook('integrate_fix_url', array(&$val)); + + if (empty($modSettings['queryless_urls']) || ($context['server']['is_cgi'] && @ini_get('cgi.fix_pathinfo') == 0 && @get_cfg_var('cgi.fix_pathinfo') == 0) || (!$context['server']['is_apache'] && !$context['server']['is_lighttpd'])) + return $val; + + $val = preg_replace('/^' . preg_quote($scripturl, '/') . '\?((?:board|topic)=[^#"]+)(#[^"]*)?$/e', '\'\' . $scripturl . \'/\' . strtr(\'$1\', \'&;=\', \'//,\') . \'.html$2\'', $val); + return $val; +} + +function cdata_parse($data, $ns = '') +{ + global $smcFunc, $cdata_override; + + // Are we not doing it? + if (!empty($cdata_override)) + return $data; + + $cdata = ' $dummy) + { + if ($dummy === false) + unset($positions[$k]); + } + + $old = $pos; + $pos = empty($positions) ? $n : min($positions); + + if ($pos - $old > 0) + $cdata .= $smcFunc['substr']($data, $old, $pos - $old); + if ($pos >= $n) + break; + + if ($smcFunc['substr']($data, $pos, 1) == '<') + { + $pos2 = $smcFunc['strpos']($data, '>', $pos); + if ($pos2 === false) + $pos2 = $n; + if ($smcFunc['substr']($data, $pos + 1, 1) == '/') + $cdata .= ']]><' . $ns . ':' . $smcFunc['substr']($data, $pos + 1, $pos2 - $pos) . ']' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . ''; + + return strtr($cdata, array('' => '')); +} + +function dumpTags($data, $i, $tag = null, $xml_format = '') +{ + global $modSettings, $context, $scripturl; + + // For every array in the data... + foreach ($data as $key => $val) + { + // Skip it, it's been set to null. + if ($val === null) + continue; + + // If a tag was passed, use it instead of the key. + $key = isset($tag) ? $tag : $key; + + // First let's indent! + echo "\n", str_repeat("\t", $i); + + // Grr, I hate kludges... almost worth doing it properly, here, but not quite. + if ($xml_format == 'atom' && $key == 'link') + { + echo ''; + continue; + } + + // If it's empty/0/nothing simply output an empty tag. + if ($val == '') + echo '<', $key, ' />'; + else + { + // Beginning tag. + if ($xml_format == 'rdf' && $key == 'item' && isset($val['link'])) + { + echo '<', $key, ' rdf:about="', fix_possible_url($val['link']), '">'; + echo "\n", str_repeat("\t", $i + 1); + echo 'text/html'; + } + elseif ($xml_format == 'atom' && $key == 'summary') + echo '<', $key, ' type="html">'; + else + echo '<', $key, '>'; + + if (is_array($val)) + { + // An array. Dump it, and then indent the tag. + dumpTags($val, $i + 1, null, $xml_format); + echo "\n", str_repeat("\t", $i), ''; + } + // A string with returns in it.... show this as a multiline element. + elseif (strpos($val, "\n") !== false || strpos($val, '
') !== false) + echo "\n", fix_possible_url($val), "\n", str_repeat("\t", $i), ''; + // A simple string. + else + echo fix_possible_url($val), ''; + } + } +} + +function getXmlMembers($xml_format) +{ + global $scripturl, $smcFunc; + + if (!allowedTo('view_mlist')) + return array(); + + // Find the most recent members. + $request = $smcFunc['db_query']('', ' + SELECT id_member, member_name, real_name, date_registered, last_login + FROM {db_prefix}members + ORDER BY id_member DESC + LIMIT {int:limit}', + array( + 'limit' => $_GET['limit'], + ) + ); + $data = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Make the data look rss-ish. + if ($xml_format == 'rss' || $xml_format == 'rss2') + $data[] = array( + 'title' => cdata_parse($row['real_name']), + 'link' => $scripturl . '?action=profile;u=' . $row['id_member'], + 'comments' => $scripturl . '?action=pm;sa=send;u=' . $row['id_member'], + 'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $row['date_registered']), + 'guid' => $scripturl . '?action=profile;u=' . $row['id_member'], + ); + elseif ($xml_format == 'rdf') + $data[] = array( + 'title' => cdata_parse($row['real_name']), + 'link' => $scripturl . '?action=profile;u=' . $row['id_member'], + ); + elseif ($xml_format == 'atom') + $data[] = array( + 'title' => cdata_parse($row['real_name']), + 'link' => $scripturl . '?action=profile;u=' . $row['id_member'], + 'published' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['date_registered']), + 'updated' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['last_login']), + 'id' => $scripturl . '?action=profile;u=' . $row['id_member'], + ); + // More logical format for the data, but harder to apply. + else + $data[] = array( + 'name' => cdata_parse($row['real_name']), + 'time' => htmlspecialchars(strip_tags(timeformat($row['date_registered']))), + 'id' => $row['id_member'], + 'link' => $scripturl . '?action=profile;u=' . $row['id_member'] + ); + } + $smcFunc['db_free_result']($request); + + return $data; +} + +function getXmlNews($xml_format) +{ + global $user_info, $scripturl, $modSettings, $board; + global $query_this_board, $smcFunc, $settings, $context; + + /* Find the latest posts that: + - are the first post in their topic. + - are on an any board OR in a specified board. + - can be seen by this user. + - are actually the latest posts. */ + + $done = false; + $loops = 0; + while (!$done) + { + $optimize_msg = implode(' AND ', $context['optimize_msg']); + $request = $smcFunc['db_query']('', ' + SELECT + m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.modified_time, + m.icon, t.id_topic, t.id_board, t.num_replies, + b.name AS bname, + mem.hide_email, IFNULL(mem.id_member, 0) AS id_member, + IFNULL(mem.email_address, m.poster_email) AS poster_email, + IFNULL(mem.real_name, m.poster_name) AS poster_name + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : ' + AND {raw:optimize_msg}') . (empty($board) ? '' : ' + AND t.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : '') . ' + ORDER BY t.id_first_msg DESC + LIMIT {int:limit}', + array( + 'current_board' => $board, + 'is_approved' => 1, + 'limit' => $_GET['limit'], + 'optimize_msg' => $optimize_msg, + ) + ); + // If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows. + if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit']) + { + $smcFunc['db_free_result']($request); + if (empty($_REQUEST['boards']) && empty($board)) + unset($context['optimize_msg']['lowest']); + else + $context['optimize_msg']['lowest'] = 'm.id_msg >= t.id_first_msg'; + $context['optimize_msg']['highest'] = 'm.id_msg <= t.id_last_msg'; + $loops++; + } + else + $done = true; + } + $data = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Limit the length of the message, if the option is set. + if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('
', "\n", $row['body'])) > $modSettings['xmlnews_maxlen']) + $row['body'] = strtr($smcFunc['substr'](str_replace('
', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '
')) . '...'; + + $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); + + censorText($row['body']); + censorText($row['subject']); + + // Being news, this actually makes sense in rss format. + if ($xml_format == 'rss' || $xml_format == 'rss2') + $data[] = array( + 'title' => cdata_parse($row['subject']), + 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'description' => cdata_parse($row['body']), + 'author' => in_array(showEmailAddress(!empty($row['hide_email']), $row['id_member']), array('yes', 'yes_permission_override')) ? $row['poster_email'] : null, + 'comments' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0', + 'category' => '', + 'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']), + 'guid' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + ); + elseif ($xml_format == 'rdf') + $data[] = array( + 'title' => cdata_parse($row['subject']), + 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'description' => cdata_parse($row['body']), + ); + elseif ($xml_format == 'atom') + $data[] = array( + 'title' => cdata_parse($row['subject']), + 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'summary' => cdata_parse($row['body']), + 'category' => array('term' => $row['id_board'], 'label' => cdata_parse($row['bname'])), + 'author' => array( + 'name' => $row['poster_name'], + 'email' => in_array(showEmailAddress(!empty($row['hide_email']), $row['id_member']), array('yes', 'yes_permission_override')) ? $row['poster_email'] : null, + 'uri' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '', + ), + 'published' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']), + 'modified' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']), + 'id' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'icon' => $settings['images_url'] . '/icons/' . $row['icon'] . '.gif', + ); + // The biggest difference here is more information. + else + $data[] = array( + 'time' => htmlspecialchars(strip_tags(timeformat($row['poster_time']))), + 'id' => $row['id_topic'], + 'subject' => cdata_parse($row['subject']), + 'body' => cdata_parse($row['body']), + 'poster' => array( + 'name' => cdata_parse($row['poster_name']), + 'id' => $row['id_member'], + 'link' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '', + ), + 'topic' => $row['id_topic'], + 'board' => array( + 'name' => cdata_parse($row['bname']), + 'id' => $row['id_board'], + 'link' => $scripturl . '?board=' . $row['id_board'] . '.0', + ), + 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + ); + } + $smcFunc['db_free_result']($request); + + return $data; +} + +function getXmlRecent($xml_format) +{ + global $user_info, $scripturl, $modSettings, $board; + global $query_this_board, $smcFunc, $settings, $context; + + $done = false; + $loops = 0; + while (!$done) + { + $optimize_msg = implode(' AND ', $context['optimize_msg']); + $request = $smcFunc['db_query']('', ' + SELECT m.id_msg + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : ' + AND {raw:optimize_msg}') . (empty($board) ? '' : ' + AND m.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? ' + AND m.approved = {int:is_approved}' : '') . ' + ORDER BY m.id_msg DESC + LIMIT {int:limit}', + array( + 'limit' => $_GET['limit'], + 'current_board' => $board, + 'is_approved' => 1, + 'optimize_msg' => $optimize_msg, + ) + ); + // If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows. + if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit']) + { + $smcFunc['db_free_result']($request); + if (empty($_REQUEST['boards']) && empty($board)) + unset($context['optimize_msg']['lowest']); + else + $context['optimize_msg']['lowest'] = $loops ? 'm.id_msg >= t.id_first_msg' : 'm.id_msg >= (t.id_last_msg - t.id_first_msg) / 2'; + $loops++; + } + else + $done = true; + } + $messages = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $messages[] = $row['id_msg']; + $smcFunc['db_free_result']($request); + + if (empty($messages)) + return array(); + + // Find the most recent posts this user can see. + $request = $smcFunc['db_query']('', ' + SELECT + m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.id_topic, t.id_board, + b.name AS bname, t.num_replies, m.id_member, m.icon, mf.id_member AS id_first_member, + IFNULL(mem.real_name, m.poster_name) AS poster_name, mf.subject AS first_subject, + IFNULL(memf.real_name, mf.poster_name) AS first_poster_name, mem.hide_email, + IFNULL(mem.email_address, m.poster_email) AS poster_email, m.modified_time + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member) + WHERE m.id_msg IN ({array_int:message_list}) + ' . (empty($board) ? '' : 'AND t.id_board = {int:current_board}') . ' + ORDER BY m.id_msg DESC + LIMIT {int:limit}', + array( + 'limit' => $_GET['limit'], + 'current_board' => $board, + 'message_list' => $messages, + ) + ); + $data = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Limit the length of the message, if the option is set. + if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('
', "\n", $row['body'])) > $modSettings['xmlnews_maxlen']) + $row['body'] = strtr($smcFunc['substr'](str_replace('
', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '
')) . '...'; + + $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); + + censorText($row['body']); + censorText($row['subject']); + + // Doesn't work as well as news, but it kinda does.. + if ($xml_format == 'rss' || $xml_format == 'rss2') + $data[] = array( + 'title' => $row['subject'], + 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + 'description' => cdata_parse($row['body']), + 'author' => in_array(showEmailAddress(!empty($row['hide_email']), $row['id_member']), array('yes', 'yes_permission_override')) ? $row['poster_email'] : null, + 'category' => cdata_parse($row['bname']), + 'comments' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0', + 'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']), + 'guid' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] + ); + elseif ($xml_format == 'rdf') + $data[] = array( + 'title' => $row['subject'], + 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + 'description' => cdata_parse($row['body']), + ); + elseif ($xml_format == 'atom') + $data[] = array( + 'title' => $row['subject'], + 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + 'summary' => cdata_parse($row['body']), + 'category' => array( + 'term' => $row['id_board'], + 'label' => cdata_parse($row['bname']) + ), + 'author' => array( + 'name' => $row['poster_name'], + 'email' => in_array(showEmailAddress(!empty($row['hide_email']), $row['id_member']), array('yes', 'yes_permission_override')) ? $row['poster_email'] : null, + 'uri' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '' + ), + 'published' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']), + 'updated' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']), + 'id' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + 'icon' => $settings['images_url'] . '/icons/' . $row['icon'] . '.gif', + ); + // A lot of information here. Should be enough to please the rss-ers. + else + $data[] = array( + 'time' => htmlspecialchars(strip_tags(timeformat($row['poster_time']))), + 'id' => $row['id_msg'], + 'subject' => cdata_parse($row['subject']), + 'body' => cdata_parse($row['body']), + 'starter' => array( + 'name' => cdata_parse($row['first_poster_name']), + 'id' => $row['id_first_member'], + 'link' => !empty($row['id_first_member']) ? $scripturl . '?action=profile;u=' . $row['id_first_member'] : '' + ), + 'poster' => array( + 'name' => cdata_parse($row['poster_name']), + 'id' => $row['id_member'], + 'link' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '' + ), + 'topic' => array( + 'subject' => cdata_parse($row['first_subject']), + 'id' => $row['id_topic'], + 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.new#new' + ), + 'board' => array( + 'name' => cdata_parse($row['bname']), + 'id' => $row['id_board'], + 'link' => $scripturl . '?board=' . $row['id_board'] . '.0' + ), + 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] + ); + } + $smcFunc['db_free_result']($request); + + return $data; +} + +function getXmlProfile($xml_format) +{ + global $scripturl, $memberContext, $user_profile, $modSettings, $user_info; + + // You must input a valid user.... + if (empty($_GET['u']) || loadMemberData((int) $_GET['u']) === false) + return array(); + + // Make sure the id is a number and not "I like trying to hack the database". + $_GET['u'] = (int) $_GET['u']; + // Load the member's contextual information! + if (!loadMemberContext($_GET['u']) || !allowedTo('profile_view_any')) + return array(); + + // Okay, I admit it, I'm lazy. Stupid $_GET['u'] is long and hard to type. + $profile = &$memberContext[$_GET['u']]; + + if ($xml_format == 'rss' || $xml_format == 'rss2') + $data = array(array( + 'title' => cdata_parse($profile['name']), + 'link' => $scripturl . '?action=profile;u=' . $profile['id'], + 'description' => cdata_parse(isset($profile['group']) ? $profile['group'] : $profile['post_group']), + 'comments' => $scripturl . '?action=pm;sa=send;u=' . $profile['id'], + 'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']), + 'guid' => $scripturl . '?action=profile;u=' . $profile['id'], + )); + elseif ($xml_format == 'rdf') + $data = array(array( + 'title' => cdata_parse($profile['name']), + 'link' => $scripturl . '?action=profile;u=' . $profile['id'], + 'description' => cdata_parse(isset($profile['group']) ? $profile['group'] : $profile['post_group']), + )); + elseif ($xml_format == 'atom') + $data[] = array( + 'title' => cdata_parse($profile['name']), + 'link' => $scripturl . '?action=profile;u=' . $profile['id'], + 'summary' => cdata_parse(isset($profile['group']) ? $profile['group'] : $profile['post_group']), + 'author' => array( + 'name' => $profile['real_name'], + 'email' => in_array(showEmailAddress(!empty($profile['hide_email']), $profile['id']), array('yes', 'yes_permission_override')) ? $profile['email'] : null, + 'uri' => !empty($profile['website']) ? $profile['website']['url'] : '' + ), + 'published' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['date_registered']), + 'updated' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['last_login']), + 'id' => $scripturl . '?action=profile;u=' . $profile['id'], + 'logo' => !empty($profile['avatar']) ? $profile['avatar']['url'] : '', + ); + else + { + $data = array( + 'username' => $user_info['is_admin'] || $user_info['id'] == $profile['id'] ? cdata_parse($profile['username']) : '', + 'name' => cdata_parse($profile['name']), + 'link' => $scripturl . '?action=profile;u=' . $profile['id'], + 'posts' => $profile['posts'], + 'post-group' => cdata_parse($profile['post_group']), + 'language' => cdata_parse($profile['language']), + 'last-login' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['last_login']), + 'registered' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']) + ); + + // Everything below here might not be set, and thus maybe shouldn't be displayed. + if ($profile['gender']['name'] != '') + $data['gender'] = cdata_parse($profile['gender']['name']); + + if ($profile['avatar']['name'] != '') + $data['avatar'] = $profile['avatar']['url']; + + // If they are online, show an empty tag... no reason to put anything inside it. + if ($profile['online']['is_online']) + $data['online'] = ''; + + if ($profile['signature'] != '') + $data['signature'] = cdata_parse($profile['signature']); + if ($profile['blurb'] != '') + $data['blurb'] = cdata_parse($profile['blurb']); + if ($profile['location'] != '') + $data['location'] = cdata_parse($profile['location']); + if ($profile['title'] != '') + $data['title'] = cdata_parse($profile['title']); + + if (!empty($profile['icq']['name']) && !(!empty($modSettings['guest_hideContacts']) && $user_info['is_guest'])) + $data['icq'] = $profile['icq']['name']; + if ($profile['aim']['name'] != '' && !(!empty($modSettings['guest_hideContacts']) && $user_info['is_guest'])) + $data['aim'] = $profile['aim']['name']; + if ($profile['msn']['name'] != '' && !(!empty($modSettings['guest_hideContacts']) && $user_info['is_guest'])) + $data['msn'] = $profile['msn']['name']; + if ($profile['yim']['name'] != '' && !(!empty($modSettings['guest_hideContacts']) && $user_info['is_guest'])) + $data['yim'] = $profile['yim']['name']; + + if ($profile['website']['title'] != '') + $data['website'] = array( + 'title' => cdata_parse($profile['website']['title']), + 'link' => $profile['website']['url'] + ); + + if ($profile['group'] != '') + $data['position'] = cdata_parse($profile['group']); + + if (!empty($modSettings['karmaMode'])) + $data['karma'] = array( + 'good' => $profile['karma']['good'], + 'bad' => $profile['karma']['bad'] + ); + + if (in_array($profile['show_email'], array('yes', 'yes_permission_override'))) + $data['email'] = $profile['email']; + + if (!empty($profile['birth_date']) && substr($profile['birth_date'], 0, 4) != '0000') + { + list ($birth_year, $birth_month, $birth_day) = sscanf($profile['birth_date'], '%d-%d-%d'); + $datearray = getdate(forum_time()); + $data['age'] = $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1); + } + } + + // Save some memory. + unset($profile, $memberContext[$_GET['u']]); + + return $data; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Notify.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Notify.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,187 @@ + $user_info['id'], + 'current_topic' => $topic, + ) + ); + $context['notification_set'] = $smcFunc['db_num_rows']($request) != 0; + $smcFunc['db_free_result']($request); + + // Set the template variables... + $context['topic_href'] = $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start']; + $context['start'] = $_REQUEST['start']; + $context['page_title'] = $txt['notification']; + + return; + } + elseif ($_GET['sa'] == 'on') + { + checkSession('get'); + + // Attempt to turn notifications on. + $smcFunc['db_insert']('ignore', + '{db_prefix}log_notify', + array('id_member' => 'int', 'id_topic' => 'int'), + array($user_info['id'], $topic), + array('id_member', 'id_topic') + ); + } + else + { + checkSession('get'); + + // Just turn notifications off. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_notify + WHERE id_member = {int:current_member} + AND id_topic = {int:current_topic}', + array( + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + ) + ); + } + + // Send them back to the topic. + redirectexit('topic=' . $topic . '.' . $_REQUEST['start']); +} + +function BoardNotify() +{ + global $scripturl, $txt, $board, $user_info, $context, $smcFunc; + + // Permissions are an important part of anything ;). + is_not_guest(); + isAllowedTo('mark_notify'); + + // You have to specify a board to turn notifications on! + if (empty($board)) + fatal_lang_error('no_board', false); + + // No subaction: find out what to do. + if (empty($_GET['sa'])) + { + // We're gonna need the notify template... + loadTemplate('Notify'); + + // Find out if they have notification set for this topic already. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}log_notify + WHERE id_member = {int:current_member} + AND id_board = {int:current_board} + LIMIT 1', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + ) + ); + $context['notification_set'] = $smcFunc['db_num_rows']($request) != 0; + $smcFunc['db_free_result']($request); + + // Set the template variables... + $context['board_href'] = $scripturl . '?board=' . $board . '.' . $_REQUEST['start']; + $context['start'] = $_REQUEST['start']; + $context['page_title'] = $txt['notification']; + $context['sub_template'] = 'notify_board'; + + return; + } + // Turn the board level notification on.... + elseif ($_GET['sa'] == 'on') + { + checkSession('get'); + + // Turn notification on. (note this just blows smoke if it's already on.) + $smcFunc['db_insert']('ignore', + '{db_prefix}log_notify', + array('id_member' => 'int', 'id_board' => 'int'), + array($user_info['id'], $board), + array('id_member', 'id_board') + ); + } + // ...or off? + else + { + checkSession('get'); + + // Turn notification off for this board. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_notify + WHERE id_member = {int:current_member} + AND id_board = {int:current_board}', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + ) + ); + } + + // Back to the board! + redirectexit('board=' . $board . '.' . $_REQUEST['start']); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/PackageGet.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/PackageGet.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,765 @@ + 'PackageServers', + 'add' => 'PackageServerAdd', + 'browse' => 'PackageGBrowse', + 'download' => 'PackageDownload', + 'remove' => 'PackageServerRemove', + 'upload' => 'PackageUpload', + ); + + // Now let's decide where we are taking this... + if (isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']])) + $context['sub_action'] = $_REQUEST['sa']; + // We need to support possible old javascript links... + elseif (isset($_GET['pgdownload'])) + $context['sub_action'] = 'download'; + else + $context['sub_action'] = 'servers'; + + // We need to force the "Download" tab as selected. + $context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'packageget'; + + // Now create the tabs for the template. + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['package_manager'], + //'help' => 'registrations', + 'description' => $txt['package_manager_desc'], + 'tabs' => array( + 'browse' => array( + ), + 'packageget' => array( + 'description' => $txt['download_packages_desc'], + ), + 'installed' => array( + 'description' => $txt['installed_packages_desc'], + ), + 'perms' => array( + 'description' => $txt['package_file_perms_desc'], + ), + 'options' => array( + 'description' => $txt['package_install_options_ftp_why'], + ), + ), + ); + + $subActions[$context['sub_action']](); +} + +function PackageServers() +{ + global $txt, $scripturl, $context, $boarddir, $sourcedir, $modSettings, $smcFunc; + + // Ensure we use the correct template, and page title. + $context['sub_template'] = 'servers'; + $context['page_title'] .= ' - ' . $txt['download_packages']; + + // Load the list of servers. + $request = $smcFunc['db_query']('', ' + SELECT id_server, name, url + FROM {db_prefix}package_servers', + array( + ) + ); + $context['servers'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['servers'][] = array( + 'name' => $row['name'], + 'url' => $row['url'], + 'id' => $row['id_server'], + ); + } + $smcFunc['db_free_result']($request); + + $context['package_download_broken'] = !is_writable($boarddir . '/Packages') || !is_writable($boarddir . '/Packages/installed.list'); + + if ($context['package_download_broken']) + { + @chmod($boarddir . '/Packages', 0777); + @chmod($boarddir . '/Packages/installed.list', 0777); + } + + $context['package_download_broken'] = !is_writable($boarddir . '/Packages') || !is_writable($boarddir . '/Packages/installed.list'); + + if ($context['package_download_broken']) + { + if (isset($_POST['ftp_username'])) + { + loadClassFile('Class-Package.php'); + $ftp = new ftp_connection($_POST['ftp_server'], $_POST['ftp_port'], $_POST['ftp_username'], $_POST['ftp_password']); + + if ($ftp->error === false) + { + // I know, I know... but a lot of people want to type /home/xyz/... which is wrong, but logical. + if (!$ftp->chdir($_POST['ftp_path'])) + { + $ftp_error = $ftp->error; + $ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $_POST['ftp_path'])); + } + } + } + + if (!isset($ftp) || $ftp->error !== false) + { + if (!isset($ftp)) + { + loadClassFile('Class-Package.php'); + $ftp = new ftp_connection(null); + } + elseif ($ftp->error !== false && !isset($ftp_error)) + $ftp_error = $ftp->last_message === null ? '' : $ftp->last_message; + + list ($username, $detect_path, $found_path) = $ftp->detect_path($boarddir); + + if ($found_path || !isset($_POST['ftp_path'])) + $_POST['ftp_path'] = $detect_path; + + if (!isset($_POST['ftp_username'])) + $_POST['ftp_username'] = $username; + + $context['package_ftp'] = array( + 'server' => isset($_POST['ftp_server']) ? $_POST['ftp_server'] : (isset($modSettings['package_server']) ? $modSettings['package_server'] : 'localhost'), + 'port' => isset($_POST['ftp_port']) ? $_POST['ftp_port'] : (isset($modSettings['package_port']) ? $modSettings['package_port'] : '21'), + 'username' => isset($_POST['ftp_username']) ? $_POST['ftp_username'] : (isset($modSettings['package_username']) ? $modSettings['package_username'] : ''), + 'path' => $_POST['ftp_path'], + 'error' => empty($ftp_error) ? null : $ftp_error, + ); + } + else + { + $context['package_download_broken'] = false; + + $ftp->chmod('Packages', 0777); + $ftp->chmod('Packages/installed.list', 0777); + + $ftp->close(); + } + } +} + +// Browse a server's list of packages. +function PackageGBrowse() +{ + global $txt, $boardurl, $context, $scripturl, $boarddir, $sourcedir, $forum_version, $context, $smcFunc; + + if (isset($_GET['server'])) + { + if ($_GET['server'] == '') + redirectexit('action=admin;area=packages;get'); + + $server = (int) $_GET['server']; + + // Query the server list to find the current server. + $request = $smcFunc['db_query']('', ' + SELECT name, url + FROM {db_prefix}package_servers + WHERE id_server = {int:current_server} + LIMIT 1', + array( + 'current_server' => $server, + ) + ); + list ($name, $url) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // If the server does not exist, dump out. + if (empty($url)) + fatal_lang_error('couldnt_connect', false); + + // If there is a relative link, append to the stored server url. + if (isset($_GET['relative'])) + $url = $url . (substr($url, -1) == '/' ? '' : '/') . $_GET['relative']; + + // Clear any "absolute" URL. Since "server" is present, "absolute" is garbage. + unset($_GET['absolute']); + } + elseif (isset($_GET['absolute']) && $_GET['absolute'] != '') + { + // Initialize the requried variables. + $server = ''; + $url = $_GET['absolute']; + $name = ''; + $_GET['package'] = $url . '/packages.xml?language=' . $context['user']['language']; + + // Clear any "relative" URL. Since "server" is not present, "relative" is garbage. + unset($_GET['relative']); + + $token = checkConfirm('get_absolute_url'); + if ($token !== true) + { + $context['sub_template'] = 'package_confirm'; + + $context['page_title'] = $txt['package_servers']; + $context['confirm_message'] = sprintf($txt['package_confirm_view_package_content'], htmlspecialchars($_GET['absolute'])); + $context['proceed_href'] = $scripturl . '?action=admin;area=packages;get;sa=browse;absolute=' . urlencode($_GET['absolute']) . ';confirm=' . $token; + + return; + } + } + // Minimum required parameter did not exist so dump out. + else + fatal_lang_error('couldnt_connect', false); + + // Attempt to connect. If unsuccessful... try the URL. + if (!isset($_GET['package']) || file_exists($_GET['package'])) + $_GET['package'] = $url . '/packages.xml?language=' . $context['user']['language']; + + // Check to be sure the packages.xml file actually exists where it is should be... or dump out. + if ((isset($_GET['absolute']) || isset($_GET['relative'])) && !url_exists($_GET['package'])) + fatal_lang_error('packageget_unable', false, array($url . '/index.php')); + + // Might take some time. + @set_time_limit(600); + + // Read packages.xml and parse into xmlArray. (the true tells it to trim things ;).) + loadClassFile('Class-Package.php'); + $listing = new xmlArray(fetch_web_data($_GET['package']), true); + + // Errm.... empty file? Try the URL.... + if (!$listing->exists('package-list')) + fatal_lang_error('packageget_unable', false, array($url . '/index.php')); + + // List out the packages... + $context['package_list'] = array(); + + $listing = $listing->path('package-list[0]'); + + // Use the package list's name if it exists. + if ($listing->exists('list-title')) + $name = $listing->fetch('list-title'); + + // Pick the correct template. + $context['sub_template'] = 'package_list'; + + $context['page_title'] = $txt['package_servers'] . ($name != '' ? ' - ' . $name : ''); + $context['package_server'] = $server; + + // By default we use an unordered list, unless there are no lists with more than one package. + $context['list_type'] = 'ul'; + + $instmods = loadInstalledPackages(); + + $installed_mods = array(); + // Look through the list of installed mods... + foreach ($instmods as $installed_mod) + $installed_mods[$installed_mod['package_id']] = $installed_mod['version']; + + // Get default author and email if they exist. + if ($listing->exists('default-author')) + { + $default_author = $smcFunc['htmlspecialchars']($listing->fetch('default-author')); + if ($listing->exists('default-author/@email')) + $default_email = $smcFunc['htmlspecialchars']($listing->fetch('default-author/@email')); + } + + // Get default web site if it exists. + if ($listing->exists('default-website')) + { + $default_website = $smcFunc['htmlspecialchars']($listing->fetch('default-website')); + if ($listing->exists('default-website/@title')) + $default_title = $smcFunc['htmlspecialchars']($listing->fetch('default-website/@title')); + } + + $the_version = strtr($forum_version, array('SMF ' => '')); + if (!empty($_SESSION['version_emulate'])) + $the_version = $_SESSION['version_emulate']; + + $packageNum = 0; + $packageSection = 0; + + $sections = $listing->set('section'); + foreach ($sections as $i => $section) + { + $context['package_list'][$packageSection] = array( + 'title' => '', + 'text' => '', + 'items' => array(), + ); + + $packages = $section->set('title|heading|text|remote|rule|modification|language|avatar-pack|theme|smiley-set'); + foreach ($packages as $thisPackage) + { + $package = array( + 'type' => $thisPackage->name(), + ); + + if (in_array($package['type'], array('title', 'text'))) + $context['package_list'][$packageSection][$package['type']] = $smcFunc['htmlspecialchars']($thisPackage->fetch('.')); + // It's a Title, Heading, Rule or Text. + elseif (in_array($package['type'], array('heading', 'rule'))) + $package['name'] = $smcFunc['htmlspecialchars']($thisPackage->fetch('.')); + // It's a Remote link. + elseif ($package['type'] == 'remote') + { + $remote_type = $thisPackage->exists('@type') ? $thisPackage->fetch('@type') : 'relative'; + + if ($remote_type == 'relative' && substr($thisPackage->fetch('@href'), 0, 7) != 'http://') + { + if (isset($_GET['absolute'])) + $current_url = $_GET['absolute'] . '/'; + elseif (isset($_GET['relative'])) + $current_url = $_GET['relative'] . '/'; + else + $current_url = ''; + + $current_url .= $thisPackage->fetch('@href'); + if (isset($_GET['absolute'])) + $package['href'] = $scripturl . '?action=admin;area=packages;get;sa=browse;absolute=' . $current_url; + else + $package['href'] = $scripturl . '?action=admin;area=packages;get;sa=browse;server=' . $context['package_server'] . ';relative=' . $current_url; + } + else + { + $current_url = $thisPackage->fetch('@href'); + $package['href'] = $scripturl . '?action=admin;area=packages;get;sa=browse;absolute=' . $current_url; + } + + $package['name'] = $smcFunc['htmlspecialchars']($thisPackage->fetch('.')); + $package['link'] = '' . $package['name'] . ''; + } + // It's a package... + else + { + if (isset($_GET['absolute'])) + $current_url = $_GET['absolute'] . '/'; + elseif (isset($_GET['relative'])) + $current_url = $_GET['relative'] . '/'; + else + $current_url = ''; + + $server_att = $server != '' ? ';server=' . $server : ''; + + $package += $thisPackage->to_array(); + + if (isset($package['website'])) + unset($package['website']); + $package['author'] = array(); + + if ($package['description'] == '') + $package['description'] = $txt['package_no_description']; + else + $package['description'] = parse_bbc(preg_replace('~\[[/]?html\]~i', '', $smcFunc['htmlspecialchars']($package['description']))); + + $package['is_installed'] = isset($installed_mods[$package['id']]); + $package['is_current'] = $package['is_installed'] && ($installed_mods[$package['id']] == $package['version']); + $package['is_newer'] = $package['is_installed'] && ($installed_mods[$package['id']] > $package['version']); + + // This package is either not installed, or installed but old. Is it supported on this version of SMF? + if (!$package['is_installed'] || (!$package['is_current'] && !$package['is_newer'])) + { + if ($thisPackage->exists('version/@for')) + $package['can_install'] = matchPackageVersion($the_version, $thisPackage->fetch('version/@for')); + } + // Okay, it's already installed AND up to date. + else + $package['can_install'] = false; + + $already_exists = getPackageInfo(basename($package['filename'])); + $package['download_conflict'] = is_array($already_exists) && $already_exists['id'] == $package['id'] && $already_exists['version'] != $package['version']; + + $package['href'] = $url . '/' . $package['filename']; + $package['name'] = $smcFunc['htmlspecialchars']($package['name']); + $package['link'] = '' . $package['name'] . ''; + $package['download']['href'] = $scripturl . '?action=admin;area=packages;get;sa=download' . $server_att . ';package=' . $current_url . $package['filename'] . ($package['download_conflict'] ? ';conflict' : '') . ';' . $context['session_var'] . '=' . $context['session_id']; + $package['download']['link'] = '' . $package['name'] . ''; + + if ($thisPackage->exists('author') || isset($default_author)) + { + if ($thisPackage->exists('author/@email')) + $package['author']['email'] = $thisPackage->fetch('author/@email'); + elseif (isset($default_email)) + $package['author']['email'] = $default_email; + + if ($thisPackage->exists('author') && $thisPackage->fetch('author') != '') + $package['author']['name'] = $smcFunc['htmlspecialchars']($thisPackage->fetch('author')); + else + $package['author']['name'] = $default_author; + + if (!empty($package['author']['email'])) + { + // Only put the "mailto:" if it looks like a valid email address. Some may wish to put a link to an SMF IM Form or other web mail form. + $package['author']['href'] = preg_match('~^[\w\.\-]+@[\w][\w\-\.]+[\w]$~', $package['author']['email']) != 0 ? 'mailto:' . $package['author']['email'] : $package['author']['email']; + $package['author']['link'] = '' . $package['author']['name'] . ''; + } + } + + if ($thisPackage->exists('website') || isset($default_website)) + { + if ($thisPackage->exists('website') && $thisPackage->exists('website/@title')) + $package['author']['website']['name'] = $smcFunc['htmlspecialchars']($thisPackage->fetch('website/@title')); + elseif (isset($default_title)) + $package['author']['website']['name'] = $default_title; + elseif ($thisPackage->exists('website')) + $package['author']['website']['name'] = $smcFunc['htmlspecialchars']($thisPackage->fetch('website')); + else + $package['author']['website']['name'] = $default_website; + + if ($thisPackage->exists('website') && $thisPackage->fetch('website') != '') + $authorhompage = $thisPackage->fetch('website'); + else + $authorhompage = $default_website; + + if (strpos(strtolower($authorhompage), 'a href') === false) + { + $package['author']['website']['href'] = $authorhompage; + $package['author']['website']['link'] = '' . $package['author']['website']['name'] . ''; + } + else + { + if (preg_match('/a href="(.+?)"/', $authorhompage, $match) == 1) + $package['author']['website']['href'] = $match[1]; + else + $package['author']['website']['href'] = ''; + $package['author']['website']['link'] = $authorhompage; + } + } + else + { + $package['author']['website']['href'] = ''; + $package['author']['website']['link'] = ''; + } + } + + $package['is_remote'] = $package['type'] == 'remote'; + $package['is_title'] = $package['type'] == 'title'; + $package['is_heading'] = $package['type'] == 'heading'; + $package['is_text'] = $package['type'] == 'text'; + $package['is_line'] = $package['type'] == 'rule'; + + $packageNum = in_array($package['type'], array('title', 'heading', 'text', 'remote', 'rule')) ? 0 : $packageNum + 1; + $package['count'] = $packageNum; + + if (!in_array($package['type'], array('title', 'text'))) + $context['package_list'][$packageSection]['items'][] = $package; + + if ($package['count'] > 1) + $context['list_type'] = 'ol'; + } + + $packageSection++; + } + + // Lets make sure we get a nice new spiffy clean $package to work with. Otherwise we get PAIN! + unset($package); + + foreach ($context['package_list'] as $ps_id => $packageSection) + { + foreach ($packageSection['items'] as $i => $package) + { + if ($package['count'] == 0 || isset($package['can_install'])) + continue; + + $context['package_list'][$ps_id]['items'][$i]['can_install'] = false; + + $packageInfo = getPackageInfo($url . '/' . $package['filename']); + if (is_array($packageInfo) && $packageInfo['xml']->exists('install')) + { + $installs = $packageInfo['xml']->set('install'); + foreach ($installs as $install) + if (!$install->exists('@for') || matchPackageVersion($the_version, $install->fetch('@for'))) + { + // Okay, this one is good to go. + $context['package_list'][$ps_id]['items'][$i]['can_install'] = true; + break; + } + } + } + } +} + +// Download a package. +function PackageDownload() +{ + global $txt, $scripturl, $boarddir, $context, $sourcedir, $smcFunc; + + // Use the downloaded sub template. + $context['sub_template'] = 'downloaded'; + + // Security is good... + checkSession('get'); + + // To download something, we need a valid server or url. + if (empty($_GET['server']) && (!empty($_GET['get']) && !empty($_REQUEST['package']))) + fatal_lang_error('package_get_error_is_zero', false); + + if (isset($_GET['server'])) + { + $server = (int) $_GET['server']; + + // Query the server table to find the requested server. + $request = $smcFunc['db_query']('', ' + SELECT name, url + FROM {db_prefix}package_servers + WHERE id_server = {int:current_server} + LIMIT 1', + array( + 'current_server' => $server, + ) + ); + list ($name, $url) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // If server does not exist then dump out. + if (empty($url)) + fatal_lang_error('couldnt_connect', false); + + $url = $url . '/'; + } + else + { + // Initialize the requried variables. + $server = ''; + $url = ''; + } + + if (isset($_REQUEST['byurl']) && !empty($_POST['filename'])) + $package_name = basename($_REQUEST['filename']); + else + $package_name = basename($_REQUEST['package']); + + if (isset($_REQUEST['conflict']) || (isset($_REQUEST['auto']) && file_exists($boarddir . '/Packages/' . $package_name))) + { + // Find the extension, change abc.tar.gz to abc_1.tar.gz... + if (strrpos(substr($package_name, 0, -3), '.') !== false) + { + $ext = substr($package_name, strrpos(substr($package_name, 0, -3), '.')); + $package_name = substr($package_name, 0, strrpos(substr($package_name, 0, -3), '.')) . '_'; + } + else + $ext = ''; + + // Find the first available. + $i = 1; + while (file_exists($boarddir . '/Packages/' . $package_name . $i . $ext)) + $i++; + + $package_name = $package_name . $i . $ext; + } + + // First make sure it's a package. + $packageInfo = getPackageInfo($url . $_REQUEST['package']); + if (!is_array($packageInfo)) + fatal_lang_error($packageInfo); + + // Use FTP if necessary. + create_chmod_control(array($boarddir . '/Packages/' . $package_name), array('destination_url' => $scripturl . '?action=admin;area=packages;get;sa=download' . (isset($_GET['server']) ? ';server=' . $_GET['server'] : '') . (isset($_REQUEST['auto']) ? ';auto' : '') . ';package=' . $_REQUEST['package'] . (isset($_REQUEST['conflict']) ? ';conflict' : '') . ';' . $context['session_var'] . '=' . $context['session_id'], 'crash_on_error' => true)); + package_put_contents($boarddir . '/Packages/' . $package_name, fetch_web_data($url . $_REQUEST['package'])); + + // Done! Did we get this package automatically? + if (preg_match('~^http://[\w_\-]+\.simplemachines\.org/~', $_REQUEST['package']) == 1 && strpos($_REQUEST['package'], 'dlattach') === false && isset($_REQUEST['auto'])) + redirectexit('action=admin;area=packages;sa=install;package=' . $package_name); + + // You just downloaded a mod from SERVER_NAME_GOES_HERE. + $context['package_server'] = $server; + + $context['package'] = getPackageInfo($package_name); + + if (!is_array($context['package'])) + fatal_lang_error('package_cant_download', false); + + if ($context['package']['type'] == 'modification') + $context['package']['install']['link'] = '[ ' . $txt['install_mod'] . ' ]'; + elseif ($context['package']['type'] == 'avatar') + $context['package']['install']['link'] = '[ ' . $txt['use_avatars'] . ' ]'; + elseif ($context['package']['type'] == 'language') + $context['package']['install']['link'] = '[ ' . $txt['add_languages'] . ' ]'; + else + $context['package']['install']['link'] = ''; + + $context['package']['list_files']['link'] = '[ ' . $txt['list_files'] . ' ]'; + + // Free a little bit of memory... + unset($context['package']['xml']); + + $context['page_title'] = $txt['download_success']; +} + +// Upload a new package to the directory. +function PackageUpload() +{ + global $txt, $scripturl, $boarddir, $context, $sourcedir; + + // Setup the correct template, even though I'll admit we ain't downloading ;) + $context['sub_template'] = 'downloaded'; + + // !!! TODO: Use FTP if the Packages directory is not writable. + + // Check the file was even sent! + if (!isset($_FILES['package']['name']) || $_FILES['package']['name'] == '') + fatal_lang_error('package_upload_error_nofile'); + elseif (!is_uploaded_file($_FILES['package']['tmp_name']) || (@ini_get('open_basedir') == '' && !file_exists($_FILES['package']['tmp_name']))) + fatal_lang_error('package_upload_error_failure'); + + // Make sure it has a sane filename. + $_FILES['package']['name'] = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $_FILES['package']['name']); + + if (strtolower(substr($_FILES['package']['name'], -4)) != '.zip' && strtolower(substr($_FILES['package']['name'], -4)) != '.tgz' && strtolower(substr($_FILES['package']['name'], -7)) != '.tar.gz') + fatal_lang_error('package_upload_error_supports', false, array('zip, tgz, tar.gz')); + + // We only need the filename... + $packageName = basename($_FILES['package']['name']); + + // Setup the destination and throw an error if the file is already there! + $destination = $boarddir . '/Packages/' . $packageName; + // !!! Maybe just roll it like we do for downloads? + if (file_exists($destination)) + fatal_lang_error('package_upload_error_exists'); + + // Now move the file. + move_uploaded_file($_FILES['package']['tmp_name'], $destination); + @chmod($destination, 0777); + + // If we got this far that should mean it's available. + $context['package'] = getPackageInfo($packageName); + $context['package_server'] = ''; + + // Not really a package, you lazy bum! + if (!is_array($context['package'])) + { + @unlink($destination); + loadLanguage('Errors'); + fatal_lang_error('package_upload_error_broken', false, $txt[$context['package']]); + } + // Is it already uploaded, maybe? + elseif ($dir = @opendir($boarddir . '/Packages')) + { + while ($package = readdir($dir)) + { + if ($package == '.' || $package == '..' || $package == 'temp' || $package == $packageName || (!(is_dir($boarddir . '/Packages/' . $package) && file_exists($boarddir . '/Packages/' . $package . '/package-info.xml')) && substr(strtolower($package), -7) != '.tar.gz' && substr(strtolower($package), -4) != '.tgz' && substr(strtolower($package), -4) != '.zip')) + continue; + + $packageInfo = getPackageInfo($package); + if (!is_array($packageInfo)) + continue; + + if ($packageInfo['id'] == $context['package']['id'] && $packageInfo['version'] == $context['package']['version']) + { + @unlink($destination); + loadLanguage('Errors'); + fatal_lang_error('package_upload_error_exists'); + } + } + closedir($dir); + } + + if ($context['package']['type'] == 'modification') + $context['package']['install']['link'] = '[ ' . $txt['install_mod'] . ' ]'; + elseif ($context['package']['type'] == 'avatar') + $context['package']['install']['link'] = '[ ' . $txt['use_avatars'] . ' ]'; + elseif ($context['package']['type'] == 'language') + $context['package']['install']['link'] = '[ ' . $txt['add_languages'] . ' ]'; + else + $context['package']['install']['link'] = ''; + + $context['package']['list_files']['link'] = '[ ' . $txt['list_files'] . ' ]'; + + unset($context['package']['xml']); + + $context['page_title'] = $txt['package_uploaded_success']; +} + +// Add a package server to the list. +function PackageServerAdd() +{ + global $smcFunc; + + // Validate the user. + checkSession(); + + // If they put a slash on the end, get rid of it. + if (substr($_POST['serverurl'], -1) == '/') + $_POST['serverurl'] = substr($_POST['serverurl'], 0, -1); + + // Are they both nice and clean? + $servername = trim($smcFunc['htmlspecialchars']($_POST['servername'])); + $serverurl = trim($smcFunc['htmlspecialchars']($_POST['serverurl'])); + + // Make sure the URL has the correct prefix. + if (strpos($serverurl, 'http://') !== 0 && strpos($serverurl, 'https://') !== 0) + $serverurl = 'http://' . $serverurl; + + $smcFunc['db_insert']('', + '{db_prefix}package_servers', + array( + 'name' => 'string-255', 'url' => 'string-255', + ), + array( + $servername, $serverurl, + ), + array('id_server') + ); + + redirectexit('action=admin;area=packages;get'); +} + +// Remove a server from the list. +function PackageServerRemove() +{ + global $smcFunc; + + checkSession('get'); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}package_servers + WHERE id_server = {int:current_server}', + array( + 'current_server' => (int) $_GET['server'], + ) + ); + + redirectexit('action=admin;area=packages;get'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Packages.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Packages.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2262 @@ + 'PackageBrowse', + 'remove' => 'PackageRemove', + 'list' => 'PackageList', + 'ftptest' => 'PackageFTPTest', + 'install' => 'PackageInstallTest', + 'install2' => 'PackageInstall', + 'uninstall' => 'PackageInstallTest', + 'uninstall2' => 'PackageInstall', + 'installed' => 'InstalledList', + 'options' => 'PackageOptions', + 'perms' => 'PackagePermissions', + 'flush' => 'FlushInstall', + 'examine' => 'ExamineFile', + 'showoperations' => 'ViewOperations', + ); + + // Work out exactly who it is we are calling. + if (isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']])) + $context['sub_action'] = $_REQUEST['sa']; + else + $context['sub_action'] = 'browse'; + + // Set up some tabs... + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['package_manager'], + // !!! 'help' => 'registrations', + 'description' => $txt['package_manager_desc'], + 'tabs' => array( + 'browse' => array( + ), + 'packageget' => array( + 'description' => $txt['download_packages_desc'], + ), + 'installed' => array( + 'description' => $txt['installed_packages_desc'], + ), + 'perms' => array( + 'description' => $txt['package_file_perms_desc'], + ), + 'options' => array( + 'description' => $txt['package_install_options_ftp_why'], + ), + ), + ); + + // Call the function we're handing control to. + $subActions[$context['sub_action']](); +} + +// Test install a package. +function PackageInstallTest() +{ + global $boarddir, $txt, $context, $scripturl, $sourcedir, $modSettings, $smcFunc, $settings; + + // You have to specify a file!! + if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') + redirectexit('action=admin;area=packages'); + $context['filename'] = preg_replace('~[\.]+~', '.', $_REQUEST['package']); + + // Do we have an existing id, for uninstalls and the like. + $context['install_id'] = isset($_REQUEST['pid']) ? (int) $_REQUEST['pid'] : 0; + + require_once($sourcedir . '/Subs-Package.php'); + + // Load up the package FTP information? + create_chmod_control(); + + // Make sure temp directory exists and is empty. + if (file_exists($boarddir . '/Packages/temp')) + deltree($boarddir . '/Packages/temp', false); + + if (!mktree($boarddir . '/Packages/temp', 0755)) + { + deltree($boarddir . '/Packages/temp', false); + if (!mktree($boarddir . '/Packages/temp', 0777)) + { + deltree($boarddir . '/Packages/temp', false); + create_chmod_control(array($boarddir . '/Packages/temp/delme.tmp'), array('destination_url' => $scripturl . '?action=admin;area=packages;sa=' . $_REQUEST['sa'] . ';package=' . $_REQUEST['package'], 'crash_on_error' => true)); + + deltree($boarddir . '/Packages/temp', false); + if (!mktree($boarddir . '/Packages/temp', 0777)) + fatal_lang_error('package_cant_download', false); + } + } + + $context['uninstalling'] = $_REQUEST['sa'] == 'uninstall'; + + // Change our last link tree item for more information on this Packages area. + $context['linktree'][count($context['linktree']) - 1] = array( + 'url' => $scripturl . '?action=admin;area=packages;sa=browse', + 'name' => $context['uninstalling'] ? $txt['package_uninstall_actions'] : $txt['install_actions'] + ); + $context['page_title'] .= ' - ' . ($context['uninstalling'] ? $txt['package_uninstall_actions'] : $txt['install_actions']); + + $context['sub_template'] = 'view_package'; + + if (!file_exists($boarddir . '/Packages/' . $context['filename'])) + { + deltree($boarddir . '/Packages/temp'); + fatal_lang_error('package_no_file', false); + } + + // Extract the files so we can get things like the readme, etc. + if (is_file($boarddir . '/Packages/' . $context['filename'])) + { + $context['extracted_files'] = read_tgz_file($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); + + if ($context['extracted_files'] && !file_exists($boarddir . '/Packages/temp/package-info.xml')) + foreach ($context['extracted_files'] as $file) + if (basename($file['filename']) == 'package-info.xml') + { + $context['base_path'] = dirname($file['filename']) . '/'; + break; + } + + if (!isset($context['base_path'])) + $context['base_path'] = ''; + } + elseif (is_dir($boarddir . '/Packages/' . $context['filename'])) + { + copytree($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); + $context['extracted_files'] = listtree($boarddir . '/Packages/temp'); + $context['base_path'] = ''; + } + else + fatal_lang_error('no_access', false); + + // Load up any custom themes we may want to install into... + $request = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE (id_theme = {int:default_theme} OR id_theme IN ({array_int:known_theme_list})) + AND variable IN ({string:name}, {string:theme_dir})', + array( + 'known_theme_list' => explode(',', $modSettings['knownThemes']), + 'default_theme' => 1, + 'name' => 'name', + 'theme_dir' => 'theme_dir', + ) + ); + $theme_paths = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $theme_paths[$row['id_theme']][$row['variable']] = $row['value']; + $smcFunc['db_free_result']($request); + + // Get the package info... + $packageInfo = getPackageInfo($context['filename']); + + if (!is_array($packageInfo)) + fatal_lang_error($packageInfo); + + $packageInfo['filename'] = $context['filename']; + $context['package_name'] = isset($packageInfo['name']) ? $packageInfo['name'] : $context['filename']; + + // Set the type of extraction... + $context['extract_type'] = isset($packageInfo['type']) ? $packageInfo['type'] : 'modification'; + + // The mod isn't installed.... unless proven otherwise. + $context['is_installed'] = false; + + // See if it is installed? + $request = $smcFunc['db_query']('', ' + SELECT version, themes_installed, db_changes + FROM {db_prefix}log_packages + WHERE package_id = {string:current_package} + AND install_state != {int:not_installed} + ORDER BY time_installed DESC + LIMIT 1', + array( + 'not_installed' => 0, + 'current_package' => $packageInfo['id'], + ) + ); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $old_themes = explode(',', $row['themes_installed']); + $old_version = $row['version']; + $db_changes = empty($row['db_changes']) ? array() : unserialize($row['db_changes']); + } + $smcFunc['db_free_result']($request); + + $context['database_changes'] = array(); + if (!empty($db_changes)) + { + foreach ($db_changes as $change) + { + if (isset($change[2]) && isset($txt['package_db_' . $change[0]])) + $context['database_changes'][] = sprintf($txt['package_db_' . $change[0]], $change[1], $change[2]); + elseif (isset($txt['package_db_' . $change[0]])) + $context['database_changes'][] = sprintf($txt['package_db_' . $change[0]], $change[1]); + else + $context['database_changes'][] = $change[0] . '-' . $change[1] . (isset($change[2]) ? '-' . $change[2] : ''); + } + } + + // Uninstalling? + if ($context['uninstalling']) + { + // Wait, it's not installed yet! + if (!isset($old_version) && $context['uninstalling']) + { + deltree($boarddir . '/Packages/temp'); + fatal_lang_error('package_cant_uninstall', false); + } + + $actions = parsePackageInfo($packageInfo['xml'], true, 'uninstall'); + + // Gadzooks! There's no uninstaller at all!? + if (empty($actions)) + { + deltree($boarddir . '/Packages/temp'); + fatal_lang_error('package_uninstall_cannot', false); + } + + // Can't edit the custom themes it's edited if you're unisntalling, they must be removed. + $context['themes_locked'] = true; + + // Only let them uninstall themes it was installed into. + foreach ($theme_paths as $id => $data) + if ($id != 1 && !in_array($id, $old_themes)) + unset($theme_paths[$id]); + } + elseif (isset($old_version) && $old_version != $packageInfo['version']) + { + // Look for an upgrade... + $actions = parsePackageInfo($packageInfo['xml'], true, 'upgrade', $old_version); + + // There was no upgrade.... + if (empty($actions)) + $context['is_installed'] = true; + else + { + // Otherwise they can only upgrade themes from the first time around. + foreach ($theme_paths as $id => $data) + if ($id != 1 && !in_array($id, $old_themes)) + unset($theme_paths[$id]); + } + } + elseif (isset($old_version) && $old_version == $packageInfo['version']) + $context['is_installed'] = true; + + if (!isset($old_version) || $context['is_installed']) + $actions = parsePackageInfo($packageInfo['xml'], true, 'install'); + + $context['actions'] = array(); + $context['ftp_needed'] = false; + $context['has_failure'] = false; + $chmod_files = array(); + + if (empty($actions)) + return; + + // This will hold data about anything that can be installed in other themes. + $themeFinds = array( + 'candidates' => array(), + 'other_themes' => array(), + ); + + // Now prepare things for the template. + foreach ($actions as $action) + { + // Not failed until proven otherwise. + $failed = false; + + if ($action['type'] == 'chmod') + { + $chmod_files[] = $action['filename']; + continue; + } + elseif ($action['type'] == 'readme') + { + if (file_exists($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename'])) + $context['package_readme'] = htmlspecialchars(trim(file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']), "\n\r")); + elseif (file_exists($action['filename'])) + $context['package_readme'] = htmlspecialchars(trim(file_get_contents($action['filename']), "\n\r")); + + if (!empty($action['parse_bbc'])) + { + require_once($sourcedir . '/Subs-Post.php'); + preparsecode($context['package_readme']); + $context['package_readme'] = parse_bbc($context['package_readme']); + } + else + $context['package_readme'] = nl2br($context['package_readme']); + + continue; + } + // Don't show redirects. + elseif ($action['type'] == 'redirect') + continue; + elseif ($action['type'] == 'error') + $context['has_failure'] = true; + elseif ($action['type'] == 'modification') + { + if (!file_exists($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename'])) + { + $context['has_failure'] = true; + + $context['actions'][] = array( + 'type' => $txt['execute_modification'], + 'action' => $smcFunc['htmlspecialchars'](strtr($action['filename'], array($boarddir => '.'))), + 'description' => $txt['package_action_error'], + 'failed' => true, + ); + } + + if ($action['boardmod']) + $mod_actions = parseBoardMod(@file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']), true, $action['reverse'], $theme_paths); + else + $mod_actions = parseModification(@file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']), true, $action['reverse'], $theme_paths); + + if (count($mod_actions) == 1 && isset($mod_actions[0]) && $mod_actions[0]['type'] == 'error' && $mod_actions[0]['filename'] == '-') + $mod_actions[0]['filename'] = $action['filename']; + + foreach ($mod_actions as $key => $mod_action) + { + // Lets get the last section of the file name. + if (isset($mod_action['filename']) && substr($mod_action['filename'], -13) != '.template.php') + $actual_filename = strtolower(substr(strrchr($mod_action['filename'], '/'), 1) . '||' . $action['filename']); + elseif (isset($mod_action['filename']) && preg_match('~([\w]*)/([\w]*)\.template\.php$~', $mod_action['filename'], $matches)) + $actual_filename = strtolower($matches[1] . '/' . $matches[2] . '.template.php' . '||' . $action['filename']); + else + $actual_filename = $key; + + if ($mod_action['type'] == 'opened') + $failed = false; + elseif ($mod_action['type'] == 'failure') + { + if (empty($mod_action['is_custom'])) + $context['has_failure'] = true; + $failed = true; + } + elseif ($mod_action['type'] == 'chmod') + { + $chmod_files[] = $mod_action['filename']; + } + elseif ($mod_action['type'] == 'saved') + { + if (!empty($mod_action['is_custom'])) + { + if (!isset($context['theme_actions'][$mod_action['is_custom']])) + $context['theme_actions'][$mod_action['is_custom']] = array( + 'name' => $theme_paths[$mod_action['is_custom']]['name'], + 'actions' => array(), + 'has_failure' => $failed, + ); + else + $context['theme_actions'][$mod_action['is_custom']]['has_failure'] |= $failed; + + $context['theme_actions'][$mod_action['is_custom']]['actions'][$actual_filename] = array( + 'type' => $txt['execute_modification'], + 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), + 'description' => $failed ? $txt['package_action_failure'] : $txt['package_action_success'], + 'failed' => $failed, + ); + } + elseif (!isset($context['actions'][$actual_filename])) + { + $context['actions'][$actual_filename] = array( + 'type' => $txt['execute_modification'], + 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), + 'description' => $failed ? $txt['package_action_failure'] : $txt['package_action_success'], + 'failed' => $failed, + ); + } + else + { + $context['actions'][$actual_filename]['failed'] |= $failed; + $context['actions'][$actual_filename]['description'] = $context['actions'][$actual_filename]['failed'] ? $txt['package_action_failure'] : $txt['package_action_success']; + } + } + elseif ($mod_action['type'] == 'skipping') + { + $context['actions'][$actual_filename] = array( + 'type' => $txt['execute_modification'], + 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), + 'description' => $txt['package_action_skipping'] + ); + } + elseif ($mod_action['type'] == 'missing' && empty($mod_action['is_custom'])) + { + $context['has_failure'] = true; + $context['actions'][$actual_filename] = array( + 'type' => $txt['execute_modification'], + 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), + 'description' => $txt['package_action_missing'], + 'failed' => true, + ); + } + elseif ($mod_action['type'] == 'error') + $context['actions'][$actual_filename] = array( + 'type' => $txt['execute_modification'], + 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), + 'description' => $txt['package_action_error'], + 'failed' => true, + ); + } + + // We need to loop again just to get the operations down correctly. + foreach ($mod_actions as $operation_key => $mod_action) + { + // Lets get the last section of the file name. + if (isset($mod_action['filename']) && substr($mod_action['filename'], -13) != '.template.php') + $actual_filename = strtolower(substr(strrchr($mod_action['filename'], '/'), 1) . '||' . $action['filename']); + elseif (isset($mod_action['filename']) && preg_match('~([\w]*)/([\w]*)\.template\.php$~', $mod_action['filename'], $matches)) + $actual_filename = strtolower($matches[1] . '/' . $matches[2] . '.template.php' . '||' . $action['filename']); + else + $actual_filename = $key; + + // We just need it for actual parse changes. + if (!in_array($mod_action['type'], array('error', 'result', 'opened', 'saved', 'end', 'missing', 'skipping', 'chmod'))) + { + if (empty($mod_action['is_custom'])) + $context['actions'][$actual_filename]['operations'][] = array( + 'type' => $txt['execute_modification'], + 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), + 'description' => $mod_action['failed'] ? $txt['package_action_failure'] : $txt['package_action_success'], + 'position' => $mod_action['position'], + 'operation_key' => $operation_key, + 'filename' => $action['filename'], + 'is_boardmod' => $action['boardmod'], + 'failed' => $mod_action['failed'], + 'ignore_failure' => !empty($mod_action['ignore_failure']), + ); + + // Themes are under the saved type. + if (isset($mod_action['is_custom']) && isset($context['theme_actions'][$mod_action['is_custom']])) + $context['theme_actions'][$mod_action['is_custom']]['actions'][$actual_filename]['operations'][] = array( + 'type' => $txt['execute_modification'], + 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), + 'description' => $mod_action['failed'] ? $txt['package_action_failure'] : $txt['package_action_success'], + 'position' => $mod_action['position'], + 'operation_key' => $operation_key, + 'filename' => $action['filename'], + 'is_boardmod' => $action['boardmod'], + 'failed' => $mod_action['failed'], + 'ignore_failure' => !empty($mod_action['ignore_failure']), + ); + } + } + + // Don't add anything else. + $thisAction = array(); + } + elseif ($action['type'] == 'code') + $thisAction = array( + 'type' => $txt['execute_code'], + 'action' => $smcFunc['htmlspecialchars']($action['filename']), + ); + elseif ($action['type'] == 'database') + { + $thisAction = array( + 'type' => $txt['execute_database_changes'], + 'action' => $smcFunc['htmlspecialchars']($action['filename']), + ); + } + elseif (in_array($action['type'], array('create-dir', 'create-file'))) + $thisAction = array( + 'type' => $txt['package_create'] . ' ' . ($action['type'] == 'create-dir' ? $txt['package_tree'] : $txt['package_file']), + 'action' => $smcFunc['htmlspecialchars'](strtr($action['destination'], array($boarddir => '.'))) + ); + elseif (in_array($action['type'], array('require-dir', 'require-file'))) + { + // Do this one... + $thisAction = array( + 'type' => $txt['package_extract'] . ' ' . ($action['type'] == 'require-dir' ? $txt['package_tree'] : $txt['package_file']), + 'action' => $smcFunc['htmlspecialchars'](strtr($action['destination'], array($boarddir => '.'))) + ); + + // Could this be theme related? + if (!empty($action['unparsed_destination']) && preg_match('~^\$(languagedir|languages_dir|imagesdir|themedir|themes_dir)~i', $action['unparsed_destination'], $matches)) + { + // Is the action already stated? + $theme_action = !empty($action['theme_action']) && in_array($action['theme_action'], array('no', 'yes', 'auto')) ? $action['theme_action'] : 'auto'; + // If it's not auto do we think we have something we can act upon? + if ($theme_action != 'auto' && !in_array($matches[1], array('languagedir', 'languages_dir', 'imagesdir', 'themedir'))) + $theme_action = ''; + // ... or if it's auto do we even want to do anything? + elseif ($theme_action == 'auto' && $matches[1] != 'imagesdir') + $theme_action = ''; + + // So, we still want to do something? + if ($theme_action != '') + $themeFinds['candidates'][] = $action; + // Otherwise is this is going into another theme record it. + elseif ($matches[1] == 'themes_dir') + $themeFinds['other_themes'][] = strtolower(strtr(parse_path($action['unparsed_destination']), array('\\' => '/')) . '/' . basename($action['filename'])); + } + } + elseif (in_array($action['type'], array('move-dir', 'move-file'))) + $thisAction = array( + 'type' => $txt['package_move'] . ' ' . ($action['type'] == 'move-dir' ? $txt['package_tree'] : $txt['package_file']), + 'action' => $smcFunc['htmlspecialchars'](strtr($action['source'], array($boarddir => '.'))) . ' => ' . $smcFunc['htmlspecialchars'](strtr($action['destination'], array($boarddir => '.'))) + ); + elseif (in_array($action['type'], array('remove-dir', 'remove-file'))) + { + $thisAction = array( + 'type' => $txt['package_delete'] . ' ' . ($action['type'] == 'remove-dir' ? $txt['package_tree'] : $txt['package_file']), + 'action' => $smcFunc['htmlspecialchars'](strtr($action['filename'], array($boarddir => '.'))) + ); + + // Could this be theme related? + if (!empty($action['unparsed_filename']) && preg_match('~^\$(languagedir|languages_dir|imagesdir|themedir|themes_dir)~i', $action['unparsed_filename'], $matches)) + { + + // Is the action already stated? + $theme_action = !empty($action['theme_action']) && in_array($action['theme_action'], array('no', 'yes', 'auto')) ? $action['theme_action'] : 'auto'; + $action['unparsed_destination'] = $action['unparsed_filename']; + // If it's not auto do we think we have something we can act upon? + if ($theme_action != 'auto' && !in_array($matches[1], array('languagedir', 'languages_dir', 'imagesdir', 'themedir'))) + $theme_action = ''; + // ... or if it's auto do we even want to do anything? + elseif ($theme_action == 'auto' && $matches[1] != 'imagesdir') + $theme_action = ''; + + // So, we still want to do something? + if ($theme_action != '') + $themeFinds['candidates'][] = $action; + // Otherwise is this is going into another theme record it. + elseif ($matches[1] == 'themes_dir') + $themeFinds['other_themes'][] = strtolower(strtr(parse_path($action['unparsed_filename']), array('\\' => '/')) . '/' . basename($action['filename'])); + } + } + + if (empty($thisAction)) + continue; + + // !!! None given? + $thisAction['description'] = isset($action['description']) ? $action['description'] : ''; + $context['actions'][] = $thisAction; + } + + // Have we got some things which we might want to do "multi-theme"? + if (!empty($themeFinds['candidates'])) + { + foreach ($themeFinds['candidates'] as $action_data) + { + // Get the part of the file we'll be dealing with. + preg_match('~^\$(languagedir|languages_dir|imagesdir|themedir)(\\|/)*(.+)*~i', $action_data['unparsed_destination'], $matches); + + if ($matches[1] == 'imagesdir') + $path = '/' . basename($settings['default_images_url']); + elseif ($matches[1] == 'languagedir' || $matches[1] == 'languages_dir') + $path = '/languages'; + else + $path = ''; + + if (!empty($matches[3])) + $path .= $matches[3]; + + if (!$context['uninstalling']) + $path .= '/' . basename($action_data['filename']); + + // Loop through each custom theme to note it's candidacy! + foreach ($theme_paths as $id => $theme_data) + { + if (isset($theme_data['theme_dir']) && $id != 1) + { + $real_path = $theme_data['theme_dir'] . $path; + // Confirm that we don't already have this dealt with by another entry. + if (!in_array(strtolower(strtr($real_path, array('\\' => '/'))), $themeFinds['other_themes'])) + { + // Check if we will need to chmod this. + if (!mktree(dirname($real_path), false)) + { + $temp = dirname($real_path); + while (!file_exists($temp) && strlen($temp) > 1) + $temp = dirname($temp); + $chmod_files[] = $temp; + } + if ($action_data['type'] == 'require-dir' && !is_writable($real_path) && (file_exists($real_path) || !is_writable(dirname($real_path)))) + $chmod_files[] = $real_path; + + if (!isset($context['theme_actions'][$id])) + $context['theme_actions'][$id] = array( + 'name' => $theme_data['name'], + 'actions' => array(), + ); + + if ($context['uninstalling']) + $context['theme_actions'][$id]['actions'][] = array( + 'type' => $txt['package_delete'] . ' ' . ($action_data['type'] == 'require-dir' ? $txt['package_tree'] : $txt['package_file']), + 'action' => strtr($real_path, array('\\' => '/', $boarddir => '.')), + 'description' => '', + 'value' => base64_encode(serialize(array('type' => $action_data['type'], 'orig' => $action_data['filename'], 'future' => $real_path, 'id' => $id))), + 'not_mod' => true, + ); + else + $context['theme_actions'][$id]['actions'][] = array( + 'type' => $txt['package_extract'] . ' ' . ($action_data['type'] == 'require-dir' ? $txt['package_tree'] : $txt['package_file']), + 'action' => strtr($real_path, array('\\' => '/', $boarddir => '.')), + 'description' => '', + 'value' => base64_encode(serialize(array('type' => $action_data['type'], 'orig' => $action_data['destination'], 'future' => $real_path, 'id' => $id))), + 'not_mod' => true, + ); + } + } + } + } + } + + // Trash the cache... which will also check permissions for us! + package_flush_cache(true); + + if (file_exists($boarddir . '/Packages/temp')) + deltree($boarddir . '/Packages/temp'); + + if (!empty($chmod_files)) + { + $ftp_status = create_chmod_control($chmod_files); + $context['ftp_needed'] = !empty($ftp_status['files']['notwritable']) && !empty($context['package_ftp']); + } + + checkSubmitOnce('register'); +} + +// Apply another type of (avatar, language, etc.) package. +function PackageInstall() +{ + global $boarddir, $txt, $context, $boardurl, $scripturl, $sourcedir, $modSettings; + global $user_info, $smcFunc; + + // Make sure we don't install this mod twice. + checkSubmitOnce('check'); + checkSession(); + + // If there's no file, what are we installing? + if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') + redirectexit('action=admin;area=packages'); + $context['filename'] = $_REQUEST['package']; + + // If this is an uninstall, we'll have an id. + $context['install_id'] = isset($_REQUEST['pid']) ? (int) $_REQUEST['pid'] : 0; + + require_once($sourcedir . '/Subs-Package.php'); + + // !!! TODO: Perhaps do it in steps, if necessary? + + $context['uninstalling'] = $_REQUEST['sa'] == 'uninstall2'; + + // Set up the linktree for other. + $context['linktree'][count($context['linktree']) - 1] = array( + 'url' => $scripturl . '?action=admin;area=packages;sa=browse', + 'name' => $context['uninstalling'] ? $txt['uninstall'] : $txt['extracting'] + ); + $context['page_title'] .= ' - ' . ($context['uninstalling'] ? $txt['uninstall'] : $txt['extracting']); + + $context['sub_template'] = 'extract_package'; + + if (!file_exists($boarddir . '/Packages/' . $context['filename'])) + fatal_lang_error('package_no_file', false); + + // Load up the package FTP information? + create_chmod_control(array(), array('destination_url' => $scripturl . '?action=admin;area=packages;sa=' . $_REQUEST['sa'] . ';package=' . $_REQUEST['package'])); + + // Make sure temp directory exists and is empty! + if (file_exists($boarddir . '/Packages/temp')) + deltree($boarddir . '/Packages/temp', false); + else + mktree($boarddir . '/Packages/temp', 0777); + + // Let the unpacker do the work. + if (is_file($boarddir . '/Packages/' . $context['filename'])) + { + $context['extracted_files'] = read_tgz_file($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); + + if (!file_exists($boarddir . '/Packages/temp/package-info.xml')) + foreach ($context['extracted_files'] as $file) + if (basename($file['filename']) == 'package-info.xml') + { + $context['base_path'] = dirname($file['filename']) . '/'; + break; + } + + if (!isset($context['base_path'])) + $context['base_path'] = ''; + } + elseif (is_dir($boarddir . '/Packages/' . $context['filename'])) + { + copytree($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); + $context['extracted_files'] = listtree($boarddir . '/Packages/temp'); + $context['base_path'] = ''; + } + else + fatal_lang_error('no_access', false); + + // Are we installing this into any custom themes? + $custom_themes = array(1); + $known_themes = explode(',', $modSettings['knownThemes']); + if (!empty($_POST['custom_theme'])) + { + foreach ($_POST['custom_theme'] as $tid) + if (in_array($tid, $known_themes)) + $custom_themes[] = (int) $tid; + } + + // Now load up the paths of the themes that we need to know about. + $request = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE id_theme IN ({array_int:custom_themes}) + AND variable IN ({string:name}, {string:theme_dir})', + array( + 'custom_themes' => $custom_themes, + 'name' => 'name', + 'theme_dir' => 'theme_dir', + ) + ); + $theme_paths = array(); + $themes_installed = array(1); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $theme_paths[$row['id_theme']][$row['variable']] = $row['value']; + $smcFunc['db_free_result']($request); + + // Are there any theme copying that we want to take place? + $context['theme_copies'] = array( + 'require-file' => array(), + 'require-dir' => array(), + ); + if (!empty($_POST['theme_changes'])) + { + foreach ($_POST['theme_changes'] as $change) + { + if (empty($change)) + continue; + $theme_data = unserialize(base64_decode($change)); + if (empty($theme_data['type'])) + continue; + + $themes_installed[] = $theme_data['id']; + $context['theme_copies'][$theme_data['type']][$theme_data['orig']][] = $theme_data['future']; + } + } + + // Get the package info... + $packageInfo = getPackageInfo($context['filename']); + if (!is_array($packageInfo)) + fatal_lang_error($packageInfo); + + $packageInfo['filename'] = $context['filename']; + + // Set the type of extraction... + $context['extract_type'] = isset($packageInfo['type']) ? $packageInfo['type'] : 'modification'; + + // Create a backup file to roll back to! (but if they do this more than once, don't run it a zillion times.) + if (!empty($modSettings['package_make_backups']) && (!isset($_SESSION['last_backup_for']) || $_SESSION['last_backup_for'] != $context['filename'] . ($context['uninstalling'] ? '$$' : '$'))) + { + $_SESSION['last_backup_for'] = $context['filename'] . ($context['uninstalling'] ? '$$' : '$'); + // !!! Internationalize this? + package_create_backup(($context['uninstalling'] ? 'backup_' : 'before_') . strtok($context['filename'], '.')); + } + + // The mod isn't installed.... unless proven otherwise. + $context['is_installed'] = false; + + // Is it actually installed? + $request = $smcFunc['db_query']('', ' + SELECT version, themes_installed, db_changes + FROM {db_prefix}log_packages + WHERE package_id = {string:current_package} + AND install_state != {int:not_installed} + ORDER BY time_installed DESC + LIMIT 1', + array( + 'not_installed' => 0, + 'current_package' => $packageInfo['id'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $old_themes = explode(',', $row['themes_installed']); + $old_version = $row['version']; + $db_changes = empty($row['db_changes']) ? array() : unserialize($row['db_changes']); + } + $smcFunc['db_free_result']($request); + + // Wait, it's not installed yet! + // !!! TODO: Replace with a better error message! + if (!isset($old_version) && $context['uninstalling']) + { + deltree($boarddir . '/Packages/temp'); + fatal_error('Hacker?', false); + } + // Uninstalling? + elseif ($context['uninstalling']) + { + $install_log = parsePackageInfo($packageInfo['xml'], false, 'uninstall'); + + // Gadzooks! There's no uninstaller at all!? + if (empty($install_log)) + fatal_lang_error('package_uninstall_cannot', false); + + // They can only uninstall from what it was originally installed into. + foreach ($theme_paths as $id => $data) + if ($id != 1 && !in_array($id, $old_themes)) + unset($theme_paths[$id]); + } + elseif (isset($old_version) && $old_version != $packageInfo['version']) + { + // Look for an upgrade... + $install_log = parsePackageInfo($packageInfo['xml'], false, 'upgrade', $old_version); + + // There was no upgrade.... + if (empty($install_log)) + $context['is_installed'] = true; + else + { + // Upgrade previous themes only! + foreach ($theme_paths as $id => $data) + if ($id != 1 && !in_array($id, $old_themes)) + unset($theme_paths[$id]); + } + } + elseif (isset($old_version) && $old_version == $packageInfo['version']) + $context['is_installed'] = true; + + if (!isset($old_version) || $context['is_installed']) + $install_log = parsePackageInfo($packageInfo['xml'], false, 'install'); + + $context['install_finished'] = false; + + // !!! TODO: Make a log of any errors that occurred and output them? + + if (!empty($install_log)) + { + $failed_steps = array(); + $failed_count = 0; + + foreach ($install_log as $action) + { + $failed_count++; + + if ($action['type'] == 'modification' && !empty($action['filename'])) + { + if ($action['boardmod']) + $mod_actions = parseBoardMod(file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse'], $theme_paths); + else + $mod_actions = parseModification(file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse'], $theme_paths); + + // Any errors worth noting? + foreach ($mod_actions as $key => $action) + { + if ($action['type'] == 'failure') + $failed_steps[] = array( + 'file' => $action['filename'], + 'large_step' => $failed_count, + 'sub_step' => $key, + 'theme' => 1, + ); + + // Gather the themes we installed into. + if (!empty($action['is_custom'])) + $themes_installed[] = $action['is_custom']; + } + } + elseif ($action['type'] == 'code' && !empty($action['filename'])) + { + // This is just here as reference for what is available. + global $txt, $boarddir, $sourcedir, $modSettings, $context, $settings, $forum_version, $smcFunc; + + // Now include the file and be done with it ;). + require($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']); + } + // Only do the database changes on uninstall if requested. + elseif ($action['type'] == 'database' && !empty($action['filename']) && (!$context['uninstalling'] || !empty($_POST['do_db_changes']))) + { + // These can also be there for database changes. + global $txt, $boarddir, $sourcedir, $modSettings, $context, $settings, $forum_version, $smcFunc; + global $db_package_log; + + // We'll likely want the package specific database functionality! + db_extend('packages'); + + // Let the file work its magic ;) + require($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']); + } + // Handle a redirect... + elseif ($action['type'] == 'redirect' && !empty($action['redirect_url'])) + { + $context['redirect_url'] = $action['redirect_url']; + $context['redirect_text'] = !empty($action['filename']) && file_exists($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']) ? file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']) : ($context['uninstalling'] ? $txt['package_uninstall_done'] : $txt['package_installed_done']); + $context['redirect_timeout'] = $action['redirect_timeout']; + + // Parse out a couple of common urls. + $urls = array( + '$boardurl' => $boardurl, + '$scripturl' => $scripturl, + '$session_var' => $context['session_var'], + '$session_id' => $context['session_id'], + ); + + $context['redirect_url'] = strtr($context['redirect_url'], $urls); + } + } + + package_flush_cache(); + + // First, ensure this change doesn't get removed by putting a stake in the ground (So to speak). + package_put_contents($boarddir . '/Packages/installed.list', time()); + + // See if this is already installed, and change it's state as required. + $request = $smcFunc['db_query']('', ' + SELECT package_id, install_state, db_changes + FROM {db_prefix}log_packages + WHERE install_state != {int:not_installed} + AND package_id = {string:current_package} + ' . ($context['install_id'] ? ' AND id_install = {int:install_id} ' : '') . ' + ORDER BY time_installed DESC + LIMIT 1', + array( + 'not_installed' => 0, + 'install_id' => $context['install_id'], + 'current_package' => $packageInfo['id'], + ) + ); + $is_upgrade = false; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Uninstalling? + if ($context['uninstalling']) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_packages + SET install_state = {int:not_installed}, member_removed = {string:member_name}, id_member_removed = {int:current_member}, + time_removed = {int:current_time} + WHERE package_id = {string:package_id}', + array( + 'current_member' => $user_info['id'], + 'not_installed' => 0, + 'current_time' => time(), + 'package_id' => $row['package_id'], + 'member_name' => $user_info['name'], + ) + ); + } + // Otherwise must be an upgrade. + else + { + $is_upgrade = true; + $old_db_changes = empty($row['db_changes']) ? array() : unserialize($row['db_changes']); + } + } + + // Assuming we're not uninstalling, add the entry. + if (!$context['uninstalling']) + { + // Any db changes from older version? + if (!empty($old_db_changes)) + $db_package_log = empty($db_package_log) ? $old_db_changes : array_merge($old_db_changes, $db_package_log); + + // If there are some database changes we might want to remove then filter them out. + if (!empty($db_package_log)) + { + // We're really just checking for entries which are create table AND add columns (etc). + $tables = array(); + function sort_table_first($a, $b) + { + if ($a[0] == $b[0]) + return 0; + return $a[0] == 'remove_table' ? -1 : 1; + } + usort($db_package_log, 'sort_table_first'); + foreach ($db_package_log as $k => $log) + { + if ($log[0] == 'remove_table') + $tables[] = $log[1]; + elseif (in_array($log[1], $tables)) + unset($db_package_log[$k]); + } + $db_changes = serialize($db_package_log); + } + else + $db_changes = ''; + + // What themes did we actually install? + $themes_installed = array_unique($themes_installed); + $themes_installed = implode(',', $themes_installed); + + // What failed steps? + $failed_step_insert = serialize($failed_steps); + + $smcFunc['db_insert']('', + '{db_prefix}log_packages', + array( + 'filename' => 'string', 'name' => 'string', 'package_id' => 'string', 'version' => 'string', + 'id_member_installed' => 'int', 'member_installed' => 'string','time_installed' => 'int', + 'install_state' => 'int', 'failed_steps' => 'string', 'themes_installed' => 'string', + 'member_removed' => 'int', 'db_changes' => 'string', + ), + array( + $packageInfo['filename'], $packageInfo['name'], $packageInfo['id'], $packageInfo['version'], + $user_info['id'], $user_info['name'], time(), + $is_upgrade ? 2 : 1, $failed_step_insert, $themes_installed, + 0, $db_changes, + ), + array('id_install') + ); + } + $smcFunc['db_free_result']($request); + + $context['install_finished'] = true; + } + + // If there's database changes - and they want them removed - let's do it last! + if (!empty($db_changes) && !empty($_POST['do_db_changes'])) + { + // We're gonna be needing the package db functions! + db_extend('packages'); + + foreach ($db_changes as $change) + { + if ($change[0] == 'remove_table' && isset($change[1])) + $smcFunc['db_drop_table']($change[1]); + elseif ($change[0] == 'remove_column' && isset($change[2])) + $smcFunc['db_remove_column']($change[1], $change[2]); + elseif ($change[0] == 'remove_index' && isset($change[2])) + $smcFunc['db_remove_index']($change[1], $change[2]); + } + } + + // Clean house... get rid of the evidence ;). + if (file_exists($boarddir . '/Packages/temp')) + deltree($boarddir . '/Packages/temp'); + + // Log what we just did. + logAction($context['uninstalling'] ? 'uninstall_package' : (!empty($is_upgrade) ? 'upgrade_package' : 'install_package'), array('package' => $smcFunc['htmlspecialchars']($packageInfo['name']), 'version' => $smcFunc['htmlspecialchars']($packageInfo['version'])), 'admin'); + + // Just in case, let's clear the whole cache to avoid anything going up the swanny. + clean_cache(); + + // Restore file permissions? + create_chmod_control(array(), array(), true); +} + +// List the files in a package. +function PackageList() +{ + global $txt, $scripturl, $boarddir, $context, $sourcedir; + + require_once($sourcedir . '/Subs-Package.php'); + + // No package? Show him or her the door. + if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') + redirectexit('action=admin;area=packages'); + + $context['linktree'][] = array( + 'url' => $scripturl . '?action=admin;area=packages;sa=list;package=' . $_REQUEST['package'], + 'name' => $txt['list_file'] + ); + $context['page_title'] .= ' - ' . $txt['list_file']; + $context['sub_template'] = 'list'; + + // The filename... + $context['filename'] = $_REQUEST['package']; + + // Let the unpacker do the work. + if (is_file($boarddir . '/Packages/' . $context['filename'])) + $context['files'] = read_tgz_file($boarddir . '/Packages/' . $context['filename'], null); + elseif (is_dir($boarddir . '/Packages/' . $context['filename'])) + $context['files'] = listtree($boarddir . '/Packages/' . $context['filename']); +} + +// List the files in a package. +function ExamineFile() +{ + global $txt, $scripturl, $boarddir, $context, $sourcedir; + + require_once($sourcedir . '/Subs-Package.php'); + + // No package? Show him or her the door. + if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') + redirectexit('action=admin;area=packages'); + + // No file? Show him or her the door. + if (!isset($_REQUEST['file']) || $_REQUEST['file'] == '') + redirectexit('action=admin;area=packages'); + + $_REQUEST['package'] = preg_replace('~[\.]+~', '.', strtr($_REQUEST['package'], array('/' => '_', '\\' => '_'))); + $_REQUEST['file'] = preg_replace('~[\.]+~', '.', $_REQUEST['file']); + + if (isset($_REQUEST['raw'])) + { + if (is_file($boarddir . '/Packages/' . $_REQUEST['package'])) + echo read_tgz_file($boarddir . '/Packages/' . $_REQUEST['package'], $_REQUEST['file'], true); + elseif (is_dir($boarddir . '/Packages/' . $_REQUEST['package'])) + echo file_get_contents($boarddir . '/Packages/' . $_REQUEST['package'] . '/' . $_REQUEST['file']); + + obExit(false); + } + + $context['linktree'][count($context['linktree']) - 1] = array( + 'url' => $scripturl . '?action=admin;area=packages;sa=list;package=' . $_REQUEST['package'], + 'name' => $txt['package_examine_file'] + ); + $context['page_title'] .= ' - ' . $txt['package_examine_file']; + $context['sub_template'] = 'examine'; + + // The filename... + $context['package'] = $_REQUEST['package']; + $context['filename'] = $_REQUEST['file']; + + // Let the unpacker do the work.... but make sure we handle images properly. + if (in_array(strtolower(strrchr($_REQUEST['file'], '.')), array('.bmp', '.gif', '.jpeg', '.jpg', '.png'))) + $context['filedata'] = '' . $_REQUEST['file'] . ''; + else + { + if (is_file($boarddir . '/Packages/' . $_REQUEST['package'])) + $context['filedata'] = htmlspecialchars(read_tgz_file($boarddir . '/Packages/' . $_REQUEST['package'], $_REQUEST['file'], true)); + elseif (is_dir($boarddir . '/Packages/' . $_REQUEST['package'])) + $context['filedata'] = htmlspecialchars(file_get_contents($boarddir . '/Packages/' . $_REQUEST['package'] . '/' . $_REQUEST['file'])); + + if (strtolower(strrchr($_REQUEST['file'], '.')) == '.php') + $context['filedata'] = highlight_php_code($context['filedata']); + } +} + +// List the installed packages. +function InstalledList() +{ + global $txt, $scripturl, $context; + + $context['page_title'] .= ' - ' . $txt['installed_packages']; + $context['sub_template'] = 'view_installed'; + + // Load the installed mods and send them to the template. + $context['installed_mods'] = loadInstalledPackages(); +} + +// Empty out the installed list. +function FlushInstall() +{ + global $boarddir, $sourcedir, $smcFunc; + + // Always check the session. + checkSession('get'); + + include_once($sourcedir . '/Subs-Package.php'); + + // Record when we last did this. + package_put_contents($boarddir . '/Packages/installed.list', time()); + + // Set everything as uninstalled. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_packages + SET install_state = {int:not_installed}', + array( + 'not_installed' => 0, + ) + ); + + redirectexit('action=admin;area=packages;sa=installed'); +} + +// Delete a package. +function PackageRemove() +{ + global $scripturl, $boarddir; + + // Check it. + checkSession('get'); + + // Ack, don't allow deletion of arbitrary files here, could become a security hole somehow! + if (!isset($_GET['package']) || $_GET['package'] == 'index.php' || $_GET['package'] == 'installed.list' || $_GET['package'] == 'backups') + redirectexit('action=admin;area=packages;sa=browse'); + $_GET['package'] = preg_replace('~[\.]+~', '.', strtr($_GET['package'], array('/' => '_', '\\' => '_'))); + + // Can't delete what's not there. + if (file_exists($boarddir . '/Packages/' . $_GET['package']) && (substr($_GET['package'], -4) == '.zip' || substr($_GET['package'], -4) == '.tgz' || substr($_GET['package'], -7) == '.tar.gz' || is_dir($boarddir . '/Packages/' . $_GET['package'])) && $_GET['package'] != 'backups' && substr($_GET['package'], 0, 1) != '.') + { + create_chmod_control(array($boarddir . '/Packages/' . $_GET['package']), array('destination_url' => $scripturl . '?action=admin;area=packages;sa=remove;package=' . $_GET['package'], 'crash_on_error' => true)); + + if (is_dir($boarddir . '/Packages/' . $_GET['package'])) + deltree($boarddir . '/Packages/' . $_GET['package']); + else + { + @chmod($boarddir . '/Packages/' . $_GET['package'], 0777); + unlink($boarddir . '/Packages/' . $_GET['package']); + } + } + + redirectexit('action=admin;area=packages;sa=browse'); +} + +// Browse a list of installed packages. +function PackageBrowse() +{ + global $txt, $boarddir, $scripturl, $context, $forum_version; + + $context['page_title'] .= ' - ' . $txt['browse_packages']; + $context['sub_template'] = 'browse'; + + $context['forum_version'] = $forum_version; + + $instmods = loadInstalledPackages(); + + $installed_mods = array(); + // Look through the list of installed mods... + foreach ($instmods as $installed_mod) + $installed_mods[$installed_mod['package_id']] = array( + 'id' => $installed_mod['id'], + 'version' => $installed_mod['version'], + ); + + $the_version = strtr($forum_version, array('SMF ' => '')); + + // Here we have a little code to help those who class themselves as something of gods, version emulation ;) + if (isset($_GET['version_emulate'])) + { + if ($_GET['version_emulate'] === 0 && isset($_SESSION['version_emulate'])) + unset($_SESSION['version_emulate']); + elseif ($_GET['version_emulate'] !== 0) + $_SESSION['version_emulate'] = strtr($_GET['version_emulate'], array('-' => ' ', '+' => ' ', 'SMF ' => '')); + } + if (!empty($_SESSION['version_emulate'])) + { + $context['forum_version'] = 'SMF ' . $_SESSION['version_emulate']; + $the_version = $_SESSION['version_emulate']; + } + + // Get a list of all the ids installed, so the latest packages won't include already installed ones. + $context['installed_mods'] = array_keys($installed_mods); + + // Empty lists for now. + $context['available_mods'] = array(); + $context['available_avatars'] = array(); + $context['available_languages'] = array(); + $context['available_other'] = array(); + $context['available_all'] = array(); + + // We need the packages directory to be writable for this. + if (!@is_writable($boarddir . '/Packages')) + create_chmod_control(array($boarddir . '/Packages'), array('destination_url' => $scripturl . '?action=admin;area=packages', 'crash_on_error' => true)); + + if ($dir = @opendir($boarddir . '/Packages')) + { + $dirs = array(); + while ($package = readdir($dir)) + { + if ($package == '.' || $package == '..' || $package == 'temp' || (!(is_dir($boarddir . '/Packages/' . $package) && file_exists($boarddir . '/Packages/' . $package . '/package-info.xml')) && substr(strtolower($package), -7) != '.tar.gz' && substr(strtolower($package), -4) != '.tgz' && substr(strtolower($package), -4) != '.zip')) + continue; + + // Skip directories or files that are named the same. + if (is_dir($boarddir . '/Packages/' . $package)) + { + if (in_array($package, $dirs)) + continue; + $dirs[] = $package; + } + elseif (substr(strtolower($package), -7) == '.tar.gz') + { + if (in_array(substr($package, 0, -7), $dirs)) + continue; + $dirs[] = substr($package, 0, -7); + } + elseif (substr(strtolower($package), -4) == '.zip' || substr(strtolower($package), -4) == '.tgz') + { + if (in_array(substr($package, 0, -4), $dirs)) + continue; + $dirs[] = substr($package, 0, -4); + } + + $packageInfo = getPackageInfo($package); + if (!is_array($packageInfo)) + continue; + + $packageInfo['installed_id'] = isset($installed_mods[$packageInfo['id']]) ? $installed_mods[$packageInfo['id']]['id'] : 0; + + $packageInfo['is_installed'] = isset($installed_mods[$packageInfo['id']]); + $packageInfo['is_current'] = $packageInfo['is_installed'] && ($installed_mods[$packageInfo['id']]['version'] == $packageInfo['version']); + $packageInfo['is_newer'] = $packageInfo['is_installed'] && ($installed_mods[$packageInfo['id']]['version'] > $packageInfo['version']); + + $packageInfo['can_install'] = false; + $packageInfo['can_uninstall'] = false; + $packageInfo['can_upgrade'] = false; + + // This package is currently NOT installed. Check if it can be. + if (!$packageInfo['is_installed'] && $packageInfo['xml']->exists('install')) + { + // Check if there's an install for *THIS* version of SMF. + $installs = $packageInfo['xml']->set('install'); + foreach ($installs as $install) + { + if (!$install->exists('@for') || matchPackageVersion($the_version, $install->fetch('@for'))) + { + // Okay, this one is good to go. + $packageInfo['can_install'] = true; + break; + } + } + } + // An already installed, but old, package. Can we upgrade it? + elseif ($packageInfo['is_installed'] && !$packageInfo['is_current'] && $packageInfo['xml']->exists('upgrade')) + { + $upgrades = $packageInfo['xml']->set('upgrade'); + + // First go through, and check against the current version of SMF. + foreach ($upgrades as $upgrade) + { + // Even if it is for this SMF, is it for the installed version of the mod? + if (!$upgrade->exists('@for') || matchPackageVersion($the_version, $upgrade->fetch('@for'))) + if (!$upgrade->exists('@from') || matchPackageVersion($installed_mods[$packageInfo['id']]['version'], $upgrade->fetch('@from'))) + { + $packageInfo['can_upgrade'] = true; + break; + } + } + } + // Note that it has to be the current version to be uninstallable. Shucks. + elseif ($packageInfo['is_installed'] && $packageInfo['is_current'] && $packageInfo['xml']->exists('uninstall')) + { + $uninstalls = $packageInfo['xml']->set('uninstall'); + + // Can we find any uninstallation methods that work for this SMF version? + foreach ($uninstalls as $uninstall) + if (!$uninstall->exists('@for') || matchPackageVersion($the_version, $uninstall->fetch('@for'))) + { + $packageInfo['can_uninstall'] = true; + break; + } + } + + // Store a complete list. + $context['available_all'][] = $packageInfo; + + // Modification. + if ($packageInfo['type'] == 'modification' || $packageInfo['type'] == 'mod') + $context['available_mods'][] = $packageInfo; + // Avatar package. + elseif ($packageInfo['type'] == 'avatar') + $context['available_avatars'][] = $packageInfo; + // Language package. + elseif ($packageInfo['type'] == 'language') + $context['available_languages'][] = $packageInfo; + // Other stuff. + else + $context['available_other'][] = $packageInfo; + } + closedir($dir); + } +} + +function PackageOptions() +{ + global $txt, $scripturl, $context, $sourcedir, $modSettings, $smcFunc; + + if (isset($_POST['submit'])) + { + checkSession('post'); + + updateSettings(array( + 'package_server' => trim($smcFunc['htmlspecialchars']($_POST['pack_server'])), + 'package_port' => trim($smcFunc['htmlspecialchars']($_POST['pack_port'])), + 'package_username' => trim($smcFunc['htmlspecialchars']($_POST['pack_user'])), + 'package_make_backups' => !empty($_POST['package_make_backups']) + )); + + redirectexit('action=admin;area=packages;sa=options'); + } + + if (preg_match('~^/home/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match)) + $default_username = $match[1]; + else + $default_username = ''; + + $context['page_title'] = $txt['package_settings']; + $context['sub_template'] = 'install_options'; + + $context['package_ftp_server'] = isset($modSettings['package_server']) ? $modSettings['package_server'] : 'localhost'; + $context['package_ftp_port'] = isset($modSettings['package_port']) ? $modSettings['package_port'] : '21'; + $context['package_ftp_username'] = isset($modSettings['package_username']) ? $modSettings['package_username'] : $default_username; + $context['package_make_backups'] = !empty($modSettings['package_make_backups']); +} + +function ViewOperations() +{ + global $context, $txt, $boarddir, $sourcedir, $smcFunc, $modSettings; + + // Can't be in here buddy. + isAllowedTo('admin_forum'); + + // We need to know the operation key for the search and replace, mod file looking at, is it a board mod? + if (!isset($_REQUEST['operation_key'], $_REQUEST['filename']) && !is_numeric($_REQUEST['operation_key'])) + fatal_lang_error('operation_invalid', 'general'); + + // Load the required file. + require_once($sourcedir . '/Subs-Package.php'); + + // Uninstalling the mod? + $reverse = isset($_REQUEST['reverse']) ? true : false; + + // Get the base name. + $context['filename'] = preg_replace('~[\.]+~', '.', $_REQUEST['package']); + + // We need to extract this again. + if (is_file($boarddir . '/Packages/' . $context['filename'])) + { + $context['extracted_files'] = read_tgz_file($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); + + if ($context['extracted_files'] && !file_exists($boarddir . '/Packages/temp/package-info.xml')) + foreach ($context['extracted_files'] as $file) + if (basename($file['filename']) == 'package-info.xml') + { + $context['base_path'] = dirname($file['filename']) . '/'; + break; + } + + if (!isset($context['base_path'])) + $context['base_path'] = ''; + } + elseif (is_dir($boarddir . '/Packages/' . $context['filename'])) + { + copytree($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); + $context['extracted_files'] = listtree($boarddir . '/Packages/temp'); + $context['base_path'] = ''; + } + + // Load up any custom themes we may want to install into... + $request = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE (id_theme = {int:default_theme} OR id_theme IN ({array_int:known_theme_list})) + AND variable IN ({string:name}, {string:theme_dir})', + array( + 'known_theme_list' => explode(',', $modSettings['knownThemes']), + 'default_theme' => 1, + 'name' => 'name', + 'theme_dir' => 'theme_dir', + ) + ); + $theme_paths = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $theme_paths[$row['id_theme']][$row['variable']] = $row['value']; + $smcFunc['db_free_result']($request); + + // Boardmod? + if (isset($_REQUEST['boardmod'])) + $mod_actions = parseBoardMod(@file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $_REQUEST['filename']), true, $reverse, $theme_paths); + else + $mod_actions = parseModification(@file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $_REQUEST['filename']), true, $reverse, $theme_paths); + + // Ok lets get the content of the file. + $context['operations'] = array( + 'search' => strtr(htmlspecialchars($mod_actions[$_REQUEST['operation_key']]['search_original']), array('[' => '[', ']' => ']')), + 'replace' => strtr(htmlspecialchars($mod_actions[$_REQUEST['operation_key']]['replace_original']), array('[' => '[', ']' => ']')), + 'position' => $mod_actions[$_REQUEST['operation_key']]['position'], + ); + + // Let's do some formatting... + $operation_text = $context['operations']['position'] == 'replace' ? 'operation_replace' : ($context['operations']['position'] == 'before' ? 'operation_after' : 'operation_before'); + $context['operations']['search'] = parse_bbc('[code=' . $txt['operation_find'] . ']' . ($context['operations']['position'] == 'end' ? '?>' : $context['operations']['search']) . '[/code]'); + $context['operations']['replace'] = parse_bbc('[code=' . $txt[$operation_text] . ']' . $context['operations']['replace'] . '[/code]'); + + // No layers + $context['template_layers'] = array(); + $context['sub_template'] = 'view_operations'; +} + +// Allow the admin to reset permissions on files. +function PackagePermissions() +{ + global $context, $txt, $modSettings, $boarddir, $sourcedir, $cachedir, $smcFunc, $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 memory eat. + @ini_set('memory_limit', '128M'); + @set_time_limit(600); + + // Load up some FTP stuff. + create_chmod_control(); + + if (empty($package_ftp) && !isset($_POST['skip_ftp'])) + { + loadClassFile('Class-Package.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']); + + // !!! 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. + $request = $smcFunc['db_query']('', ' + SELECT value + FROM {db_prefix}themes + WHERE id_theme > {int:default_theme_id} + AND id_member = {int:guest_id} + AND variable = {string:theme_dir} + ORDER BY value ASC', + array( + 'default_theme_id' => 1, + 'guest_id' => 0, + 'theme_dir' => 'theme_dir', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (substr(strtolower(strtr($row['value'], array('\\' => '/'))), 0, strlen($boarddir) + 7) == strtolower(strtr($boarddir, array('\\' => '/')) . '/Themes')) + $context['file_tree'][strtr($boarddir, array('\\' => '/'))]['contents']['Themes']['contents'][substr($row['value'], strlen($boarddir) + 8)] = array( + 'type' => 'dir_recursive', + 'list_contents' => true, + 'contents' => array( + 'languages' => array( + 'type' => 'dir', + 'list_contents' => true, + ), + ), + ); + else + { + $context['file_tree'][strtr($row['value'], array('\\' => '/'))] = array( + 'type' => 'dir_recursive', + 'list_contents' => true, + 'contents' => array( + 'languages' => array( + 'type' => 'dir', + 'list_contents' => true, + ), + ), + ); + } + } + $smcFunc['db_free_result']($request); + + // If we're submitting then let's move on to another function to keep things cleaner.. + if (isset($_POST['action_changes'])) + return PackagePermissionsAction(); + + $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'; + $context['template_layers'] = array(); + } +} + +function fetchPerms__recursive($path, &$data, $level) +{ + global $context; + + $isLikelyPath = false; + foreach ($context['look_for'] as $possiblePath) + if (substr($possiblePath, 0, strlen($path)) == $path) + $isLikelyPath = true; + + // Is this where we stop? + if (isset($_GET['xml']) && !empty($context['look_for']) && !$isLikelyPath) + return; + elseif ($level > $context['default_level'] && !$isLikelyPath) + return; + + // Are we actually interested in saving this data? + $save_data = empty($context['only_find']) || $context['only_find'] == $path; + + //!!! Shouldn't happen - but better error message? + if (!is_dir($path)) + fatal_lang_error('no_access', false); + + // This is where we put stuff we've found for sorting. + $foundData = array( + 'files' => array(), + 'folders' => array(), + ); + + $dh = opendir($path); + while ($entry = readdir($dh)) + { + // Some kind of file? + if (!is_dir($path . '/' . $entry)) + { + // Are we listing PHP files in this directory? + if ($save_data && !empty($data['list_contents']) && substr($entry, -4) == '.php') + $foundData['files'][$entry] = true; + // A file we were looking for. + elseif ($save_data && isset($data['contents'][$entry])) + $foundData['files'][$entry] = true; + } + // It's a directory - we're interested one way or another, probably... + elseif ($entry != '.' && $entry != '..') + { + // Going further? + if ((!empty($data['type']) && $data['type'] == 'dir_recursive') || (isset($data['contents'][$entry]) && (!empty($data['contents'][$entry]['list_contents']) || (!empty($data['contents'][$entry]['type']) && $data['contents'][$entry]['type'] == 'dir_recursive')))) + { + if (!isset($data['contents'][$entry])) + $foundData['folders'][$entry] = 'dir_recursive'; + else + $foundData['folders'][$entry] = true; + + // If this wasn't expected inherit the recusiveness... + if (!isset($data['contents'][$entry])) + // We need to do this as we will be going all recursive. + $data['contents'][$entry] = array( + 'type' => 'dir_recursive', + ); + + // Actually do the recursive stuff... + fetchPerms__recursive($path . '/' . $entry, $data['contents'][$entry], $level + 1); + } + // Maybe it is a folder we are not descending into. + elseif (isset($data['contents'][$entry])) + $foundData['folders'][$entry] = true; + // Otherwise we stop here. + } + } + closedir($dh); + + // Nothing to see here? + if (!$save_data) + return; + + // Now actually add the data, starting with the folders. + ksort($foundData['folders']); + foreach ($foundData['folders'] as $folder => $type) + { + $additional_data = array( + 'perms' => array( + 'chmod' => @is_writable($path . '/' . $folder), + 'perms' => @fileperms($path . '/' . $folder), + ), + ); + if ($type !== true) + $additional_data['type'] = $type; + + // If there's an offset ignore any folders in XML mode. + if (isset($_GET['xml']) && $context['file_offset'] == 0) + { + $context['xml_data']['folders']['children'][] = array( + 'attributes' => array( + 'writable' => $additional_data['perms']['chmod'] ? 1 : 0, + 'permissions' => substr(sprintf('%o', $additional_data['perms']['perms']), -4), + 'folder' => 1, + 'path' => $context['only_find'], + 'level' => $level, + 'more' => 0, + 'offset' => $context['file_offset'], + 'my_ident' => preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $context['only_find'] . '/' . $folder), + 'ident' => preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $context['only_find']), + ), + 'value' => $folder, + ); + } + elseif (!isset($_GET['xml'])) + { + if (isset($data['contents'][$folder])) + $data['contents'][$folder] = array_merge($data['contents'][$folder], $additional_data); + else + $data['contents'][$folder] = $additional_data; + } + } + + // Now we want to do a similar thing with files. + ksort($foundData['files']); + $counter = -1; + foreach ($foundData['files'] as $file => $dummy) + { + $counter++; + + // Have we reached our offset? + if ($context['file_offset'] > $counter) + continue; + // Gone too far? + if ($counter > ($context['file_offset'] + $context['file_limit'])) + continue; + + $additional_data = array( + 'perms' => array( + 'chmod' => @is_writable($path . '/' . $file), + 'perms' => @fileperms($path . '/' . $file), + ), + ); + + // XML? + if (isset($_GET['xml'])) + { + $context['xml_data']['folders']['children'][] = array( + 'attributes' => array( + 'writable' => $additional_data['perms']['chmod'] ? 1 : 0, + 'permissions' => substr(sprintf('%o', $additional_data['perms']['perms']), -4), + 'folder' => 0, + 'path' => $context['only_find'], + 'level' => $level, + 'more' => $counter == ($context['file_offset'] + $context['file_limit']) ? 1 : 0, + 'offset' => $context['file_offset'], + 'my_ident' => preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $context['only_find'] . '/' . $file), + 'ident' => preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $context['only_find']), + ), + 'value' => $file, + ); + } + elseif ($counter != ($context['file_offset'] + $context['file_limit'])) + { + if (isset($data['contents'][$file])) + $data['contents'][$file] = array_merge($data['contents'][$file], $additional_data); + else + $data['contents'][$file] = $additional_data; + } + } +} + +// Actually action the permission changes they want. +function PackagePermissionsAction() +{ + global $context, $txt, $time_start, $package_ftp; + + umask(0); + + $timeout_limit = 5; + + $context['method'] = $_POST['method'] == 'individual' ? 'individual' : 'predefined'; + $context['sub_template'] = 'action_permissions'; + $context['page_title'] = $txt['package_file_perms_applying']; + $context['back_look_data'] = isset($_POST['back_look']) ? $_POST['back_look'] : array(); + + // Skipping use of FTP? + if (empty($package_ftp)) + $context['skip_ftp'] = true; + + // We'll start off in a good place, security. Make sure that if we're dealing with individual files that they seem in the right place. + if ($context['method'] == 'individual') + { + // Only these path roots are legal. + $legal_roots = array_keys($context['file_tree']); + $context['custom_value'] = (int) $_POST['custom_value']; + + // Continuing? + if (isset($_POST['toProcess'])) + $_POST['permStatus'] = unserialize(base64_decode($_POST['toProcess'])); + + if (isset($_POST['permStatus'])) + { + $context['to_process'] = array(); + $validate_custom = false; + foreach ($_POST['permStatus'] as $path => $status) + { + // Nothing to see here? + if ($status == 'no_change') + continue; + $legal = false; + foreach ($legal_roots as $root) + if (substr($path, 0, strlen($root)) == $root) + $legal = true; + + if (!$legal) + continue; + + // Check it exists. + if (!file_exists($path)) + continue; + + if ($status == 'custom') + $validate_custom = true; + + // Now add it. + $context['to_process'][$path] = $status; + } + $context['total_items'] = isset($_POST['totalItems']) ? (int) $_POST['totalItems'] : count($context['to_process']); + + // Make sure the chmod status is valid? + if ($validate_custom) + { + if (preg_match('~^[4567][4567][4567]$~', $context['custom_value']) == false) + fatal_error($txt['chmod_value_invalid']); + } + + // Nothing to do? + if (empty($context['to_process'])) + redirectexit('action=admin;area=packages;sa=perms' . (!empty($context['back_look_data']) ? ';back_look=' . base64_encode(serialize($context['back_look_data'])) : '') . ';' . $context['session_var'] . '=' . $context['session_id']); + } + // Should never get here, + else + fatal_lang_error('no_access', false); + + // Setup the custom value. + $custom_value = octdec('0' . $context['custom_value']); + + // Start processing items. + foreach ($context['to_process'] as $path => $status) + { + if (in_array($status, array('execute', 'writable', 'read'))) + package_chmod($path, $status); + elseif ($status == 'custom' && !empty($custom_value)) + { + // Use FTP if we have it. + if (!empty($package_ftp) && !empty($_SESSION['pack_ftp'])) + { + $ftp_file = strtr($path, array($_SESSION['pack_ftp']['root'] => '')); + $package_ftp->chmod($ftp_file, $custom_value); + } + else + @chmod($path, $custom_value); + } + + // This fish is fried... + unset($context['to_process'][$path]); + + // See if we're out of time? + if (time() - array_sum(explode(' ', $time_start)) > $timeout_limit) + return false; + } + } + // If predefined this is a little different. + else + { + $context['predefined_type'] = isset($_POST['predefined']) ? $_POST['predefined'] : 'restricted'; + + $context['total_items'] = isset($_POST['totalItems']) ? (int) $_POST['totalItems'] : 0; + $context['directory_list'] = isset($_POST['dirList']) ? unserialize(base64_decode($_POST['dirList'])) : array(); + + $context['file_offset'] = isset($_POST['fileOffset']) ? (int) $_POST['fileOffset'] : 0; + + // Haven't counted the items yet? + if (empty($context['total_items'])) + { + function count_directories__recursive($dir) + { + global $context; + + $count = 0; + $dh = @opendir($dir); + while ($entry = readdir($dh)) + { + if ($entry != '.' && $entry != '..' && is_dir($dir . '/' . $entry)) + { + $context['directory_list'][$dir . '/' . $entry] = 1; + $count++; + $count += count_directories__recursive($dir . '/' . $entry); + } + } + closedir($dh); + + return $count; + } + + foreach ($context['file_tree'] as $path => $data) + { + if (is_dir($path)) + { + $context['directory_list'][$path] = 1; + $context['total_items'] += count_directories__recursive($path); + $context['total_items']++; + } + } + } + + // Have we built up our list of special files? + if (!isset($_POST['specialFiles']) && $context['predefined_type'] != 'free') + { + $context['special_files'] = array(); + function build_special_files__recursive($path, &$data) + { + global $context; + + if (!empty($data['writable_on'])) + if ($context['predefined_type'] == 'standard' || $data['writable_on'] == 'restrictive') + $context['special_files'][$path] = 1; + + if (!empty($data['contents'])) + foreach ($data['contents'] as $name => $contents) + build_special_files__recursive($path . '/' . $name, $contents); + } + + foreach ($context['file_tree'] as $path => $data) + build_special_files__recursive($path, $data); + } + // Free doesn't need special files. + elseif ($context['predefined_type'] == 'free') + $context['special_files'] = array(); + else + $context['special_files'] = unserialize(base64_decode($_POST['specialFiles'])); + + // Now we definitely know where we are, we need to go through again doing the chmod! + foreach ($context['directory_list'] as $path => $dummy) + { + // Do the contents of the directory first. + $dh = @opendir($path); + $file_count = 0; + $dont_chmod = false; + while ($entry = readdir($dh)) + { + $file_count++; + // Actually process this file? + if (!$dont_chmod && !is_dir($path . '/' . $entry) && (empty($context['file_offset']) || $context['file_offset'] < $file_count)) + { + $status = $context['predefined_type'] == 'free' || isset($context['special_files'][$path . '/' . $entry]) ? 'writable' : 'execute'; + package_chmod($path . '/' . $entry, $status); + } + + // See if we're out of time? + if (!$dont_chmod && time() - array_sum(explode(' ', $time_start)) > $timeout_limit) + { + $dont_chmod = true; + // Don't do this again. + $context['file_offset'] = $file_count; + } + } + closedir($dh); + + // If this is set it means we timed out half way through. + if ($dont_chmod) + { + $context['total_files'] = $file_count; + return false; + } + + // Do the actual directory. + $status = $context['predefined_type'] == 'free' || isset($context['special_files'][$path]) ? 'writable' : 'execute'; + package_chmod($path, $status); + + // We've finished the directory so no file offset, and no record. + $context['file_offset'] = 0; + unset($context['directory_list'][$path]); + + // See if we're out of time? + if (time() - array_sum(explode(' ', $time_start)) > $timeout_limit) + return false; + } + } + + // If we're here we are done! + redirectexit('action=admin;area=packages;sa=perms' . (!empty($context['back_look_data']) ? ';back_look=' . base64_encode(serialize($context['back_look_data'])) : '') . ';' . $context['session_var'] . '=' . $context['session_id']); +} + +// Test an FTP connection. +function PackageFTPTest() +{ + global $context, $txt, $package_ftp; + + checkSession('get'); + + // Try to make the FTP connection. + create_chmod_control(array(), array('force_find_error' => true)); + + // Deal with the template stuff. + loadTemplate('Xml'); + $context['sub_template'] = 'generic_xml'; + $context['template_layers'] = array(); + + // Define the return data, this is simple. + $context['xml_data'] = array( + 'results' => array( + 'identifier' => 'result', + 'children' => array( + array( + 'attributes' => array( + 'success' => !empty($package_ftp) ? 1 : 0, + ), + 'value' => !empty($package_ftp) ? $txt['package_ftp_test_success'] : (isset($context['package_ftp'], $context['package_ftp']['error']) ? $context['package_ftp']['error'] : $txt['package_ftp_test_failed']), + ), + ), + ), + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/PersonalMessage.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/PersonalMessage.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,3618 @@ + $user_info['groups'], + ) + ); + list ($maxMessage, $minMessage) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['message_limit'] = $minMessage == 0 ? 0 : $maxMessage; + + // Save us doing it again! + cache_put_data('msgLimit:' . $user_info['id'], $context['message_limit'], 360); + } + + // Prepare the context for the capacity bar. + if (!empty($context['message_limit'])) + { + $bar = ($user_info['messages'] * 100) / $context['message_limit']; + + $context['limit_bar'] = array( + 'messages' => $user_info['messages'], + 'allowed' => $context['message_limit'], + 'percent' => $bar, + 'bar' => min(100, (int) $bar), + 'text' => sprintf($txt['pm_currently_using'], $user_info['messages'], round($bar, 1)), + ); + } + + // a previous message was sent successfully? show a small indication. + if (isset($_GET['done']) && ($_GET['done'] == 'sent')) + $context['pm_sent'] = true; + + // Now we have the labels, and assuming we have unsorted mail, apply our rules! + if ($user_settings['new_pm']) + { + $context['labels'] = $user_settings['message_labels'] == '' ? array() : explode(',', $user_settings['message_labels']); + foreach ($context['labels'] as $id_label => $label_name) + $context['labels'][(int) $id_label] = array( + 'id' => $id_label, + 'name' => trim($label_name), + 'messages' => 0, + 'unread_messages' => 0, + ); + $context['labels'][-1] = array( + 'id' => -1, + 'name' => $txt['pm_msg_label_inbox'], + 'messages' => 0, + 'unread_messages' => 0, + ); + + ApplyRules(); + updateMemberData($user_info['id'], array('new_pm' => 0)); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}pm_recipients + SET is_new = {int:not_new} + WHERE id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'not_new' => 0, + ) + ); + } + + // Load the label data. + if ($user_settings['new_pm'] || ($context['labels'] = cache_get_data('labelCounts:' . $user_info['id'], 720)) === null) + { + $context['labels'] = $user_settings['message_labels'] == '' ? array() : explode(',', $user_settings['message_labels']); + foreach ($context['labels'] as $id_label => $label_name) + $context['labels'][(int) $id_label] = array( + 'id' => $id_label, + 'name' => trim($label_name), + 'messages' => 0, + 'unread_messages' => 0, + ); + $context['labels'][-1] = array( + 'id' => -1, + 'name' => $txt['pm_msg_label_inbox'], + 'messages' => 0, + 'unread_messages' => 0, + ); + + // Looks like we need to reseek! + $result = $smcFunc['db_query']('', ' + SELECT labels, is_read, COUNT(*) AS num + FROM {db_prefix}pm_recipients + WHERE id_member = {int:current_member} + AND deleted = {int:not_deleted} + GROUP BY labels, is_read', + array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $this_labels = explode(',', $row['labels']); + foreach ($this_labels as $this_label) + { + $context['labels'][(int) $this_label]['messages'] += $row['num']; + if (!($row['is_read'] & 1)) + $context['labels'][(int) $this_label]['unread_messages'] += $row['num']; + } + } + $smcFunc['db_free_result']($result); + + // Store it please! + cache_put_data('labelCounts:' . $user_info['id'], $context['labels'], 720); + } + + // This determines if we have more labels than just the standard inbox. + $context['currently_using_labels'] = count($context['labels']) > 1 ? 1 : 0; + + // Some stuff for the labels... + $context['current_label_id'] = isset($_REQUEST['l']) && isset($context['labels'][(int) $_REQUEST['l']]) ? (int) $_REQUEST['l'] : -1; + $context['current_label'] = &$context['labels'][(int) $context['current_label_id']]['name']; + $context['folder'] = !isset($_REQUEST['f']) || $_REQUEST['f'] != 'sent' ? 'inbox' : 'sent'; + + // This is convenient. Do you know how annoying it is to do this every time?! + $context['current_label_redirect'] = 'action=pm;f=' . $context['folder'] . (isset($_GET['start']) ? ';start=' . $_GET['start'] : '') . (isset($_REQUEST['l']) ? ';l=' . $_REQUEST['l'] : ''); + $context['can_issue_warning'] = in_array('w', $context['admin_features']) && allowedTo('issue_warning') && $modSettings['warning_settings'][0] == 1; + + // Build the linktree for all the actions... + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm', + 'name' => $txt['personal_messages'] + ); + + // Preferences... + $context['display_mode'] = WIRELESS ? 0 : $user_settings['pm_prefs'] & 3; + + $subActions = array( + 'addbuddy' => 'WirelessAddBuddy', + 'manlabels' => 'ManageLabels', + 'manrules' => 'ManageRules', + 'pmactions' => 'MessageActionsApply', + 'prune' => 'MessagePrune', + 'removeall' => 'MessageKillAllQuery', + 'removeall2' => 'MessageKillAll', + 'report' => 'ReportMessage', + 'search' => 'MessageSearch', + 'search2' => 'MessageSearch2', + 'send' => 'MessagePost', + 'send2' => 'MessagePost2', + 'settings' => 'MessageSettings', + ); + + if (!isset($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']])) + MessageFolder(); + else + { + messageIndexBar($_REQUEST['sa']); + $subActions[$_REQUEST['sa']](); + } +} + +// A sidebar to easily access different areas of the section +function messageIndexBar($area) +{ + global $txt, $context, $scripturl, $sourcedir, $sc, $modSettings, $settings, $user_info, $options; + + $pm_areas = array( + 'folders' => array( + 'title' => $txt['pm_messages'], + 'areas' => array( + 'send' => array( + 'label' => $txt['new_message'], + 'custom_url' => $scripturl . '?action=pm;sa=send', + 'permission' => allowedTo('pm_send'), + ), + 'inbox' => array( + 'label' => $txt['inbox'], + 'custom_url' => $scripturl . '?action=pm', + ), + 'sent' => array( + 'label' => $txt['sent_items'], + 'custom_url' => $scripturl . '?action=pm;f=sent', + ), + ), + ), + 'labels' => array( + 'title' => $txt['pm_labels'], + 'areas' => array(), + ), + 'actions' => array( + 'title' => $txt['pm_actions'], + 'areas' => array( + 'search' => array( + 'label' => $txt['pm_search_bar_title'], + 'custom_url' => $scripturl . '?action=pm;sa=search', + ), + 'prune' => array( + 'label' => $txt['pm_prune'], + 'custom_url' => $scripturl . '?action=pm;sa=prune' + ), + ), + ), + 'pref' => array( + 'title' => $txt['pm_preferences'], + 'areas' => array( + 'manlabels' => array( + 'label' => $txt['pm_manage_labels'], + 'custom_url' => $scripturl . '?action=pm;sa=manlabels', + ), + 'manrules' => array( + 'label' => $txt['pm_manage_rules'], + 'custom_url' => $scripturl . '?action=pm;sa=manrules', + ), + 'settings' => array( + 'label' => $txt['pm_settings'], + 'custom_url' => $scripturl . '?action=pm;sa=settings', + ), + ), + ), + ); + + // Handle labels. + if (empty($context['currently_using_labels'])) + unset($pm_areas['labels']); + else + { + // Note we send labels by id as it will have less problems in the querystring. + $unread_in_labels = 0; + foreach ($context['labels'] as $label) + { + if ($label['id'] == -1) + continue; + + // Count the amount of unread items in labels. + $unread_in_labels += $label['unread_messages']; + + // Add the label to the menu. + $pm_areas['labels']['areas']['label' . $label['id']] = array( + 'label' => $label['name'] . (!empty($label['unread_messages']) ? ' (' . $label['unread_messages'] . ')' : ''), + 'custom_url' => $scripturl . '?action=pm;l=' . $label['id'], + 'unread_messages' => $label['unread_messages'], + 'messages' => $label['messages'], + ); + } + + if (!empty($unread_in_labels)) + $pm_areas['labels']['title'] .= ' (' . $unread_in_labels . ')'; + } + + $pm_areas['folders']['areas']['inbox']['unread_messages'] = &$context['labels'][-1]['unread_messages']; + $pm_areas['folders']['areas']['inbox']['messages'] = &$context['labels'][-1]['messages']; + if (!empty($context['labels'][-1]['unread_messages'])) + { + $pm_areas['folders']['areas']['inbox']['label'] .= ' (' . $context['labels'][-1]['unread_messages'] . ')'; + $pm_areas['folders']['title'] .= ' (' . $context['labels'][-1]['unread_messages'] . ')'; + } + + // Do we have a limit on the amount of messages we can keep? + if (!empty($context['message_limit'])) + { + $bar = round(($user_info['messages'] * 100) / $context['message_limit'], 1); + + $context['limit_bar'] = array( + 'messages' => $user_info['messages'], + 'allowed' => $context['message_limit'], + 'percent' => $bar, + 'bar' => $bar > 100 ? 100 : (int) $bar, + 'text' => sprintf($txt['pm_currently_using'], $user_info['messages'], $bar) + ); + } + + require_once($sourcedir . '/Subs-Menu.php'); + + // What page is this, again? + $current_page = $scripturl . '?action=pm' . (!empty($_REQUEST['sa']) ? ';sa=' . $_REQUEST['sa'] : '') . (!empty($context['folder']) ? ';f=' . $context['folder'] : '') . (!empty($context['current_label_id']) ? ';l=' . $context['current_label_id'] : ''); + + // Set a few options for the menu. + $menuOptions = array( + 'current_area' => $area, + 'disable_url_session_check' => true, + 'toggle_url' => $current_page . ';togglebar', + 'toggle_redirect_url' => $current_page, + ); + + // Actually create the menu! + $pm_include_data = createMenu($pm_areas, $menuOptions); + unset($pm_areas); + + // Make a note of the Unique ID for this menu. + $context['pm_menu_id'] = $context['max_menu_id']; + $context['pm_menu_name'] = 'menu_data_' . $context['pm_menu_id']; + + // Set the selected item. + $context['menu_item_selected'] = $pm_include_data['current_area']; + + // obExit will know what to do! + if (!WIRELESS) + $context['template_layers'][] = 'pm'; +} + +// A folder, ie. inbox/sent etc. +function MessageFolder() +{ + global $txt, $scripturl, $modSettings, $context, $subjects_request; + global $messages_request, $user_info, $recipients, $options, $smcFunc, $memberContext, $user_settings; + + // Changing view? + if (isset($_GET['view'])) + { + $context['display_mode'] = $context['display_mode'] > 1 ? 0 : $context['display_mode'] + 1; + updateMemberData($user_info['id'], array('pm_prefs' => ($user_settings['pm_prefs'] & 252) | $context['display_mode'])); + } + + // Make sure the starting location is valid. + if (isset($_GET['start']) && $_GET['start'] != 'new') + $_GET['start'] = (int) $_GET['start']; + elseif (!isset($_GET['start']) && !empty($options['view_newest_pm_first'])) + $_GET['start'] = 0; + else + $_GET['start'] = 'new'; + + // Set up some basic theme stuff. + $context['from_or_to'] = $context['folder'] != 'sent' ? 'from' : 'to'; + $context['get_pmessage'] = 'prepareMessageContext'; + $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1; + + $labelQuery = $context['folder'] != 'sent' ? ' + AND FIND_IN_SET(' . $context['current_label_id'] . ', pmr.labels) != 0' : ''; + + // Set the index bar correct! + messageIndexBar($context['current_label_id'] == -1 ? $context['folder'] : 'label' . $context['current_label_id']); + + // Sorting the folder. + $sort_methods = array( + 'date' => 'pm.id_pm', + 'name' => 'IFNULL(mem.real_name, \'\')', + 'subject' => 'pm.subject', + ); + + // They didn't pick one, use the forum default. + if (!isset($_GET['sort']) || !isset($sort_methods[$_GET['sort']])) + { + $context['sort_by'] = 'date'; + $_GET['sort'] = 'pm.id_pm'; + // An overriding setting? + $descending = !empty($options['view_newest_pm_first']); + } + // Otherwise use the defaults: ascending, by date. + else + { + $context['sort_by'] = $_GET['sort']; + $_GET['sort'] = $sort_methods[$_GET['sort']]; + $descending = isset($_GET['desc']); + } + + $context['sort_direction'] = $descending ? 'down' : 'up'; + + // Why would you want access to your sent items if you're not allowed to send anything? + if ($context['folder'] == 'sent') + isAllowedTo('pm_send'); + + // Set the text to resemble the current folder. + $pmbox = $context['folder'] != 'sent' ? $txt['inbox'] : $txt['sent_items']; + $txt['delete_all'] = str_replace('PMBOX', $pmbox, $txt['delete_all']); + + // Now, build the link tree! + if ($context['current_label_id'] == -1) + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;f=' . $context['folder'], + 'name' => $pmbox + ); + + // Build it further for a label. + if ($context['current_label_id'] != -1) + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;f=' . $context['folder'] . ';l=' . $context['current_label_id'], + 'name' => $txt['pm_current_label'] . ': ' . $context['current_label'] + ); + + // Figure out how many messages there are. + if ($context['folder'] == 'sent') + $request = $smcFunc['db_query']('', ' + SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ') + FROM {db_prefix}personal_messages AS pm + WHERE pm.id_member_from = {int:current_member} + AND pm.deleted_by_sender = {int:not_deleted}', + array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + ) + ); + else + $request = $smcFunc['db_query']('', ' + SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ') + FROM {db_prefix}pm_recipients AS pmr' . ($context['display_mode'] == 2 ? ' + INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)' : '') . ' + WHERE pmr.id_member = {int:current_member} + AND pmr.deleted = {int:not_deleted}' . $labelQuery, + array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + ) + ); + list ($max_messages) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Only show the button if there are messages to delete. + $context['show_delete'] = $max_messages > 0; + + // Start on the last page. + if (!is_numeric($_GET['start']) || $_GET['start'] >= $max_messages) + $_GET['start'] = ($max_messages - 1) - (($max_messages - 1) % $modSettings['defaultMaxMessages']); + elseif ($_GET['start'] < 0) + $_GET['start'] = 0; + + // ... but wait - what if we want to start from a specific message? + if (isset($_GET['pmid'])) + { + $pmID = (int) $_GET['pmid']; + + // Make sure you have access to this PM. + if (!isAccessiblePM($pmID, $context['folder'] == 'sent' ? 'outbox' : 'inbox')) + fatal_lang_error('no_access', false); + + $context['current_pm'] = $pmID; + + // With only one page of PM's we're gonna want page 1. + if ($max_messages <= $modSettings['defaultMaxMessages']) + $_GET['start'] = 0; + // If we pass kstart we assume we're in the right place. + elseif (!isset($_GET['kstart'])) + { + if ($context['folder'] == 'sent') + $request = $smcFunc['db_query']('', ' + SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ') + FROM {db_prefix}personal_messages + WHERE id_member_from = {int:current_member} + AND deleted_by_sender = {int:not_deleted} + AND id_pm ' . ($descending ? '>' : '<') . ' {int:id_pm}', + array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + 'id_pm' => $pmID, + ) + ); + else + $request = $smcFunc['db_query']('', ' + SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ') + FROM {db_prefix}pm_recipients AS pmr' . ($context['display_mode'] == 2 ? ' + INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)' : '') . ' + WHERE pmr.id_member = {int:current_member} + AND pmr.deleted = {int:not_deleted}' . $labelQuery . ' + AND pmr.id_pm ' . ($descending ? '>' : '<') . ' {int:id_pm}', + array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + 'id_pm' => $pmID, + ) + ); + + list ($_GET['start']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // To stop the page index's being abnormal, start the page on the page the message would normally be located on... + $_GET['start'] = $modSettings['defaultMaxMessages'] * (int) ($_GET['start'] / $modSettings['defaultMaxMessages']); + } + } + + // Sanitize and validate pmsg variable if set. + if (isset($_GET['pmsg'])) + { + $pmsg = (int) $_GET['pmsg']; + + if (!isAccessiblePM($pmsg, $context['folder'] == 'sent' ? 'outbox' : 'inbox')) + fatal_lang_error('no_access', false); + } + + // Set up the page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=pm;f=' . $context['folder'] . (isset($_REQUEST['l']) ? ';l=' . (int) $_REQUEST['l'] : '') . ';sort=' . $context['sort_by'] . ($descending ? ';desc' : ''), $_GET['start'], $max_messages, $modSettings['defaultMaxMessages']); + $context['start'] = $_GET['start']; + + // Determine the navigation context (especially useful for the wireless template). + $context['links'] = array( + 'first' => $_GET['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?action=pm;start=0' : '', + 'prev' => $_GET['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?action=pm;start=' . ($_GET['start'] - $modSettings['defaultMaxMessages']) : '', + 'next' => $_GET['start'] + $modSettings['defaultMaxMessages'] < $max_messages ? $scripturl . '?action=pm;start=' . ($_GET['start'] + $modSettings['defaultMaxMessages']) : '', + 'last' => $_GET['start'] + $modSettings['defaultMaxMessages'] < $max_messages ? $scripturl . '?action=pm;start=' . (floor(($max_messages - 1) / $modSettings['defaultMaxMessages']) * $modSettings['defaultMaxMessages']) : '', + 'up' => $scripturl, + ); + $context['page_info'] = array( + 'current_page' => $_GET['start'] / $modSettings['defaultMaxMessages'] + 1, + 'num_pages' => floor(($max_messages - 1) / $modSettings['defaultMaxMessages']) + 1 + ); + + // First work out what messages we need to see - if grouped is a little trickier... + if ($context['display_mode'] == 2) + { + // On a non-default sort due to PostgreSQL we have to do a harder sort. + if ($smcFunc['db_title'] == 'PostgreSQL' && $_GET['sort'] != 'pm.id_pm') + { + $sub_request = $smcFunc['db_query']('', ' + SELECT MAX({raw:sort}) AS sort_param, pm.id_pm_head + FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? ($context['sort_by'] == 'name' ? ' + LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : ' + INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm + AND pmr.id_member = {int:current_member} + AND pmr.deleted = {int:not_deleted} + ' . $labelQuery . ')') . ($context['sort_by'] == 'name' ? ( ' + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:id_member})') : '') . ' + WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {int:current_member} + AND pm.deleted_by_sender = {int:not_deleted}' : '1=1') . (empty($pmsg) ? '' : ' + AND pm.id_pm = {int:id_pm}') . ' + GROUP BY pm.id_pm_head + ORDER BY sort_param' . ($descending ? ' DESC' : ' ASC') . (empty($pmsg) ? ' + LIMIT ' . $_GET['start'] . ', ' . $modSettings['defaultMaxMessages'] : ''), + array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + 'id_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from', + 'id_pm' => isset($pmsg) ? $pmsg : '0', + 'sort' => $_GET['sort'], + ) + ); + $sub_pms = array(); + while ($row = $smcFunc['db_fetch_assoc']($sub_request)) + $sub_pms[$row['id_pm_head']] = $row['sort_param']; + + $smcFunc['db_free_result']($sub_request); + + $request = $smcFunc['db_query']('', ' + SELECT pm.id_pm AS id_pm, pm.id_pm_head + FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? ($context['sort_by'] == 'name' ? ' + LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : ' + INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm + AND pmr.id_member = {int:current_member} + AND pmr.deleted = {int:not_deleted} + ' . $labelQuery . ')') . ($context['sort_by'] == 'name' ? ( ' + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:id_member})') : '') . ' + WHERE ' . (empty($sub_pms) ? '0=1' : 'pm.id_pm IN ({array_int:pm_list})') . ' + ORDER BY ' . ($_GET['sort'] == 'pm.id_pm' && $context['folder'] != 'sent' ? 'id_pm' : '{raw:sort}') . ($descending ? ' DESC' : ' ASC') . (empty($pmsg) ? ' + LIMIT ' . $_GET['start'] . ', ' . $modSettings['defaultMaxMessages'] : ''), + array( + 'current_member' => $user_info['id'], + 'pm_list' => array_keys($sub_pms), + 'not_deleted' => 0, + 'sort' => $_GET['sort'], + 'id_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from', + ) + ); + } + else + { + $request = $smcFunc['db_query']('pm_conversation_list', ' + SELECT MAX(pm.id_pm) AS id_pm, pm.id_pm_head + FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? ($context['sort_by'] == 'name' ? ' + LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : ' + INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm + AND pmr.id_member = {int:current_member} + AND pmr.deleted = {int:deleted_by} + ' . $labelQuery . ')') . ($context['sort_by'] == 'name' ? ( ' + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:pm_member})') : '') . ' + WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {int:current_member} + AND pm.deleted_by_sender = {int:deleted_by}' : '1=1') . (empty($pmsg) ? '' : ' + AND pm.id_pm = {int:pmsg}') . ' + GROUP BY pm.id_pm_head + ORDER BY ' . ($_GET['sort'] == 'pm.id_pm' && $context['folder'] != 'sent' ? 'id_pm' : '{raw:sort}') . ($descending ? ' DESC' : ' ASC') . (empty($_GET['pmsg']) ? ' + LIMIT ' . $_GET['start'] . ', ' . $modSettings['defaultMaxMessages'] : ''), + array( + 'current_member' => $user_info['id'], + 'deleted_by' => 0, + 'sort' => $_GET['sort'], + 'pm_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from', + 'pmsg' => isset($pmsg) ? (int) $pmsg : 0, + ) + ); + } + } + // This is kinda simple! + else + { + // !!!SLOW This query uses a filesort. (inbox only.) + $request = $smcFunc['db_query']('', ' + SELECT pm.id_pm, pm.id_pm_head, pm.id_member_from + FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? '' . ($context['sort_by'] == 'name' ? ' + LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : ' + INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm + AND pmr.id_member = {int:current_member} + AND pmr.deleted = {int:is_deleted} + ' . $labelQuery . ')') . ($context['sort_by'] == 'name' ? ( ' + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:pm_member})') : '') . ' + WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {raw:current_member} + AND pm.deleted_by_sender = {int:is_deleted}' : '1=1') . (empty($pmsg) ? '' : ' + AND pm.id_pm = {int:pmsg}') . ' + ORDER BY ' . ($_GET['sort'] == 'pm.id_pm' && $context['folder'] != 'sent' ? 'pmr.id_pm' : '{raw:sort}') . ($descending ? ' DESC' : ' ASC') . (empty($pmsg) ? ' + LIMIT ' . $_GET['start'] . ', ' . $modSettings['defaultMaxMessages'] : ''), + array( + 'current_member' => $user_info['id'], + 'is_deleted' => 0, + 'sort' => $_GET['sort'], + 'pm_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from', + 'pmsg' => isset($pmsg) ? (int) $pmsg : 0, + ) + ); + } + // Load the id_pms and initialize recipients. + $pms = array(); + $lastData = array(); + $posters = $context['folder'] == 'sent' ? array($user_info['id']) : array(); + $recipients = array(); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($recipients[$row['id_pm']])) + { + if (isset($row['id_member_from'])) + $posters[$row['id_pm']] = $row['id_member_from']; + $pms[$row['id_pm']] = $row['id_pm']; + $recipients[$row['id_pm']] = array( + 'to' => array(), + 'bcc' => array() + ); + } + + // Keep track of the last message so we know what the head is without another query! + if ((empty($pmID) && (empty($options['view_newest_pm_first']) || !isset($lastData))) || empty($lastData) || (!empty($pmID) && $pmID == $row['id_pm'])) + $lastData = array( + 'id' => $row['id_pm'], + 'head' => $row['id_pm_head'], + ); + } + $smcFunc['db_free_result']($request); + + // Make sure that we have been given a correct head pm id! + if ($context['display_mode'] == 2 && !empty($pmID) && $pmID != $lastData['id']) + fatal_lang_error('no_access', false); + + if (!empty($pms)) + { + // Select the correct current message. + if (empty($pmID)) + $context['current_pm'] = $lastData['id']; + + // This is a list of the pm's that are used for "full" display. + if ($context['display_mode'] == 0) + $display_pms = $pms; + else + $display_pms = array($context['current_pm']); + + // At this point we know the main id_pm's. But - if we are looking at conversations we need the others! + if ($context['display_mode'] == 2) + { + $request = $smcFunc['db_query']('', ' + SELECT pm.id_pm, pm.id_member_from, pm.deleted_by_sender, pmr.id_member, pmr.deleted + FROM {db_prefix}personal_messages AS pm + INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm) + WHERE pm.id_pm_head = {int:id_pm_head} + AND ((pm.id_member_from = {int:current_member} AND pm.deleted_by_sender = {int:not_deleted}) + OR (pmr.id_member = {int:current_member} AND pmr.deleted = {int:not_deleted})) + ORDER BY pm.id_pm', + array( + 'current_member' => $user_info['id'], + 'id_pm_head' => $lastData['head'], + 'not_deleted' => 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // This is, frankly, a joke. We will put in a workaround for people sending to themselves - yawn! + if ($context['folder'] == 'sent' && $row['id_member_from'] == $user_info['id'] && $row['deleted_by_sender'] == 1) + continue; + elseif ($row['id_member'] == $user_info['id'] & $row['deleted'] == 1) + continue; + + if (!isset($recipients[$row['id_pm']])) + $recipients[$row['id_pm']] = array( + 'to' => array(), + 'bcc' => array() + ); + $display_pms[] = $row['id_pm']; + $posters[$row['id_pm']] = $row['id_member_from']; + } + $smcFunc['db_free_result']($request); + } + + // This is pretty much EVERY pm! + $all_pms = array_merge($pms, $display_pms); + $all_pms = array_unique($all_pms); + + // Get recipients (don't include bcc-recipients for your inbox, you're not supposed to know :P). + $request = $smcFunc['db_query']('', ' + SELECT pmr.id_pm, mem_to.id_member AS id_member_to, mem_to.real_name AS to_name, pmr.bcc, pmr.labels, pmr.is_read + FROM {db_prefix}pm_recipients AS pmr + LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member) + WHERE pmr.id_pm IN ({array_int:pm_list})', + array( + 'pm_list' => $all_pms, + ) + ); + $context['message_labels'] = array(); + $context['message_replied'] = array(); + $context['message_unread'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($context['folder'] == 'sent' || empty($row['bcc'])) + $recipients[$row['id_pm']][empty($row['bcc']) ? 'to' : 'bcc'][] = empty($row['id_member_to']) ? $txt['guest_title'] : '' . $row['to_name'] . ''; + + if ($row['id_member_to'] == $user_info['id'] && $context['folder'] != 'sent') + { + $context['message_replied'][$row['id_pm']] = $row['is_read'] & 2; + $context['message_unread'][$row['id_pm']] = $row['is_read'] == 0; + + $row['labels'] = $row['labels'] == '' ? array() : explode(',', $row['labels']); + foreach ($row['labels'] as $v) + { + if (isset($context['labels'][(int) $v])) + $context['message_labels'][$row['id_pm']][(int) $v] = array('id' => $v, 'name' => $context['labels'][(int) $v]['name']); + } + } + } + $smcFunc['db_free_result']($request); + + // Make sure we don't load unnecessary data. + if ($context['display_mode'] == 1) + { + foreach ($posters as $k => $v) + if (!in_array($k, $display_pms)) + unset($posters[$k]); + } + + // Load any users.... + $posters = array_unique($posters); + if (!empty($posters)) + loadMemberData($posters); + + // If we're on grouped/restricted view get a restricted list of messages. + if ($context['display_mode'] != 0) + { + // Get the order right. + $orderBy = array(); + foreach (array_reverse($pms) as $pm) + $orderBy[] = 'pm.id_pm = ' . $pm; + + // Seperate query for these bits! + $subjects_request = $smcFunc['db_query']('', ' + SELECT pm.id_pm, pm.subject, pm.id_member_from, pm.msgtime, IFNULL(mem.real_name, pm.from_name) AS from_name, + IFNULL(mem.id_member, 0) AS not_guest + FROM {db_prefix}personal_messages AS pm + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from) + WHERE pm.id_pm IN ({array_int:pm_list}) + ORDER BY ' . implode(', ', $orderBy) . ' + LIMIT ' . count($pms), + array( + 'pm_list' => $pms, + ) + ); + } + + // Execute the query! + $messages_request = $smcFunc['db_query']('', ' + SELECT pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name + FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? ' + LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') . ($context['sort_by'] == 'name' ? ' + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:id_member})' : '') . ' + WHERE pm.id_pm IN ({array_int:display_pms})' . ($context['folder'] == 'sent' ? ' + GROUP BY pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name' : '') . ' + ORDER BY ' . ($context['display_mode'] == 2 ? 'pm.id_pm' : $_GET['sort']) . ($descending ? ' DESC' : ' ASC') . ' + LIMIT ' . count($display_pms), + array( + 'display_pms' => $display_pms, + 'id_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from', + ) + ); + } + else + $messages_request = false; + + $context['can_send_pm'] = allowedTo('pm_send'); + if (!WIRELESS) + $context['sub_template'] = 'folder'; + $context['page_title'] = $txt['pm_inbox']; + + // Finally mark the relevant messages as read. + if ($context['folder'] != 'sent' && !empty($context['labels'][(int) $context['current_label_id']]['unread_messages'])) + { + // If the display mode is "old sk00l" do them all... + if ($context['display_mode'] == 0) + markMessages(null, $context['current_label_id']); + // Otherwise do just the current one! + elseif (!empty($context['current_pm'])) + markMessages($display_pms, $context['current_label_id']); + } +} + +// Get a personal message for the theme. (used to save memory.) +function prepareMessageContext($type = 'subject', $reset = false) +{ + global $txt, $scripturl, $modSettings, $context, $messages_request, $memberContext, $recipients, $smcFunc; + global $user_info, $subjects_request; + + // Count the current message number.... + static $counter = null; + if ($counter === null || $reset) + $counter = $context['start']; + + static $temp_pm_selected = null; + if ($temp_pm_selected === null) + { + $temp_pm_selected = isset($_SESSION['pm_selected']) ? $_SESSION['pm_selected'] : array(); + $_SESSION['pm_selected'] = array(); + } + + // If we're in non-boring view do something exciting! + if ($context['display_mode'] != 0 && $subjects_request && $type == 'subject') + { + $subject = $smcFunc['db_fetch_assoc']($subjects_request); + if (!$subject) + { + $smcFunc['db_free_result']($subjects_request); + return false; + } + + $subject['subject'] = $subject['subject'] == '' ? $txt['no_subject'] : $subject['subject']; + censorText($subject['subject']); + + $output = array( + 'id' => $subject['id_pm'], + 'member' => array( + 'id' => $subject['id_member_from'], + 'name' => $subject['from_name'], + 'link' => $subject['not_guest'] ? '' . $subject['from_name'] . '' : $subject['from_name'], + ), + 'recipients' => &$recipients[$subject['id_pm']], + 'subject' => $subject['subject'], + 'time' => timeformat($subject['msgtime']), + 'timestamp' => forum_time(true, $subject['msgtime']), + 'number_recipients' => count($recipients[$subject['id_pm']]['to']), + 'labels' => &$context['message_labels'][$subject['id_pm']], + 'fully_labeled' => count($context['message_labels'][$subject['id_pm']]) == count($context['labels']), + 'is_replied_to' => &$context['message_replied'][$subject['id_pm']], + 'is_unread' => &$context['message_unread'][$subject['id_pm']], + 'is_selected' => !empty($temp_pm_selected) && in_array($subject['id_pm'], $temp_pm_selected), + ); + + return $output; + } + + // Bail if it's false, ie. no messages. + if ($messages_request == false) + return false; + + // Reset the data? + if ($reset == true) + return @$smcFunc['db_data_seek']($messages_request, 0); + + // Get the next one... bail if anything goes wrong. + $message = $smcFunc['db_fetch_assoc']($messages_request); + if (!$message) + { + if ($type != 'subject') + $smcFunc['db_free_result']($messages_request); + + return false; + } + + // Use '(no subject)' if none was specified. + $message['subject'] = $message['subject'] == '' ? $txt['no_subject'] : $message['subject']; + + // Load the message's information - if it's not there, load the guest information. + if (!loadMemberContext($message['id_member_from'], true)) + { + $memberContext[$message['id_member_from']]['name'] = $message['from_name']; + $memberContext[$message['id_member_from']]['id'] = 0; + // Sometimes the forum sends messages itself (Warnings are an example) - in this case don't label it from a guest. + $memberContext[$message['id_member_from']]['group'] = $message['from_name'] == $context['forum_name'] ? '' : $txt['guest_title']; + $memberContext[$message['id_member_from']]['link'] = $message['from_name']; + $memberContext[$message['id_member_from']]['email'] = ''; + $memberContext[$message['id_member_from']]['show_email'] = showEmailAddress(true, 0); + $memberContext[$message['id_member_from']]['is_guest'] = true; + } + else + { + $memberContext[$message['id_member_from']]['can_view_profile'] = allowedTo('profile_view_any') || ($message['id_member_from'] == $user_info['id'] && allowedTo('profile_view_own')); + $memberContext[$message['id_member_from']]['can_see_warning'] = !isset($context['disabled_fields']['warning_status']) && $memberContext[$message['id_member_from']]['warning_status'] && ($context['user']['can_mod'] || (!empty($modSettings['warning_show']) && ($modSettings['warning_show'] > 1 || $message['id_member_from'] == $user_info['id']))); + } + + // Censor all the important text... + censorText($message['body']); + censorText($message['subject']); + + // Run UBBC interpreter on the message. + $message['body'] = parse_bbc($message['body'], true, 'pm' . $message['id_pm']); + + // Send the array. + $output = array( + 'alternate' => $counter % 2, + 'id' => $message['id_pm'], + 'member' => &$memberContext[$message['id_member_from']], + 'subject' => $message['subject'], + 'time' => timeformat($message['msgtime']), + 'timestamp' => forum_time(true, $message['msgtime']), + 'counter' => $counter, + 'body' => $message['body'], + 'recipients' => &$recipients[$message['id_pm']], + 'number_recipients' => count($recipients[$message['id_pm']]['to']), + 'labels' => &$context['message_labels'][$message['id_pm']], + 'fully_labeled' => count($context['message_labels'][$message['id_pm']]) == count($context['labels']), + 'is_replied_to' => &$context['message_replied'][$message['id_pm']], + 'is_unread' => &$context['message_unread'][$message['id_pm']], + 'is_selected' => !empty($temp_pm_selected) && in_array($message['id_pm'], $temp_pm_selected), + ); + + $counter++; + + return $output; +} + +function MessageSearch() +{ + global $context, $txt, $scripturl, $modSettings, $smcFunc; + + if (isset($_REQUEST['params'])) + { + $temp_params = explode('|"|', base64_decode(strtr($_REQUEST['params'], array(' ' => '+')))); + $context['search_params'] = array(); + foreach ($temp_params as $i => $data) + { + @list ($k, $v) = explode('|\'|', $data); + $context['search_params'][$k] = $v; + } + } + if (isset($_REQUEST['search'])) + $context['search_params']['search'] = un_htmlspecialchars($_REQUEST['search']); + + if (isset($context['search_params']['search'])) + $context['search_params']['search'] = htmlspecialchars($context['search_params']['search']); + if (isset($context['search_params']['userspec'])) + $context['search_params']['userspec'] = htmlspecialchars($context['search_params']['userspec']); + + if (!empty($context['search_params']['searchtype'])) + $context['search_params']['searchtype'] = 2; + + if (!empty($context['search_params']['minage'])) + $context['search_params']['minage'] = (int) $context['search_params']['minage']; + + if (!empty($context['search_params']['maxage'])) + $context['search_params']['maxage'] = (int) $context['search_params']['maxage']; + + $context['search_params']['subject_only'] = !empty($context['search_params']['subject_only']); + $context['search_params']['show_complete'] = !empty($context['search_params']['show_complete']); + + // Create the array of labels to be searched. + $context['search_labels'] = array(); + $searchedLabels = isset($context['search_params']['labels']) && $context['search_params']['labels'] != '' ? explode(',', $context['search_params']['labels']) : array(); + foreach ($context['labels'] as $label) + { + $context['search_labels'][] = array( + 'id' => $label['id'], + 'name' => $label['name'], + 'checked' => !empty($searchedLabels) ? in_array($label['id'], $searchedLabels) : true, + ); + } + + // Are all the labels checked? + $context['check_all'] = empty($searchedLabels) || count($context['search_labels']) == count($searchedLabels); + + // Load the error text strings if there were errors in the search. + if (!empty($context['search_errors'])) + { + loadLanguage('Errors'); + $context['search_errors']['messages'] = array(); + foreach ($context['search_errors'] as $search_error => $dummy) + { + if ($search_error == 'messages') + continue; + + $context['search_errors']['messages'][] = $txt['error_' . $search_error]; + } + } + + $context['simple_search'] = isset($context['search_params']['advanced']) ? empty($context['search_params']['advanced']) : !empty($modSettings['simpleSearch']) && !isset($_REQUEST['advanced']); + $context['page_title'] = $txt['pm_search_title']; + $context['sub_template'] = 'search'; + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;sa=search', + 'name' => $txt['pm_search_bar_title'], + ); +} + +function MessageSearch2() +{ + global $scripturl, $modSettings, $user_info, $context, $txt; + global $memberContext, $smcFunc; + + if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search']) + fatal_lang_error('loadavg_search_disabled', false); + + // !!! For the moment force the folder to the inbox. + $context['folder'] = 'inbox'; + + // Some useful general permissions. + $context['can_send_pm'] = allowedTo('pm_send'); + + // Some hardcoded veriables that can be tweaked if required. + $maxMembersToSearch = 500; + + // Extract all the search parameters. + $search_params = array(); + if (isset($_REQUEST['params'])) + { + $temp_params = explode('|"|', base64_decode(strtr($_REQUEST['params'], array(' ' => '+')))); + foreach ($temp_params as $i => $data) + { + @list ($k, $v) = explode('|\'|', $data); + $search_params[$k] = $v; + } + } + + $context['start'] = isset($_GET['start']) ? (int) $_GET['start'] : 0; + + // Store whether simple search was used (needed if the user wants to do another query). + if (!isset($search_params['advanced'])) + $search_params['advanced'] = empty($_REQUEST['advanced']) ? 0 : 1; + + // 1 => 'allwords' (default, don't set as param) / 2 => 'anywords'. + if (!empty($search_params['searchtype']) || (!empty($_REQUEST['searchtype']) && $_REQUEST['searchtype'] == 2)) + $search_params['searchtype'] = 2; + + // Minimum age of messages. Default to zero (don't set param in that case). + if (!empty($search_params['minage']) || (!empty($_REQUEST['minage']) && $_REQUEST['minage'] > 0)) + $search_params['minage'] = !empty($search_params['minage']) ? (int) $search_params['minage'] : (int) $_REQUEST['minage']; + + // Maximum age of messages. Default to infinite (9999 days: param not set). + if (!empty($search_params['maxage']) || (!empty($_REQUEST['maxage']) && $_REQUEST['maxage'] != 9999)) + $search_params['maxage'] = !empty($search_params['maxage']) ? (int) $search_params['maxage'] : (int) $_REQUEST['maxage']; + + $search_params['subject_only'] = !empty($search_params['subject_only']) || !empty($_REQUEST['subject_only']); + $search_params['show_complete'] = !empty($search_params['show_complete']) || !empty($_REQUEST['show_complete']); + + // Default the user name to a wildcard matching every user (*). + if (!empty($search_params['user_spec']) || (!empty($_REQUEST['userspec']) && $_REQUEST['userspec'] != '*')) + $search_params['userspec'] = isset($search_params['userspec']) ? $search_params['userspec'] : $_REQUEST['userspec']; + + // This will be full of all kinds of parameters! + $searchq_parameters = array(); + + // If there's no specific user, then don't mention it in the main query. + if (empty($search_params['userspec'])) + $userQuery = ''; + else + { + $userString = strtr($smcFunc['htmlspecialchars']($search_params['userspec'], ENT_QUOTES), array('"' => '"')); + $userString = strtr($userString, array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_')); + + preg_match_all('~"([^"]+)"~', $userString, $matches); + $possible_users = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $userString))); + + for ($k = 0, $n = count($possible_users); $k < $n; $k++) + { + $possible_users[$k] = trim($possible_users[$k]); + + if (strlen($possible_users[$k]) == 0) + unset($possible_users[$k]); + } + + // Who matches those criteria? + // !!! This doesn't support sent item searching. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE real_name LIKE {raw:real_name_implode}', + array( + 'real_name_implode' => '\'' . implode('\' OR real_name LIKE \'', $possible_users) . '\'', + ) + ); + // Simply do nothing if there're too many members matching the criteria. + if ($smcFunc['db_num_rows']($request) > $maxMembersToSearch) + $userQuery = ''; + elseif ($smcFunc['db_num_rows']($request) == 0) + { + $userQuery = 'AND pm.id_member_from = 0 AND (pm.from_name LIKE {raw:guest_user_name_implode})'; + $searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR pm.from_name LIKE \'', $possible_users) . '\''; + } + else + { + $memberlist = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $memberlist[] = $row['id_member']; + $userQuery = 'AND (pm.id_member_from IN ({array_int:member_list}) OR (pm.id_member_from = 0 AND (pm.from_name LIKE {raw:guest_user_name_implode})))'; + $searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR pm.from_name LIKE \'', $possible_users) . '\''; + $searchq_parameters['member_list'] = $memberlist; + } + $smcFunc['db_free_result']($request); + } + + // Setup the sorting variables... + // !!! Add more in here! + $sort_columns = array( + 'pm.id_pm', + ); + if (empty($search_params['sort']) && !empty($_REQUEST['sort'])) + list ($search_params['sort'], $search_params['sort_dir']) = array_pad(explode('|', $_REQUEST['sort']), 2, ''); + $search_params['sort'] = !empty($search_params['sort']) && in_array($search_params['sort'], $sort_columns) ? $search_params['sort'] : 'pm.id_pm'; + $search_params['sort_dir'] = !empty($search_params['sort_dir']) && $search_params['sort_dir'] == 'asc' ? 'asc' : 'desc'; + + // Sort out any labels we may be searching by. + $labelQuery = ''; + if ($context['folder'] == 'inbox' && !empty($search_params['advanced']) && $context['currently_using_labels']) + { + // Came here from pagination? Put them back into $_REQUEST for sanitization. + if (isset($search_params['labels'])) + $_REQUEST['searchlabel'] = explode(',', $search_params['labels']); + + // Assuming we have some labels - make them all integers. + if (!empty($_REQUEST['searchlabel']) && is_array($_REQUEST['searchlabel'])) + { + foreach ($_REQUEST['searchlabel'] as $key => $id) + $_REQUEST['searchlabel'][$key] = (int) $id; + } + else + $_REQUEST['searchlabel'] = array(); + + // Now that everything is cleaned up a bit, make the labels a param. + $search_params['labels'] = implode(',', $_REQUEST['searchlabel']); + + // No labels selected? That must be an error! + if (empty($_REQUEST['searchlabel'])) + $context['search_errors']['no_labels_selected'] = true; + // Otherwise prepare the query! + elseif (count($_REQUEST['searchlabel']) != count($context['labels'])) + { + $labelQuery = ' + AND {raw:label_implode}'; + + $labelStatements = array(); + foreach ($_REQUEST['searchlabel'] as $label) + $labelStatements[] = $smcFunc['db_quote']('FIND_IN_SET({string:label}, pmr.labels) != 0', array( + 'label' => $label, + )); + + $searchq_parameters['label_implode'] = '(' . implode(' OR ', $labelStatements) . ')'; + } + } + + // What are we actually searching for? + $search_params['search'] = !empty($search_params['search']) ? $search_params['search'] : (isset($_REQUEST['search']) ? $_REQUEST['search'] : ''); + // If we ain't got nothing - we should error! + if (!isset($search_params['search']) || $search_params['search'] == '') + $context['search_errors']['invalid_search_string'] = true; + + // Extract phrase parts first (e.g. some words "this is a phrase" some more words.) + preg_match_all('~(?:^|\s)([-]?)"([^"]+)"(?:$|\s)~' . ($context['utf8'] ? 'u' : ''), $search_params['search'], $matches, PREG_PATTERN_ORDER); + $searchArray = $matches[2]; + + // Remove the phrase parts and extract the words. + $tempSearch = explode(' ', preg_replace('~(?:^|\s)(?:[-]?)"(?:[^"]+)"(?:$|\s)~' . ($context['utf8'] ? 'u' : ''), ' ', $search_params['search'])); + + // A minus sign in front of a word excludes the word.... so... + $excludedWords = array(); + + // .. first, we check for things like -"some words", but not "-some words". + foreach ($matches[1] as $index => $word) + if ($word == '-') + { + $word = $smcFunc['strtolower'](trim($searchArray[$index])); + if (strlen($word) > 0) + $excludedWords[] = $word; + unset($searchArray[$index]); + } + + // Now we look for -test, etc.... normaller. + foreach ($tempSearch as $index => $word) + if (strpos(trim($word), '-') === 0) + { + $word = substr($smcFunc['strtolower'](trim($word)), 1); + if (strlen($word) > 0) + $excludedWords[] = $word; + unset($tempSearch[$index]); + } + + $searchArray = array_merge($searchArray, $tempSearch); + + // Trim everything and make sure there are no words that are the same. + foreach ($searchArray as $index => $value) + { + $searchArray[$index] = $smcFunc['strtolower'](trim($value)); + if ($searchArray[$index] == '') + unset($searchArray[$index]); + else + { + // Sort out entities first. + $searchArray[$index] = $smcFunc['htmlspecialchars']($searchArray[$index]); + } + } + $searchArray = array_unique($searchArray); + + // Create an array of replacements for highlighting. + $context['mark'] = array(); + foreach ($searchArray as $word) + $context['mark'][$word] = '' . $word . ''; + + // This contains *everything* + $searchWords = array_merge($searchArray, $excludedWords); + + // Make sure at least one word is being searched for. + if (empty($searchArray)) + $context['search_errors']['invalid_search_string'] = true; + + // Sort out the search query so the user can edit it - if they want. + $context['search_params'] = $search_params; + if (isset($context['search_params']['search'])) + $context['search_params']['search'] = htmlspecialchars($context['search_params']['search']); + if (isset($context['search_params']['userspec'])) + $context['search_params']['userspec'] = htmlspecialchars($context['search_params']['userspec']); + + // Now we have all the parameters, combine them together for pagination and the like... + $context['params'] = array(); + foreach ($search_params as $k => $v) + $context['params'][] = $k . '|\'|' . $v; + $context['params'] = base64_encode(implode('|"|', $context['params'])); + + // Compile the subject query part. + $andQueryParts = array(); + + foreach ($searchWords as $index => $word) + { + if ($word == '') + continue; + + if ($search_params['subject_only']) + $andQueryParts[] = 'pm.subject' . (in_array($word, $excludedWords) ? ' NOT' : '') . ' LIKE {string:search_' . $index . '}'; + else + $andQueryParts[] = '(pm.subject' . (in_array($word, $excludedWords) ? ' NOT' : '') . ' LIKE {string:search_' . $index . '} ' . (in_array($word, $excludedWords) ? 'AND pm.body NOT' : 'OR pm.body') . ' LIKE {string:search_' . $index . '})'; + $searchq_parameters['search_' . $index] = '%' . strtr($word, array('_' => '\\_', '%' => '\\%')) . '%'; + } + + $searchQuery = ' 1=1'; + if (!empty($andQueryParts)) + $searchQuery = implode(!empty($search_params['searchtype']) && $search_params['searchtype'] == 2 ? ' OR ' : ' AND ', $andQueryParts); + + // Age limits? + $timeQuery = ''; + if (!empty($search_params['minage'])) + $timeQuery .= ' AND pm.msgtime < ' . (time() - $search_params['minage'] * 86400); + if (!empty($search_params['maxage'])) + $timeQuery .= ' AND pm.msgtime > ' . (time() - $search_params['maxage'] * 86400); + + // If we have errors - return back to the first screen... + if (!empty($context['search_errors'])) + { + $_REQUEST['params'] = $context['params']; + return MessageSearch(); + } + + // Get the amount of results. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}pm_recipients AS pmr + INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm) + WHERE ' . ($context['folder'] == 'inbox' ? ' + pmr.id_member = {int:current_member} + AND pmr.deleted = {int:not_deleted}' : ' + pm.id_member_from = {int:current_member} + AND pm.deleted_by_sender = {int:not_deleted}') . ' + ' . $userQuery . $labelQuery . $timeQuery . ' + AND (' . $searchQuery . ')', + array_merge($searchq_parameters, array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + )) + ); + list ($numResults) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Get all the matching messages... using standard search only (No caching and the like!) + // !!! This doesn't support sent item searching yet. + $request = $smcFunc['db_query']('', ' + SELECT pm.id_pm, pm.id_pm_head, pm.id_member_from + FROM {db_prefix}pm_recipients AS pmr + INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm) + WHERE ' . ($context['folder'] == 'inbox' ? ' + pmr.id_member = {int:current_member} + AND pmr.deleted = {int:not_deleted}' : ' + pm.id_member_from = {int:current_member} + AND pm.deleted_by_sender = {int:not_deleted}') . ' + ' . $userQuery . $labelQuery . $timeQuery . ' + AND (' . $searchQuery . ') + ORDER BY ' . $search_params['sort'] . ' ' . $search_params['sort_dir'] . ' + LIMIT ' . $context['start'] . ', ' . $modSettings['search_results_per_page'], + array_merge($searchq_parameters, array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + )) + ); + $foundMessages = array(); + $posters = array(); + $head_pms = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $foundMessages[] = $row['id_pm']; + $posters[] = $row['id_member_from']; + $head_pms[$row['id_pm']] = $row['id_pm_head']; + } + $smcFunc['db_free_result']($request); + + // Find the real head pms! + if ($context['display_mode'] == 2 && !empty($head_pms)) + { + $request = $smcFunc['db_query']('', ' + SELECT MAX(pm.id_pm) AS id_pm, pm.id_pm_head + FROM {db_prefix}personal_messages AS pm + INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm) + WHERE pm.id_pm_head IN ({array_int:head_pms}) + AND pmr.id_member = {int:current_member} + AND pmr.deleted = {int:not_deleted} + GROUP BY pm.id_pm_head + LIMIT {int:limit}', + array( + 'head_pms' => array_unique($head_pms), + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + 'limit' => count($head_pms), + ) + ); + $real_pm_ids = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $real_pm_ids[$row['id_pm_head']] = $row['id_pm']; + $smcFunc['db_free_result']($request); + } + + // Load the users... + $posters = array_unique($posters); + if (!empty($posters)) + loadMemberData($posters); + + // Sort out the page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=pm;sa=search2;params=' . $context['params'], $_GET['start'], $numResults, $modSettings['search_results_per_page'], false); + + $context['message_labels'] = array(); + $context['message_replied'] = array(); + $context['personal_messages'] = array(); + + if (!empty($foundMessages)) + { + // Now get recipients (but don't include bcc-recipients for your inbox, you're not supposed to know :P!) + $request = $smcFunc['db_query']('', ' + SELECT + pmr.id_pm, mem_to.id_member AS id_member_to, mem_to.real_name AS to_name, + pmr.bcc, pmr.labels, pmr.is_read + FROM {db_prefix}pm_recipients AS pmr + LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member) + WHERE pmr.id_pm IN ({array_int:message_list})', + array( + 'message_list' => $foundMessages, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($context['folder'] == 'sent' || empty($row['bcc'])) + $recipients[$row['id_pm']][empty($row['bcc']) ? 'to' : 'bcc'][] = empty($row['id_member_to']) ? $txt['guest_title'] : '' . $row['to_name'] . ''; + + if ($row['id_member_to'] == $user_info['id'] && $context['folder'] != 'sent') + { + $context['message_replied'][$row['id_pm']] = $row['is_read'] & 2; + + $row['labels'] = $row['labels'] == '' ? array() : explode(',', $row['labels']); + // This is a special need for linking to messages. + foreach ($row['labels'] as $v) + { + if (isset($context['labels'][(int) $v])) + $context['message_labels'][$row['id_pm']][(int) $v] = array('id' => $v, 'name' => $context['labels'][(int) $v]['name']); + + // Here we find the first label on a message - for linking to posts in results + if (!isset($context['first_label'][$row['id_pm']]) && !in_array('-1', $row['labels'])) + $context['first_label'][$row['id_pm']] = (int) $v; + } + } + } + + // Prepare the query for the callback! + $request = $smcFunc['db_query']('', ' + SELECT pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name + FROM {db_prefix}personal_messages AS pm + WHERE pm.id_pm IN ({array_int:message_list}) + ORDER BY ' . $search_params['sort'] . ' ' . $search_params['sort_dir'] . ' + LIMIT ' . count($foundMessages), + array( + 'message_list' => $foundMessages, + ) + ); + $counter = 0; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If there's no message subject, use the default. + $row['subject'] = $row['subject'] == '' ? $txt['no_subject'] : $row['subject']; + + // Load this posters context info, if it ain't there then fill in the essentials... + if (!loadMemberContext($row['id_member_from'], true)) + { + $memberContext[$row['id_member_from']]['name'] = $row['from_name']; + $memberContext[$row['id_member_from']]['id'] = 0; + $memberContext[$row['id_member_from']]['group'] = $txt['guest_title']; + $memberContext[$row['id_member_from']]['link'] = $row['from_name']; + $memberContext[$row['id_member_from']]['email'] = ''; + $memberContext[$row['id_member_from']]['show_email'] = showEmailAddress(true, 0); + $memberContext[$row['id_member_from']]['is_guest'] = true; + } + + // Censor anything we don't want to see... + censorText($row['body']); + censorText($row['subject']); + + // Parse out any BBC... + $row['body'] = parse_bbc($row['body'], true, 'pm' . $row['id_pm']); + + $href = $scripturl . '?action=pm;f=' . $context['folder'] . (isset($context['first_label'][$row['id_pm']]) ? ';l=' . $context['first_label'][$row['id_pm']] : '') . ';pmid=' . ($context['display_mode'] == 2 && isset($real_pm_ids[$head_pms[$row['id_pm']]]) ? $real_pm_ids[$head_pms[$row['id_pm']]] : $row['id_pm']) . '#msg' . $row['id_pm']; + $context['personal_messages'][] = array( + 'id' => $row['id_pm'], + 'member' => &$memberContext[$row['id_member_from']], + 'subject' => $row['subject'], + 'body' => $row['body'], + 'time' => timeformat($row['msgtime']), + 'recipients' => &$recipients[$row['id_pm']], + 'labels' => &$context['message_labels'][$row['id_pm']], + 'fully_labeled' => count($context['message_labels'][$row['id_pm']]) == count($context['labels']), + 'is_replied_to' => &$context['message_replied'][$row['id_pm']], + 'href' => $href, + 'link' => '' . $row['subject'] . '', + 'counter' => ++$counter, + ); + } + $smcFunc['db_free_result']($request); + } + + // Finish off the context. + $context['page_title'] = $txt['pm_search_title']; + $context['sub_template'] = 'search_results'; + $context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'search'; + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;sa=search', + 'name' => $txt['pm_search_bar_title'], + ); +} + +// Send a new message? +function MessagePost() +{ + global $txt, $sourcedir, $scripturl, $modSettings; + global $context, $options, $smcFunc, $language, $user_info; + + isAllowedTo('pm_send'); + + loadLanguage('PersonalMessage'); + // Just in case it was loaded from somewhere else. + if (!WIRELESS) + { + loadTemplate('PersonalMessage'); + $context['sub_template'] = 'send'; + } + + // Extract out the spam settings - cause it's neat. + list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']); + + // Set the title... + $context['page_title'] = $txt['send_message']; + + $context['reply'] = isset($_REQUEST['pmsg']) || isset($_REQUEST['quote']); + + // Check whether we've gone over the limit of messages we can send per hour. + if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1') + { + // How many messages have they sent this last hour? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(pr.id_pm) AS post_count + FROM {db_prefix}personal_messages AS pm + INNER JOIN {db_prefix}pm_recipients AS pr ON (pr.id_pm = pm.id_pm) + WHERE pm.id_member_from = {int:current_member} + AND pm.msgtime > {int:msgtime}', + array( + 'current_member' => $user_info['id'], + 'msgtime' => time() - 3600, + ) + ); + list ($postCount) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour']) + fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour'])); + } + + // Quoting/Replying to a message? + if (!empty($_REQUEST['pmsg'])) + { + $pmsg = (int) $_REQUEST['pmsg']; + + // Make sure this is yours. + if (!isAccessiblePM($pmsg)) + fatal_lang_error('no_access', false); + + // Work out whether this is one you've received? + $request = $smcFunc['db_query']('', ' + SELECT + id_pm + FROM {db_prefix}pm_recipients + WHERE id_pm = {int:id_pm} + AND id_member = {int:current_member} + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'id_pm' => $pmsg, + ) + ); + $isReceived = $smcFunc['db_num_rows']($request) != 0; + $smcFunc['db_free_result']($request); + + // Get the quoted message (and make sure you're allowed to see this quote!). + $request = $smcFunc['db_query']('', ' + SELECT + pm.id_pm, CASE WHEN pm.id_pm_head = {int:id_pm_head_empty} THEN pm.id_pm ELSE pm.id_pm_head END AS pm_head, + pm.body, pm.subject, pm.msgtime, mem.member_name, IFNULL(mem.id_member, 0) AS id_member, + IFNULL(mem.real_name, pm.from_name) AS real_name + FROM {db_prefix}personal_messages AS pm' . (!$isReceived ? '' : ' + INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = {int:id_pm})') . ' + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from) + WHERE pm.id_pm = {int:id_pm}' . (!$isReceived ? ' + AND pm.id_member_from = {int:current_member}' : ' + AND pmr.id_member = {int:current_member}') . ' + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'id_pm_head_empty' => 0, + 'id_pm' => $pmsg, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('pm_not_yours', false); + $row_quoted = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Censor the message. + censorText($row_quoted['subject']); + censorText($row_quoted['body']); + + // Add 'Re: ' to it.... + if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix'))) + { + if ($language === $user_info['language']) + $context['response_prefix'] = $txt['response_prefix']; + else + { + loadLanguage('index', $language, false); + $context['response_prefix'] = $txt['response_prefix']; + loadLanguage('index'); + } + cache_put_data('response_prefix', $context['response_prefix'], 600); + } + $form_subject = $row_quoted['subject']; + if ($context['reply'] && trim($context['response_prefix']) != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0) + $form_subject = $context['response_prefix'] . $form_subject; + + if (isset($_REQUEST['quote'])) + { + // Remove any nested quotes and
... + $form_message = preg_replace('~
~i', "\n", $row_quoted['body']); + if (!empty($modSettings['removeNestedQuotes'])) + $form_message = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $form_message); + if (empty($row_quoted['id_member'])) + $form_message = '[quote author="' . $row_quoted['real_name'] . '"]' . "\n" . $form_message . "\n" . '[/quote]'; + else + $form_message = '[quote author=' . $row_quoted['real_name'] . ' link=action=profile;u=' . $row_quoted['id_member'] . ' date=' . $row_quoted['msgtime'] . ']' . "\n" . $form_message . "\n" . '[/quote]'; + } + else + $form_message = ''; + + // Do the BBC thang on the message. + $row_quoted['body'] = parse_bbc($row_quoted['body'], true, 'pm' . $row_quoted['id_pm']); + + // Set up the quoted message array. + $context['quoted_message'] = array( + 'id' => $row_quoted['id_pm'], + 'pm_head' => $row_quoted['pm_head'], + 'member' => array( + 'name' => $row_quoted['real_name'], + 'username' => $row_quoted['member_name'], + 'id' => $row_quoted['id_member'], + 'href' => !empty($row_quoted['id_member']) ? $scripturl . '?action=profile;u=' . $row_quoted['id_member'] : '', + 'link' => !empty($row_quoted['id_member']) ? '' . $row_quoted['real_name'] . '' : $row_quoted['real_name'], + ), + 'subject' => $row_quoted['subject'], + 'time' => timeformat($row_quoted['msgtime']), + 'timestamp' => forum_time(true, $row_quoted['msgtime']), + 'body' => $row_quoted['body'] + ); + } + else + { + $context['quoted_message'] = false; + $form_subject = ''; + $form_message = ''; + } + + $context['recipients'] = array( + 'to' => array(), + 'bcc' => array(), + ); + + // Sending by ID? Replying to all? Fetch the real_name(s). + if (isset($_REQUEST['u'])) + { + // If the user is replying to all, get all the other members this was sent to.. + if ($_REQUEST['u'] == 'all' && isset($row_quoted)) + { + // Firstly, to reply to all we clearly already have $row_quoted - so have the original member from. + if ($row_quoted['id_member'] != $user_info['id']) + $context['recipients']['to'][] = array( + 'id' => $row_quoted['id_member'], + 'name' => htmlspecialchars($row_quoted['real_name']), + ); + + // Now to get the others. + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member, mem.real_name + FROM {db_prefix}pm_recipients AS pmr + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = pmr.id_member) + WHERE pmr.id_pm = {int:id_pm} + AND pmr.id_member != {int:current_member} + AND pmr.bcc = {int:not_bcc}', + array( + 'current_member' => $user_info['id'], + 'id_pm' => $pmsg, + 'not_bcc' => 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['recipients']['to'][] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + ); + $smcFunc['db_free_result']($request); + } + else + { + $_REQUEST['u'] = explode(',', $_REQUEST['u']); + foreach ($_REQUEST['u'] as $key => $uID) + $_REQUEST['u'][$key] = (int) $uID; + + $_REQUEST['u'] = array_unique($_REQUEST['u']); + + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE id_member IN ({array_int:member_list}) + LIMIT ' . count($_REQUEST['u']), + array( + 'member_list' => $_REQUEST['u'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['recipients']['to'][] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + ); + $smcFunc['db_free_result']($request); + } + + // Get a literal name list in case the user has JavaScript disabled. + $names = array(); + foreach ($context['recipients']['to'] as $to) + $names[] = $to['name']; + $context['to_value'] = empty($names) ? '' : '"' . implode('", "', $names) . '"'; + } + else + $context['to_value'] = ''; + + // Set the defaults... + $context['subject'] = $form_subject != '' ? $form_subject : $txt['no_subject']; + $context['message'] = str_replace(array('"', '<', '>', ' '), array('"', '<', '>', ' '), $form_message); + $context['post_error'] = array(); + $context['copy_to_outbox'] = !empty($options['copy_to_outbox']); + + // And build the link tree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;sa=send', + 'name' => $txt['new_message'] + ); + + $modSettings['disable_wysiwyg'] = !empty($modSettings['disable_wysiwyg']) || empty($modSettings['enableBBC']); + + // Needed for the WYSIWYG editor. + require_once($sourcedir . '/Subs-Editor.php'); + + // Now create the editor. + $editorOptions = array( + 'id' => 'message', + 'value' => $context['message'], + 'height' => '175px', + 'width' => '100%', + 'labels' => array( + 'post_button' => $txt['send_message'], + ), + ); + create_control_richedit($editorOptions); + + // Store the ID for old compatibility. + $context['post_box_name'] = $editorOptions['id']; + + $context['bcc_value'] = ''; + + $context['require_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification']; + if ($context['require_verification']) + { + $verificationOptions = array( + 'id' => 'pm', + ); + $context['require_verification'] = create_control_verification($verificationOptions); + $context['visual_verification_id'] = $verificationOptions['id']; + } + + // Register this form and get a sequence number in $context. + checkSubmitOnce('register'); +} + +// An error in the message... +function messagePostError($error_types, $named_recipients, $recipient_ids = array()) +{ + global $txt, $context, $scripturl, $modSettings; + global $smcFunc, $user_info, $sourcedir; + + $context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'send'; + + if (!WIRELESS) + $context['sub_template'] = 'send'; + + $context['page_title'] = $txt['send_message']; + + // Got some known members? + $context['recipients'] = array( + 'to' => array(), + 'bcc' => array(), + ); + if (!empty($recipient_ids['to']) || !empty($recipient_ids['bcc'])) + { + $allRecipients = array_merge($recipient_ids['to'], $recipient_ids['bcc']); + + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE id_member IN ({array_int:member_list})', + array( + 'member_list' => $allRecipients, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $recipientType = in_array($row['id_member'], $recipient_ids['bcc']) ? 'bcc' : 'to'; + $context['recipients'][$recipientType][] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + ); + } + $smcFunc['db_free_result']($request); + } + + // Set everything up like before.... + $context['subject'] = isset($_REQUEST['subject']) ? $smcFunc['htmlspecialchars']($_REQUEST['subject']) : ''; + $context['message'] = isset($_REQUEST['message']) ? str_replace(array(' '), array('  '), $smcFunc['htmlspecialchars']($_REQUEST['message'])) : ''; + $context['copy_to_outbox'] = !empty($_REQUEST['outbox']); + $context['reply'] = !empty($_REQUEST['replied_to']); + + if ($context['reply']) + { + $_REQUEST['replied_to'] = (int) $_REQUEST['replied_to']; + + $request = $smcFunc['db_query']('', ' + SELECT + pm.id_pm, CASE WHEN pm.id_pm_head = {int:no_id_pm_head} THEN pm.id_pm ELSE pm.id_pm_head END AS pm_head, + pm.body, pm.subject, pm.msgtime, mem.member_name, IFNULL(mem.id_member, 0) AS id_member, + IFNULL(mem.real_name, pm.from_name) AS real_name + FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? '' : ' + INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = {int:replied_to})') . ' + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from) + WHERE pm.id_pm = {int:replied_to}' . ($context['folder'] == 'sent' ? ' + AND pm.id_member_from = {int:current_member}' : ' + AND pmr.id_member = {int:current_member}') . ' + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'no_id_pm_head' => 0, + 'replied_to' => $_REQUEST['replied_to'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('pm_not_yours', false); + $row_quoted = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + censorText($row_quoted['subject']); + censorText($row_quoted['body']); + + $context['quoted_message'] = array( + 'id' => $row_quoted['id_pm'], + 'pm_head' => $row_quoted['pm_head'], + 'member' => array( + 'name' => $row_quoted['real_name'], + 'username' => $row_quoted['member_name'], + 'id' => $row_quoted['id_member'], + 'href' => !empty($row_quoted['id_member']) ? $scripturl . '?action=profile;u=' . $row_quoted['id_member'] : '', + 'link' => !empty($row_quoted['id_member']) ? '' . $row_quoted['real_name'] . '' : $row_quoted['real_name'], + ), + 'subject' => $row_quoted['subject'], + 'time' => timeformat($row_quoted['msgtime']), + 'timestamp' => forum_time(true, $row_quoted['msgtime']), + 'body' => parse_bbc($row_quoted['body'], true, 'pm' . $row_quoted['id_pm']), + ); + } + + // Build the link tree.... + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;sa=send', + 'name' => $txt['new_message'] + ); + + // Set each of the errors for the template. + loadLanguage('Errors'); + $context['post_error'] = array( + 'messages' => array(), + ); + foreach ($error_types as $error_type) + { + $context['post_error'][$error_type] = true; + if (isset($txt['error_' . $error_type])) + { + if ($error_type == 'long_message') + $txt['error_' . $error_type] = sprintf($txt['error_' . $error_type], $modSettings['max_messageLength']); + $context['post_error']['messages'][] = $txt['error_' . $error_type]; + } + } + + // We need to load the editor once more. + require_once($sourcedir . '/Subs-Editor.php'); + + // Create it... + $editorOptions = array( + 'id' => 'message', + 'value' => $context['message'], + 'width' => '90%', + 'labels' => array( + 'post_button' => $txt['send_message'], + ), + ); + create_control_richedit($editorOptions); + + // ... and store the ID again... + $context['post_box_name'] = $editorOptions['id']; + + // Check whether we need to show the code again. + $context['require_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification']; + if ($context['require_verification']) + { + require_once($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'pm', + ); + $context['require_verification'] = create_control_verification($verificationOptions); + $context['visual_verification_id'] = $verificationOptions['id']; + } + + $context['to_value'] = empty($named_recipients['to']) ? '' : '"' . implode('", "', $named_recipients['to']) . '"'; + $context['bcc_value'] = empty($named_recipients['bcc']) ? '' : '"' . implode('", "', $named_recipients['bcc']) . '"'; + + // No check for the previous submission is needed. + checkSubmitOnce('free'); + + // Acquire a new form sequence number. + checkSubmitOnce('register'); +} + +// Send it! +function MessagePost2() +{ + global $txt, $context, $sourcedir; + global $user_info, $modSettings, $scripturl, $smcFunc; + + isAllowedTo('pm_send'); + require_once($sourcedir . '/Subs-Auth.php'); + + loadLanguage('PersonalMessage', '', false); + + // Extract out the spam settings - it saves database space! + list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']); + + // Check whether we've gone over the limit of messages we can send per hour - fatal error if fails! + if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1') + { + // How many have they sent this last hour? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(pr.id_pm) AS post_count + FROM {db_prefix}personal_messages AS pm + INNER JOIN {db_prefix}pm_recipients AS pr ON (pr.id_pm = pm.id_pm) + WHERE pm.id_member_from = {int:current_member} + AND pm.msgtime > {int:msgtime}', + array( + 'current_member' => $user_info['id'], + 'msgtime' => time() - 3600, + ) + ); + list ($postCount) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour']) + fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour'])); + } + + // If we came from WYSIWYG then turn it back into BBC regardless. + if (!empty($_POST['message_mode']) && isset($_POST['message'])) + { + require_once($sourcedir . '/Subs-Editor.php'); + $_POST['message'] = html_to_bbc($_POST['message']); + + // We need to unhtml it now as it gets done shortly. + $_POST['message'] = un_htmlspecialchars($_POST['message']); + + // We need this in case of errors etc. + $_REQUEST['message'] = $_POST['message']; + } + + // Initialize the errors we're about to make. + $post_errors = array(); + + // If your session timed out, show an error, but do allow to re-submit. + if (checkSession('post', '', false) != '') + $post_errors[] = 'session_timeout'; + + $_REQUEST['subject'] = isset($_REQUEST['subject']) ? trim($_REQUEST['subject']) : ''; + $_REQUEST['to'] = empty($_POST['to']) ? (empty($_GET['to']) ? '' : $_GET['to']) : $_POST['to']; + $_REQUEST['bcc'] = empty($_POST['bcc']) ? (empty($_GET['bcc']) ? '' : $_GET['bcc']) : $_POST['bcc']; + + // Route the input from the 'u' parameter to the 'to'-list. + if (!empty($_POST['u'])) + $_POST['recipient_to'] = explode(',', $_POST['u']); + + // Construct the list of recipients. + $recipientList = array(); + $namedRecipientList = array(); + $namesNotFound = array(); + foreach (array('to', 'bcc') as $recipientType) + { + // First, let's see if there's user ID's given. + $recipientList[$recipientType] = array(); + if (!empty($_POST['recipient_' . $recipientType]) && is_array($_POST['recipient_' . $recipientType])) + { + foreach ($_POST['recipient_' . $recipientType] as $recipient) + $recipientList[$recipientType][] = (int) $recipient; + } + + // Are there also literal names set? + if (!empty($_REQUEST[$recipientType])) + { + // We're going to take out the "s anyway ;). + $recipientString = strtr($_REQUEST[$recipientType], array('\\"' => '"')); + + preg_match_all('~"([^"]+)"~', $recipientString, $matches); + $namedRecipientList[$recipientType] = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $recipientString)))); + + foreach ($namedRecipientList[$recipientType] as $index => $recipient) + { + if (strlen(trim($recipient)) > 0) + $namedRecipientList[$recipientType][$index] = $smcFunc['htmlspecialchars']($smcFunc['strtolower'](trim($recipient))); + else + unset($namedRecipientList[$recipientType][$index]); + } + + if (!empty($namedRecipientList[$recipientType])) + { + $foundMembers = findMembers($namedRecipientList[$recipientType]); + + // Assume all are not found, until proven otherwise. + $namesNotFound[$recipientType] = $namedRecipientList[$recipientType]; + + foreach ($foundMembers as $member) + { + $testNames = array( + $smcFunc['strtolower']($member['username']), + $smcFunc['strtolower']($member['name']), + $smcFunc['strtolower']($member['email']), + ); + + if (count(array_intersect($testNames, $namedRecipientList[$recipientType])) !== 0) + { + $recipientList[$recipientType][] = $member['id']; + + // Get rid of this username, since we found it. + $namesNotFound[$recipientType] = array_diff($namesNotFound[$recipientType], $testNames); + } + } + } + } + + // Selected a recipient to be deleted? Remove them now. + if (!empty($_POST['delete_recipient'])) + $recipientList[$recipientType] = array_diff($recipientList[$recipientType], array((int) $_POST['delete_recipient'])); + + // Make sure we don't include the same name twice + $recipientList[$recipientType] = array_unique($recipientList[$recipientType]); + } + + // Are we changing the recipients some how? + $is_recipient_change = !empty($_POST['delete_recipient']) || !empty($_POST['to_submit']) || !empty($_POST['bcc_submit']); + + // Check if there's at least one recipient. + if (empty($recipientList['to']) && empty($recipientList['bcc'])) + $post_errors[] = 'no_to'; + + // Make sure that we remove the members who did get it from the screen. + if (!$is_recipient_change) + { + foreach ($recipientList as $recipientType => $dummy) + { + if (!empty($namesNotFound[$recipientType])) + { + $post_errors[] = 'bad_' . $recipientType; + + // Since we already have a post error, remove the previous one. + $post_errors = array_diff($post_errors, array('no_to')); + + foreach ($namesNotFound[$recipientType] as $name) + $context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name); + } + } + } + + // Did they make any mistakes? + if ($_REQUEST['subject'] == '') + $post_errors[] = 'no_subject'; + if (!isset($_REQUEST['message']) || $_REQUEST['message'] == '') + $post_errors[] = 'no_message'; + elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_REQUEST['message']) > $modSettings['max_messageLength']) + $post_errors[] = 'long_message'; + else + { + // Preparse the message. + $message = $_REQUEST['message']; + preparsecode($message); + + // Make sure there's still some content left without the tags. + if ($smcFunc['htmltrim'](strip_tags(parse_bbc($smcFunc['htmlspecialchars']($message, ENT_QUOTES), false), '')) === '' && (!allowedTo('admin_forum') || strpos($message, '[html]') === false)) + $post_errors[] = 'no_message'; + } + + // Wrong verification code? + if (!$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification']) + { + require_once($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'pm', + ); + $context['require_verification'] = create_control_verification($verificationOptions, true); + + if (is_array($context['require_verification'])) + { + $post_errors = array_merge($post_errors, $context['require_verification']); + } + } + + // If they did, give a chance to make ammends. + if (!empty($post_errors) && !$is_recipient_change && !isset($_REQUEST['preview'])) + return messagePostError($post_errors, $namedRecipientList, $recipientList); + + // Want to take a second glance before you send? + if (isset($_REQUEST['preview'])) + { + // Set everything up to be displayed. + $context['preview_subject'] = $smcFunc['htmlspecialchars']($_REQUEST['subject']); + $context['preview_message'] = $smcFunc['htmlspecialchars']($_REQUEST['message'], ENT_QUOTES); + preparsecode($context['preview_message'], true); + + // Parse out the BBC if it is enabled. + $context['preview_message'] = parse_bbc($context['preview_message']); + + // Censor, as always. + censorText($context['preview_subject']); + censorText($context['preview_message']); + + // Set a descriptive title. + $context['page_title'] = $txt['preview'] . ' - ' . $context['preview_subject']; + + // Pretend they messed up but don't ignore if they really did :P. + return messagePostError($post_errors, $namedRecipientList, $recipientList); + } + + // Adding a recipient cause javascript ain't working? + elseif ($is_recipient_change) + { + // Maybe we couldn't find one? + foreach ($namesNotFound as $recipientType => $names) + { + $post_errors[] = 'bad_' . $recipientType; + foreach ($names as $name) + $context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name); + } + + return messagePostError(array(), $namedRecipientList, $recipientList); + } + + // Before we send the PM, let's make sure we don't have an abuse of numbers. + elseif (!empty($modSettings['max_pm_recipients']) && count($recipientList['to']) + count($recipientList['bcc']) > $modSettings['max_pm_recipients'] && !allowedTo(array('moderate_forum', 'send_mail', 'admin_forum'))) + { + $context['send_log'] = array( + 'sent' => array(), + 'failed' => array(sprintf($txt['pm_too_many_recipients'], $modSettings['max_pm_recipients'])), + ); + return messagePostError($post_errors, $namedRecipientList, $recipientList); + } + + // Protect from message spamming. + spamProtection('pm'); + + // Prevent double submission of this form. + checkSubmitOnce('check'); + + // Do the actual sending of the PM. + if (!empty($recipientList['to']) || !empty($recipientList['bcc'])) + $context['send_log'] = sendpm($recipientList, $_REQUEST['subject'], $_REQUEST['message'], !empty($_REQUEST['outbox']), null, !empty($_REQUEST['pm_head']) ? (int) $_REQUEST['pm_head'] : 0); + else + $context['send_log'] = array( + 'sent' => array(), + 'failed' => array() + ); + + // Mark the message as "replied to". + if (!empty($context['send_log']['sent']) && !empty($_REQUEST['replied_to']) && isset($_REQUEST['f']) && $_REQUEST['f'] == 'inbox') + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}pm_recipients + SET is_read = is_read | 2 + WHERE id_pm = {int:replied_to} + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'replied_to' => (int) $_REQUEST['replied_to'], + ) + ); + } + + // If one or more of the recipient were invalid, go back to the post screen with the failed usernames. + if (!empty($context['send_log']['failed'])) + return messagePostError($post_errors, $namesNotFound, array( + 'to' => array_intersect($recipientList['to'], $context['send_log']['failed']), + 'bcc' => array_intersect($recipientList['bcc'], $context['send_log']['failed']) + )); + + // Message sent successfully? + if (!empty($context['send_log']) && empty($context['send_log']['failed'])) + $context['current_label_redirect'] = $context['current_label_redirect'] . ';done=sent'; + + // Go back to the where they sent from, if possible... + redirectexit($context['current_label_redirect']); +} + +// This function lists all buddies for wireless protocols. +function WirelessAddBuddy() +{ + global $scripturl, $txt, $user_info, $context, $smcFunc; + + isAllowedTo('pm_send'); + $context['page_title'] = $txt['wireless_pm_add_buddy']; + + $current_buddies = empty($_REQUEST['u']) ? array() : explode(',', $_REQUEST['u']); + foreach ($current_buddies as $key => $buddy) + $current_buddies[$key] = (int) $buddy; + + $base_url = $scripturl . '?action=pm;sa=send;u=' . (empty($current_buddies) ? '' : implode(',', $current_buddies) . ','); + $context['pm_href'] = $scripturl . '?action=pm;sa=send' . (empty($current_buddies) ? '' : ';u=' . implode(',', $current_buddies)); + + $context['buddies'] = array(); + if (!empty($user_info['buddies'])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE id_member IN ({array_int:buddy_list}) + ORDER BY real_name + LIMIT ' . count($user_info['buddies']), + array( + 'buddy_list' => $user_info['buddies'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['buddies'][] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + 'selected' => in_array($row['id_member'], $current_buddies), + 'add_href' => $base_url . $row['id_member'], + ); + $smcFunc['db_free_result']($request); + } +} + +// This function performs all additional stuff... +function MessageActionsApply() +{ + global $txt, $context, $user_info, $options, $smcFunc; + + checkSession('request'); + + if (isset($_REQUEST['del_selected'])) + $_REQUEST['pm_action'] = 'delete'; + + if (isset($_REQUEST['pm_action']) && $_REQUEST['pm_action'] != '' && !empty($_REQUEST['pms']) && is_array($_REQUEST['pms'])) + { + foreach ($_REQUEST['pms'] as $pm) + $_REQUEST['pm_actions'][(int) $pm] = $_REQUEST['pm_action']; + } + + if (empty($_REQUEST['pm_actions'])) + redirectexit($context['current_label_redirect']); + + // If we are in conversation, we may need to apply this to every message in the conversation. + if ($context['display_mode'] == 2 && isset($_REQUEST['conversation'])) + { + $id_pms = array(); + foreach ($_REQUEST['pm_actions'] as $pm => $dummy) + $id_pms[] = (int) $pm; + + $request = $smcFunc['db_query']('', ' + SELECT id_pm_head, id_pm + FROM {db_prefix}personal_messages + WHERE id_pm IN ({array_int:id_pms})', + array( + 'id_pms' => $id_pms, + ) + ); + $pm_heads = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $pm_heads[$row['id_pm_head']] = $row['id_pm']; + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT id_pm, id_pm_head + FROM {db_prefix}personal_messages + WHERE id_pm_head IN ({array_int:pm_heads})', + array( + 'pm_heads' => array_keys($pm_heads), + ) + ); + // Copy the action from the single to PM to the others. + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (isset($pm_heads[$row['id_pm_head']]) && isset($_REQUEST['pm_actions'][$pm_heads[$row['id_pm_head']]])) + $_REQUEST['pm_actions'][$row['id_pm']] = $_REQUEST['pm_actions'][$pm_heads[$row['id_pm_head']]]; + } + $smcFunc['db_free_result']($request); + } + + $to_delete = array(); + $to_label = array(); + $label_type = array(); + foreach ($_REQUEST['pm_actions'] as $pm => $action) + { + if ($action === 'delete') + $to_delete[] = (int) $pm; + else + { + if (substr($action, 0, 4) == 'add_') + { + $type = 'add'; + $action = substr($action, 4); + } + elseif (substr($action, 0, 4) == 'rem_') + { + $type = 'rem'; + $action = substr($action, 4); + } + else + $type = 'unk'; + + if ($action == '-1' || $action == '0' || (int) $action > 0) + { + $to_label[(int) $pm] = (int) $action; + $label_type[(int) $pm] = $type; + } + } + } + + // Deleting, it looks like? + if (!empty($to_delete)) + deleteMessages($to_delete, $context['display_mode'] == 2 ? null : $context['folder']); + + // Are we labeling anything? + if (!empty($to_label) && $context['folder'] == 'inbox') + { + $updateErrors = 0; + + // Get information about each message... + $request = $smcFunc['db_query']('', ' + SELECT id_pm, labels + FROM {db_prefix}pm_recipients + WHERE id_member = {int:current_member} + AND id_pm IN ({array_int:to_label}) + LIMIT ' . count($to_label), + array( + 'current_member' => $user_info['id'], + 'to_label' => array_keys($to_label), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $labels = $row['labels'] == '' ? array('-1') : explode(',', trim($row['labels'])); + + // Already exists? Then... unset it! + $ID_LABEL = array_search($to_label[$row['id_pm']], $labels); + if ($ID_LABEL !== false && $label_type[$row['id_pm']] !== 'add') + unset($labels[$ID_LABEL]); + elseif ($label_type[$row['id_pm']] !== 'rem') + $labels[] = $to_label[$row['id_pm']]; + + if (!empty($options['pm_remove_inbox_label']) && $to_label[$row['id_pm']] != '-1' && ($key = array_search('-1', $labels)) !== false) + unset($labels[$key]); + + $set = implode(',', array_unique($labels)); + if ($set == '') + $set = '-1'; + + // Check that this string isn't going to be too large for the database. + if ($set > 60) + $updateErrors++; + else + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}pm_recipients + SET labels = {string:labels} + WHERE id_pm = {int:id_pm} + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'id_pm' => $row['id_pm'], + 'labels' => $set, + ) + ); + } + } + $smcFunc['db_free_result']($request); + + // Any errors? + // !!! Separate the sprintf? + if (!empty($updateErrors)) + fatal_lang_error('labels_too_many', true, array($updateErrors)); + } + + // Back to the folder. + $_SESSION['pm_selected'] = array_keys($to_label); + redirectexit($context['current_label_redirect'] . (count($to_label) == 1 ? '#msg' . $_SESSION['pm_selected'][0] : ''), count($to_label) == 1 && $context['browser']['is_ie']); +} + +// Are you sure you want to PERMANENTLY (mostly) delete ALL your messages? +function MessageKillAllQuery() +{ + global $txt, $context; + + // Only have to set up the template.... + $context['sub_template'] = 'ask_delete'; + $context['page_title'] = $txt['delete_all']; + $context['delete_all'] = $_REQUEST['f'] == 'all'; + + // And set the folder name... + $txt['delete_all'] = str_replace('PMBOX', $context['folder'] != 'sent' ? $txt['inbox'] : $txt['sent_items'], $txt['delete_all']); +} + +// Delete ALL the messages! +function MessageKillAll() +{ + global $context; + + checkSession('get'); + + // If all then delete all messages the user has. + if ($_REQUEST['f'] == 'all') + deleteMessages(null, null); + // Otherwise just the selected folder. + else + deleteMessages(null, $_REQUEST['f'] != 'sent' ? 'inbox' : 'sent'); + + // Done... all gone. + redirectexit($context['current_label_redirect']); +} + +// This function allows the user to delete all messages older than so many days. +function MessagePrune() +{ + global $txt, $context, $user_info, $scripturl, $smcFunc; + + // Actually delete the messages. + if (isset($_REQUEST['age'])) + { + checkSession(); + + // Calculate the time to delete before. + $deleteTime = max(0, time() - (86400 * (int) $_REQUEST['age'])); + + // Array to store the IDs in. + $toDelete = array(); + + // Select all the messages they have sent older than $deleteTime. + $request = $smcFunc['db_query']('', ' + SELECT id_pm + FROM {db_prefix}personal_messages + WHERE deleted_by_sender = {int:not_deleted} + AND id_member_from = {int:current_member} + AND msgtime < {int:msgtime}', + array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + 'msgtime' => $deleteTime, + ) + ); + while ($row = $smcFunc['db_fetch_row']($request)) + $toDelete[] = $row[0]; + $smcFunc['db_free_result']($request); + + // Select all messages in their inbox older than $deleteTime. + $request = $smcFunc['db_query']('', ' + SELECT pmr.id_pm + FROM {db_prefix}pm_recipients AS pmr + INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm) + WHERE pmr.deleted = {int:not_deleted} + AND pmr.id_member = {int:current_member} + AND pm.msgtime < {int:msgtime}', + array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + 'msgtime' => $deleteTime, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $toDelete[] = $row['id_pm']; + $smcFunc['db_free_result']($request); + + // Delete the actual messages. + deleteMessages($toDelete); + + // Go back to their inbox. + redirectexit($context['current_label_redirect']); + } + + // Build the link tree elements. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;sa=prune', + 'name' => $txt['pm_prune'] + ); + + $context['sub_template'] = 'prune'; + $context['page_title'] = $txt['pm_prune']; +} + +// Delete the specified personal messages. +function deleteMessages($personal_messages, $folder = null, $owner = null) +{ + global $user_info, $smcFunc; + + if ($owner === null) + $owner = array($user_info['id']); + elseif (empty($owner)) + return; + elseif (!is_array($owner)) + $owner = array($owner); + + if ($personal_messages !== null) + { + if (empty($personal_messages) || !is_array($personal_messages)) + return; + + foreach ($personal_messages as $index => $delete_id) + $personal_messages[$index] = (int) $delete_id; + + $where = ' + AND id_pm IN ({array_int:pm_list})'; + } + else + $where = ''; + + if ($folder == 'sent' || $folder === null) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}personal_messages + SET deleted_by_sender = {int:is_deleted} + WHERE id_member_from IN ({array_int:member_list}) + AND deleted_by_sender = {int:not_deleted}' . $where, + array( + 'member_list' => $owner, + 'is_deleted' => 1, + 'not_deleted' => 0, + 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(), + ) + ); + } + if ($folder != 'sent' || $folder === null) + { + // Calculate the number of messages each member's gonna lose... + $request = $smcFunc['db_query']('', ' + SELECT id_member, COUNT(*) AS num_deleted_messages, CASE WHEN is_read & 1 >= 1 THEN 1 ELSE 0 END AS is_read + FROM {db_prefix}pm_recipients + WHERE id_member IN ({array_int:member_list}) + AND deleted = {int:not_deleted}' . $where . ' + GROUP BY id_member, is_read', + array( + 'member_list' => $owner, + 'not_deleted' => 0, + 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(), + ) + ); + // ...And update the statistics accordingly - now including unread messages!. + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['is_read']) + updateMemberData($row['id_member'], array('instant_messages' => $where == '' ? 0 : 'instant_messages - ' . $row['num_deleted_messages'])); + else + updateMemberData($row['id_member'], array('instant_messages' => $where == '' ? 0 : 'instant_messages - ' . $row['num_deleted_messages'], 'unread_messages' => $where == '' ? 0 : 'unread_messages - ' . $row['num_deleted_messages'])); + + // If this is the current member we need to make their message count correct. + if ($user_info['id'] == $row['id_member']) + { + $user_info['messages'] -= $row['num_deleted_messages']; + if (!($row['is_read'])) + $user_info['unread_messages'] -= $row['num_deleted_messages']; + } + } + $smcFunc['db_free_result']($request); + + // Do the actual deletion. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}pm_recipients + SET deleted = {int:is_deleted} + WHERE id_member IN ({array_int:member_list}) + AND deleted = {int:not_deleted}' . $where, + array( + 'member_list' => $owner, + 'is_deleted' => 1, + 'not_deleted' => 0, + 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(), + ) + ); + } + + // If sender and recipients all have deleted their message, it can be removed. + $request = $smcFunc['db_query']('', ' + SELECT pm.id_pm AS sender, pmr.id_pm + FROM {db_prefix}personal_messages AS pm + LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm AND pmr.deleted = {int:not_deleted}) + WHERE pm.deleted_by_sender = {int:is_deleted} + ' . str_replace('id_pm', 'pm.id_pm', $where) . ' + GROUP BY sender, pmr.id_pm + HAVING pmr.id_pm IS null', + array( + 'not_deleted' => 0, + 'is_deleted' => 1, + 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(), + ) + ); + $remove_pms = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $remove_pms[] = $row['sender']; + $smcFunc['db_free_result']($request); + + if (!empty($remove_pms)) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}personal_messages + WHERE id_pm IN ({array_int:pm_list})', + array( + 'pm_list' => $remove_pms, + ) + ); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}pm_recipients + WHERE id_pm IN ({array_int:pm_list})', + array( + 'pm_list' => $remove_pms, + ) + ); + } + + // Any cached numbers may be wrong now. + cache_put_data('labelCounts:' . $user_info['id'], null, 720); +} + +// Mark personal messages read. +function markMessages($personal_messages = null, $label = null, $owner = null) +{ + global $user_info, $context, $smcFunc; + + if ($owner === null) + $owner = $user_info['id']; + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}pm_recipients + SET is_read = is_read | 1 + WHERE id_member = {int:id_member} + AND NOT (is_read & 1 >= 1)' . ($label === null ? '' : ' + AND FIND_IN_SET({string:label}, labels) != 0') . ($personal_messages !== null ? ' + AND id_pm IN ({array_int:personal_messages})' : ''), + array( + 'personal_messages' => $personal_messages, + 'id_member' => $owner, + 'label' => $label, + ) + ); + + // If something wasn't marked as read, get the number of unread messages remaining. + if ($smcFunc['db_affected_rows']() > 0) + { + if ($owner == $user_info['id']) + { + foreach ($context['labels'] as $label) + $context['labels'][(int) $label['id']]['unread_messages'] = 0; + } + + $result = $smcFunc['db_query']('', ' + SELECT labels, COUNT(*) AS num + FROM {db_prefix}pm_recipients + WHERE id_member = {int:id_member} + AND NOT (is_read & 1 >= 1) + AND deleted = {int:is_not_deleted} + GROUP BY labels', + array( + 'id_member' => $owner, + 'is_not_deleted' => 0, + ) + ); + $total_unread = 0; + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $total_unread += $row['num']; + + if ($owner != $user_info['id']) + continue; + + $this_labels = explode(',', $row['labels']); + foreach ($this_labels as $this_label) + $context['labels'][(int) $this_label]['unread_messages'] += $row['num']; + } + $smcFunc['db_free_result']($result); + + // Need to store all this. + cache_put_data('labelCounts:' . $owner, $context['labels'], 720); + updateMemberData($owner, array('unread_messages' => $total_unread)); + + // If it was for the current member, reflect this in the $user_info array too. + if ($owner == $user_info['id']) + $user_info['unread_messages'] = $total_unread; + } +} + +// This function handles adding, deleting and editing labels on messages. +function ManageLabels() +{ + global $txt, $context, $user_info, $scripturl, $smcFunc; + + // Build the link tree elements... + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;sa=manlabels', + 'name' => $txt['pm_manage_labels'] + ); + + $context['page_title'] = $txt['pm_manage_labels']; + $context['sub_template'] = 'labels'; + + $the_labels = array(); + // Add all existing labels to the array to save, slashing them as necessary... + foreach ($context['labels'] as $label) + { + if ($label['id'] != -1) + $the_labels[$label['id']] = $label['name']; + } + + if (isset($_POST[$context['session_var']])) + { + checkSession('post'); + + // This will be for updating messages. + $message_changes = array(); + $new_labels = array(); + $rule_changes = array(); + + // Will most likely need this. + LoadRules(); + + // Adding a new label? + if (isset($_POST['add'])) + { + $_POST['label'] = strtr($smcFunc['htmlspecialchars'](trim($_POST['label'])), array(',' => ',')); + + if ($smcFunc['strlen']($_POST['label']) > 30) + $_POST['label'] = $smcFunc['substr']($_POST['label'], 0, 30); + if ($_POST['label'] != '') + $the_labels[] = $_POST['label']; + } + // Deleting an existing label? + elseif (isset($_POST['delete'], $_POST['delete_label'])) + { + $i = 0; + foreach ($the_labels as $id => $name) + { + if (isset($_POST['delete_label'][$id])) + { + unset($the_labels[$id]); + $message_changes[$id] = true; + } + else + $new_labels[$id] = $i++; + } + } + // The hardest one to deal with... changes. + elseif (isset($_POST['save']) && !empty($_POST['label_name'])) + { + $i = 0; + foreach ($the_labels as $id => $name) + { + if ($id == -1) + continue; + elseif (isset($_POST['label_name'][$id])) + { + $_POST['label_name'][$id] = trim(strtr($smcFunc['htmlspecialchars']($_POST['label_name'][$id]), array(',' => ','))); + + if ($smcFunc['strlen']($_POST['label_name'][$id]) > 30) + $_POST['label_name'][$id] = $smcFunc['substr']($_POST['label_name'][$id], 0, 30); + if ($_POST['label_name'][$id] != '') + { + $the_labels[(int) $id] = $_POST['label_name'][$id]; + $new_labels[$id] = $i++; + } + else + { + unset($the_labels[(int) $id]); + $message_changes[(int) $id] = true; + } + } + else + $new_labels[$id] = $i++; + } + } + + // Save the label status. + updateMemberData($user_info['id'], array('message_labels' => implode(',', $the_labels))); + + // Update all the messages currently with any label changes in them! + if (!empty($message_changes)) + { + $searchArray = array_keys($message_changes); + + if (!empty($new_labels)) + { + for ($i = max($searchArray) + 1, $n = max(array_keys($new_labels)); $i <= $n; $i++) + $searchArray[] = $i; + } + + // Now find the messages to change. + $request = $smcFunc['db_query']('', ' + SELECT id_pm, labels + FROM {db_prefix}pm_recipients + WHERE FIND_IN_SET({raw:find_label_implode}, labels) != 0 + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'find_label_implode' => '\'' . implode('\', labels) != 0 OR FIND_IN_SET(\'', $searchArray) . '\'', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Do the long task of updating them... + $toChange = explode(',', $row['labels']); + + foreach ($toChange as $key => $value) + if (in_array($value, $searchArray)) + { + if (isset($new_labels[$value])) + $toChange[$key] = $new_labels[$value]; + else + unset($toChange[$key]); + } + + if (empty($toChange)) + $toChange[] = '-1'; + + // Update the message. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}pm_recipients + SET labels = {string:new_labels} + WHERE id_pm = {int:id_pm} + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'id_pm' => $row['id_pm'], + 'new_labels' => implode(',', array_unique($toChange)), + ) + ); + } + $smcFunc['db_free_result']($request); + + // Now do the same the rules - check through each rule. + foreach ($context['rules'] as $k => $rule) + { + // Each action... + foreach ($rule['actions'] as $k2 => $action) + { + if ($action['t'] != 'lab' || !in_array($action['v'], $searchArray)) + continue; + + $rule_changes[] = $rule['id']; + // If we're here we have a label which is either changed or gone... + if (isset($new_labels[$action['v']])) + $context['rules'][$k]['actions'][$k2]['v'] = $new_labels[$action['v']]; + else + unset($context['rules'][$k]['actions'][$k2]); + } + } + } + + // If we have rules to change do so now. + if (!empty($rule_changes)) + { + $rule_changes = array_unique($rule_changes); + // Update/delete as appropriate. + foreach ($rule_changes as $k => $id) + if (!empty($context['rules'][$id]['actions'])) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}pm_rules + SET actions = {string:actions} + WHERE id_rule = {int:id_rule} + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'id_rule' => $id, + 'actions' => serialize($context['rules'][$id]['actions']), + ) + ); + unset($rule_changes[$k]); + } + + // Anything left here means it's lost all actions... + if (!empty($rule_changes)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}pm_rules + WHERE id_rule IN ({array_int:rule_list}) + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'rule_list' => $rule_changes, + ) + ); + } + + // Make sure we're not caching this! + cache_put_data('labelCounts:' . $user_info['id'], null, 720); + + // To make the changes appear right away, redirect. + redirectexit('action=pm;sa=manlabels'); + } +} + +// Edit Personal Message Settings +function MessageSettings() +{ + global $txt, $user_settings, $user_info, $context, $sourcedir, $smcFunc; + global $scripturl, $profile_vars, $cur_profile, $user_profile; + + // Need this for the display. + require_once($sourcedir . '/Profile.php'); + require_once($sourcedir . '/Profile-Modify.php'); + + // We want them to submit back to here. + $context['profile_custom_submit_url'] = $scripturl . '?action=pm;sa=settings;save'; + + loadMemberData($user_info['id'], false, 'profile'); + $cur_profile = $user_profile[$user_info['id']]; + + loadLanguage('Profile'); + loadTemplate('Profile'); + + $context['page_title'] = $txt['pm_settings']; + $context['user']['is_owner'] = true; + $context['id_member'] = $user_info['id']; + $context['require_password'] = false; + $context['menu_item_selected'] = 'settings'; + $context['submit_button_text'] = $txt['pm_settings']; + $context['profile_header_text'] = $txt['personal_messages']; + + // Add our position to the linktree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;sa=settings', + 'name' => $txt['pm_settings'] + ); + + // Are they saving? + if (isset($_REQUEST['save'])) + { + checkSession('post'); + + // Mimic what profile would do. + $_POST = htmltrim__recursive($_POST); + $_POST = htmlspecialchars__recursive($_POST); + + // Save the fields. + saveProfileFields(); + + if (!empty($profile_vars)) + updateMemberData($user_info['id'], $profile_vars); + } + + // Load up the fields. + pmprefs($user_info['id']); +} + +// Allows a user to report a personal message they receive to the administrator. +function ReportMessage() +{ + global $txt, $context, $scripturl, $sourcedir; + global $user_info, $language, $modSettings, $smcFunc; + + // Check that this feature is even enabled! + if (empty($modSettings['enableReportPM']) || empty($_REQUEST['pmsg'])) + fatal_lang_error('no_access', false); + + $pmsg = (int) $_REQUEST['pmsg']; + + if (!isAccessiblePM($pmsg, 'inbox')) + fatal_lang_error('no_access', false); + + $context['pm_id'] = $pmsg; + $context['page_title'] = $txt['pm_report_title']; + + // If we're here, just send the user to the template, with a few useful context bits. + if (!isset($_POST['report'])) + { + $context['sub_template'] = 'report_message'; + + // !!! I don't like being able to pick who to send it to. Favoritism, etc. sucks. + // Now, get all the administrators. + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0 + ORDER BY real_name', + array( + 'admin_group' => 1, + ) + ); + $context['admins'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['admins'][$row['id_member']] = $row['real_name']; + $smcFunc['db_free_result']($request); + + // How many admins in total? + $context['admin_count'] = count($context['admins']); + } + // Otherwise, let's get down to the sending stuff. + else + { + // Check the session before proceeding any further! + checkSession('post'); + + // First, pull out the message contents, and verify it actually went to them! + $request = $smcFunc['db_query']('', ' + SELECT pm.subject, pm.body, pm.msgtime, pm.id_member_from, IFNULL(m.real_name, pm.from_name) AS sender_name + FROM {db_prefix}personal_messages AS pm + INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm) + LEFT JOIN {db_prefix}members AS m ON (m.id_member = pm.id_member_from) + WHERE pm.id_pm = {int:id_pm} + AND pmr.id_member = {int:current_member} + AND pmr.deleted = {int:not_deleted} + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'id_pm' => $context['pm_id'], + 'not_deleted' => 0, + ) + ); + // Can only be a hacker here! + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + list ($subject, $body, $time, $memberFromID, $memberFromName) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Remove the line breaks... + $body = preg_replace('~
~i', "\n", $body); + + // Get any other recipients of the email. + $request = $smcFunc['db_query']('', ' + SELECT mem_to.id_member AS id_member_to, mem_to.real_name AS to_name, pmr.bcc + FROM {db_prefix}pm_recipients AS pmr + LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member) + WHERE pmr.id_pm = {int:id_pm} + AND pmr.id_member != {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'id_pm' => $context['pm_id'], + ) + ); + $recipients = array(); + $hidden_recipients = 0; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If it's hidden still don't reveal their names - privacy after all ;) + if ($row['bcc']) + $hidden_recipients++; + else + $recipients[] = '[url=' . $scripturl . '?action=profile;u=' . $row['id_member_to'] . ']' . $row['to_name'] . '[/url]'; + } + $smcFunc['db_free_result']($request); + + if ($hidden_recipients) + $recipients[] = sprintf($txt['pm_report_pm_hidden'], $hidden_recipients); + + // Now let's get out and loop through the admins. + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, lngfile + FROM {db_prefix}members + WHERE (id_group = {int:admin_id} OR FIND_IN_SET({int:admin_id}, additional_groups) != 0) + ' . (empty($_POST['ID_ADMIN']) ? '' : 'AND id_member = {int:specific_admin}') . ' + ORDER BY lngfile', + array( + 'admin_id' => 1, + 'specific_admin' => isset($_POST['ID_ADMIN']) ? (int) $_POST['ID_ADMIN'] : 0, + ) + ); + + // Maybe we shouldn't advertise this? + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + + $memberFromName = un_htmlspecialchars($memberFromName); + + // Prepare the message storage array. + $messagesToSend = array(); + // Loop through each admin, and add them to the right language pile... + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Need to send in the correct language! + $cur_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']; + + if (!isset($messagesToSend[$cur_language])) + { + loadLanguage('PersonalMessage', $cur_language, false); + + // Make the body. + $report_body = str_replace(array('{REPORTER}', '{SENDER}'), array(un_htmlspecialchars($user_info['name']), $memberFromName), $txt['pm_report_pm_user_sent']); + $report_body .= "\n" . '[b]' . $_POST['reason'] . '[/b]' . "\n\n"; + if (!empty($recipients)) + $report_body .= $txt['pm_report_pm_other_recipients'] . ' ' . implode(', ', $recipients) . "\n\n"; + $report_body .= $txt['pm_report_pm_unedited_below'] . "\n" . '[quote author=' . (empty($memberFromID) ? '"' . $memberFromName . '"' : $memberFromName . ' link=action=profile;u=' . $memberFromID . ' date=' . $time) . ']' . "\n" . un_htmlspecialchars($body) . '[/quote]'; + + // Plonk it in the array ;) + $messagesToSend[$cur_language] = array( + 'subject' => ($smcFunc['strpos']($subject, $txt['pm_report_pm_subject']) === false ? $txt['pm_report_pm_subject'] : '') . un_htmlspecialchars($subject), + 'body' => $report_body, + 'recipients' => array( + 'to' => array(), + 'bcc' => array() + ), + ); + } + + // Add them to the list. + $messagesToSend[$cur_language]['recipients']['to'][$row['id_member']] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + + // Send a different email for each language. + foreach ($messagesToSend as $lang => $message) + sendpm($message['recipients'], $message['subject'], $message['body']); + + // Give the user their own language back! + if (!empty($modSettings['userLanguage'])) + loadLanguage('PersonalMessage', '', false); + + // Leave them with a template. + $context['sub_template'] = 'report_message_complete'; + } +} + +// List all rules, and allow adding/entering etc.... +function ManageRules() +{ + global $txt, $context, $user_info, $scripturl, $smcFunc; + + // The link tree - gotta have this :o + $context['linktree'][] = array( + 'url' => $scripturl . '?action=pm;sa=manrules', + 'name' => $txt['pm_manage_rules'] + ); + + $context['page_title'] = $txt['pm_manage_rules']; + $context['sub_template'] = 'rules'; + + // Load them... load them!! + LoadRules(); + + // Likely to need all the groups! + $request = $smcFunc['db_query']('', ' + SELECT mg.id_group, mg.group_name, IFNULL(gm.id_member, 0) AS can_moderate, mg.hidden + FROM {db_prefix}membergroups AS mg + LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member}) + WHERE mg.min_posts = {int:min_posts} + AND mg.id_group != {int:moderator_group} + AND mg.hidden = {int:not_hidden} + ORDER BY mg.group_name', + array( + 'current_member' => $user_info['id'], + 'min_posts' => -1, + 'moderator_group' => 3, + 'not_hidden' => 0, + ) + ); + $context['groups'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Hide hidden groups! + if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups')) + continue; + + $context['groups'][$row['id_group']] = $row['group_name']; + } + $smcFunc['db_free_result']($request); + + // Applying all rules? + if (isset($_GET['apply'])) + { + checkSession('get'); + + ApplyRules(true); + redirectexit('action=pm;sa=manrules'); + } + // Editing a specific one? + if (isset($_GET['add'])) + { + $context['rid'] = isset($_GET['rid']) && isset($context['rules'][$_GET['rid']])? (int) $_GET['rid'] : 0; + $context['sub_template'] = 'add_rule'; + + // Current rule information... + if ($context['rid']) + { + $context['rule'] = $context['rules'][$context['rid']]; + $members = array(); + // Need to get member names! + foreach ($context['rule']['criteria'] as $k => $criteria) + if ($criteria['t'] == 'mid' && !empty($criteria['v'])) + $members[(int) $criteria['v']] = $k; + + if (!empty($members)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, member_name + FROM {db_prefix}members + WHERE id_member IN ({array_int:member_list})', + array( + 'member_list' => array_keys($members), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['rule']['criteria'][$members[$row['id_member']]]['v'] = $row['member_name']; + $smcFunc['db_free_result']($request); + } + } + else + $context['rule'] = array( + 'id' => '', + 'name' => '', + 'criteria' => array(), + 'actions' => array(), + 'logic' => 'and', + ); + } + // Saving? + elseif (isset($_GET['save'])) + { + checkSession('post'); + $context['rid'] = isset($_GET['rid']) && isset($context['rules'][$_GET['rid']])? (int) $_GET['rid'] : 0; + + // Name is easy! + $ruleName = $smcFunc['htmlspecialchars'](trim($_POST['rule_name'])); + if (empty($ruleName)) + fatal_lang_error('pm_rule_no_name', false); + + // Sanity check... + if (empty($_POST['ruletype']) || empty($_POST['acttype'])) + fatal_lang_error('pm_rule_no_criteria', false); + + // Let's do the criteria first - it's also hardest! + $criteria = array(); + foreach ($_POST['ruletype'] as $ind => $type) + { + // Check everything is here... + if ($type == 'gid' && (!isset($_POST['ruledefgroup'][$ind]) || !isset($context['groups'][$_POST['ruledefgroup'][$ind]]))) + continue; + elseif ($type != 'bud' && !isset($_POST['ruledef'][$ind])) + continue; + + // Members need to be found. + if ($type == 'mid') + { + $name = trim($_POST['ruledef'][$ind]); + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE real_name = {string:member_name} + OR member_name = {string:member_name}', + array( + 'member_name' => $name, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + continue; + list ($memID) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $criteria[] = array('t' => 'mid', 'v' => $memID); + } + elseif ($type == 'bud') + $criteria[] = array('t' => 'bud', 'v' => 1); + elseif ($type == 'gid') + $criteria[] = array('t' => 'gid', 'v' => (int) $_POST['ruledefgroup'][$ind]); + elseif (in_array($type, array('sub', 'msg')) && trim($_POST['ruledef'][$ind]) != '') + $criteria[] = array('t' => $type, 'v' => $smcFunc['htmlspecialchars'](trim($_POST['ruledef'][$ind]))); + } + + // Also do the actions! + $actions = array(); + $doDelete = 0; + $isOr = $_POST['rule_logic'] == 'or' ? 1 : 0; + foreach ($_POST['acttype'] as $ind => $type) + { + // Picking a valid label? + if ($type == 'lab' && (!isset($_POST['labdef'][$ind]) || !isset($context['labels'][$_POST['labdef'][$ind] - 1]))) + continue; + + // Record what we're doing. + if ($type == 'del') + $doDelete = 1; + elseif ($type == 'lab') + $actions[] = array('t' => 'lab', 'v' => (int) $_POST['labdef'][$ind] - 1); + } + + if (empty($criteria) || (empty($actions) && !$doDelete)) + fatal_lang_error('pm_rule_no_criteria', false); + + // What are we storing? + $criteria = serialize($criteria); + $actions = serialize($actions); + + // Create the rule? + if (empty($context['rid'])) + $smcFunc['db_insert']('', + '{db_prefix}pm_rules', + array( + 'id_member' => 'int', 'rule_name' => 'string', 'criteria' => 'string', 'actions' => 'string', + 'delete_pm' => 'int', 'is_or' => 'int', + ), + array( + $user_info['id'], $ruleName, $criteria, $actions, $doDelete, $isOr, + ), + array('id_rule') + ); + else + $smcFunc['db_query']('', ' + UPDATE {db_prefix}pm_rules + SET rule_name = {string:rule_name}, criteria = {string:criteria}, actions = {string:actions}, + delete_pm = {int:delete_pm}, is_or = {int:is_or} + WHERE id_rule = {int:id_rule} + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'delete_pm' => $doDelete, + 'is_or' => $isOr, + 'id_rule' => $context['rid'], + 'rule_name' => $ruleName, + 'criteria' => $criteria, + 'actions' => $actions, + ) + ); + + redirectexit('action=pm;sa=manrules'); + } + // Deleting? + elseif (isset($_POST['delselected']) && !empty($_POST['delrule'])) + { + checkSession('post'); + $toDelete = array(); + foreach ($_POST['delrule'] as $k => $v) + $toDelete[] = (int) $k; + + if (!empty($toDelete)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}pm_rules + WHERE id_rule IN ({array_int:delete_list}) + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'delete_list' => $toDelete, + ) + ); + + redirectexit('action=pm;sa=manrules'); + } +} + +// This will apply rules to all unread messages. If all_messages is set will, clearly, do it to all! +function ApplyRules($all_messages = false) +{ + global $user_info, $smcFunc, $context, $options; + + // Want this - duh! + loadRules(); + + // No rules? + if (empty($context['rules'])) + return; + + // Just unread ones? + $ruleQuery = $all_messages ? '' : ' AND pmr.is_new = 1'; + + //!!! Apply all should have timeout protection! + // Get all the messages that match this. + $request = $smcFunc['db_query']('', ' + SELECT + pmr.id_pm, pm.id_member_from, pm.subject, pm.body, mem.id_group, pmr.labels + FROM {db_prefix}pm_recipients AS pmr + INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from) + WHERE pmr.id_member = {int:current_member} + AND pmr.deleted = {int:not_deleted} + ' . $ruleQuery, + array( + 'current_member' => $user_info['id'], + 'not_deleted' => 0, + ) + ); + $actions = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + foreach ($context['rules'] as $rule) + { + $match = false; + // Loop through all the criteria hoping to make a match. + foreach ($rule['criteria'] as $criterium) + { + if (($criterium['t'] == 'mid' && $criterium['v'] == $row['id_member_from']) || ($criterium['t'] == 'gid' && $criterium['v'] == $row['id_group']) || ($criterium['t'] == 'sub' && strpos($row['subject'], $criterium['v']) !== false) || ($criterium['t'] == 'msg' && strpos($row['body'], $criterium['v']) !== false)) + $match = true; + // If we're adding and one criteria don't match then we stop! + elseif ($rule['logic'] == 'and') + { + $match = false; + break; + } + } + + // If we have a match the rule must be true - act! + if ($match) + { + if ($rule['delete']) + $actions['deletes'][] = $row['id_pm']; + else + { + foreach ($rule['actions'] as $ruleAction) + { + if ($ruleAction['t'] == 'lab') + { + // Get a basic pot started! + if (!isset($actions['labels'][$row['id_pm']])) + $actions['labels'][$row['id_pm']] = empty($row['labels']) ? array() : explode(',', $row['labels']); + $actions['labels'][$row['id_pm']][] = $ruleAction['v']; + } + } + } + } + } + } + $smcFunc['db_free_result']($request); + + // Deletes are easy! + if (!empty($actions['deletes'])) + deleteMessages($actions['deletes']); + + // Relabel? + if (!empty($actions['labels'])) + { + foreach ($actions['labels'] as $pm => $labels) + { + // Quickly check each label is valid! + $realLabels = array(); + foreach ($context['labels'] as $label) + if (in_array($label['id'], $labels) && ($label['id'] != -1 || empty($options['pm_remove_inbox_label']))) + $realLabels[] = $label['id']; + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}pm_recipients + SET labels = {string:new_labels} + WHERE id_pm = {int:id_pm} + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'id_pm' => $pm, + 'new_labels' => empty($realLabels) ? '' : implode(',', $realLabels), + ) + ); + } + } +} + +// Load up all the rules for the current user. +function LoadRules($reload = false) +{ + global $user_info, $context, $smcFunc; + + if (isset($context['rules']) && !$reload) + return; + + $request = $smcFunc['db_query']('', ' + SELECT + id_rule, rule_name, criteria, actions, delete_pm, is_or + FROM {db_prefix}pm_rules + WHERE id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + ) + ); + $context['rules'] = array(); + // Simply fill in the data! + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['rules'][$row['id_rule']] = array( + 'id' => $row['id_rule'], + 'name' => $row['rule_name'], + 'criteria' => unserialize($row['criteria']), + 'actions' => unserialize($row['actions']), + 'delete' => $row['delete_pm'], + 'logic' => $row['is_or'] ? 'or' : 'and', + ); + + if ($row['delete_pm']) + $context['rules'][$row['id_rule']]['actions'][] = array('t' => 'del', 'v' => 1); + } + $smcFunc['db_free_result']($request); +} + +// Check if the PM is available to the current user. +function isAccessiblePM($pmID, $validFor = 'in_or_outbox') +{ + global $user_info, $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT + pm.id_member_from = {int:id_current_member} AND pm.deleted_by_sender = {int:not_deleted} AS valid_for_outbox, + pmr.id_pm IS NOT NULL AS valid_for_inbox + FROM {db_prefix}personal_messages AS pm + LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm AND pmr.id_member = {int:id_current_member} AND pmr.deleted = {int:not_deleted}) + WHERE pm.id_pm = {int:id_pm} + AND ((pm.id_member_from = {int:id_current_member} AND pm.deleted_by_sender = {int:not_deleted}) OR pmr.id_pm IS NOT NULL)', + array( + 'id_pm' => $pmID, + 'id_current_member' => $user_info['id'], + 'not_deleted' => 0, + ) + ); + + if ($smcFunc['db_num_rows']($request) === 0) + { + $smcFunc['db_free_result']($request); + return false; + } + + $validationResult = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + switch ($validFor) + { + case 'inbox': + return !empty($validationResult['valid_for_inbox']); + break; + + case 'outbox': + return !empty($validationResult['valid_for_outbox']); + break; + + case 'in_or_outbox': + return !empty($validationResult['valid_for_inbox']) || !empty($validationResult['valid_for_outbox']); + break; + + default: + trigger_error('Undefined validation type given', E_USER_ERROR); + break; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Poll.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Poll.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,973 @@ + $user_info['id'], + 'current_topic' => $topic, + 'not_guest' => 0, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('poll_error', false); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // If this is a guest can they vote? + if ($user_info['is_guest']) + { + // Guest voting disabled? + if (!$row['guest_vote']) + fatal_lang_error('guest_vote_disabled'); + // Guest already voted? + elseif (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $row['id_poll'] . ',') !== false) + { + // ;id,timestamp,[vote,vote...]; etc + $guestinfo = explode(';', $_COOKIE['guest_poll_vote']); + // Find the poll we're after. + foreach ($guestinfo as $i => $guestvoted) + { + $guestvoted = explode(',', $guestvoted); + if ($guestvoted[0] == $row['id_poll']) + break; + } + // Has the poll been reset since guest voted? + if ($row['reset_poll'] > $guestvoted[1]) + { + // Remove the poll info from the cookie to allow guest to vote again + unset($guestinfo[$i]); + if (!empty($guestinfo)) + $_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo); + else + unset($_COOKIE['guest_poll_vote']); + } + else + fatal_lang_error('poll_error', false); + unset($guestinfo, $guestvoted, $i); + } + } + + // Is voting locked or has it expired? + if (!empty($row['voting_locked']) || (!empty($row['expire_time']) && time() > $row['expire_time'])) + fatal_lang_error('poll_error', false); + + // If they have already voted and aren't allowed to change their vote - hence they are outta here! + if (!$user_info['is_guest'] && $row['selected'] != -1 && empty($row['change_vote'])) + fatal_lang_error('poll_error', false); + // Otherwise if they can change their vote yet they haven't sent any options... remove their vote and redirect. + elseif (!empty($row['change_vote']) && !$user_info['is_guest']) + { + checkSession('request'); + $pollOptions = array(); + + // Find out what they voted for before. + $request = $smcFunc['db_query']('', ' + SELECT id_choice + FROM {db_prefix}log_polls + WHERE id_member = {int:current_member} + AND id_poll = {int:id_poll}', + array( + 'current_member' => $user_info['id'], + 'id_poll' => $row['id_poll'], + ) + ); + while ($choice = $smcFunc['db_fetch_row']($request)) + $pollOptions[] = $choice[0]; + $smcFunc['db_free_result']($request); + + // Just skip it if they had voted for nothing before. + if (!empty($pollOptions)) + { + // Update the poll totals. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}poll_choices + SET votes = votes - 1 + WHERE id_poll = {int:id_poll} + AND id_choice IN ({array_int:poll_options}) + AND votes > {int:votes}', + array( + 'poll_options' => $pollOptions, + 'id_poll' => $row['id_poll'], + 'votes' => 0, + ) + ); + + // Delete off the log. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_polls + WHERE id_member = {int:current_member} + AND id_poll = {int:id_poll}', + array( + 'current_member' => $user_info['id'], + 'id_poll' => $row['id_poll'], + ) + ); + } + + // Redirect back to the topic so the user can vote again! + if (empty($_POST['options'])) + redirectexit('topic=' . $topic . '.' . $_REQUEST['start']); + } + + checkSession('request'); + + // Make sure the option(s) are valid. + if (empty($_POST['options'])) + fatal_lang_error('didnt_select_vote', false); + + // Too many options checked! + if (count($_REQUEST['options']) > $row['max_votes']) + fatal_lang_error('poll_too_many_votes', false, array($row['max_votes'])); + + $pollOptions = array(); + $inserts = array(); + foreach ($_REQUEST['options'] as $id) + { + $id = (int) $id; + + $pollOptions[] = $id; + $inserts[] = array($row['id_poll'], $user_info['id'], $id); + } + + // Add their vote to the tally. + $smcFunc['db_insert']('insert', + '{db_prefix}log_polls', + array('id_poll' => 'int', 'id_member' => 'int', 'id_choice' => 'int'), + $inserts, + array('id_poll', 'id_member', 'id_choice') + ); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}poll_choices + SET votes = votes + 1 + WHERE id_poll = {int:id_poll} + AND id_choice IN ({array_int:poll_options})', + array( + 'poll_options' => $pollOptions, + 'id_poll' => $row['id_poll'], + ) + ); + + // If it's a guest don't let them vote again. + if ($user_info['is_guest'] && count($pollOptions) > 0) + { + // Time is stored in case the poll is reset later, plus what they voted for. + $_COOKIE['guest_poll_vote'] = empty($_COOKIE['guest_poll_vote']) ? '' : $_COOKIE['guest_poll_vote']; + // ;id,timestamp,[vote,vote...]; etc + $_COOKIE['guest_poll_vote'] .= ';' . $row['id_poll'] . ',' . time() . ',' . (count($pollOptions) > 1 ? explode(',' . $pollOptions) : $pollOptions[0]); + + // Increase num guest voters count by 1 + $smcFunc['db_query']('', ' + UPDATE {db_prefix}polls + SET num_guest_voters = num_guest_voters + 1 + WHERE id_poll = {int:id_poll}', + array( + 'id_poll' => $row['id_poll'], + ) + ); + + require_once($sourcedir . '/Subs-Auth.php'); + $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); + setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], 0); + } + + // Return to the post... + redirectexit('topic=' . $topic . '.' . $_REQUEST['start']); +} + +// Lock the voting for a poll. +function LockVoting() +{ + global $topic, $user_info, $smcFunc; + + checkSession('get'); + + // Get the poll starter, ID, and whether or not it is locked. + $request = $smcFunc['db_query']('', ' + SELECT t.id_member_started, t.id_poll, p.voting_locked + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll) + WHERE t.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + list ($memberID, $pollID, $voting_locked) = $smcFunc['db_fetch_row']($request); + + // If the user _can_ modify the poll.... + if (!allowedTo('poll_lock_any')) + isAllowedTo('poll_lock_' . ($user_info['id'] == $memberID ? 'own' : 'any')); + + // It's been locked by a non-moderator. + if ($voting_locked == '1') + $voting_locked = '0'; + // Locked by a moderator, and this is a moderator. + elseif ($voting_locked == '2' && allowedTo('moderate_board')) + $voting_locked = '0'; + // Sorry, a moderator locked it. + elseif ($voting_locked == '2' && !allowedTo('moderate_board')) + fatal_lang_error('locked_by_admin', 'user'); + // A moderator *is* locking it. + elseif ($voting_locked == '0' && allowedTo('moderate_board')) + $voting_locked = '2'; + // Well, it's gonna be locked one way or another otherwise... + else + $voting_locked = '1'; + + // Lock! *Poof* - no one can vote. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}polls + SET voting_locked = {int:voting_locked} + WHERE id_poll = {int:id_poll}', + array( + 'voting_locked' => $voting_locked, + 'id_poll' => $pollID, + ) + ); + + redirectexit('topic=' . $topic . '.' . $_REQUEST['start']); +} + +// Ask what to change in a poll. +function EditPoll() +{ + global $txt, $user_info, $context, $topic, $board, $smcFunc, $sourcedir, $scripturl; + + if (empty($topic)) + fatal_lang_error('no_access', false); + + loadLanguage('Post'); + loadTemplate('Poll'); + + $context['can_moderate_poll'] = isset($_REQUEST['add']) ? 1 : allowedTo('moderate_board'); + $context['start'] = (int) $_REQUEST['start']; + $context['is_edit'] = isset($_REQUEST['add']) ? 0 : 1; + + // Check if a poll currently exists on this topic, and get the id, question and starter. + $request = $smcFunc['db_query']('', ' + SELECT + t.id_member_started, p.id_poll, p.question, p.hide_results, p.expire_time, p.max_votes, p.change_vote, + m.subject, p.guest_vote, p.id_member AS poll_starter + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll) + WHERE t.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + + // Assume the the topic exists, right? + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_board'); + // Get the poll information. + $pollinfo = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // If we are adding a new poll - make sure that there isn't already a poll there. + if (!$context['is_edit'] && !empty($pollinfo['id_poll'])) + fatal_lang_error('poll_already_exists'); + // Otherwise, if we're editing it, it does exist I assume? + elseif ($context['is_edit'] && empty($pollinfo['id_poll'])) + fatal_lang_error('poll_not_found'); + + // Can you do this? + if ($context['is_edit'] && !allowedTo('poll_edit_any')) + isAllowedTo('poll_edit_' . ($user_info['id'] == $pollinfo['id_member_started'] || ($pollinfo['poll_starter'] != 0 && $user_info['id'] == $pollinfo['poll_starter']) ? 'own' : 'any')); + elseif (!$context['is_edit'] && !allowedTo('poll_add_any')) + isAllowedTo('poll_add_' . ($user_info['id'] == $pollinfo['id_member_started'] ? 'own' : 'any')); + + // Do we enable guest voting? + require_once($sourcedir . '/Subs-Members.php'); + $groupsAllowedVote = groupsAllowedTo('poll_vote', $board); + + // Want to make sure before you actually submit? Must be a lot of options, or something. + if (isset($_POST['preview'])) + { + $question = $smcFunc['htmlspecialchars']($_POST['question']); + + // Basic theme info... + $context['poll'] = array( + 'id' => $pollinfo['id_poll'], + 'question' => $question, + 'hide_results' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'], + 'change_vote' => isset($_POST['poll_change_vote']), + 'guest_vote' => isset($_POST['poll_guest_vote']), + 'guest_vote_allowed' => in_array(-1, $groupsAllowedVote['allowed']), + 'max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']), + ); + + // Start at number one with no last id to speak of. + $number = 1; + $last_id = 0; + + // Get all the choices - if this is an edit. + if ($context['is_edit']) + { + $request = $smcFunc['db_query']('', ' + SELECT label, votes, id_choice + FROM {db_prefix}poll_choices + WHERE id_poll = {int:id_poll}', + array( + 'id_poll' => $pollinfo['id_poll'], + ) + ); + $context['choices'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Get the highest id so we can add more without reusing. + if ($row['id_choice'] >= $last_id) + $last_id = $row['id_choice'] + 1; + + // They cleared this by either omitting it or emptying it. + if (!isset($_POST['options'][$row['id_choice']]) || $_POST['options'][$row['id_choice']] == '') + continue; + + censorText($row['label']); + + // Add the choice! + $context['choices'][$row['id_choice']] = array( + 'id' => $row['id_choice'], + 'number' => $number++, + 'votes' => $row['votes'], + 'label' => $row['label'], + 'is_last' => false + ); + } + $smcFunc['db_free_result']($request); + } + + // Work out how many options we have, so we get the 'is_last' field right... + $totalPostOptions = 0; + foreach ($_POST['options'] as $id => $label) + if ($label != '') + $totalPostOptions++; + + $count = 1; + // If an option exists, update it. If it is new, add it - but don't reuse ids! + foreach ($_POST['options'] as $id => $label) + { + $label = $smcFunc['htmlspecialchars']($label); + censorText($label); + + if (isset($context['choices'][$id])) + $context['choices'][$id]['label'] = $label; + elseif ($label != '') + $context['choices'][] = array( + 'id' => $last_id++, + 'number' => $number++, + 'label' => $label, + 'votes' => -1, + 'is_last' => $count++ == $totalPostOptions && $totalPostOptions > 1 ? true : false, + ); + } + + // Make sure we have two choices for sure! + if ($totalPostOptions < 2) + { + // Need two? + if ($totalPostOptions == 0) + $context['choices'][] = array( + 'id' => $last_id++, + 'number' => $number++, + 'label' => '', + 'votes' => -1, + 'is_last' => false + ); + $poll_errors[] = 'poll_few'; + } + + // Always show one extra box... + $context['choices'][] = array( + 'id' => $last_id++, + 'number' => $number++, + 'label' => '', + 'votes' => -1, + 'is_last' => true + ); + + if ($context['can_moderate_poll']) + $context['poll']['expiration'] = $_POST['poll_expire']; + + // Check the question/option count for errors. + if (trim($_POST['question']) == '' && empty($context['poll_error'])) + $poll_errors[] = 'no_question'; + + // No check is needed, since nothing is really posted. + checkSubmitOnce('free'); + + // Take a check for any errors... assuming we haven't already done so! + if (!empty($poll_errors) && empty($context['poll_error'])) + { + loadLanguage('Errors'); + + $context['poll_error'] = array('messages' => array()); + foreach ($poll_errors as $poll_error) + { + $context['poll_error'][$poll_error] = true; + $context['poll_error']['messages'][] = $txt['error_' . $poll_error]; + } + } + } + else + { + // Basic theme info... + $context['poll'] = array( + 'id' => $pollinfo['id_poll'], + 'question' => $pollinfo['question'], + 'hide_results' => $pollinfo['hide_results'], + 'max_votes' => $pollinfo['max_votes'], + 'change_vote' => !empty($pollinfo['change_vote']), + 'guest_vote' => !empty($pollinfo['guest_vote']), + 'guest_vote_allowed' => in_array(-1, $groupsAllowedVote['allowed']), + ); + + // Poll expiration time? + $context['poll']['expiration'] = empty($pollinfo['expire_time']) || !allowedTo('moderate_board') ? '' : ceil($pollinfo['expire_time'] <= time() ? -1 : ($pollinfo['expire_time'] - time()) / (3600 * 24)); + + // Get all the choices - if this is an edit. + if ($context['is_edit']) + { + $request = $smcFunc['db_query']('', ' + SELECT label, votes, id_choice + FROM {db_prefix}poll_choices + WHERE id_poll = {int:id_poll}', + array( + 'id_poll' => $pollinfo['id_poll'], + ) + ); + $context['choices'] = array(); + $number = 1; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + censorText($row['label']); + + $context['choices'][$row['id_choice']] = array( + 'id' => $row['id_choice'], + 'number' => $number++, + 'votes' => $row['votes'], + 'label' => $row['label'], + 'is_last' => false + ); + } + $smcFunc['db_free_result']($request); + + $last_id = max(array_keys($context['choices'])) + 1; + + // Add an extra choice... + $context['choices'][] = array( + 'id' => $last_id, + 'number' => $number, + 'votes' => -1, + 'label' => '', + 'is_last' => true + ); + } + // New poll? + else + { + // Setup the default poll options. + $context['poll'] = array( + 'id' => 0, + 'question' => '', + 'hide_results' => 0, + 'max_votes' => 1, + 'change_vote' => 0, + 'guest_vote' => 0, + 'guest_vote_allowed' => in_array(-1, $groupsAllowedVote['allowed']), + 'expiration' => '', + ); + + // Make all five poll choices empty. + $context['choices'] = array( + array('id' => 0, 'number' => 1, 'votes' => -1, 'label' => '', 'is_last' => false), + array('id' => 1, 'number' => 2, 'votes' => -1, 'label' => '', 'is_last' => false), + array('id' => 2, 'number' => 3, 'votes' => -1, 'label' => '', 'is_last' => false), + array('id' => 3, 'number' => 4, 'votes' => -1, 'label' => '', 'is_last' => false), + array('id' => 4, 'number' => 5, 'votes' => -1, 'label' => '', 'is_last' => true) + ); + } + } + $context['page_title'] = $context['is_edit'] ? $txt['poll_edit'] : $txt['add_poll']; + + // Build the link tree. + censorText($pollinfo['subject']); + $context['linktree'][] = array( + 'url' => $scripturl . '?topic=' . $topic . '.0', + 'name' => $pollinfo['subject'], + ); + $context['linktree'][] = array( + 'name' => $context['page_title'], + ); + + // Register this form in the session variables. + checkSubmitOnce('register'); +} + +// Change a poll... +function EditPoll2() +{ + global $txt, $topic, $board, $context; + global $modSettings, $user_info, $smcFunc, $sourcedir; + + // Sneaking off, are we? + if (empty($_POST)) + redirectexit('action=editpoll;topic=' . $topic . '.0'); + + if (checkSession('post', '', false) != '') + $poll_errors[] = 'session_timeout'; + + if (isset($_POST['preview'])) + return EditPoll(); + + // HACKERS (!!) can't edit :P. + if (empty($topic)) + fatal_lang_error('no_access', false); + + // Is this a new poll, or editing an existing? + $isEdit = isset($_REQUEST['add']) ? 0 : 1; + + // Get the starter and the poll's ID - if it's an edit. + $request = $smcFunc['db_query']('', ' + SELECT t.id_member_started, t.id_poll, p.id_member AS poll_starter, p.expire_time + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll) + WHERE t.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_board'); + $bcinfo = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Check their adding/editing is valid. + if (!$isEdit && !empty($bcinfo['id_poll'])) + fatal_lang_error('poll_already_exists'); + // Are we editing a poll which doesn't exist? + elseif ($isEdit && empty($bcinfo['id_poll'])) + fatal_lang_error('poll_not_found'); + + // Check if they have the power to add or edit the poll. + if ($isEdit && !allowedTo('poll_edit_any')) + isAllowedTo('poll_edit_' . ($user_info['id'] == $bcinfo['id_member_started'] || ($bcinfo['poll_starter'] != 0 && $user_info['id'] == $bcinfo['poll_starter']) ? 'own' : 'any')); + elseif (!$isEdit && !allowedTo('poll_add_any')) + isAllowedTo('poll_add_' . ($user_info['id'] == $bcinfo['id_member_started'] ? 'own' : 'any')); + + $optionCount = 0; + // Ensure the user is leaving a valid amount of options - there must be at least two. + foreach ($_POST['options'] as $k => $option) + { + if (trim($option) != '') + $optionCount++; + } + if ($optionCount < 2) + $poll_errors[] = 'poll_few'; + + // Also - ensure they are not removing the question. + if (trim($_POST['question']) == '') + $poll_errors[] = 'no_question'; + + // Got any errors to report? + if (!empty($poll_errors)) + { + loadLanguage('Errors'); + // Previewing. + $_POST['preview'] = true; + + $context['poll_error'] = array('messages' => array()); + foreach ($poll_errors as $poll_error) + { + $context['poll_error'][$poll_error] = true; + $context['poll_error']['messages'][] = $txt['error_' . $poll_error]; + } + + return EditPoll(); + } + + // Prevent double submission of this form. + checkSubmitOnce('check'); + + // Now we've done all our error checking, let's get the core poll information cleaned... question first. + $_POST['question'] = $smcFunc['htmlspecialchars']($_POST['question']); + $_POST['question'] = $smcFunc['truncate']($_POST['question'], 255); + + $_POST['poll_hide'] = (int) $_POST['poll_hide']; + $_POST['poll_expire'] = isset($_POST['poll_expire']) ? (int) $_POST['poll_expire'] : 0; + $_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0; + $_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0; + + // Make sure guests are actually allowed to vote generally. + if ($_POST['poll_guest_vote']) + { + require_once($sourcedir . '/Subs-Members.php'); + $allowedGroups = groupsAllowedTo('poll_vote', $board); + if (!in_array(-1, $allowedGroups['allowed'])) + $_POST['poll_guest_vote'] = 0; + } + + // Ensure that the number options allowed makes sense, and the expiration date is valid. + if (!$isEdit || allowedTo('moderate_board')) + { + $_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']); + + if (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2) + $_POST['poll_hide'] = 1; + elseif (!$isEdit || $_POST['poll_expire'] != ceil($bcinfo['expire_time'] <= time() ? -1 : ($bcinfo['expire_time'] - time()) / (3600 * 24))) + $_POST['poll_expire'] = empty($_POST['poll_expire']) ? '0' : time() + $_POST['poll_expire'] * 3600 * 24; + else + $_POST['poll_expire'] = $bcinfo['expire_time']; + + if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0) + $_POST['poll_max_votes'] = 1; + else + $_POST['poll_max_votes'] = (int) $_POST['poll_max_votes']; + } + + // If we're editing, let's commit the changes. + if ($isEdit) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}polls + SET question = {string:question}, change_vote = {int:change_vote},' . (allowedTo('moderate_board') ? ' + hide_results = {int:hide_results}, expire_time = {int:expire_time}, max_votes = {int:max_votes}, + guest_vote = {int:guest_vote}' : ' + hide_results = CASE WHEN expire_time = {int:expire_time_zero} AND {int:hide_results} = 2 THEN 1 ELSE {int:hide_results} END') . ' + WHERE id_poll = {int:id_poll}', + array( + 'change_vote' => $_POST['poll_change_vote'], + 'hide_results' => $_POST['poll_hide'], + 'expire_time' => !empty($_POST['poll_expire']) ? $_POST['poll_expire'] : 0, + 'max_votes' => !empty($_POST['poll_max_votes']) ? $_POST['poll_max_votes'] : 0, + 'guest_vote' => $_POST['poll_guest_vote'], + 'expire_time_zero' => 0, + 'id_poll' => $bcinfo['id_poll'], + 'question' => $_POST['question'], + ) + ); + } + // Otherwise, let's get our poll going! + else + { + // Create the poll. + $smcFunc['db_insert']('', + '{db_prefix}polls', + array( + 'question' => 'string-255', 'hide_results' => 'int', 'max_votes' => 'int', 'expire_time' => 'int', 'id_member' => 'int', + 'poster_name' => 'string-255', 'change_vote' => 'int', 'guest_vote' => 'int' + ), + array( + $_POST['question'], $_POST['poll_hide'], $_POST['poll_max_votes'], $_POST['poll_expire'], $user_info['id'], + $user_info['username'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'], + ), + array('id_poll') + ); + + // Set the poll ID. + $bcinfo['id_poll'] = $smcFunc['db_insert_id']('{db_prefix}polls', 'id_poll'); + + // Link the poll to the topic + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET id_poll = {int:id_poll} + WHERE id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + 'id_poll' => $bcinfo['id_poll'], + ) + ); + } + + // Get all the choices. (no better way to remove all emptied and add previously non-existent ones.) + $request = $smcFunc['db_query']('', ' + SELECT id_choice + FROM {db_prefix}poll_choices + WHERE id_poll = {int:id_poll}', + array( + 'id_poll' => $bcinfo['id_poll'], + ) + ); + $choices = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $choices[] = $row['id_choice']; + $smcFunc['db_free_result']($request); + + $delete_options = array(); + foreach ($_POST['options'] as $k => $option) + { + // Make sure the key is numeric for sanity's sake. + $k = (int) $k; + + // They've cleared the box. Either they want it deleted, or it never existed. + if (trim($option) == '') + { + // They want it deleted. Bye. + if (in_array($k, $choices)) + $delete_options[] = $k; + + // Skip the rest... + continue; + } + + // Dress the option up for its big date with the database. + $option = $smcFunc['htmlspecialchars']($option); + + // If it's already there, update it. If it's not... add it. + if (in_array($k, $choices)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}poll_choices + SET label = {string:option_name} + WHERE id_poll = {int:id_poll} + AND id_choice = {int:id_choice}', + array( + 'id_poll' => $bcinfo['id_poll'], + 'id_choice' => $k, + 'option_name' => $option, + ) + ); + else + $smcFunc['db_insert']('', + '{db_prefix}poll_choices', + array( + 'id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255', 'votes' => 'int', + ), + array( + $bcinfo['id_poll'], $k, $option, 0, + ), + array() + ); + } + + // I'm sorry, but... well, no one was choosing you. Poor options, I'll put you out of your misery. + if (!empty($delete_options)) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_polls + WHERE id_poll = {int:id_poll} + AND id_choice IN ({array_int:delete_options})', + array( + 'delete_options' => $delete_options, + 'id_poll' => $bcinfo['id_poll'], + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}poll_choices + WHERE id_poll = {int:id_poll} + AND id_choice IN ({array_int:delete_options})', + array( + 'delete_options' => $delete_options, + 'id_poll' => $bcinfo['id_poll'], + ) + ); + } + + // Shall I reset the vote count, sir? + if (isset($_POST['resetVoteCount'])) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}polls + SET num_guest_voters = {int:no_votes}, reset_poll = {int:time} + WHERE id_poll = {int:id_poll}', + array( + 'no_votes' => 0, + 'id_poll' => $bcinfo['id_poll'], + 'time' => time(), + ) + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}poll_choices + SET votes = {int:no_votes} + WHERE id_poll = {int:id_poll}', + array( + 'no_votes' => 0, + 'id_poll' => $bcinfo['id_poll'], + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_polls + WHERE id_poll = {int:id_poll}', + array( + 'id_poll' => $bcinfo['id_poll'], + ) + ); + } + + // Off we go. + redirectexit('topic=' . $topic . '.' . $_REQUEST['start']); +} + +// Remove a poll from a topic without removing the topic. +function RemovePoll() +{ + global $topic, $user_info, $smcFunc; + + // Make sure the topic is not empty. + if (empty($topic)) + fatal_lang_error('no_access', false); + + // Verify the session. + checkSession('get'); + + // Check permissions. + if (!allowedTo('poll_remove_any')) + { + $request = $smcFunc['db_query']('', ' + SELECT t.id_member_started, p.id_member AS poll_starter + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll) + WHERE t.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + list ($topicStarter, $pollStarter) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + isAllowedTo('poll_remove_' . ($topicStarter == $user_info['id'] || ($pollStarter != 0 && $user_info['id'] == $pollStarter) ? 'own' : 'any')); + } + + // Retrieve the poll ID. + $request = $smcFunc['db_query']('', ' + SELECT id_poll + FROM {db_prefix}topics + WHERE id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + list ($pollID) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Remove all user logs for this poll. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_polls + WHERE id_poll = {int:id_poll}', + array( + 'id_poll' => $pollID, + ) + ); + // Remove all poll choices. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}poll_choices + WHERE id_poll = {int:id_poll}', + array( + 'id_poll' => $pollID, + ) + ); + // Remove the poll itself. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}polls + WHERE id_poll = {int:id_poll}', + array( + 'id_poll' => $pollID, + ) + ); + // Finally set the topic poll ID back to 0! + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET id_poll = {int:no_poll} + WHERE id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + 'no_poll' => 0, + ) + ); + + // Take the moderator back to the topic. + redirectexit('topic=' . $topic . '.' . $_REQUEST['start']); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Post.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Post.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2894 @@ + (int) $_REQUEST['msg'], + )); + if ($smcFunc['db_num_rows']($request) != 1) + unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']); + else + list ($topic) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // Check if it's locked. It isn't locked if no topic is specified. + if (!empty($topic)) + { + $request = $smcFunc['db_query']('', ' + SELECT + t.locked, IFNULL(ln.id_topic, 0) AS notify, t.is_sticky, t.id_poll, t.id_last_msg, mf.id_member, + t.id_first_msg, mf.subject, + CASE WHEN ml.poster_time > ml.modified_time THEN ml.poster_time ELSE ml.modified_time END AS last_post_time + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}log_notify AS ln ON (ln.id_topic = t.id_topic AND ln.id_member = {int:current_member}) + LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg) + WHERE t.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + ) + ); + list ($locked, $context['notify'], $sticky, $pollID, $context['topic_last_message'], $id_member_poster, $id_first_msg, $first_subject, $lastPostTime) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // If this topic already has a poll, they sure can't add another. + if (isset($_REQUEST['poll']) && $pollID > 0) + unset($_REQUEST['poll']); + + if (empty($_REQUEST['msg'])) + { + if ($user_info['is_guest'] && !allowedTo('post_reply_any') && (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any'))) + is_not_guest(); + + // By default the reply will be approved... + $context['becomes_approved'] = true; + if ($id_member_poster != $user_info['id']) + { + if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any')) + $context['becomes_approved'] = false; + else + isAllowedTo('post_reply_any'); + } + elseif (!allowedTo('post_reply_any')) + { + if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) + $context['becomes_approved'] = false; + else + isAllowedTo('post_reply_own'); + } + } + else + $context['becomes_approved'] = true; + + $context['can_lock'] = allowedTo('lock_any') || ($user_info['id'] == $id_member_poster && allowedTo('lock_own')); + $context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']); + + $context['notify'] = !empty($context['notify']); + $context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $sticky; + } + else + { + $context['becomes_approved'] = true; + if ((!$context['make_event'] || !empty($board))) + { + if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics')) + $context['becomes_approved'] = false; + else + isAllowedTo('post_new'); + } + + $locked = 0; + // !!! These won't work if you're making an event. + $context['can_lock'] = allowedTo(array('lock_any', 'lock_own')); + $context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']); + + $context['notify'] = !empty($context['notify']); + $context['sticky'] = !empty($_REQUEST['sticky']); + } + + // !!! These won't work if you're posting an event! + $context['can_notify'] = allowedTo('mark_any_notify'); + $context['can_move'] = allowedTo('move_any'); + $context['move'] = !empty($_REQUEST['move']); + $context['announce'] = !empty($_REQUEST['announce']); + // You can only announce topics that will get approved... + $context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved']; + $context['locked'] = !empty($locked) || !empty($_REQUEST['lock']); + $context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])); + + // Generally don't show the approval box... (Assume we want things approved) + $context['show_approval'] = false; + + // An array to hold all the attachments for this topic. + $context['current_attachments'] = array(); + + // Don't allow a post if it's locked and you aren't all powerful. + if ($locked && !allowedTo('moderate_board')) + fatal_lang_error('topic_locked', false); + // Check the users permissions - is the user allowed to add or post a poll? + if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1') + { + // New topic, new poll. + if (empty($topic)) + isAllowedTo('poll_post'); + // This is an old topic - but it is yours! Can you add to it? + elseif ($user_info['id'] == $id_member_poster && !allowedTo('poll_add_any')) + isAllowedTo('poll_add_own'); + // If you're not the owner, can you add to any poll? + else + isAllowedTo('poll_add_any'); + + require_once($sourcedir . '/Subs-Members.php'); + $allowedVoteGroups = groupsAllowedTo('poll_vote', $board); + + // Set up the poll options. + $context['poll_options'] = array( + 'max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']), + 'hide' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'], + 'expire' => !isset($_POST['poll_expire']) ? '' : $_POST['poll_expire'], + 'change_vote' => isset($_POST['poll_change_vote']), + 'guest_vote' => isset($_POST['poll_guest_vote']), + 'guest_vote_enabled' => in_array(-1, $allowedVoteGroups['allowed']), + ); + + // 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) + ); + } + + if ($context['make_event']) + { + // They might want to pick a board. + if (!isset($context['current_board'])) + $context['current_board'] = 0; + + // Start loading up the event info. + $context['event'] = array(); + $context['event']['title'] = isset($_REQUEST['evtitle']) ? htmlspecialchars(stripslashes($_REQUEST['evtitle'])) : ''; + + $context['event']['id'] = isset($_REQUEST['eventid']) ? (int) $_REQUEST['eventid'] : -1; + $context['event']['new'] = $context['event']['id'] == -1; + + // Permissions check! + isAllowedTo('calendar_post'); + + // Editing an event? (but NOT previewing!?) + if (!$context['event']['new'] && !isset($_REQUEST['subject'])) + { + // If the user doesn't have permission to edit the post in this topic, redirect them. + if ((empty($id_member_poster) || $id_member_poster != $user_info['id'] || !allowedTo('modify_own')) && !allowedTo('modify_any')) + { + require_once($sourcedir . '/Calendar.php'); + return CalendarPost(); + } + + // Get the current event information. + $request = $smcFunc['db_query']('', ' + SELECT + id_member, title, MONTH(start_date) AS month, DAYOFMONTH(start_date) AS day, + YEAR(start_date) AS year, (TO_DAYS(end_date) - TO_DAYS(start_date)) AS span + FROM {db_prefix}calendar + WHERE id_event = {int:id_event} + LIMIT 1', + array( + 'id_event' => $context['event']['id'], + ) + ); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Make sure the user is allowed to edit this event. + if ($row['id_member'] != $user_info['id']) + isAllowedTo('calendar_edit_any'); + elseif (!allowedTo('calendar_edit_any')) + isAllowedTo('calendar_edit_own'); + + $context['event']['month'] = $row['month']; + $context['event']['day'] = $row['day']; + $context['event']['year'] = $row['year']; + $context['event']['title'] = $row['title']; + $context['event']['span'] = $row['span'] + 1; + } + else + { + $today = getdate(); + + // You must have a month and year specified! + if (!isset($_REQUEST['month'])) + $_REQUEST['month'] = $today['mon']; + if (!isset($_REQUEST['year'])) + $_REQUEST['year'] = $today['year']; + + $context['event']['month'] = (int) $_REQUEST['month']; + $context['event']['year'] = (int) $_REQUEST['year']; + $context['event']['day'] = isset($_REQUEST['day']) ? $_REQUEST['day'] : ($_REQUEST['month'] == $today['mon'] ? $today['mday'] : 0); + $context['event']['span'] = isset($_REQUEST['span']) ? $_REQUEST['span'] : 1; + + // Make sure the year and month are in the valid range. + if ($context['event']['month'] < 1 || $context['event']['month'] > 12) + fatal_lang_error('invalid_month', false); + if ($context['event']['year'] < $modSettings['cal_minyear'] || $context['event']['year'] > $modSettings['cal_maxyear']) + fatal_lang_error('invalid_year', false); + + // Get a list of boards they can post in. + $boards = boardsAllowedTo('post_new'); + if (empty($boards)) + fatal_lang_error('cannot_post_new', 'user'); + + // Load a list of boards for this event in the context. + require_once($sourcedir . '/Subs-MessageIndex.php'); + $boardListOptions = array( + 'included_boards' => in_array(0, $boards) ? null : $boards, + 'not_redirection' => true, + 'use_permissions' => true, + 'selected_board' => empty($context['current_board']) ? $modSettings['cal_defaultboard'] : $context['current_board'], + ); + $context['event']['categories'] = getBoardList($boardListOptions); + } + + // Find the last day of the month. + $context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year'])); + + $context['event']['board'] = !empty($board) ? $board : $modSettings['cal_defaultboard']; + } + + if (empty($context['post_errors'])) + $context['post_errors'] = array(); + + // See if any new replies have come along. + if (empty($_REQUEST['msg']) && !empty($topic)) + { + if (empty($options['no_new_reply_warning']) && isset($_REQUEST['last_msg']) && $context['topic_last_message'] > $_REQUEST['last_msg']) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic} + AND id_msg > {int:last_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND approved = {int:approved}') . ' + LIMIT 1', + array( + 'current_topic' => $topic, + 'last_msg' => (int) $_REQUEST['last_msg'], + 'approved' => 1, + ) + ); + list ($context['new_replies']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (!empty($context['new_replies'])) + { + if ($context['new_replies'] == 1) + $txt['error_new_reply'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply']; + else + $txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']); + + // If they've come from the display page then we treat the error differently.... + if (isset($_GET['last_msg'])) + $newRepliesError = $context['new_replies']; + else + $context['post_error'][$context['new_replies'] == 1 ? 'new_reply' : 'new_replies'] = true; + + $modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts']; + } + } + // Check whether this is a really old post being bumped... + if (!empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky) && !isset($_REQUEST['subject'])) + $oldTopicError = true; + } + + // Get a response prefix (like 'Re:') in the default forum language. + if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix'))) + { + if ($language === $user_info['language']) + $context['response_prefix'] = $txt['response_prefix']; + else + { + loadLanguage('index', $language, false); + $context['response_prefix'] = $txt['response_prefix']; + loadLanguage('index'); + } + cache_put_data('response_prefix', $context['response_prefix'], 600); + } + + // Previewing, modifying, or posting? + if (isset($_REQUEST['message']) || !empty($context['post_error'])) + { + // Validate inputs. + if (empty($context['post_error'])) + { + if (htmltrim__recursive(htmlspecialchars__recursive($_REQUEST['subject'])) == '') + $context['post_error']['no_subject'] = true; + if (htmltrim__recursive(htmlspecialchars__recursive($_REQUEST['message'])) == '') + $context['post_error']['no_message'] = true; + if (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_REQUEST['message']) > $modSettings['max_messageLength']) + $context['post_error']['long_message'] = true; + + // Are you... a guest? + if ($user_info['is_guest']) + { + $_REQUEST['guestname'] = !isset($_REQUEST['guestname']) ? '' : trim($_REQUEST['guestname']); + $_REQUEST['email'] = !isset($_REQUEST['email']) ? '' : trim($_REQUEST['email']); + + // Validate the name and email. + if (!isset($_REQUEST['guestname']) || trim(strtr($_REQUEST['guestname'], '_', ' ')) == '') + $context['post_error']['no_name'] = true; + elseif ($smcFunc['strlen']($_REQUEST['guestname']) > 25) + $context['post_error']['long_name'] = true; + else + { + require_once($sourcedir . '/Subs-Members.php'); + if (isReservedName(htmlspecialchars($_REQUEST['guestname']), 0, true, false)) + $context['post_error']['bad_name'] = true; + } + + if (empty($modSettings['guest_post_no_email'])) + { + if (!isset($_REQUEST['email']) || $_REQUEST['email'] == '') + $context['post_error']['no_email'] = true; + elseif (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_REQUEST['email']) == 0) + $context['post_error']['bad_email'] = true; + } + } + + // This is self explanatory - got any questions? + if (isset($_REQUEST['question']) && trim($_REQUEST['question']) == '') + $context['post_error']['no_question'] = true; + + // This means they didn't click Post and get an error. + $really_previewing = true; + } + else + { + if (!isset($_REQUEST['subject'])) + $_REQUEST['subject'] = ''; + if (!isset($_REQUEST['message'])) + $_REQUEST['message'] = ''; + if (!isset($_REQUEST['icon'])) + $_REQUEST['icon'] = 'xx'; + + // They are previewing if they asked to preview (i.e. came from quick reply). + $really_previewing = !empty($_POST['preview']); + } + + // In order to keep the approval status flowing through, we have to pass it through the form... + $context['becomes_approved'] = empty($_REQUEST['not_approved']); + $context['show_approval'] = isset($_REQUEST['approve']) ? ($_REQUEST['approve'] ? 2 : 1) : 0; + $context['can_announce'] &= $context['becomes_approved']; + + // Set up the inputs for the form. + $form_subject = strtr($smcFunc['htmlspecialchars']($_REQUEST['subject']), array("\r" => '', "\n" => '', "\t" => '')); + $form_message = $smcFunc['htmlspecialchars']($_REQUEST['message'], ENT_QUOTES); + + // Make sure the subject isn't too long - taking into account special characters. + if ($smcFunc['strlen']($form_subject) > 100) + $form_subject = $smcFunc['substr']($form_subject, 0, 100); + + // Have we inadvertently trimmed off the subject of useful information? + if ($smcFunc['htmltrim']($form_subject) === '') + $context['post_error']['no_subject'] = true; + + // Any errors occurred? + if (!empty($context['post_error'])) + { + loadLanguage('Errors'); + + $context['error_type'] = 'minor'; + + $context['post_error']['messages'] = array(); + foreach ($context['post_error'] as $post_error => $dummy) + { + if ($post_error == 'messages') + continue; + + if ($post_error == 'long_message') + $txt['error_' . $post_error] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']); + + $context['post_error']['messages'][] = $txt['error_' . $post_error]; + + // If it's not a minor error flag it as such. + if (!in_array($post_error, array('new_reply', 'not_approved', 'new_replies', 'old_topic', 'need_qr_verification'))) + $context['error_type'] = 'serious'; + } + } + + if (isset($_REQUEST['poll'])) + { + $context['question'] = isset($_REQUEST['question']) ? $smcFunc['htmlspecialchars'](trim($_REQUEST['question'])) : ''; + + $context['choices'] = array(); + $choice_id = 0; + + $_POST['options'] = empty($_POST['options']) ? array() : htmlspecialchars__recursive($_POST['options']); + foreach ($_POST['options'] as $option) + { + if (trim($option) == '') + continue; + + $context['choices'][] = array( + 'id' => $choice_id++, + 'number' => $choice_id, + 'label' => $option, + 'is_last' => false + ); + } + + if (count($context['choices']) < 2) + { + $context['choices'][] = array( + 'id' => $choice_id++, + 'number' => $choice_id, + 'label' => '', + 'is_last' => false + ); + $context['choices'][] = array( + 'id' => $choice_id++, + 'number' => $choice_id, + 'label' => '', + 'is_last' => false + ); + } + $context['choices'][count($context['choices']) - 1]['is_last'] = true; + } + + // Are you... a guest? + if ($user_info['is_guest']) + { + $_REQUEST['guestname'] = !isset($_REQUEST['guestname']) ? '' : trim($_REQUEST['guestname']); + $_REQUEST['email'] = !isset($_REQUEST['email']) ? '' : trim($_REQUEST['email']); + + $_REQUEST['guestname'] = htmlspecialchars($_REQUEST['guestname']); + $context['name'] = $_REQUEST['guestname']; + $_REQUEST['email'] = htmlspecialchars($_REQUEST['email']); + $context['email'] = $_REQUEST['email']; + + $user_info['name'] = $_REQUEST['guestname']; + } + + // Only show the preview stuff if they hit Preview. + if ($really_previewing == true || isset($_REQUEST['xml'])) + { + // Set up the preview message and subject and censor them... + $context['preview_message'] = $form_message; + preparsecode($form_message, true); + preparsecode($context['preview_message']); + + // Do all bulletin board code tags, with or without smileys. + $context['preview_message'] = parse_bbc($context['preview_message'], isset($_REQUEST['ns']) ? 0 : 1); + + if ($form_subject != '') + { + $context['preview_subject'] = $form_subject; + + censorText($context['preview_subject']); + censorText($context['preview_message']); + } + else + $context['preview_subject'] = '' . $txt['no_subject'] . ''; + + // Protect any CDATA blocks. + if (isset($_REQUEST['xml'])) + $context['preview_message'] = strtr($context['preview_message'], array(']]>' => ']]]]>')); + } + + // Set up the checkboxes. + $context['notify'] = !empty($_REQUEST['notify']); + $context['use_smileys'] = !isset($_REQUEST['ns']); + + $context['icon'] = isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : 'xx'; + + // Set the destination action for submission. + $context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['msg']) ? ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '') . (isset($_REQUEST['poll']) ? ';poll' : ''); + $context['submit_label'] = isset($_REQUEST['msg']) ? $txt['save'] : $txt['post']; + + // Previewing an edit? + if (isset($_REQUEST['msg']) && !empty($topic)) + { + // Get the existing message. + $request = $smcFunc['db_query']('', ' + SELECT + m.id_member, m.modified_time, m.smileys_enabled, m.body, + m.poster_name, m.poster_email, m.subject, m.icon, m.approved, + IFNULL(a.size, -1) AS filesize, a.filename, a.id_attach, + a.approved AS attachment_approved, t.id_member_started AS id_member_poster, + m.poster_time + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic}) + LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type}) + WHERE m.id_msg = {int:id_msg} + AND m.id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + 'attachment_type' => 0, + 'id_msg' => $_REQUEST['msg'], + ) + ); + // The message they were trying to edit was most likely deleted. + // !!! Change this error message? + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_board', false); + $row = $smcFunc['db_fetch_assoc']($request); + + $attachment_stuff = array($row); + while ($row2 = $smcFunc['db_fetch_assoc']($request)) + $attachment_stuff[] = $row2; + $smcFunc['db_free_result']($request); + + if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any')) + { + // Give an extra five minutes over the disable time threshold, so they can type - assuming the post is public. + if ($row['approved'] && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) + fatal_lang_error('modify_post_time_passed', false); + elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_own')) + isAllowedTo('modify_replies'); + else + isAllowedTo('modify_own'); + } + elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_any')) + isAllowedTo('modify_replies'); + else + isAllowedTo('modify_any'); + + if (!empty($modSettings['attachmentEnable'])) + { + $request = $smcFunc['db_query']('', ' + SELECT IFNULL(size, -1) AS filesize, filename, id_attach, approved + FROM {db_prefix}attachments + WHERE id_msg = {int:id_msg} + AND attachment_type = {int:attachment_type}', + array( + 'id_msg' => (int) $_REQUEST['msg'], + 'attachment_type' => 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['filesize'] <= 0) + continue; + $context['current_attachments'][] = array( + 'name' => htmlspecialchars($row['filename']), + 'id' => $row['id_attach'], + 'approved' => $row['approved'], + ); + } + $smcFunc['db_free_result']($request); + } + + // Allow moderators to change names.... + if (allowedTo('moderate_forum') && !empty($topic)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, poster_name, poster_email + FROM {db_prefix}messages + WHERE id_msg = {int:id_msg} + AND id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + 'id_msg' => (int) $_REQUEST['msg'], + ) + ); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + if (empty($row['id_member'])) + { + $context['name'] = htmlspecialchars($row['poster_name']); + $context['email'] = htmlspecialchars($row['poster_email']); + } + } + } + + // No check is needed, since nothing is really posted. + checkSubmitOnce('free'); + } + // Editing a message... + elseif (isset($_REQUEST['msg']) && !empty($topic)) + { + $_REQUEST['msg'] = (int) $_REQUEST['msg']; + + // Get the existing message. + $request = $smcFunc['db_query']('', ' + SELECT + m.id_member, m.modified_time, m.smileys_enabled, m.body, + m.poster_name, m.poster_email, m.subject, m.icon, m.approved, + IFNULL(a.size, -1) AS filesize, a.filename, a.id_attach, + a.approved AS attachment_approved, t.id_member_started AS id_member_poster, + m.poster_time + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic}) + LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type}) + WHERE m.id_msg = {int:id_msg} + AND m.id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + 'attachment_type' => 0, + 'id_msg' => $_REQUEST['msg'], + ) + ); + // The message they were trying to edit was most likely deleted. + // !!! Change this error message? + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_board', false); + $row = $smcFunc['db_fetch_assoc']($request); + + $attachment_stuff = array($row); + while ($row2 = $smcFunc['db_fetch_assoc']($request)) + $attachment_stuff[] = $row2; + $smcFunc['db_free_result']($request); + + if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any')) + { + // Give an extra five minutes over the disable time threshold, so they can type - assuming the post is public. + if ($row['approved'] && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) + fatal_lang_error('modify_post_time_passed', false); + elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_own')) + isAllowedTo('modify_replies'); + else + isAllowedTo('modify_own'); + } + elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_any')) + isAllowedTo('modify_replies'); + else + isAllowedTo('modify_any'); + + // When was it last modified? + if (!empty($row['modified_time'])) + $context['last_modified'] = timeformat($row['modified_time']); + + // Get the stuff ready for the form. + $form_subject = $row['subject']; + $form_message = un_preparsecode($row['body']); + censorText($form_message); + censorText($form_subject); + + // Check the boxes that should be checked. + $context['use_smileys'] = !empty($row['smileys_enabled']); + $context['icon'] = $row['icon']; + + // Show an "approve" box if the user can approve it, and the message isn't approved. + if (!$row['approved'] && !$context['show_approval']) + $context['show_approval'] = allowedTo('approve_posts'); + + // Load up 'em attachments! + foreach ($attachment_stuff as $attachment) + { + if ($attachment['filesize'] >= 0 && !empty($modSettings['attachmentEnable'])) + $context['current_attachments'][] = array( + 'name' => htmlspecialchars($attachment['filename']), + 'id' => $attachment['id_attach'], + 'approved' => $attachment['attachment_approved'], + ); + } + + // Allow moderators to change names.... + if (allowedTo('moderate_forum') && empty($row['id_member'])) + { + $context['name'] = htmlspecialchars($row['poster_name']); + $context['email'] = htmlspecialchars($row['poster_email']); + } + + // Set the destinaton. + $context['destination'] = 'post2;start=' . $_REQUEST['start'] . ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] . (isset($_REQUEST['poll']) ? ';poll' : ''); + $context['submit_label'] = $txt['save']; + } + // Posting... + else + { + // By default.... + $context['use_smileys'] = true; + $context['icon'] = 'xx'; + + if ($user_info['is_guest']) + { + $context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : ''; + $context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : ''; + } + $context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['poll']) ? ';poll' : ''); + + $context['submit_label'] = $txt['post']; + + // Posting a quoted reply? + if (!empty($topic) && !empty($_REQUEST['quote'])) + { + // Make sure they _can_ quote this post, and if so get it. + $request = $smcFunc['db_query']('', ' + SELECT m.subject, IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board}) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_msg = {int:id_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND m.approved = {int:is_approved}') . ' + LIMIT 1', + array( + 'id_msg' => (int) $_REQUEST['quote'], + 'is_approved' => 1, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('quoted_post_deleted', false); + list ($form_subject, $mname, $mdate, $form_message) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Add 'Re: ' to the front of the quoted subject. + if (trim($context['response_prefix']) != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0) + $form_subject = $context['response_prefix'] . $form_subject; + + // Censor the message and subject. + censorText($form_message); + censorText($form_subject); + + // But if it's in HTML world, turn them into htmlspecialchar's so they can be edited! + if (strpos($form_message, '[html]') !== false) + { + $parts = preg_split('~(\[/code\]|\[code(?:=[^\]]+)?\])~i', $form_message, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $n = count($parts); $i < $n; $i++) + { + // It goes 0 = outside, 1 = begin tag, 2 = inside, 3 = close tag, repeat. + if ($i % 4 == 0) + $parts[$i] = preg_replace('~\[html\](.+?)\[/html\]~ise', '\'[html]\' . preg_replace(\'~~i\', \'<br />
\', \'$1\') . \'[/html]\'', $parts[$i]); + } + $form_message = implode('', $parts); + } + + $form_message = preg_replace('~
~i', "\n", $form_message); + + // Remove any nested quotes, if necessary. + if (!empty($modSettings['removeNestedQuotes'])) + $form_message = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $form_message); + + // Add a quote string on the front and end. + $form_message = '[quote author=' . $mname . ' link=topic=' . $topic . '.msg' . (int) $_REQUEST['quote'] . '#msg' . (int) $_REQUEST['quote'] . ' date=' . $mdate . ']' . "\n" . rtrim($form_message) . "\n" . '[/quote]'; + } + // Posting a reply without a quote? + elseif (!empty($topic) && empty($_REQUEST['quote'])) + { + // Get the first message's subject. + $form_subject = $first_subject; + + // Add 'Re: ' to the front of the subject. + if (trim($context['response_prefix']) != '' && $form_subject != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0) + $form_subject = $context['response_prefix'] . $form_subject; + + // Censor the subject. + censorText($form_subject); + + $form_message = ''; + } + else + { + $form_subject = isset($_GET['subject']) ? $_GET['subject'] : ''; + $form_message = ''; + } + } + + // !!! This won't work if you're posting an event. + if (allowedTo('post_attachment') || allowedTo('post_unapproved_attachments')) + { + if (empty($_SESSION['temp_attachments'])) + $_SESSION['temp_attachments'] = array(); + + if (!empty($modSettings['currentAttachmentUploadDir'])) + { + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + + // Just use the current path for temp files. + $current_attach_dir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']]; + } + else + $current_attach_dir = $modSettings['attachmentUploadDir']; + + // If this isn't a new post, check the current attachments. + if (isset($_REQUEST['msg'])) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*), SUM(size) + FROM {db_prefix}attachments + WHERE id_msg = {int:id_msg} + AND attachment_type = {int:attachment_type}', + array( + 'id_msg' => (int) $_REQUEST['msg'], + 'attachment_type' => 0, + ) + ); + list ($quantity, $total_size) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + { + $quantity = 0; + $total_size = 0; + } + + $temp_start = 0; + + if (!empty($_SESSION['temp_attachments'])) + { + if ($context['current_action'] != 'post2' || !empty($_POST['from_qr'])) + { + $context['post_error']['messages'][] = $txt['error_temp_attachments']; + $context['error_type'] = 'minor'; + } + + foreach ($_SESSION['temp_attachments'] as $attachID => $name) + { + $temp_start++; + + if (preg_match('~^post_tmp_' . $user_info['id'] . '_\d+$~', $attachID) == 0) + { + unset($_SESSION['temp_attachments'][$attachID]); + continue; + } + + if (!empty($_POST['attach_del']) && !in_array($attachID, $_POST['attach_del'])) + { + $deleted_attachments = true; + unset($_SESSION['temp_attachments'][$attachID]); + @unlink($current_attach_dir . '/' . $attachID); + continue; + } + + $quantity++; + $total_size += filesize($current_attach_dir . '/' . $attachID); + + $context['current_attachments'][] = array( + 'name' => htmlspecialchars($name), + 'id' => $attachID, + 'approved' => 1, + ); + } + } + + if (!empty($_POST['attach_del'])) + { + $del_temp = array(); + foreach ($_POST['attach_del'] as $i => $dummy) + $del_temp[$i] = (int) $dummy; + + foreach ($context['current_attachments'] as $k => $dummy) + if (!in_array($dummy['id'], $del_temp)) + { + $context['current_attachments'][$k]['unchecked'] = true; + $deleted_attachments = !isset($deleted_attachments) || is_bool($deleted_attachments) ? 1 : $deleted_attachments + 1; + $quantity--; + } + } + + if (!empty($_FILES['attachment'])) + foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) + { + if ($_FILES['attachment']['name'][$n] == '') + continue; + + if (!is_uploaded_file($_FILES['attachment']['tmp_name'][$n]) || (@ini_get('open_basedir') == '' && !file_exists($_FILES['attachment']['tmp_name'][$n]))) + fatal_lang_error('attach_timeout', 'critical'); + + if (!empty($modSettings['attachmentSizeLimit']) && $_FILES['attachment']['size'][$n] > $modSettings['attachmentSizeLimit'] * 1024) + fatal_lang_error('file_too_big', false, array($modSettings['attachmentSizeLimit'])); + + $quantity++; + if (!empty($modSettings['attachmentNumPerPostLimit']) && $quantity > $modSettings['attachmentNumPerPostLimit']) + fatal_lang_error('attachments_limit_per_post', false, array($modSettings['attachmentNumPerPostLimit'])); + + $total_size += $_FILES['attachment']['size'][$n]; + if (!empty($modSettings['attachmentPostLimit']) && $total_size > $modSettings['attachmentPostLimit'] * 1024) + fatal_lang_error('file_too_big', false, array($modSettings['attachmentPostLimit'])); + + if (!empty($modSettings['attachmentCheckExtensions'])) + { + if (!in_array(strtolower(substr(strrchr($_FILES['attachment']['name'][$n], '.'), 1)), explode(',', strtolower($modSettings['attachmentExtensions'])))) + fatal_error($_FILES['attachment']['name'][$n] . '.
' . $txt['cant_upload_type'] . ' ' . $modSettings['attachmentExtensions'] . '.', false); + } + + if (!empty($modSettings['attachmentDirSizeLimit'])) + { + // Make sure the directory isn't full. + $dirSize = 0; + $dir = @opendir($current_attach_dir) or fatal_lang_error('cant_access_upload_path', 'critical'); + while ($file = readdir($dir)) + { + if ($file == '.' || $file == '..') + continue; + + if (preg_match('~^post_tmp_\d+_\d+$~', $file) != 0) + { + // Temp file is more than 5 hours old! + if (filemtime($current_attach_dir . '/' . $file) < time() - 18000) + @unlink($current_attach_dir . '/' . $file); + continue; + } + + $dirSize += filesize($current_attach_dir . '/' . $file); + } + closedir($dir); + + // Too big! Maybe you could zip it or something... + if ($_FILES['attachment']['size'][$n] + $dirSize > $modSettings['attachmentDirSizeLimit'] * 1024) + fatal_lang_error('ran_out_of_space'); + } + + if (!is_writable($current_attach_dir)) + fatal_lang_error('attachments_no_write', 'critical'); + + $attachID = 'post_tmp_' . $user_info['id'] . '_' . $temp_start++; + $_SESSION['temp_attachments'][$attachID] = basename($_FILES['attachment']['name'][$n]); + $context['current_attachments'][] = array( + 'name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n])), + 'id' => $attachID, + 'approved' => 1, + ); + + $destName = $current_attach_dir . '/' . $attachID; + + if (!move_uploaded_file($_FILES['attachment']['tmp_name'][$n], $destName)) + fatal_lang_error('attach_timeout', 'critical'); + @chmod($destName, 0644); + } + } + + // If we are coming here to make a reply, and someone has already replied... make a special warning message. + if (isset($newRepliesError)) + { + $context['post_error']['messages'][] = $newRepliesError == 1 ? $txt['error_new_reply'] : $txt['error_new_replies']; + $context['error_type'] = 'minor'; + } + + if (isset($oldTopicError)) + { + $context['post_error']['messages'][] = sprintf($txt['error_old_topic'], $modSettings['oldTopicDays']); + $context['error_type'] = 'minor'; + } + + // What are you doing? Posting a poll, modifying, previewing, new post, or reply... + if (isset($_REQUEST['poll'])) + $context['page_title'] = $txt['new_poll']; + elseif ($context['make_event']) + $context['page_title'] = $context['event']['id'] == -1 ? $txt['calendar_post_event'] : $txt['calendar_edit']; + elseif (isset($_REQUEST['msg'])) + $context['page_title'] = $txt['modify_msg']; + elseif (isset($_REQUEST['subject'], $context['preview_subject'])) + $context['page_title'] = $txt['preview'] . ' - ' . strip_tags($context['preview_subject']); + elseif (empty($topic)) + $context['page_title'] = $txt['start_new_topic']; + else + $context['page_title'] = $txt['post_reply']; + + // Build the link tree. + if (empty($topic)) + $context['linktree'][] = array( + 'name' => '' . $txt['start_new_topic'] . '' + ); + else + $context['linktree'][] = array( + 'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'], + 'name' => $form_subject, + 'extra_before' => '' . $context['page_title'] . ' ( ', + 'extra_after' => ' )' + ); + + // Give wireless a linktree url to the post screen, so that they can switch to full version. + if (WIRELESS) + $context['linktree'][count($context['linktree']) - 1]['url'] = $scripturl . '?action=post;' . (!empty($topic) ? 'topic=' . $topic : 'board=' . $board) . '.' . $_REQUEST['start'] . (isset($_REQUEST['msg']) ? ';msg=' . (int) $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : ''); + + // If they've unchecked an attachment, they may still want to attach that many more files, but don't allow more than num_allowed_attachments. + // !!! This won't work if you're posting an event. + $context['num_allowed_attachments'] = empty($modSettings['attachmentNumPerPostLimit']) ? 50 : min($modSettings['attachmentNumPerPostLimit'] - count($context['current_attachments']) + (isset($deleted_attachments) ? $deleted_attachments : 0), $modSettings['attachmentNumPerPostLimit']); + $context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments'))) && $context['num_allowed_attachments'] > 0; + $context['can_post_attachment_unapproved'] = allowedTo('post_attachment'); + + $context['subject'] = addcslashes($form_subject, '"'); + $context['message'] = str_replace(array('"', '<', '>', ' '), array('"', '<', '>', ' '), $form_message); + + // Needed for the editor and message icons. + require_once($sourcedir . '/Subs-Editor.php'); + + // Now create the editor. + $editorOptions = array( + 'id' => 'message', + 'value' => $context['message'], + 'labels' => array( + 'post_button' => $context['submit_label'], + ), + // add height and width for the editor + 'height' => '175px', + 'width' => '100%', + // We do XML preview here. + 'preview_type' => 2, + ); + create_control_richedit($editorOptions); + + // Store the ID. + $context['post_box_name'] = $editorOptions['id']; + + $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; + + $context['icon_url'] = ''; + for ($i = 0, $n = count($context['icons']); $i < $n; $i++) + { + $context['icons'][$i]['selected'] = $context['icon'] == $context['icons'][$i]['value']; + if ($context['icons'][$i]['selected']) + $context['icon_url'] = $context['icons'][$i]['url']; + } + if (empty($context['icon_url'])) + { + $context['icon_url'] = $settings[file_exists($settings['theme_dir'] . '/images/post/' . $context['icon'] . '.gif') ? 'images_url' : 'default_images_url'] . '/post/' . $context['icon'] . '.gif'; + array_unshift($context['icons'], array( + 'value' => $context['icon'], + 'name' => $txt['current_icon'], + 'url' => $context['icon_url'], + 'is_last' => empty($context['icons']), + 'selected' => true, + )); + } + + if (!empty($topic) && !empty($modSettings['topicSummaryPosts'])) + getTopic(); + + // If the user can post attachments prepare the warning labels. + if ($context['can_post_attachment']) + { + $context['allowed_extensions'] = strtr($modSettings['attachmentExtensions'], array(',' => ', ')); + $context['attachment_restrictions'] = array(); + $attachmentRestrictionTypes = array('attachmentNumPerPostLimit', 'attachmentPostLimit', 'attachmentSizeLimit'); + foreach ($attachmentRestrictionTypes as $type) + if (!empty($modSettings[$type])) + $context['attachment_restrictions'][] = sprintf($txt['attach_restrict_' . $type], $modSettings[$type]); + } + + $context['back_to_topic'] = isset($_REQUEST['goback']) || (isset($_REQUEST['msg']) && !isset($_REQUEST['subject'])); + $context['show_additional_options'] = !empty($_POST['additional_options']) || !empty($_SESSION['temp_attachments']) || !empty($deleted_attachments); + + $context['is_new_topic'] = empty($topic); + $context['is_new_post'] = !isset($_REQUEST['msg']); + $context['is_first_post'] = $context['is_new_topic'] || (isset($_REQUEST['msg']) && $_REQUEST['msg'] == $id_first_msg); + + // 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($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'post', + ); + $context['require_verification'] = create_control_verification($verificationOptions); + $context['visual_verification_id'] = $verificationOptions['id']; + } + + // If they came from quick reply, and have to enter verification details, give them some notice. + if (!empty($_REQUEST['from_qr']) && !empty($context['require_verification'])) + { + $context['post_error']['messages'][] = $txt['enter_verification_details']; + $context['error_type'] = 'minor'; + } + + // WYSIWYG only works if BBC is enabled + $modSettings['disable_wysiwyg'] = !empty($modSettings['disable_wysiwyg']) || empty($modSettings['enableBBC']); + + // Register this form in the session variables. + checkSubmitOnce('register'); + + // Finally, load the template. + if (WIRELESS && WIRELESS_PROTOCOL != 'wap') + $context['sub_template'] = WIRELESS_PROTOCOL . '_post'; + elseif (!isset($_REQUEST['xml'])) + loadTemplate('Post'); +} + +function Post2() +{ + global $board, $topic, $txt, $modSettings, $sourcedir, $context; + global $user_info, $board_info, $options, $smcFunc; + + // Sneaking off, are we? + if (empty($_POST) && empty($topic)) + redirectexit('action=post;board=' . $board . '.0'); + elseif (empty($_POST) && !empty($topic)) + redirectexit('action=post;topic=' . $topic . '.0'); + + // No need! + $context['robot_no_index'] = true; + + // If we came from WYSIWYG then turn it back into BBC regardless. + if (!empty($_REQUEST['message_mode']) && isset($_REQUEST['message'])) + { + require_once($sourcedir . '/Subs-Editor.php'); + + $_REQUEST['message'] = html_to_bbc($_REQUEST['message']); + + // We need to unhtml it now as it gets done shortly. + $_REQUEST['message'] = un_htmlspecialchars($_REQUEST['message']); + + // We need this for everything else. + $_POST['message'] = $_REQUEST['message']; + } + + // Previewing? Go back to start. + if (isset($_REQUEST['preview'])) + return Post(); + + // Prevent double submission of this form. + checkSubmitOnce('check'); + + // No errors as yet. + $post_errors = array(); + + // If the session has timed out, let the user re-submit their form. + if (checkSession('post', '', false) != '') + $post_errors[] = 'session_timeout'; + + // Wrong verification code? + if (!$user_info['is_admin'] && !$user_info['is_mod'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || ($user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1))) + { + require_once($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'post', + ); + $context['require_verification'] = create_control_verification($verificationOptions, true); + if (is_array($context['require_verification'])) + $post_errors = array_merge($post_errors, $context['require_verification']); + } + + require_once($sourcedir . '/Subs-Post.php'); + loadLanguage('Post'); + + // If this isn't a new topic load the topic info that we need. + if (!empty($topic)) + { + $request = $smcFunc['db_query']('', ' + SELECT locked, is_sticky, id_poll, approved, id_first_msg, id_last_msg, id_member_started, id_board + FROM {db_prefix}topics + WHERE id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + $topic_info = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Though the topic should be there, it might have vanished. + if (!is_array($topic_info)) + fatal_lang_error('topic_doesnt_exist'); + + // Did this topic suddenly move? Just checking... + if ($topic_info['id_board'] != $board) + fatal_lang_error('not_a_topic'); + } + + // Replying to a topic? + if (!empty($topic) && !isset($_REQUEST['msg'])) + { + // Don't allow a post if it's locked. + if ($topic_info['locked'] != 0 && !allowedTo('moderate_board')) + fatal_lang_error('topic_locked', false); + + // Sorry, multiple polls aren't allowed... yet. You should stop giving me ideas :P. + if (isset($_REQUEST['poll']) && $topic_info['id_poll'] > 0) + unset($_REQUEST['poll']); + + // Do the permissions and approval stuff... + $becomesApproved = true; + if ($topic_info['id_member_started'] != $user_info['id']) + { + if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any')) + $becomesApproved = false; + else + isAllowedTo('post_reply_any'); + } + elseif (!allowedTo('post_reply_any')) + { + if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) + $becomesApproved = false; + else + isAllowedTo('post_reply_own'); + } + + if (isset($_POST['lock'])) + { + // Nothing is changed to the lock. + if ((empty($topic_info['locked']) && empty($_POST['lock'])) || (!empty($_POST['lock']) && !empty($topic_info['locked']))) + unset($_POST['lock']); + // You're have no permission to lock this topic. + elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started'])) + unset($_POST['lock']); + // You are allowed to (un)lock your own topic only. + elseif (!allowedTo('lock_any')) + { + // You cannot override a moderator lock. + if ($topic_info['locked'] == 1) + unset($_POST['lock']); + else + $_POST['lock'] = empty($_POST['lock']) ? 0 : 2; + } + // Hail mighty moderator, (un)lock this topic immediately. + else + $_POST['lock'] = empty($_POST['lock']) ? 0 : 1; + } + + // So you wanna (un)sticky this...let's see. + if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || $_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky'))) + unset($_POST['sticky']); + + // If the number of replies has changed, if the setting is enabled, go back to Post() - which handles the error. + if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg']) + { + $_REQUEST['preview'] = true; + return Post(); + } + + $posterIsGuest = $user_info['is_guest']; + } + // Posting a new topic. + elseif (empty($topic)) + { + // Now don't be silly, new topics will get their own id_msg soon enough. + unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']); + + // Do like, the permissions, for safety and stuff... + $becomesApproved = true; + if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics')) + $becomesApproved = false; + else + isAllowedTo('post_new'); + + if (isset($_POST['lock'])) + { + // New topics are by default not locked. + if (empty($_POST['lock'])) + unset($_POST['lock']); + // Besides, you need permission. + elseif (!allowedTo(array('lock_any', 'lock_own'))) + unset($_POST['lock']); + // A moderator-lock (1) can override a user-lock (2). + else + $_POST['lock'] = allowedTo('lock_any') ? 1 : 2; + } + + if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || empty($_POST['sticky']) || !allowedTo('make_sticky'))) + unset($_POST['sticky']); + + $posterIsGuest = $user_info['is_guest']; + } + // Modifying an existing message? + elseif (isset($_REQUEST['msg']) && !empty($topic)) + { + $_REQUEST['msg'] = (int) $_REQUEST['msg']; + + $request = $smcFunc['db_query']('', ' + SELECT id_member, poster_name, poster_email, poster_time, approved + FROM {db_prefix}messages + WHERE id_msg = {int:id_msg} + LIMIT 1', + array( + 'id_msg' => $_REQUEST['msg'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('cant_find_messages', false); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + if (!empty($topic_info['locked']) && !allowedTo('moderate_board')) + fatal_lang_error('topic_locked', false); + + if (isset($_POST['lock'])) + { + // Nothing changes to the lock status. + if ((empty($_POST['lock']) && empty($topic_info['locked'])) || (!empty($_POST['lock']) && !empty($topic_info['locked']))) + unset($_POST['lock']); + // You're simply not allowed to (un)lock this. + elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started'])) + unset($_POST['lock']); + // You're only allowed to lock your own topics. + elseif (!allowedTo('lock_any')) + { + // You're not allowed to break a moderator's lock. + if ($topic_info['locked'] == 1) + unset($_POST['lock']); + // Lock it with a soft lock or unlock it. + else + $_POST['lock'] = empty($_POST['lock']) ? 0 : 2; + } + // You must be the moderator. + else + $_POST['lock'] = empty($_POST['lock']) ? 0 : 1; + } + + // Change the sticky status of this topic? + if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky'])) + unset($_POST['sticky']); + + if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any')) + { + if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) + fatal_lang_error('modify_post_time_passed', false); + elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own')) + isAllowedTo('modify_replies'); + else + isAllowedTo('modify_own'); + } + elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any')) + { + isAllowedTo('modify_replies'); + + // If you're modifying a reply, I say it better be logged... + $moderationAction = true; + } + else + { + isAllowedTo('modify_any'); + + // Log it, assuming you're not modifying your own post. + if ($row['id_member'] != $user_info['id']) + $moderationAction = true; + } + + $posterIsGuest = empty($row['id_member']); + + // Can they approve it? + $can_approve = allowedTo('approve_posts'); + $becomesApproved = $modSettings['postmod_active'] ? ($can_approve && !$row['approved'] ? (!empty($_REQUEST['approve']) ? 1 : 0) : $row['approved']) : 1; + $approve_has_changed = $row['approved'] != $becomesApproved; + + if (!allowedTo('moderate_forum') || !$posterIsGuest) + { + $_POST['guestname'] = $row['poster_name']; + $_POST['email'] = $row['poster_email']; + } + } + + // If the poster is a guest evaluate the legality of name and email. + if ($posterIsGuest) + { + $_POST['guestname'] = !isset($_POST['guestname']) ? '' : trim($_POST['guestname']); + $_POST['email'] = !isset($_POST['email']) ? '' : trim($_POST['email']); + + if ($_POST['guestname'] == '' || $_POST['guestname'] == '_') + $post_errors[] = 'no_name'; + if ($smcFunc['strlen']($_POST['guestname']) > 25) + $post_errors[] = 'long_name'; + + if (empty($modSettings['guest_post_no_email'])) + { + // Only check if they changed it! + if (!isset($row) || $row['poster_email'] != $_POST['email']) + { + if (!allowedTo('moderate_forum') && (!isset($_POST['email']) || $_POST['email'] == '')) + $post_errors[] = 'no_email'; + if (!allowedTo('moderate_forum') && preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['email']) == 0) + $post_errors[] = 'bad_email'; + } + + // Now make sure this email address is not banned from posting. + isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title'])); + } + + // In case they are making multiple posts this visit, help them along by storing their name. + if (empty($post_errors)) + { + $_SESSION['guest_name'] = $_POST['guestname']; + $_SESSION['guest_email'] = $_POST['email']; + } + } + + // Check the subject and message. + if (!isset($_POST['subject']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) === '') + $post_errors[] = 'no_subject'; + if (!isset($_POST['message']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['message']), ENT_QUOTES) === '') + $post_errors[] = 'no_message'; + elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['message']) > $modSettings['max_messageLength']) + $post_errors[] = 'long_message'; + else + { + // Prepare the message a bit for some additional testing. + $_POST['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES); + + // Preparse code. (Zef) + if ($user_info['is_guest']) + $user_info['name'] = $_POST['guestname']; + preparsecode($_POST['message']); + + // Let's see if there's still some content left without the tags. + if ($smcFunc['htmltrim'](strip_tags(parse_bbc($_POST['message'], false), '')) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false)) + $post_errors[] = 'no_message'; + } + if (isset($_POST['calendar']) && !isset($_REQUEST['deleteevent']) && $smcFunc['htmltrim']($_POST['evtitle']) === '') + $post_errors[] = 'no_event'; + // You are not! + if (isset($_POST['message']) && strtolower($_POST['message']) == 'i am the administrator.' && !$user_info['is_admin']) + fatal_error('Knave! Masquerader! Charlatan!', false); + + // Validate the poll... + if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1') + { + if (!empty($topic) && !isset($_REQUEST['msg'])) + fatal_lang_error('no_access', false); + + // This is a new topic... so it's a new poll. + if (empty($topic)) + isAllowedTo('poll_post'); + // Can you add to your own topics? + elseif ($user_info['id'] == $topic_info['id_member_started'] && !allowedTo('poll_add_any')) + isAllowedTo('poll_add_own'); + // Can you add polls to any topic, then? + else + isAllowedTo('poll_add_any'); + + if (!isset($_POST['question']) || trim($_POST['question']) == '') + $post_errors[] = 'no_question'; + + $_POST['options'] = empty($_POST['options']) ? array() : htmltrim__recursive($_POST['options']); + + // Get rid of empty ones. + foreach ($_POST['options'] as $k => $option) + if ($option == '') + unset($_POST['options'][$k], $_POST['options'][$k]); + + // What are you going to vote between with one choice?!? + if (count($_POST['options']) < 2) + $post_errors[] = 'poll_few'; + } + + if ($posterIsGuest) + { + // If user is a guest, make sure the chosen name isn't taken. + require_once($sourcedir . '/Subs-Members.php'); + if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($row['poster_name']) || $_POST['guestname'] != $row['poster_name'])) + $post_errors[] = 'bad_name'; + } + // If the user isn't a guest, get his or her name and email. + elseif (!isset($_REQUEST['msg'])) + { + $_POST['guestname'] = $user_info['username']; + $_POST['email'] = $user_info['email']; + } + + // Any mistakes? + if (!empty($post_errors)) + { + loadLanguage('Errors'); + // Previewing. + $_REQUEST['preview'] = true; + + $context['post_error'] = array('messages' => array()); + foreach ($post_errors as $post_error) + { + $context['post_error'][$post_error] = true; + if ($post_error == 'long_message') + $txt['error_' . $post_error] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']); + + $context['post_error']['messages'][] = $txt['error_' . $post_error]; + } + + return Post(); + } + + // Make sure the user isn't spamming the board. + if (!isset($_REQUEST['msg'])) + spamProtection('post'); + + // At about this point, we're posting and that's that. + ignore_user_abort(true); + @set_time_limit(300); + + // Add special html entities to the subject, name, and email. + $_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => '')); + $_POST['guestname'] = htmlspecialchars($_POST['guestname']); + $_POST['email'] = htmlspecialchars($_POST['email']); + + // At this point, we want to make sure the subject isn't too long. + if ($smcFunc['strlen']($_POST['subject']) > 100) + $_POST['subject'] = $smcFunc['substr']($_POST['subject'], 0, 100); + + // Make the poll... + if (isset($_REQUEST['poll'])) + { + // Make sure that the user has not entered a ridiculous number of options.. + if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0) + $_POST['poll_max_votes'] = 1; + elseif ($_POST['poll_max_votes'] > count($_POST['options'])) + $_POST['poll_max_votes'] = count($_POST['options']); + else + $_POST['poll_max_votes'] = (int) $_POST['poll_max_votes']; + + $_POST['poll_expire'] = (int) $_POST['poll_expire']; + $_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']); + + // Just set it to zero if it's not there.. + if (!isset($_POST['poll_hide'])) + $_POST['poll_hide'] = 0; + else + $_POST['poll_hide'] = (int) $_POST['poll_hide']; + $_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0; + + $_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0; + // Make sure guests are actually allowed to vote generally. + if ($_POST['poll_guest_vote']) + { + require_once($sourcedir . '/Subs-Members.php'); + $allowedVoteGroups = groupsAllowedTo('poll_vote', $board); + if (!in_array(-1, $allowedVoteGroups['allowed'])) + $_POST['poll_guest_vote'] = 0; + } + + // If the user tries to set the poll too far in advance, don't let them. + if (!empty($_POST['poll_expire']) && $_POST['poll_expire'] < 1) + fatal_lang_error('poll_range_error', false); + // Don't allow them to select option 2 for hidden results if it's not time limited. + elseif (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2) + $_POST['poll_hide'] = 1; + + // Clean up the question and answers. + $_POST['question'] = htmlspecialchars($_POST['question']); + $_POST['question'] = $smcFunc['truncate']($_POST['question'], 255); + $_POST['question'] = preg_replace('~&#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', $_POST['question']); + $_POST['options'] = htmlspecialchars__recursive($_POST['options']); + } + + // Check if they are trying to delete any current attachments.... + if (isset($_REQUEST['msg'], $_POST['attach_del']) && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')))) + { + $del_temp = array(); + foreach ($_POST['attach_del'] as $i => $dummy) + $del_temp[$i] = (int) $dummy; + + require_once($sourcedir . '/ManageAttachments.php'); + $attachmentQuery = array( + 'attachment_type' => 0, + 'id_msg' => (int) $_REQUEST['msg'], + 'not_id_attach' => $del_temp, + ); + removeAttachments($attachmentQuery); + } + + // ...or attach a new file... + if (isset($_FILES['attachment']['name']) || (!empty($_SESSION['temp_attachments']) && empty($_POST['from_qr']))) + { + // Verify they can post them! + if (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_attachments')) + isAllowedTo('post_attachment'); + + // Make sure we're uploading to the right place. + if (!empty($modSettings['currentAttachmentUploadDir'])) + { + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + + // The current directory, of course! + $current_attach_dir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']]; + } + else + $current_attach_dir = $modSettings['attachmentUploadDir']; + + // If this isn't a new post, check the current attachments. + if (isset($_REQUEST['msg'])) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*), SUM(size) + FROM {db_prefix}attachments + WHERE id_msg = {int:id_msg} + AND attachment_type = {int:attachment_type}', + array( + 'id_msg' => (int) $_REQUEST['msg'], + 'attachment_type' => 0, + ) + ); + list ($quantity, $total_size) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + { + $quantity = 0; + $total_size = 0; + } + + if (!empty($_SESSION['temp_attachments'])) + foreach ($_SESSION['temp_attachments'] as $attachID => $name) + { + if (preg_match('~^post_tmp_' . $user_info['id'] . '_\d+$~', $attachID) == 0) + continue; + + if (!empty($_POST['attach_del']) && !in_array($attachID, $_POST['attach_del'])) + { + unset($_SESSION['temp_attachments'][$attachID]); + @unlink($current_attach_dir . '/' . $attachID); + continue; + } + + $_FILES['attachment']['tmp_name'][] = $attachID; + $_FILES['attachment']['name'][] = $name; + $_FILES['attachment']['size'][] = filesize($current_attach_dir . '/' . $attachID); + list ($_FILES['attachment']['width'][], $_FILES['attachment']['height'][]) = @getimagesize($current_attach_dir . '/' . $attachID); + + unset($_SESSION['temp_attachments'][$attachID]); + } + + if (!isset($_FILES['attachment']['name'])) + $_FILES['attachment']['tmp_name'] = array(); + + $attachIDs = array(); + foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) + { + if ($_FILES['attachment']['name'][$n] == '') + continue; + + // Have we reached the maximum number of files we are allowed? + $quantity++; + if (!empty($modSettings['attachmentNumPerPostLimit']) && $quantity > $modSettings['attachmentNumPerPostLimit']) + { + checkSubmitOnce('free'); + fatal_lang_error('attachments_limit_per_post', false, array($modSettings['attachmentNumPerPostLimit'])); + } + + // Check the total upload size for this post... + $total_size += $_FILES['attachment']['size'][$n]; + if (!empty($modSettings['attachmentPostLimit']) && $total_size > $modSettings['attachmentPostLimit'] * 1024) + { + checkSubmitOnce('free'); + fatal_lang_error('file_too_big', false, array($modSettings['attachmentPostLimit'])); + } + + $attachmentOptions = array( + 'post' => isset($_REQUEST['msg']) ? $_REQUEST['msg'] : 0, + 'poster' => $user_info['id'], + 'name' => $_FILES['attachment']['name'][$n], + 'tmp_name' => $_FILES['attachment']['tmp_name'][$n], + 'size' => $_FILES['attachment']['size'][$n], + 'approved' => !$modSettings['postmod_active'] || allowedTo('post_attachment'), + ); + + if (createAttachment($attachmentOptions)) + { + $attachIDs[] = $attachmentOptions['id']; + if (!empty($attachmentOptions['thumb'])) + $attachIDs[] = $attachmentOptions['thumb']; + } + else + { + if (in_array('could_not_upload', $attachmentOptions['errors'])) + { + checkSubmitOnce('free'); + fatal_lang_error('attach_timeout', 'critical'); + } + if (in_array('too_large', $attachmentOptions['errors'])) + { + checkSubmitOnce('free'); + fatal_lang_error('file_too_big', false, array($modSettings['attachmentSizeLimit'])); + } + if (in_array('bad_extension', $attachmentOptions['errors'])) + { + checkSubmitOnce('free'); + fatal_error($attachmentOptions['name'] . '.
' . $txt['cant_upload_type'] . ' ' . $modSettings['attachmentExtensions'] . '.', false); + } + if (in_array('directory_full', $attachmentOptions['errors'])) + { + checkSubmitOnce('free'); + fatal_lang_error('ran_out_of_space', 'critical'); + } + if (in_array('bad_filename', $attachmentOptions['errors'])) + { + checkSubmitOnce('free'); + fatal_error(basename($attachmentOptions['name']) . '.
' . $txt['restricted_filename'] . '.', 'critical'); + } + if (in_array('taken_filename', $attachmentOptions['errors'])) + { + checkSubmitOnce('free'); + fatal_lang_error('filename_exists'); + } + if (in_array('bad_attachment', $attachmentOptions['errors'])) + { + checkSubmitOnce('free'); + fatal_lang_error('bad_attachment'); + } + } + } + } + + // Make the poll... + if (isset($_REQUEST['poll'])) + { + // Create the poll. + $smcFunc['db_insert']('', + '{db_prefix}polls', + array( + 'question' => 'string-255', 'hide_results' => 'int', 'max_votes' => 'int', 'expire_time' => 'int', 'id_member' => 'int', + 'poster_name' => 'string-255', 'change_vote' => 'int', 'guest_vote' => 'int' + ), + array( + $_POST['question'], $_POST['poll_hide'], $_POST['poll_max_votes'], (empty($_POST['poll_expire']) ? 0 : time() + $_POST['poll_expire'] * 3600 * 24), $user_info['id'], + $_POST['guestname'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'], + ), + array('id_poll') + ); + $id_poll = $smcFunc['db_insert_id']('{db_prefix}polls', 'id_poll'); + + // Create each answer choice. + $i = 0; + $pollOptions = array(); + foreach ($_POST['options'] as $option) + { + $pollOptions[] = array($id_poll, $i, $option); + $i++; + } + + $smcFunc['db_insert']('insert', + '{db_prefix}poll_choices', + array('id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255'), + $pollOptions, + array('id_poll', 'id_choice') + ); + } + else + $id_poll = 0; + + // Creating a new topic? + $newTopic = empty($_REQUEST['msg']) && empty($topic); + + $_POST['icon'] = !empty($attachIDs) && $_POST['icon'] == 'xx' ? 'clip' : $_POST['icon']; + + // Collect all parameters for the creation or modification of a post. + $msgOptions = array( + 'id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'], + 'subject' => $_POST['subject'], + 'body' => $_POST['message'], + 'icon' => preg_replace('~[\./\\\\*:"\'<>]~', '', $_POST['icon']), + 'smileys_enabled' => !isset($_POST['ns']), + 'attachments' => empty($attachIDs) ? array() : $attachIDs, + 'approved' => $becomesApproved, + ); + $topicOptions = array( + 'id' => empty($topic) ? 0 : $topic, + 'board' => $board, + 'poll' => isset($_REQUEST['poll']) ? $id_poll : null, + 'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null, + 'sticky_mode' => isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics']) ? (int) $_POST['sticky'] : null, + 'mark_as_read' => true, + 'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']), + ); + $posterOptions = array( + 'id' => $user_info['id'], + 'name' => $_POST['guestname'], + 'email' => $_POST['email'], + 'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'], + ); + + // This is an already existing message. Edit it. + if (!empty($_REQUEST['msg'])) + { + // Have admins allowed people to hide their screwups? + if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member']) + { + $msgOptions['modify_time'] = time(); + $msgOptions['modify_name'] = $user_info['name']; + } + + // This will save some time... + if (empty($approve_has_changed)) + unset($msgOptions['approved']); + + modifyPost($msgOptions, $topicOptions, $posterOptions); + } + // This is a new topic or an already existing one. Save it. + else + { + createPost($msgOptions, $topicOptions, $posterOptions); + + if (isset($topicOptions['id'])) + $topic = $topicOptions['id']; + } + + // Editing or posting an event? + if (isset($_POST['calendar']) && (!isset($_REQUEST['eventid']) || $_REQUEST['eventid'] == -1)) + { + require_once($sourcedir . '/Subs-Calendar.php'); + + // Make sure they can link an event to this post. + canLinkEvent(); + + // Insert the event. + $eventOptions = array( + 'board' => $board, + 'topic' => $topic, + 'title' => $_POST['evtitle'], + 'member' => $user_info['id'], + 'start_date' => sprintf('%04d-%02d-%02d', $_POST['year'], $_POST['month'], $_POST['day']), + 'span' => isset($_POST['span']) && $_POST['span'] > 0 ? min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1) : 0, + ); + insertEvent($eventOptions); + } + elseif (isset($_POST['calendar'])) + { + $_REQUEST['eventid'] = (int) $_REQUEST['eventid']; + + // Validate the post... + require_once($sourcedir . '/Subs-Calendar.php'); + validateEventPost(); + + // If you're not allowed to edit any events, you have to be the poster. + if (!allowedTo('calendar_edit_any')) + { + // Get the event's poster. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}calendar + WHERE id_event = {int:id_event}', + array( + 'id_event' => $_REQUEST['eventid'], + ) + ); + $row2 = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Silly hacker, Trix are for kids. ...probably trademarked somewhere, this is FAIR USE! (parody...) + isAllowedTo('calendar_edit_' . ($row2['id_member'] == $user_info['id'] ? 'own' : 'any')); + } + + // Delete it? + if (isset($_REQUEST['deleteevent'])) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}calendar + WHERE id_event = {int:id_event}', + array( + 'id_event' => $_REQUEST['eventid'], + ) + ); + // ... or just update it? + else + { + $span = !empty($modSettings['cal_allowspan']) && !empty($_REQUEST['span']) ? min((int) $modSettings['cal_maxspan'], (int) $_REQUEST['span'] - 1) : 0; + $start_time = mktime(0, 0, 0, (int) $_REQUEST['month'], (int) $_REQUEST['day'], (int) $_REQUEST['year']); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}calendar + SET end_date = {date:end_date}, + start_date = {date:start_date}, + title = {string:title} + WHERE id_event = {int:id_event}', + array( + 'end_date' => strftime('%Y-%m-%d', $start_time + $span * 86400), + 'start_date' => strftime('%Y-%m-%d', $start_time), + 'id_event' => $_REQUEST['eventid'], + 'title' => $smcFunc['htmlspecialchars']($_REQUEST['evtitle'], ENT_QUOTES), + ) + ); + } + updateSettings(array( + 'calendar_updated' => time(), + )); + } + + // Marking read should be done even for editing messages.... + // Mark all the parents read. (since you just posted and they will be unread.) + if (!$user_info['is_guest'] && !empty($board_info['parent_boards'])) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_boards + SET id_msg = {int:id_msg} + WHERE id_member = {int:current_member} + AND id_board IN ({array_int:board_list})', + array( + 'current_member' => $user_info['id'], + 'board_list' => array_keys($board_info['parent_boards']), + 'id_msg' => $modSettings['maxMsgID'], + ) + ); + } + + // Turn notification on or off. (note this just blows smoke if it's already on or off.) + if (!empty($_POST['notify']) && allowedTo('mark_any_notify')) + { + $smcFunc['db_insert']('ignore', + '{db_prefix}log_notify', + array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int'), + array($user_info['id'], $topic, 0), + array('id_member', 'id_topic', 'id_board') + ); + } + elseif (!$newTopic) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_notify + WHERE id_member = {int:current_member} + AND id_topic = {int:current_topic}', + array( + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + ) + ); + + // Log an act of moderation - modifying. + if (!empty($moderationAction)) + logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $row['id_member'], 'board' => $board)); + + if (isset($_POST['lock']) && $_POST['lock'] != 2) + logAction('lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board'])); + + if (isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics'])) + logAction('sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board'])); + + // Notify any members who have notification turned on for this topic - only do this if it's going to be approved(!) + if ($becomesApproved) + { + if ($newTopic) + { + $notifyData = array( + 'body' => $_POST['message'], + 'subject' => $_POST['subject'], + 'name' => $user_info['name'], + 'poster' => $user_info['id'], + 'msg' => $msgOptions['id'], + 'board' => $board, + 'topic' => $topic, + ); + notifyMembersBoard($notifyData); + } + elseif (empty($_REQUEST['msg'])) + { + // Only send it to everyone if the topic is approved, otherwise just to the topic starter if they want it. + if ($topic_info['approved']) + sendNotifications($topic, 'reply'); + else + sendNotifications($topic, 'reply', array(), $topic_info['id_member_started']); + } + } + + // Returning to the topic? + if (!empty($_REQUEST['goback'])) + { + // Mark the board as read.... because it might get confusing otherwise. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_boards + SET id_msg = {int:maxMsgID} + WHERE id_member = {int:current_member} + AND id_board = {int:current_board}', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'maxMsgID' => $modSettings['maxMsgID'], + ) + ); + } + + if ($board_info['num_topics'] == 0) + cache_put_data('board-' . $board, null, 120); + + if (!empty($_POST['announce_topic'])) + redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback')); + + if (!empty($_POST['move']) && allowedTo('move_any')) + redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback')); + + // Return to post if the mod is on. + if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback'])) + redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], $context['browser']['is_ie']); + elseif (!empty($_REQUEST['goback'])) + redirectexit('topic=' . $topic . '.new#new', $context['browser']['is_ie']); + // Dut-dut-duh-duh-DUH-duh-dut-duh-duh! *dances to the Final Fantasy Fanfare...* + else + redirectexit('board=' . $board . '.0'); +} + +// General function for topic announcements. +function AnnounceTopic() +{ + global $context, $txt, $topic; + + isAllowedTo('announce_topic'); + + validateSession(); + + if (empty($topic)) + fatal_lang_error('topic_gone', false); + + loadLanguage('Post'); + loadTemplate('Post'); + + $subActions = array( + 'selectgroup' => 'AnnouncementSelectMembergroup', + 'send' => 'AnnouncementSend', + ); + + $context['page_title'] = $txt['announce_topic']; + + // Call the function based on the sub-action. + $subActions[isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'selectgroup'](); +} + +// Allow a user to chose the membergroups to send the announcement to. +function AnnouncementSelectMembergroup() +{ + global $txt, $context, $topic, $board, $board_info, $smcFunc; + + $groups = array_merge($board_info['groups'], array(1)); + foreach ($groups as $id => $group) + $groups[$id] = (int) $group; + + $context['groups'] = array(); + if (in_array(0, $groups)) + { + $context['groups'][0] = array( + 'id' => 0, + 'name' => $txt['announce_regular_members'], + 'member_count' => 'n/a', + ); + } + + // Get all membergroups that have access to the board the announcement was made on. + $request = $smcFunc['db_query']('', ' + SELECT mg.id_group, COUNT(mem.id_member) AS num_members + FROM {db_prefix}membergroups AS mg + LEFT JOIN {db_prefix}members AS mem ON (mem.id_group = mg.id_group OR FIND_IN_SET(mg.id_group, mem.additional_groups) != 0 OR mg.id_group = mem.id_post_group) + WHERE mg.id_group IN ({array_int:group_list}) + GROUP BY mg.id_group', + array( + 'group_list' => $groups, + 'newbie_id_group' => 4, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['groups'][$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => '', + 'member_count' => $row['num_members'], + ); + } + $smcFunc['db_free_result']($request); + + // Now get the membergroup names. + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name + FROM {db_prefix}membergroups + WHERE id_group IN ({array_int:group_list})', + array( + 'group_list' => $groups, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['groups'][$row['id_group']]['name'] = $row['group_name']; + $smcFunc['db_free_result']($request); + + // Get the subject of the topic we're about to announce. + $request = $smcFunc['db_query']('', ' + SELECT m.subject + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE t.id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + ) + ); + list ($context['topic_subject']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + censorText($context['announce_topic']['subject']); + + $context['move'] = isset($_REQUEST['move']) ? 1 : 0; + $context['go_back'] = isset($_REQUEST['goback']) ? 1 : 0; + + $context['sub_template'] = 'announce'; +} + +// Send the announcement in chunks. +function AnnouncementSend() +{ + global $topic, $board, $board_info, $context, $modSettings; + global $language, $scripturl, $txt, $user_info, $sourcedir, $smcFunc; + + checkSession(); + + // !!! Might need an interface? + $chunkSize = empty($modSettings['mail_queue']) ? 50 : 500; + + $context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start']; + $groups = array_merge($board_info['groups'], array(1)); + + if (isset($_POST['membergroups'])) + $_POST['who'] = explode(',', $_POST['membergroups']); + + // Check whether at least one membergroup was selected. + if (empty($_POST['who'])) + fatal_lang_error('no_membergroup_selected'); + + // Make sure all membergroups are integers and can access the board of the announcement. + foreach ($_POST['who'] as $id => $mg) + $_POST['who'][$id] = in_array((int) $mg, $groups) ? (int) $mg : 0; + + // Get the topic subject and censor it. + $request = $smcFunc['db_query']('', ' + SELECT m.id_msg, m.subject, m.body + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE t.id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + ) + ); + list ($id_msg, $context['topic_subject'], $message) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + censorText($context['topic_subject']); + censorText($message); + + $message = trim(un_htmlspecialchars(strip_tags(strtr(parse_bbc($message, false, $id_msg), array('
' => "\n", '' => "\n", '' => "\n", '[' => '[', ']' => ']'))))); + + // We need this in order to be able send emails. + require_once($sourcedir . '/Subs-Post.php'); + + // Select the email addresses for this batch. + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member, mem.email_address, mem.lngfile + FROM {db_prefix}members AS mem + WHERE mem.id_member != {int:current_member}' . (!empty($modSettings['allow_disableAnnounce']) ? ' + AND mem.notify_announcements = {int:notify_announcements}' : '') . ' + AND mem.is_activated = {int:is_activated} + AND (mem.id_group IN ({array_int:group_list}) OR mem.id_post_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:additional_group_list}, mem.additional_groups) != 0) + AND mem.id_member > {int:start} + ORDER BY mem.id_member + LIMIT ' . $chunkSize, + array( + 'current_member' => $user_info['id'], + 'group_list' => $_POST['who'], + 'notify_announcements' => 1, + 'is_activated' => 1, + 'start' => $context['start'], + 'additional_group_list' => implode(', mem.additional_groups) != 0 OR FIND_IN_SET(', $_POST['who']), + ) + ); + + // All members have received a mail. Go to the next screen. + if ($smcFunc['db_num_rows']($request) == 0) + { + if (!empty($_REQUEST['move']) && allowedTo('move_any')) + redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback')); + elseif (!empty($_REQUEST['goback'])) + redirectexit('topic=' . $topic . '.new;boardseen#new', $context['browser']['is_ie']); + else + redirectexit('board=' . $board . '.0'); + } + + // Loop through all members that'll receive an announcement in this batch. + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $cur_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']; + + // If the language wasn't defined yet, load it and compose a notification message. + if (!isset($announcements[$cur_language])) + { + $replacements = array( + 'TOPICSUBJECT' => $context['topic_subject'], + 'MESSAGE' => $message, + 'TOPICLINK' => $scripturl . '?topic=' . $topic . '.0', + ); + + $emaildata = loadEmailTemplate('new_announcement', $replacements, $cur_language); + + $announcements[$cur_language] = array( + 'subject' => $emaildata['subject'], + 'body' => $emaildata['body'], + 'recipients' => array(), + ); + } + + $announcements[$cur_language]['recipients'][$row['id_member']] = $row['email_address']; + $context['start'] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + + // For each language send a different mail - low priority... + foreach ($announcements as $lang => $mail) + sendmail($mail['recipients'], $mail['subject'], $mail['body'], null, null, false, 5); + + $context['percentage_done'] = round(100 * $context['start'] / $modSettings['latestMember'], 1); + + $context['move'] = empty($_REQUEST['move']) ? 0 : 1; + $context['go_back'] = empty($_REQUEST['goback']) ? 0 : 1; + $context['membergroups'] = implode(',', $_POST['who']); + $context['sub_template'] = 'announcement_send'; + + // Go back to the correct language for the user ;). + if (!empty($modSettings['userLanguage'])) + loadLanguage('Post'); +} + +// Notify members of a new post. +function notifyMembersBoard(&$topicData) +{ + global $txt, $scripturl, $language, $user_info; + global $modSettings, $sourcedir, $board, $smcFunc, $context; + + require_once($sourcedir . '/Subs-Post.php'); + + // Do we have one or lots of topics? + if (isset($topicData['body'])) + $topicData = array($topicData); + + // Find out what boards we have... and clear out any rubbish! + $boards = array(); + foreach ($topicData as $key => $topic) + { + if (!empty($topic['board'])) + $boards[$topic['board']][] = $key; + else + { + unset($topic[$key]); + continue; + } + + // Censor the subject and body... + censorText($topicData[$key]['subject']); + censorText($topicData[$key]['body']); + + $topicData[$key]['subject'] = un_htmlspecialchars($topicData[$key]['subject']); + $topicData[$key]['body'] = trim(un_htmlspecialchars(strip_tags(strtr(parse_bbc($topicData[$key]['body'], false), array('
' => "\n", '' => "\n", '' => "\n", '[' => '[', ']' => ']'))))); + } + + // Just the board numbers. + $board_index = array_unique(array_keys($boards)); + + if (empty($board_index)) + return; + + // Yea, we need to add this to the digest queue. + $digest_insert = array(); + foreach ($topicData as $id => $data) + $digest_insert[] = array($data['topic'], $data['msg'], 'topic', $user_info['id']); + $smcFunc['db_insert']('', + '{db_prefix}log_digest', + array( + 'id_topic' => 'int', 'id_msg' => 'int', 'note_type' => 'string', 'exclude' => 'int', + ), + $digest_insert, + array() + ); + + // Find the members with notification on for these boards. + $members = $smcFunc['db_query']('', ' + SELECT + mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_send_body, mem.lngfile, + ln.sent, ln.id_board, mem.id_group, mem.additional_groups, b.member_groups, + mem.id_post_group + FROM {db_prefix}log_notify AS ln + INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board) + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member) + WHERE ln.id_board IN ({array_int:board_list}) + AND mem.id_member != {int:current_member} + AND mem.is_activated = {int:is_activated} + AND mem.notify_types != {int:notify_types} + AND mem.notify_regularity < {int:notify_regularity} + ORDER BY mem.lngfile', + array( + 'current_member' => $user_info['id'], + 'board_list' => $board_index, + 'is_activated' => 1, + 'notify_types' => 4, + 'notify_regularity' => 2, + ) + ); + while ($rowmember = $smcFunc['db_fetch_assoc']($members)) + { + if ($rowmember['id_group'] != 1) + { + $allowed = explode(',', $rowmember['member_groups']); + $rowmember['additional_groups'] = explode(',', $rowmember['additional_groups']); + $rowmember['additional_groups'][] = $rowmember['id_group']; + $rowmember['additional_groups'][] = $rowmember['id_post_group']; + + if (count(array_intersect($allowed, $rowmember['additional_groups'])) == 0) + continue; + } + + $langloaded = loadLanguage('EmailTemplates', empty($rowmember['lngfile']) || empty($modSettings['userLanguage']) ? $language : $rowmember['lngfile'], false); + + // Now loop through all the notifications to send for this board. + if (empty($boards[$rowmember['id_board']])) + continue; + + $sentOnceAlready = 0; + foreach ($boards[$rowmember['id_board']] as $key) + { + // Don't notify the guy who started the topic! + //!!! In this case actually send them a "it's approved hooray" email + if ($topicData[$key]['poster'] == $rowmember['id_member']) + continue; + + // Setup the string for adding the body to the message, if a user wants it. + $send_body = empty($modSettings['disallow_sendBody']) && !empty($rowmember['notify_send_body']); + + $replacements = array( + 'TOPICSUBJECT' => $topicData[$key]['subject'], + 'TOPICLINK' => $scripturl . '?topic=' . $topicData[$key]['topic'] . '.new#new', + 'MESSAGE' => $topicData[$key]['body'], + 'UNSUBSCRIBELINK' => $scripturl . '?action=notifyboard;board=' . $topicData[$key]['board'] . '.0', + ); + + if (!$send_body) + unset($replacements['MESSAGE']); + + // Figure out which email to send off + $emailtype = ''; + + // Send only if once is off or it's on and it hasn't been sent. + if (!empty($rowmember['notify_regularity']) && !$sentOnceAlready && empty($rowmember['sent'])) + $emailtype = 'notify_boards_once'; + elseif (empty($rowmember['notify_regularity'])) + $emailtype = 'notify_boards'; + + if (!empty($emailtype)) + { + $emailtype .= $send_body ? '_body' : ''; + $emaildata = loadEmailTemplate($emailtype, $replacements, $langloaded); + sendmail($rowmember['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 3); + } + + $sentOnceAlready = 1; + } + } + $smcFunc['db_free_result']($members); + + // Sent! + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_notify + SET sent = {int:is_sent} + WHERE id_board IN ({array_int:board_list}) + AND id_member != {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'board_list' => $board_index, + 'is_sent' => 1, + ) + ); +} + +// Get the topic for display purposes. +function getTopic() +{ + global $topic, $modSettings, $context, $smcFunc, $counter, $options; + + if (isset($_REQUEST['xml'])) + $limit = ' + LIMIT ' . (empty($context['new_replies']) ? '0' : $context['new_replies']); + else + $limit = empty($modSettings['topicSummaryPosts']) ? '' : ' + LIMIT ' . (int) $modSettings['topicSummaryPosts']; + + // If you're modifying, get only those posts before the current one. (otherwise get all.) + $request = $smcFunc['db_query']('', ' + SELECT + IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time, + m.body, m.smileys_enabled, m.id_msg, m.id_member + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_topic = {int:current_topic}' . (isset($_REQUEST['msg']) ? ' + AND m.id_msg < {int:id_msg}' : '') .(!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND m.approved = {int:approved}') . ' + ORDER BY m.id_msg DESC' . $limit, + array( + 'current_topic' => $topic, + 'id_msg' => isset($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0, + 'approved' => 1, + ) + ); + $context['previous_posts'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Censor, BBC, ... + censorText($row['body']); + $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); + + // ...and store. + $context['previous_posts'][] = array( + 'counter' => $counter++, + 'alternate' => $counter % 2, + 'poster' => $row['poster_name'], + 'message' => $row['body'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'id' => $row['id_msg'], + 'is_new' => !empty($context['new_replies']), + 'is_ignored' => !empty($modSettings['enable_buddylist']) && !empty($options['posts_apply_ignore_list']) && in_array($row['id_member'], $context['user']['ignoreusers']), + ); + + if (!empty($context['new_replies'])) + $context['new_replies']--; + } + $smcFunc['db_free_result']($request); +} + +function QuoteFast() +{ + global $modSettings, $user_info, $txt, $settings, $context; + global $sourcedir, $smcFunc; + + loadLanguage('Post'); + if (!isset($_REQUEST['xml'])) + loadTemplate('Post'); + + include_once($sourcedir . '/Subs-Post.php'); + + $moderate_boards = boardsAllowedTo('moderate_board'); + + // Where we going if we need to? + $context['post_box_name'] = isset($_GET['pb']) ? $_GET['pb'] : ''; + + $request = $smcFunc['db_query']('', ' + SELECT IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body, m.id_topic, m.subject, + m.id_board, m.id_member, m.approved + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board}) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_msg = {int:id_msg}' . (isset($_REQUEST['modify']) || (!empty($moderate_boards) && $moderate_boards[0] == 0) ? '' : ' + AND (t.locked = {int:not_locked}' . (empty($moderate_boards) ? '' : ' OR b.id_board IN ({array_int:moderation_board_list})') . ')') . ' + LIMIT 1', + array( + 'current_member' => $user_info['id'], + 'moderation_board_list' => $moderate_boards, + 'id_msg' => (int) $_REQUEST['quote'], + 'not_locked' => 0, + ) + ); + $context['close_window'] = $smcFunc['db_num_rows']($request) == 0; + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $context['sub_template'] = 'quotefast'; + if (!empty($row)) + $can_view_post = $row['approved'] || ($row['id_member'] != 0 && $row['id_member'] == $user_info['id']) || allowedTo('approve_posts', $row['id_board']); + + if (!empty($can_view_post)) + { + // Remove special formatting we don't want anymore. + $row['body'] = un_preparsecode($row['body']); + + // Censor the message! + censorText($row['body']); + + $row['body'] = preg_replace('~
~i', "\n", $row['body']); + + // Want to modify a single message by double clicking it? + if (isset($_REQUEST['modify'])) + { + censorText($row['subject']); + + $context['sub_template'] = 'modifyfast'; + $context['message'] = array( + 'id' => $_REQUEST['quote'], + 'body' => $row['body'], + 'subject' => addcslashes($row['subject'], '"'), + ); + + return; + } + + // Remove any nested quotes. + if (!empty($modSettings['removeNestedQuotes'])) + $row['body'] = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $row['body']); + + // Make the body HTML if need be. + if (!empty($_REQUEST['mode'])) + { + require_once($sourcedir . '/Subs-Editor.php'); + $row['body'] = strtr($row['body'], array('<' => '#smlt#', '>' => '#smgt#', '&' => '#smamp#')); + $row['body'] = bbc_to_html($row['body']); + $lb = '
'; + } + else + $lb = "\n"; + + // Add a quote string on the front and end. + $context['quote']['xml'] = '[quote author=' . $row['poster_name'] . ' link=topic=' . $row['id_topic'] . '.msg' . (int) $_REQUEST['quote'] . '#msg' . (int) $_REQUEST['quote'] . ' date=' . $row['poster_time'] . ']' . $lb . $row['body'] . $lb . '[/quote]'; + $context['quote']['text'] = strtr(un_htmlspecialchars($context['quote']['xml']), array('\'' => '\\\'', '\\' => '\\\\', "\n" => '\\n', '' => '')); + $context['quote']['xml'] = strtr($context['quote']['xml'], array(' ' => ' ', '<' => '<', '>' => '>')); + + $context['quote']['mozilla'] = strtr($smcFunc['htmlspecialchars']($context['quote']['text']), array('"' => '"')); + } + // !!! Needs a nicer interface. + // In case our message has been removed in the meantime. + elseif (isset($_REQUEST['modify'])) + { + $context['sub_template'] = 'modifyfast'; + $context['message'] = array( + 'id' => 0, + 'body' => '', + 'subject' => '', + ); + } + else + $context['quote'] = array( + 'xml' => '', + 'mozilla' => '', + 'text' => '', + ); +} + +function JavaScriptModify() +{ + global $sourcedir, $modSettings, $board, $topic, $txt; + global $user_info, $context, $smcFunc, $language; + + // We have to have a topic! + if (empty($topic)) + obExit(false); + + checkSession('get'); + require_once($sourcedir . '/Subs-Post.php'); + + // Assume the first message if no message ID was given. + $request = $smcFunc['db_query']('', ' + SELECT + t.locked, t.num_replies, t.id_member_started, t.id_first_msg, + m.id_msg, m.id_member, m.poster_time, m.subject, m.smileys_enabled, m.body, m.icon, + m.modified_time, m.modified_name, m.approved + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic}) + WHERE m.id_msg = {raw:id_msg} + AND m.id_topic = {int:current_topic}' . (allowedTo('approve_posts') ? '' : (!$modSettings['postmod_active'] ? ' + AND (m.id_member != {int:guest_id} AND m.id_member = {int:current_member})' : ' + AND (m.approved = {int:is_approved} OR (m.id_member != {int:guest_id} AND m.id_member = {int:current_member}))')), + array( + 'current_member' => $user_info['id'], + 'current_topic' => $topic, + 'id_msg' => empty($_REQUEST['msg']) ? 't.id_first_msg' : (int) $_REQUEST['msg'], + 'is_approved' => 1, + 'guest_id' => 0, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_board', false); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Change either body or subject requires permissions to modify messages. + if (isset($_POST['message']) || isset($_POST['subject']) || isset($_REQUEST['icon'])) + { + if (!empty($row['locked'])) + isAllowedTo('moderate_board'); + + if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any')) + { + if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) + fatal_lang_error('modify_post_time_passed', false); + elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_own')) + isAllowedTo('modify_replies'); + else + isAllowedTo('modify_own'); + } + // Otherwise, they're locked out; someone who can modify the replies is needed. + elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_any')) + isAllowedTo('modify_replies'); + else + isAllowedTo('modify_any'); + + // Only log this action if it wasn't your message. + $moderationAction = $row['id_member'] != $user_info['id']; + } + + $post_errors = array(); + if (isset($_POST['subject']) && $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) !== '') + { + $_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => '')); + + // Maximum number of characters. + if ($smcFunc['strlen']($_POST['subject']) > 100) + $_POST['subject'] = $smcFunc['substr']($_POST['subject'], 0, 100); + } + elseif (isset($_POST['subject'])) + { + $post_errors[] = 'no_subject'; + unset($_POST['subject']); + } + + if (isset($_POST['message'])) + { + if ($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['message'])) === '') + { + $post_errors[] = 'no_message'; + unset($_POST['message']); + } + elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['message']) > $modSettings['max_messageLength']) + { + $post_errors[] = 'long_message'; + unset($_POST['message']); + } + else + { + $_POST['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES); + + preparsecode($_POST['message']); + + if ($smcFunc['htmltrim'](strip_tags(parse_bbc($_POST['message'], false), '')) === '') + { + $post_errors[] = 'no_message'; + unset($_POST['message']); + } + } + } + + if (isset($_POST['lock'])) + { + if (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $row['id_member'])) + unset($_POST['lock']); + elseif (!allowedTo('lock_any')) + { + if ($row['locked'] == 1) + unset($_POST['lock']); + else + $_POST['lock'] = empty($_POST['lock']) ? 0 : 2; + } + elseif (!empty($row['locked']) && !empty($_POST['lock']) || $_POST['lock'] == $row['locked']) + unset($_POST['lock']); + else + $_POST['lock'] = empty($_POST['lock']) ? 0 : 1; + } + + if (isset($_POST['sticky']) && !allowedTo('make_sticky')) + unset($_POST['sticky']); + + if (empty($post_errors)) + { + $msgOptions = array( + 'id' => $row['id_msg'], + 'subject' => isset($_POST['subject']) ? $_POST['subject'] : null, + 'body' => isset($_POST['message']) ? $_POST['message'] : null, + 'icon' => isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : null, + ); + $topicOptions = array( + 'id' => $topic, + 'board' => $board, + 'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null, + 'sticky_mode' => isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics']) ? (int) $_POST['sticky'] : null, + 'mark_as_read' => true, + ); + $posterOptions = array(); + + // Only consider marking as editing if they have edited the subject, message or icon. + if ((isset($_POST['subject']) && $_POST['subject'] != $row['subject']) || (isset($_POST['message']) && $_POST['message'] != $row['body']) || (isset($_REQUEST['icon']) && $_REQUEST['icon'] != $row['icon'])) + { + // And even then only if the time has passed... + if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member']) + { + $msgOptions['modify_time'] = time(); + $msgOptions['modify_name'] = $user_info['name']; + } + } + // If nothing was changed there's no need to add an entry to the moderation log. + else + $moderationAction = false; + + modifyPost($msgOptions, $topicOptions, $posterOptions); + + // If we didn't change anything this time but had before put back the old info. + if (!isset($msgOptions['modify_time']) && !empty($row['modified_time'])) + { + $msgOptions['modify_time'] = $row['modified_time']; + $msgOptions['modify_name'] = $row['modified_name']; + } + + // Changing the first subject updates other subjects to 'Re: new_subject'. + if (isset($_POST['subject']) && isset($_REQUEST['change_all_subjects']) && $row['id_first_msg'] == $row['id_msg'] && !empty($row['num_replies']) && (allowedTo('modify_any') || ($row['id_member_started'] == $user_info['id'] && allowedTo('modify_replies')))) + { + // Get the proper (default language) response prefix first. + if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix'))) + { + if ($language === $user_info['language']) + $context['response_prefix'] = $txt['response_prefix']; + else + { + loadLanguage('index', $language, false); + $context['response_prefix'] = $txt['response_prefix']; + loadLanguage('index'); + } + cache_put_data('response_prefix', $context['response_prefix'], 600); + } + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET subject = {string:subject} + WHERE id_topic = {int:current_topic} + AND id_msg != {int:id_first_msg}', + array( + 'current_topic' => $topic, + 'id_first_msg' => $row['id_first_msg'], + 'subject' => $context['response_prefix'] . $_POST['subject'], + ) + ); + } + + if (!empty($moderationAction)) + logAction('modify', array('topic' => $topic, 'message' => $row['id_msg'], 'member' => $row['id_member'], 'board' => $board)); + } + + if (isset($_REQUEST['xml'])) + { + $context['sub_template'] = 'modifydone'; + if (empty($post_errors) && isset($msgOptions['subject']) && isset($msgOptions['body'])) + { + $context['message'] = array( + 'id' => $row['id_msg'], + 'modified' => array( + 'time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '', + 'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0, + 'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '', + ), + 'subject' => $msgOptions['subject'], + 'first_in_topic' => $row['id_msg'] == $row['id_first_msg'], + 'body' => strtr($msgOptions['body'], array(']]>' => ']]]]>')), + ); + + censorText($context['message']['subject']); + censorText($context['message']['body']); + + $context['message']['body'] = parse_bbc($context['message']['body'], $row['smileys_enabled'], $row['id_msg']); + } + // Topic? + elseif (empty($post_errors)) + { + $context['sub_template'] = 'modifytopicdone'; + $context['message'] = array( + 'id' => $row['id_msg'], + 'modified' => array( + 'time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '', + 'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0, + 'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '', + ), + 'subject' => isset($msgOptions['subject']) ? $msgOptions['subject'] : '', + ); + + censorText($context['message']['subject']); + } + else + { + $context['message'] = array( + 'id' => $row['id_msg'], + 'errors' => array(), + 'error_in_subject' => in_array('no_subject', $post_errors), + 'error_in_body' => in_array('no_message', $post_errors) || in_array('long_message', $post_errors), + ); + + loadLanguage('Errors'); + foreach ($post_errors as $post_error) + { + if ($post_error == 'long_message') + $context['message']['errors'][] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']); + else + $context['message']['errors'][] = $txt['error_' . $post_error]; + } + } + } + else + obExit(false); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/PostModeration.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/PostModeration.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,601 @@ + 'ApproveMessage', + 'attachments' => 'UnapprovedAttachments', + 'replies' => 'UnapprovedPosts', + 'topics' => 'UnapprovedPosts', + ); + + // Pick something valid... + if (!isset($_REQUEST['sa']) || !isset($subactions[$_REQUEST['sa']])) + $_REQUEST['sa'] = 'replies'; + + $subactions[$_REQUEST['sa']](); +} + +// View all unapproved posts. +function UnapprovedPosts() +{ + global $txt, $scripturl, $context, $user_info, $sourcedir, $smcFunc; + + $context['current_view'] = isset($_GET['sa']) && $_GET['sa'] == 'topics' ? 'topics' : 'replies'; + $context['page_title'] = $txt['mc_unapproved_posts']; + + // Work out what boards we can work in! + $approve_boards = boardsAllowedTo('approve_posts'); + + // If we filtered by board remove ones outside of this board. + //!!! Put a message saying we're filtered? + if (isset($_REQUEST['brd'])) + { + $filter_board = array((int) $_REQUEST['brd']); + $approve_boards = $approve_boards == array(0) ? $filter_board : array_intersect($approve_boards, $filter_board); + } + + if ($approve_boards == array(0)) + $approve_query = ''; + elseif (!empty($approve_boards)) + $approve_query = ' AND m.id_board IN (' . implode(',', $approve_boards) . ')'; + // Nada, zip, etc... + else + $approve_query = ' AND 0'; + + // We also need to know where we can delete topics and/or replies to. + if ($context['current_view'] == 'topics') + { + $delete_own_boards = boardsAllowedTo('remove_own'); + $delete_any_boards = boardsAllowedTo('remove_any'); + $delete_own_replies = array(); + } + else + { + $delete_own_boards = boardsAllowedTo('delete_own'); + $delete_any_boards = boardsAllowedTo('delete_any'); + $delete_own_replies = boardsAllowedTo('delete_own_replies'); + } + + $toAction = array(); + // Check if we have something to do? + if (isset($_GET['approve'])) + $toAction[] = (int) $_GET['approve']; + // Just a deletion? + elseif (isset($_GET['delete'])) + $toAction[] = (int) $_GET['delete']; + // Lots of approvals? + elseif (isset($_POST['item'])) + foreach ($_POST['item'] as $item) + $toAction[] = (int) $item; + + // What are we actually doing. + if (isset($_GET['approve']) || (isset($_POST['do']) && $_POST['do'] == 'approve')) + $curAction = 'approve'; + elseif (isset($_GET['delete']) || (isset($_POST['do']) && $_POST['do'] == 'delete')) + $curAction = 'delete'; + + // Right, so we have something to do? + if (!empty($toAction) && isset($curAction)) + { + checkSession('request'); + + // Handy shortcut. + $any_array = $curAction == 'approve' ? $approve_boards : $delete_any_boards; + + // Now for each message work out whether it's actually a topic, and what board it's on. + $request = $smcFunc['db_query']('', ' + SELECT m.id_msg, m.id_member, m.id_board, m.subject, t.id_topic, t.id_first_msg, t.id_member_started + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + LEFT JOIN {db_prefix}boards AS b ON (t.id_board = b.id_board) + WHERE m.id_msg IN ({array_int:message_list}) + AND m.approved = {int:not_approved} + AND {query_see_board}', + array( + 'message_list' => $toAction, + 'not_approved' => 0, + ) + ); + $toAction = array(); + $details = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If it's not within what our view is ignore it... + if (($row['id_msg'] == $row['id_first_msg'] && $context['current_view'] != 'topics') || ($row['id_msg'] != $row['id_first_msg'] && $context['current_view'] != 'replies')) + continue; + + $can_add = false; + // If we're approving this is simple. + if ($curAction == 'approve' && ($any_array == array(0) || in_array($row['id_board'], $any_array))) + { + $can_add = true; + } + // Delete requires more permission checks... + elseif ($curAction == 'delete') + { + // Own post is easy! + if ($row['id_member'] == $user_info['id'] && ($delete_own_boards == array(0) || in_array($row['id_board'], $delete_own_boards))) + $can_add = true; + // Is it a reply to their own topic? + elseif ($row['id_member'] == $row['id_member_started'] && $row['id_msg'] != $row['id_first_msg'] && ($delete_own_replies == array(0) || in_array($row['id_board'], $delete_own_replies))) + $can_add = true; + // Someone elses? + elseif ($row['id_member'] != $user_info['id'] && ($delete_any_boards == array(0) || in_array($row['id_board'], $delete_any_boards))) + $can_add = true; + } + + if ($can_add) + $anItem = $context['current_view'] == 'topics' ? $row['id_topic'] : $row['id_msg']; + $toAction[] = $anItem; + + // All clear. What have we got now, what, what? + $details[$anItem] = array(); + $details[$anItem]["subject"] = $row['subject']; + $details[$anItem]["topic"] = $row['id_topic']; + $details[$anItem]["member"] = ($context['current_view'] == 'topics') ? $row['id_member_started'] : $row['id_member']; + $details[$anItem]["board"] = $row['id_board']; + } + $smcFunc['db_free_result']($request); + + // If we have anything left we can actually do the approving (etc). + if (!empty($toAction)) + { + if ($curAction == 'approve') + { + approveMessages ($toAction, $details, $context['current_view']); + } + else + { + removeMessages ($toAction, $details, $context['current_view']); + } + } + } + + // How many unapproved posts are there? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic AND t.id_first_msg != m.id_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE m.approved = {int:not_approved} + AND {query_see_board} + ' . $approve_query, + array( + 'not_approved' => 0, + ) + ); + list ($context['total_unapproved_posts']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // What about topics? Normally we'd use the table alias t for topics but lets use m so we don't have to redo our approve query. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(m.id_topic) + FROM {db_prefix}topics AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE m.approved = {int:not_approved} + AND {query_see_board} + ' . $approve_query, + array( + 'not_approved' => 0, + ) + ); + list ($context['total_unapproved_topics']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=postmod;sa=' . $context['current_view'] . (isset($_REQUEST['brd']) ? ';brd=' . (int) $_REQUEST['brd'] : ''), $_GET['start'], $context['current_view'] == 'topics' ? $context['total_unapproved_topics'] : $context['total_unapproved_posts'], 10); + $context['start'] = $_GET['start']; + + // We have enough to make some pretty tabs! + $context[$context['moderation_menu_name']]['tab_data'] = array( + 'title' => $txt['mc_unapproved_posts'], + 'help' => 'postmod', + 'description' => $txt['mc_unapproved_posts_desc'], + ); + + // Update the tabs with the correct number of posts. + $context['menu_data_' . $context['moderation_menu_id']]['sections']['posts']['areas']['postmod']['subsections']['posts']['label'] .= ' (' . $context['total_unapproved_posts'] . ')'; + $context['menu_data_' . $context['moderation_menu_id']]['sections']['posts']['areas']['postmod']['subsections']['topics']['label'] .= ' (' . $context['total_unapproved_topics'] . ')'; + + // If we are filtering some boards out then make sure to send that along with the links. + if (isset($_REQUEST['brd'])) + { + $context['menu_data_' . $context['moderation_menu_id']]['sections']['posts']['areas']['postmod']['subsections']['posts']['add_params'] = ';brd=' . (int) $_REQUEST['brd']; + $context['menu_data_' . $context['moderation_menu_id']]['sections']['posts']['areas']['postmod']['subsections']['topics']['add_params'] = ';brd=' . (int) $_REQUEST['brd']; + } + + // Get all unapproved posts. + $request = $smcFunc['db_query']('', ' + SELECT m.id_msg, m.id_topic, m.id_board, m.subject, m.body, m.id_member, + IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.smileys_enabled, + t.id_member_started, t.id_first_msg, b.name AS board_name, c.id_cat, c.name AS cat_name + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE m.approved = {int:not_approved} + AND t.id_first_msg ' . ($context['current_view'] == 'topics' ? '=' : '!=') . ' m.id_msg + AND {query_see_board} + ' . $approve_query . ' + LIMIT ' . $context['start'] . ', 10', + array( + 'not_approved' => 0, + ) + ); + $context['unapproved_items'] = array(); + for ($i = 1; $row = $smcFunc['db_fetch_assoc']($request); $i++) + { + // Can delete is complicated, let's solve it first... is it their own post? + if ($row['id_member'] == $user_info['id'] && ($delete_own_boards == array(0) || in_array($row['id_board'], $delete_own_boards))) + $can_delete = true; + // Is it a reply to their own topic? + elseif ($row['id_member'] == $row['id_member_started'] && $row['id_msg'] != $row['id_first_msg'] && ($delete_own_replies == array(0) || in_array($row['id_board'], $delete_own_replies))) + $can_delete = true; + // Someone elses? + elseif ($row['id_member'] != $user_info['id'] && ($delete_any_boards == array(0) || in_array($row['id_board'], $delete_any_boards))) + $can_delete = true; + else + $can_delete = false; + + $context['unapproved_items'][] = array( + 'id' => $row['id_msg'], + 'alternate' => $i % 2, + 'counter' => $context['start'] + $i, + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + 'subject' => $row['subject'], + 'body' => parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']), + 'time' => timeformat($row['poster_time']), + 'poster' => array( + 'id' => $row['id_member'], + 'name' => $row['poster_name'], + 'link' => $row['id_member'] ? '' . $row['poster_name'] . '' : $row['poster_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + ), + 'topic' => array( + 'id' => $row['id_topic'], + ), + 'board' => array( + 'id' => $row['id_board'], + 'name' => $row['board_name'], + ), + 'category' => array( + 'id' => $row['id_cat'], + 'name' => $row['cat_name'], + ), + 'can_delete' => $can_delete, + ); + } + $smcFunc['db_free_result']($request); + + $context['sub_template'] = 'unapproved_posts'; +} + +// View all unapproved attachments. +function UnapprovedAttachments() +{ + global $txt, $scripturl, $context, $user_info, $sourcedir, $smcFunc; + + $context['page_title'] = $txt['mc_unapproved_attachments']; + + // Once again, permissions are king! + $approve_boards = boardsAllowedTo('approve_posts'); + + if ($approve_boards == array(0)) + $approve_query = ''; + elseif (!empty($approve_boards)) + $approve_query = ' AND m.id_board IN (' . implode(',', $approve_boards) . ')'; + else + $approve_query = ' AND 0'; + + // Get together the array of things to act on, if any. + $attachments = array(); + if (isset($_GET['approve'])) + $attachments[] = (int) $_GET['approve']; + elseif (isset($_GET['delete'])) + $attachments[] = (int) $_GET['delete']; + elseif (isset($_POST['item'])) + foreach ($_POST['item'] as $item) + $attachments[] = (int) $item; + + // Are we approving or deleting? + if (isset($_GET['approve']) || (isset($_POST['do']) && $_POST['do'] == 'approve')) + $curAction = 'approve'; + elseif (isset($_GET['delete']) || (isset($_POST['do']) && $_POST['do'] == 'delete')) + $curAction = 'delete'; + + // Something to do, let's do it! + if (!empty($attachments) && isset($curAction)) + { + checkSession('request'); + + // This will be handy. + require_once($sourcedir . '/ManageAttachments.php'); + + // Confirm the attachments are eligible for changing! + $request = $smcFunc['db_query']('', ' + SELECT a.id_attach + FROM {db_prefix}attachments AS a + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) + LEFT JOIN {db_prefix}boards AS b ON (m.id_board = b.id_board) + WHERE a.id_attach IN ({array_int:attachments}) + AND a.approved = {int:not_approved} + AND a.attachment_type = {int:attachment_type} + AND {query_see_board} + ' . $approve_query, + array( + 'attachments' => $attachments, + 'not_approved' => 0, + 'attachment_type' => 0, + ) + ); + $attachments = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $attachments[] = $row['id_attach']; + $smcFunc['db_free_result']($request); + + // Assuming it wasn't all like, proper illegal, we can do the approving. + if (!empty($attachments)) + { + if ($curAction == 'approve') + ApproveAttachments($attachments); + else + removeAttachments(array('id_attach' => $attachments)); + } + } + + // How many unapproved attachments in total? + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}attachments AS a + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE a.approved = {int:not_approved} + AND a.attachment_type = {int:attachment_type} + AND {query_see_board} + ' . $approve_query, + array( + 'not_approved' => 0, + 'attachment_type' => 0, + ) + ); + list ($context['total_unapproved_attachments']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=attachmod;sa=attachments', $_GET['start'], $context['total_unapproved_attachments'], 10); + $context['start'] = $_GET['start']; + + // Get all unapproved attachments. + $request = $smcFunc['db_query']('', ' + SELECT a.id_attach, a.filename, a.size, m.id_msg, m.id_topic, m.id_board, m.subject, m.body, m.id_member, + IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time, + t.id_member_started, t.id_first_msg, b.name AS board_name, c.id_cat, c.name AS cat_name + FROM {db_prefix}attachments AS a + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE a.approved = {int:not_approved} + AND a.attachment_type = {int:attachment_type} + AND {query_see_board} + ' . $approve_query . ' + LIMIT ' . $context['start'] . ', 10', + array( + 'not_approved' => 0, + 'attachment_type' => 0, + ) + ); + $context['unapproved_items'] = array(); + for ($i = 1; $row = $smcFunc['db_fetch_assoc']($request); $i++) + { + $context['unapproved_items'][] = array( + 'id' => $row['id_attach'], + 'alternate' => $i % 2, + 'filename' => $row['filename'], + 'size' => round($row['size'] / 1024, 2), + 'time' => timeformat($row['poster_time']), + 'poster' => array( + 'id' => $row['id_member'], + 'name' => $row['poster_name'], + 'link' => $row['id_member'] ? '' . $row['poster_name'] . '' : $row['poster_name'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + ), + 'message' => array( + 'id' => $row['id_msg'], + 'subject' => $row['subject'], + 'body' => parse_bbc($row['body']), + 'time' => timeformat($row['poster_time']), + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + ), + 'topic' => array( + 'id' => $row['id_topic'], + ), + 'board' => array( + 'id' => $row['id_board'], + 'name' => $row['board_name'], + ), + 'category' => array( + 'id' => $row['id_cat'], + 'name' => $row['cat_name'], + ), + ); + } + $smcFunc['db_free_result']($request); + + $context['sub_template'] = 'unapproved_attachments'; +} + +// Approve a post, just the one. +function ApproveMessage() +{ + global $user_info, $topic, $board, $sourcedir, $smcFunc; + + checkSession('get'); + + $_REQUEST['msg'] = (int) $_REQUEST['msg']; + + require_once($sourcedir . '/Subs-Post.php'); + + isAllowedTo('approve_posts'); + + $request = $smcFunc['db_query']('', ' + SELECT t.id_member_started, t.id_first_msg, m.id_member, m.subject, m.approved + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic}) + WHERE m.id_msg = {int:id_msg} + AND m.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + 'id_msg' => $_REQUEST['msg'], + ) + ); + list ($starter, $first_msg, $poster, $subject, $approved) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // If it's the first in a topic then the whole topic gets approved! + if ($first_msg == $_REQUEST['msg']) + { + approveTopics($topic, !$approved); + + if ($starter != $user_info['id']) + logAction('approve_topic', array('topic' => $topic, 'subject' => $subject, 'member' => $starter, 'board' => $board)); + } + else + { + approvePosts($_REQUEST['msg'], !$approved); + + if ($poster != $user_info['id']) + logAction('approve', array('topic' => $topic, 'subject' => $subject, 'member' => $poster, 'board' => $board)); + } + + redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg']. '#msg' . $_REQUEST['msg']); +} + +// Approve a batch of posts (or topics in their own right) +function approveMessages($messages, $messageDetails, $current_view = 'replies') +{ + global $sourcedir; + + require_once($sourcedir . '/Subs-Post.php'); + if ($current_view == 'topics') + { + approveTopics($messages); + // and tell the world about it + foreach ($messages as $topic) + { + logAction('approve_topic', array('topic' => $topic, 'subject' => $messageDetails[$topic]['subject'], 'member' => $messageDetails[$topic]['member'], 'board' => $messageDetails[$topic]['board'])); + } + } + else + { + approvePosts($messages); + // and tell the world about it again + foreach ($messages as $post) + { + logAction('approve', array('topic' => $messageDetails[$post]['topic'], 'subject' => $messageDetails[$post]['subject'], 'member' => $messageDetails[$post]['member'], 'board' => $messageDetails[$post]['board'])); + } + } +} + +// This is a helper function - basically approve everything! +function approveAllData() +{ + global $smcFunc, $sourcedir; + + // Start with messages and topics. + $request = $smcFunc['db_query']('', ' + SELECT id_msg + FROM {db_prefix}messages + WHERE approved = {int:not_approved}', + array( + 'not_approved' => 0, + ) + ); + $msgs = array(); + while ($row = $smcFunc['db_fetch_row']($request)) + $msgs[] = $row[0]; + $smcFunc['db_free_result']($request); + + if (!empty($msgs)) + { + require_once($sourcedir . '/Subs-Post.php'); + approvePosts($msgs); + } + + // Now do attachments + $request = $smcFunc['db_query']('', ' + SELECT id_attach + FROM {db_prefix}attachments + WHERE approved = {int:not_approved}', + array( + 'not_approved' => 0, + ) + ); + $attaches = array(); + while ($row = $smcFunc['db_fetch_row']($request)) + $attaches[] = $row[0]; + $smcFunc['db_free_result']($request); + + if (!empty($attaches)) + { + require_once($sourcedir . '/ManageAttachments.php'); + ApproveAttachments($attaches); + } +} + +// remove a batch of messages (or topics) +function removeMessages($messages, $messageDetails, $current_view = 'replies') +{ + global $sourcedir, $modSettings; + require_once($sourcedir . '/RemoveTopic.php'); + if ($current_view == 'topics') + { + removeTopics($messages); + // and tell the world about it + foreach ($messages as $topic) + // Note, only log topic ID in native form if it's not gone forever. + logAction('remove', array( + (empty($modSettings['recycle_enable']) || $modSettings['recycle_board'] != $messageDetails[$topic]['board'] ? 'topic' : 'old_topic_id') => $topic, 'subject' => $messageDetails[$topic]['subject'], 'member' => $messageDetails[$topic]['member'], 'board' => $messageDetails[$topic]['board'])); + } + else + { + foreach ($messages as $post) + { + removeMessage($post); + logAction('delete', array( + (empty($modSettings['recycle_enable']) || $modSettings['recycle_board'] != $messageDetails[$post]['board'] ? 'topic' : 'old_topic_id') => $messageDetails[$post]['topic'], 'subject' => $messageDetails[$post]['subject'], 'member' => $messageDetails[$post]['member'], 'board' => $messageDetails[$post]['board'])); + } + } +} +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Printpage.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Printpage.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,107 @@ + $topic, + ) + ); + // Redirect to the boardindex if no valid topic id is provided. + if ($smcFunc['db_num_rows']($request) == 0) + redirectexit(); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Lets "output" all that info. + loadTemplate('Printpage'); + $context['template_layers'] = array('print'); + $context['board_name'] = $board_info['name']; + $context['category_name'] = $board_info['cat']['name']; + $context['poster_name'] = $row['poster_name']; + $context['post_time'] = timeformat($row['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. + $request = $smcFunc['db_query']('', ' + SELECT subject, poster_time, body, IFNULL(mem.real_name, poster_name) AS poster_name + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && !allowedTo('approve_posts') ? ' + AND (m.approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR m.id_member = {int:current_member}') . ')' : '') . ' + ORDER BY m.id_msg', + array( + 'current_topic' => $topic, + 'is_approved' => 1, + 'current_member' => $user_info['id'], + ) + ); + $context['posts'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Censor the subject and message. + censorText($row['subject']); + censorText($row['body']); + + $context['posts'][] = array( + 'subject' => $row['subject'], + 'member' => $row['poster_name'], + 'time' => timeformat($row['poster_time'], false), + 'timestamp' => forum_time(true, $row['poster_time']), + 'body' => parse_bbc($row['body'], 'print'), + ); + + if (!isset($context['topic_subject'])) + $context['topic_subject'] = $row['subject']; + } + $smcFunc['db_free_result']($request); + + // Set a canonical URL for this page. + $context['canonical_url'] = $scripturl . '?topic=' . $topic . '.0'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Profile-Actions.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Profile-Actions.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,792 @@ + $user_profile[$memID]['is_activated'] >= 10 ? 11 : 1, 'validation_code' => '')); + + // If we are doing approval, update the stats for the member just in case. + if (in_array($user_profile[$memID]['is_activated'], array(3, 4, 13, 14))) + updateSettings(array('unapprovedMembers' => ($modSettings['unapprovedMembers'] > 1 ? $modSettings['unapprovedMembers'] - 1 : 0))); + + // Make sure we update the stats too. + updateStats('member', false); + } + + // Leave it be... + redirectexit('action=profile;u=' . $memID . ';area=summary'); +} + +// Issue/manage a users warning status. +function issueWarning($memID) +{ + global $txt, $scripturl, $modSettings, $user_info, $mbname; + global $context, $cur_profile, $memberContext, $smcFunc, $sourcedir; + + // Get all the actual settings. + list ($modSettings['warning_enable'], $modSettings['user_limit']) = explode(',', $modSettings['warning_settings']); + + // This stores any legitimate errors. + $issueErrors = array(); + + // Doesn't hurt to be overly cautious. + if (empty($modSettings['warning_enable']) || ($context['user']['is_owner'] && !$cur_profile['warning']) || !allowedTo('issue_warning')) + fatal_lang_error('no_access', false); + + // Make sure things which are disabled stay disabled. + $modSettings['warning_watch'] = !empty($modSettings['warning_watch']) ? $modSettings['warning_watch'] : 110; + $modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110; + $modSettings['warning_mute'] = !empty($modSettings['warning_mute']) ? $modSettings['warning_mute'] : 110; + + $context['warning_limit'] = allowedTo('admin_forum') ? 0 : $modSettings['user_limit']; + $context['member']['warning'] = $cur_profile['warning']; + $context['member']['name'] = $cur_profile['real_name']; + + // What are the limits we can apply? + $context['min_allowed'] = 0; + $context['max_allowed'] = 100; + if ($context['warning_limit'] > 0) + { + // Make sure we cannot go outside of our limit for the day. + $request = $smcFunc['db_query']('', ' + SELECT SUM(counter) + FROM {db_prefix}log_comments + WHERE id_recipient = {int:selected_member} + AND id_member = {int:current_member} + AND comment_type = {string:warning} + AND log_time > {int:day_time_period}', + array( + 'current_member' => $user_info['id'], + 'selected_member' => $memID, + 'day_time_period' => time() - 86400, + 'warning' => 'warning', + ) + ); + list ($current_applied) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['min_allowed'] = max(0, $cur_profile['warning'] - $current_applied - $context['warning_limit']); + $context['max_allowed'] = min(100, $cur_profile['warning'] - $current_applied + $context['warning_limit']); + } + + // Defaults. + $context['warning_data'] = array( + 'reason' => '', + 'notify' => '', + 'notify_subject' => '', + 'notify_body' => '', + ); + + // Are we saving? + if (isset($_POST['save'])) + { + // Security is good here. + checkSession('post'); + + // This cannot be empty! + $_POST['warn_reason'] = isset($_POST['warn_reason']) ? trim($_POST['warn_reason']) : ''; + if ($_POST['warn_reason'] == '' && !$context['user']['is_owner']) + $issueErrors[] = 'warning_no_reason'; + $_POST['warn_reason'] = $smcFunc['htmlspecialchars']($_POST['warn_reason']); + + // If the value hasn't changed it's either no JS or a real no change (Which this will pass) + if ($_POST['warning_level'] == 'SAME') + $_POST['warning_level'] = $_POST['warning_level_nojs']; + + $_POST['warning_level'] = (int) $_POST['warning_level']; + $_POST['warning_level'] = max(0, min(100, $_POST['warning_level'])); + if ($_POST['warning_level'] < $context['min_allowed']) + $_POST['warning_level'] = $context['min_allowed']; + elseif ($_POST['warning_level'] > $context['max_allowed']) + $_POST['warning_level'] = $context['max_allowed']; + + // Do we actually have to issue them with a PM? + $id_notice = 0; + if (!empty($_POST['warn_notify']) && empty($issueErrors)) + { + $_POST['warn_sub'] = trim($_POST['warn_sub']); + $_POST['warn_body'] = trim($_POST['warn_body']); + if (empty($_POST['warn_sub']) || empty($_POST['warn_body'])) + $issueErrors[] = 'warning_notify_blank'; + // Send the PM? + else + { + require_once($sourcedir . '/Subs-Post.php'); + $from = array( + 'id' => 0, + 'name' => $context['forum_name'], + 'username' => $context['forum_name'], + ); + sendpm(array('to' => array($memID), 'bcc' => array()), $_POST['warn_sub'], $_POST['warn_body'], false, $from); + + // Log the notice! + $smcFunc['db_insert']('', + '{db_prefix}log_member_notices', + array( + 'subject' => 'string-255', 'body' => 'string-65534', + ), + array( + $smcFunc['htmlspecialchars']($_POST['warn_sub']), $smcFunc['htmlspecialchars']($_POST['warn_body']), + ), + array('id_notice') + ); + $id_notice = $smcFunc['db_insert_id']('{db_prefix}log_member_notices', 'id_notice'); + } + } + + // Just in case - make sure notice is valid! + $id_notice = (int) $id_notice; + + // What have we changed? + $level_change = $_POST['warning_level'] - $cur_profile['warning']; + + // No errors? Proceed! Only log if you're not the owner. + if (empty($issueErrors)) + { + // Log what we've done! + if (!$context['user']['is_owner']) + $smcFunc['db_insert']('', + '{db_prefix}log_comments', + array( + 'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int', 'recipient_name' => 'string-255', + 'log_time' => 'int', 'id_notice' => 'int', 'counter' => 'int', 'body' => 'string-65534', + ), + array( + $user_info['id'], $user_info['name'], 'warning', $memID, $cur_profile['real_name'], + time(), $id_notice, $level_change, $_POST['warn_reason'], + ), + array('id_comment') + ); + + // Make the change. + updateMemberData($memID, array('warning' => $_POST['warning_level'])); + + // Leave a lovely message. + $context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : $txt['profile_warning_success']; + } + else + { + // Get the base stuff done. + loadLanguage('Errors'); + $context['custom_error_title'] = $txt['profile_warning_errors_occured']; + + // Fill in the suite of errors. + $context['post_errors'] = array(); + foreach ($issueErrors as $error) + $context['post_errors'][] = $txt[$error]; + + // Try to remember some bits. + $context['warning_data'] = array( + 'reason' => $_POST['warn_reason'], + 'notify' => !empty($_POST['warn_notify']), + 'notify_subject' => isset($_POST['warn_sub']) ? $_POST['warn_sub'] : '', + 'notify_body' => isset($_POST['warn_body']) ? $_POST['warn_body'] : '', + ); + } + + // Show the new improved warning level. + $context['member']['warning'] = $_POST['warning_level']; + } + + $context['page_title'] = $txt['profile_issue_warning']; + + // Work our the various levels. + $context['level_effects'] = array( + 0 => $txt['profile_warning_effect_none'], + $modSettings['warning_watch'] => $txt['profile_warning_effect_watch'], + $modSettings['warning_moderate'] => $txt['profile_warning_effect_moderation'], + $modSettings['warning_mute'] => $txt['profile_warning_effect_mute'], + ); + $context['current_level'] = 0; + foreach ($context['level_effects'] as $limit => $dummy) + if ($context['member']['warning'] >= $limit) + $context['current_level'] = $limit; + + // Load up all the old warnings - count first! + $context['total_warnings'] = list_getUserWarningCount($memID); + + // Make the page index. + $context['start'] = (int) $_REQUEST['start']; + $perPage = (int) $modSettings['defaultMaxMessages']; + $context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=issuewarning', $context['start'], $context['total_warnings'], $perPage); + + // Now do the data itself. + $context['previous_warnings'] = list_getUserWarnings($context['start'], $perPage, 'log_time DESC', $memID); + + // Are they warning because of a message? + if (isset($_REQUEST['msg']) && 0 < (int) $_REQUEST['msg']) + { + $request = $smcFunc['db_query']('', ' + SELECT subject + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE id_msg = {int:message} + AND {query_see_board} + LIMIT 1', + array( + 'message' => (int) $_REQUEST['msg'], + ) + ); + if ($smcFunc['db_num_rows']($request) != 0) + { + $context['warning_for_message'] = (int) $_REQUEST['msg']; + list ($context['warned_message_subject']) = $smcFunc['db_fetch_row']($request); + } + $smcFunc['db_free_result']($request); + + } + + // Didn't find the message? + if (empty($context['warning_for_message'])) + { + $context['warning_for_message'] = 0; + $context['warned_message_subject'] = ''; + } + + // Any custom templates? + $context['notification_templates'] = array(); + + $request = $smcFunc['db_query']('', ' + SELECT recipient_name AS template_title, body + FROM {db_prefix}log_comments + WHERE comment_type = {string:warntpl} + AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})', + array( + 'warntpl' => 'warntpl', + 'generic' => 0, + 'current_member' => $user_info['id'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If we're not warning for a message skip any that are. + if (!$context['warning_for_message'] && strpos($row['body'], '{MESSAGE}') !== false) + continue; + + $context['notification_templates'][] = array( + 'title' => $row['template_title'], + 'body' => $row['body'], + ); + } + $smcFunc['db_free_result']($request); + + // Setup the "default" templates. + foreach (array('spamming', 'offence', 'insulting') as $type) + $context['notification_templates'][] = array( + 'title' => $txt['profile_warning_notify_title_' . $type], + 'body' => sprintf($txt['profile_warning_notify_template_outline' . (!empty($context['warning_for_message']) ? '_post' : '')], $txt['profile_warning_notify_for_' . $type]), + ); + + // Replace all the common variables in the templates. + foreach ($context['notification_templates'] as $k => $name) + $context['notification_templates'][$k]['body'] = strtr($name['body'], array('{MEMBER}' => un_htmlspecialchars($context['member']['name']), '{MESSAGE}' => '[url=' . $scripturl . '?msg=' . $context['warning_for_message'] . ']' . un_htmlspecialchars($context['warned_message_subject']) . '[/url]', '{SCRIPTURL}' => $scripturl, '{FORUMNAME}' => $mbname, '{REGARDS}' => $txt['regards_team'])); +} + +// Get the number of warnings a user has. +function list_getUserWarningCount($memID) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_comments + WHERE id_recipient = {int:selected_member} + AND comment_type = {string:warning}', + array( + 'selected_member' => $memID, + 'warning' => 'warning', + ) + ); + list ($total_warnings) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $total_warnings; +} + +// Get the data about a users warnings. +function list_getUserWarnings($start, $items_per_page, $sort, $memID) +{ + global $smcFunc, $scripturl; + + $request = $smcFunc['db_query']('', ' + SELECT IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lc.member_name) AS member_name, + lc.log_time, lc.body, lc.counter, lc.id_notice + FROM {db_prefix}log_comments AS lc + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member) + WHERE lc.id_recipient = {int:selected_member} + AND lc.comment_type = {string:warning} + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + 'selected_member' => $memID, + 'warning' => 'warning', + ) + ); + $previous_warnings = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $previous_warnings[] = array( + 'issuer' => array( + 'id' => $row['id_member'], + 'link' => $row['id_member'] ? ('' . $row['member_name'] . '') : $row['member_name'], + ), + 'time' => timeformat($row['log_time']), + 'reason' => $row['body'], + 'counter' => $row['counter'] > 0 ? '+' . $row['counter'] : $row['counter'], + 'id_notice' => $row['id_notice'], + ); + } + $smcFunc['db_free_result']($request); + + return $previous_warnings; +} + +// Present a screen to make sure the user wants to be deleted +function deleteAccount($memID) +{ + global $txt, $context, $user_info, $modSettings, $cur_profile, $smcFunc; + + if (!$context['user']['is_owner']) + isAllowedTo('profile_remove_any'); + elseif (!allowedTo('profile_remove_any')) + isAllowedTo('profile_remove_own'); + + // Permissions for removing stuff... + $context['can_delete_posts'] = !$context['user']['is_owner'] && allowedTo('moderate_forum'); + + // Can they do this, or will they need approval? + $context['needs_approval'] = $context['user']['is_owner'] && !empty($modSettings['approveAccountDeletion']) && !allowedTo('moderate_forum'); + $context['page_title'] = $txt['deleteAccount'] . ': ' . $cur_profile['real_name']; +} + +function deleteAccount2($profile_vars, $post_errors, $memID) +{ + global $user_info, $sourcedir, $context, $cur_profile, $modSettings, $smcFunc; + + // Try get more time... + @set_time_limit(600); + + // !!! Add a way to delete pms as well? + + if (!$context['user']['is_owner']) + isAllowedTo('profile_remove_any'); + elseif (!allowedTo('profile_remove_any')) + isAllowedTo('profile_remove_own'); + + checkSession(); + + $old_profile = &$cur_profile; + + // Too often, people remove/delete their own only account. + if (in_array(1, explode(',', $old_profile['additional_groups'])) || $old_profile['id_group'] == 1) + { + // Are you allowed to administrate the forum, as they are? + isAllowedTo('admin_forum'); + + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0) + AND id_member != {int:selected_member} + LIMIT 1', + array( + 'admin_group' => 1, + 'selected_member' => $memID, + ) + ); + list ($another) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (empty($another)) + fatal_lang_error('at_least_one_admin', 'critical'); + } + + // This file is needed for the deleteMembers function. + require_once($sourcedir . '/Subs-Members.php'); + + // Do you have permission to delete others profiles, or is that your profile you wanna delete? + if ($memID != $user_info['id']) + { + isAllowedTo('profile_remove_any'); + + // Now, have you been naughty and need your posts deleting? + // !!! Should this check board permissions? + if ($_POST['remove_type'] != 'none' && allowedTo('moderate_forum')) + { + // Include RemoveTopics - essential for this type of work! + require_once($sourcedir . '/RemoveTopic.php'); + + // First off we delete any topics the member has started - if they wanted topics being done. + if ($_POST['remove_type'] == 'topics') + { + // Fetch all topics started by this user within the time period. + $request = $smcFunc['db_query']('', ' + SELECT t.id_topic + FROM {db_prefix}topics AS t + WHERE t.id_member_started = {int:selected_member}', + array( + 'selected_member' => $memID, + ) + ); + $topicIDs = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topicIDs[] = $row['id_topic']; + $smcFunc['db_free_result']($request); + + // Actually remove the topics. + // !!! This needs to check permissions, but we'll let it slide for now because of moderate_forum already being had. + removeTopics($topicIDs); + } + + // Now delete the remaining messages. + $request = $smcFunc['db_query']('', ' + SELECT m.id_msg + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic + AND t.id_first_msg != m.id_msg) + WHERE m.id_member = {int:selected_member}', + array( + 'selected_member' => $memID, + ) + ); + // This could take a while... but ya know it's gonna be worth it in the end. + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + removeMessage($row['id_msg']); + } + $smcFunc['db_free_result']($request); + } + + // Only delete this poor members account if they are actually being booted out of camp. + if (isset($_POST['deleteAccount'])) + deleteMembers($memID); + } + // Do they need approval to delete? + elseif (empty($post_errors) && !empty($modSettings['approveAccountDeletion']) && !allowedTo('moderate_forum')) + { + // Setup their account for deletion ;) + updateMemberData($memID, array('is_activated' => 4)); + // Another account needs approval... + updateSettings(array('unapprovedMembers' => true), true); + } + // Also check if you typed your password correctly. + elseif (empty($post_errors)) + { + deleteMembers($memID); + + require_once($sourcedir . '/LogInOut.php'); + LogOut(true); + + redirectExit(); + } +} + +// Function for doing all the paid subscription stuff - kinda. +function subscriptions($memID) +{ + global $context, $txt, $sourcedir, $modSettings, $smcFunc, $scripturl; + + // Load the paid template anyway. + loadTemplate('ManagePaid'); + loadLanguage('ManagePaid'); + + // Load all of the subscriptions. + require_once($sourcedir . '/ManagePaid.php'); + loadSubscriptions(); + $context['member']['id'] = $memID; + + // Remove any invalid ones. + foreach ($context['subscriptions'] as $id => $sub) + { + // Work out the costs. + $costs = @unserialize($sub['real_cost']); + + $cost_array = array(); + if ($sub['real_length'] == 'F') + { + foreach ($costs as $duration => $cost) + { + if ($cost != 0) + $cost_array[$duration] = $cost; + } + } + else + { + $cost_array['fixed'] = $costs['fixed']; + } + + if (empty($cost_array)) + unset($context['subscriptions'][$id]); + else + { + $context['subscriptions'][$id]['member'] = 0; + $context['subscriptions'][$id]['subscribed'] = false; + $context['subscriptions'][$id]['costs'] = $cost_array; + } + } + + // Work out what gateways are enabled. + $gateways = loadPaymentGateways(); + foreach ($gateways as $id => $gateway) + { + $gateways[$id] = new $gateway['display_class'](); + if (!$gateways[$id]->gatewayEnabled()) + unset($gateways[$id]); + } + + // No gateways yet? + if (empty($gateways)) + fatal_error($txt['paid_admin_not_setup_gateway']); + + // Get the current subscriptions. + $request = $smcFunc['db_query']('', ' + SELECT id_sublog, id_subscribe, start_time, end_time, status, payments_pending, pending_details + FROM {db_prefix}log_subscribed + WHERE id_member = {int:selected_member}', + array( + 'selected_member' => $memID, + ) + ); + $context['current'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // The subscription must exist! + if (!isset($context['subscriptions'][$row['id_subscribe']])) + continue; + + $context['current'][$row['id_subscribe']] = array( + 'id' => $row['id_sublog'], + 'sub_id' => $row['id_subscribe'], + 'hide' => $row['status'] == 0 && $row['end_time'] == 0 && $row['payments_pending'] == 0, + 'name' => $context['subscriptions'][$row['id_subscribe']]['name'], + 'start' => timeformat($row['start_time'], false), + 'end' => $row['end_time'] == 0 ? $txt['not_applicable'] : timeformat($row['end_time'], false), + 'pending_details' => $row['pending_details'], + 'status' => $row['status'], + 'status_text' => $row['status'] == 0 ? ($row['payments_pending'] ? $txt['paid_pending'] : $txt['paid_finished']) : $txt['paid_active'], + ); + + if ($row['status'] == 1) + $context['subscriptions'][$row['id_subscribe']]['subscribed'] = true; + } + $smcFunc['db_free_result']($request); + + // Simple "done"? + if (isset($_GET['done'])) + { + $_GET['sub_id'] = (int) $_GET['sub_id']; + + // Must exist but let's be sure... + if (isset($context['current'][$_GET['sub_id']])) + { + // What are the details like? + $current_pending = @unserialize($context['current'][$_GET['sub_id']]['pending_details']); + if (!empty($current_pending)) + { + $current_pending = array_reverse($current_pending); + foreach ($current_pending as $id => $sub) + { + // Just find one and change it. + if ($sub[0] == $_GET['sub_id'] && $sub[3] == 'prepay') + { + $current_pending[$id][3] = 'payback'; + break; + } + } + + // Save the details back. + $pending_details = serialize($current_pending); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET payments_pending = payments_pending + 1, pending_details = {string:pending_details} + WHERE id_sublog = {int:current_subscription_id} + AND id_member = {int:selected_member}', + array( + 'current_subscription_id' => $context['current'][$_GET['sub_id']]['id'], + 'selected_member' => $memID, + 'pending_details' => $pending_details, + ) + ); + } + } + + $context['sub_template'] = 'paid_done'; + return; + } + // If this is confirmation then it's simpler... + if (isset($_GET['confirm']) && isset($_POST['sub_id']) && is_array($_POST['sub_id'])) + { + // Hopefully just one. + foreach ($_POST['sub_id'] as $k => $v) + $ID_SUB = (int) $k; + + if (!isset($context['subscriptions'][$ID_SUB]) || $context['subscriptions'][$ID_SUB]['active'] == 0) + fatal_lang_error('paid_sub_not_active'); + + // Simplify... + $context['sub'] = $context['subscriptions'][$ID_SUB]; + $period = 'xx'; + if ($context['sub']['flexible']) + $period = isset($_POST['cur'][$ID_SUB]) && isset($context['sub']['costs'][$_POST['cur'][$ID_SUB]]) ? $_POST['cur'][$ID_SUB] : 'xx'; + + // Check we have a valid cost. + if ($context['sub']['flexible'] && $period == 'xx') + fatal_lang_error('paid_sub_not_active'); + + // Sort out the cost/currency. + $context['currency'] = $modSettings['paid_currency_code']; + $context['recur'] = $context['sub']['repeatable']; + + if ($context['sub']['flexible']) + { + // Real cost... + $context['value'] = $context['sub']['costs'][$_POST['cur'][$ID_SUB]]; + $context['cost'] = sprintf($modSettings['paid_currency_symbol'], $context['value']) . '/' . $txt[$_POST['cur'][$ID_SUB]]; + // The period value for paypal. + $context['paypal_period'] = strtoupper(substr($_POST['cur'][$ID_SUB], 0, 1)); + } + else + { + // Real cost... + $context['value'] = $context['sub']['costs']['fixed']; + $context['cost'] = sprintf($modSettings['paid_currency_symbol'], $context['value']); + + // Recur? + preg_match('~(\d*)(\w)~', $context['sub']['real_length'], $match); + $context['paypal_unit'] = $match[1]; + $context['paypal_period'] = $match[2]; + } + + // Setup the gateway context. + $context['gateways'] = array(); + foreach ($gateways as $id => $gateway) + { + $fields = $gateways[$id]->fetchGatewayFields($context['sub']['id'] . '+' . $memID, $context['sub'], $context['value'], $period, $scripturl . '?action=profile;u=' . $memID . ';area=subscriptions;sub_id=' . $context['sub']['id'] . ';done'); + if (!empty($fields['form'])) + $context['gateways'][] = $fields; + } + + // Bugger?! + if (empty($context['gateways'])) + fatal_error($txt['paid_admin_not_setup_gateway']); + + // Now we are going to assume they want to take this out ;) + $new_data = array($context['sub']['id'], $context['value'], $period, 'prepay'); + if (isset($context['current'][$context['sub']['id']])) + { + // What are the details like? + $current_pending = array(); + if ($context['current'][$context['sub']['id']]['pending_details'] != '') + $current_pending = @unserialize($context['current'][$context['sub']['id']]['pending_details']); + // Don't get silly. + if (count($current_pending) > 9) + $current_pending = array(); + $pending_count = 0; + // Only record real pending payments as will otherwise confuse the admin! + foreach ($current_pending as $pending) + if ($pending[3] == 'payback') + $pending_count++; + + if (!in_array($new_data, $current_pending)) + { + $current_pending[] = $new_data; + $pending_details = serialize($current_pending); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET payments_pending = {int:pending_count}, pending_details = {string:pending_details} + WHERE id_sublog = {int:current_subscription_item} + AND id_member = {int:selected_member}', + array( + 'pending_count' => $pending_count, + 'current_subscription_item' => $context['current'][$context['sub']['id']]['id'], + 'selected_member' => $memID, + 'pending_details' => $pending_details, + ) + ); + } + + } + // Never had this before, lovely. + else + { + $pending_details = serialize(array($new_data)); + $smcFunc['db_insert']('', + '{db_prefix}log_subscribed', + array( + 'id_subscribe' => 'int', 'id_member' => 'int', 'status' => 'int', 'payments_pending' => 'int', 'pending_details' => 'string-65534', + 'start_time' => 'int', 'vendor_ref' => 'string-255', + ), + array( + $context['sub']['id'], $memID, 0, 0, $pending_details, + time(), '', + ), + array('id_sublog') + ); + } + + // Change the template. + $context['sub_template'] = 'choose_payment'; + + // Quit. + return; + } + else + $context['sub_template'] = 'user_subscription'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Profile-Modify.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Profile-Modify.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,3410 @@ + array( + 'type' => 'text', + 'label' => $txt['aim'], + 'subtext' => $txt['your_aim'], + 'size' => 24, + 'value' => strtr(empty($cur_profile['aim']) ? '' : $cur_profile['aim'], '+', ' '), + 'permission' => 'profile_extra', + 'input_validate' => create_function('&$value', ' + $value = strtr($value, \' \', \'+\'); + return true; + '), + ), + 'avatar_choice' => array( + 'type' => 'callback', + 'callback_func' => 'avatar_select', + // This handles the permissions too. + 'preload' => 'profileLoadAvatarData', + 'input_validate' => 'profileSaveAvatarData', + 'save_key' => 'avatar', + ), + 'bday1' => array( + 'type' => 'callback', + 'callback_func' => 'birthdate', + 'permission' => 'profile_extra', + 'preload' => create_function('', ' + global $cur_profile, $context; + + // Split up the birthdate.... + list ($uyear, $umonth, $uday) = explode(\'-\', empty($cur_profile[\'birthdate\']) || $cur_profile[\'birthdate\'] == \'0001-01-01\' ? \'0000-00-00\' : $cur_profile[\'birthdate\']); + $context[\'member\'][\'birth_date\'] = array( + \'year\' => $uyear == \'0004\' ? \'0000\' : $uyear, + \'month\' => $umonth, + \'day\' => $uday, + ); + + return true; + '), + 'input_validate' => create_function('&$value', ' + global $profile_vars, $cur_profile; + + if (isset($_POST[\'bday2\'], $_POST[\'bday3\']) && $value > 0 && $_POST[\'bday2\'] > 0) + { + // Set to blank? + if ((int) $_POST[\'bday3\'] == 1 && (int) $_POST[\'bday2\'] == 1 && (int) $value == 1) + $value = \'0001-01-01\'; + else + $value = checkdate($value, $_POST[\'bday2\'], $_POST[\'bday3\'] < 4 ? 4 : $_POST[\'bday3\']) ? sprintf(\'%04d-%02d-%02d\', $_POST[\'bday3\'] < 4 ? 4 : $_POST[\'bday3\'], $_POST[\'bday1\'], $_POST[\'bday2\']) : \'0001-01-01\'; + } + else + $value = \'0001-01-01\'; + + $profile_vars[\'birthdate\'] = $value; + $cur_profile[\'birthdate\'] = $value; + return false; + '), + ), + // Setting the birthdate the old style way? + 'birthdate' => array( + 'type' => 'hidden', + 'permission' => 'profile_extra', + 'input_validate' => create_function('&$value', ' + global $cur_profile; + // !!! Should we check for this year and tell them they made a mistake :P? (based on coppa at least?) + if (preg_match(\'/(\d{4})[\-\., ](\d{2})[\-\., ](\d{2})/\', $value, $dates) === 1) + { + $value = checkdate($dates[2], $dates[3], $dates[1] < 4 ? 4 : $dates[1]) ? sprintf(\'%04d-%02d-%02d\', $dates[1] < 4 ? 4 : $dates[1], $dates[2], $dates[3]) : \'0001-01-01\'; + return true; + } + else + { + $value = empty($cur_profile[\'birthdate\']) ? \'0001-01-01\' : $cur_profile[\'birthdate\']; + return false; + } + '), + ), + 'date_registered' => array( + 'type' => 'text', + 'value' => empty($cur_profile['date_registered']) ? $txt['not_applicable'] : strftime('%Y-%m-%d', $cur_profile['date_registered'] + ($user_info['time_offset'] + $modSettings['time_offset']) * 3600), + 'label' => $txt['date_registered'], + 'log_change' => true, + 'permission' => 'moderate_forum', + 'input_validate' => create_function('&$value', ' + global $txt, $user_info, $modSettings, $cur_profile, $context; + + // Bad date! Go try again - please? + if (($value = strtotime($value)) === -1) + { + $value = $cur_profile[\'date_registered\']; + return $txt[\'invalid_registration\'] . \' \' . strftime(\'%d %b %Y \' . (strpos($user_info[\'time_format\'], \'%H\') !== false ? \'%I:%M:%S %p\' : \'%H:%M:%S\'), forum_time(false)); + } + // As long as it doesn\'t equal "N/A"... + elseif ($value != $txt[\'not_applicable\'] && $value != strtotime(strftime(\'%Y-%m-%d\', $cur_profile[\'date_registered\'] + ($user_info[\'time_offset\'] + $modSettings[\'time_offset\']) * 3600))) + $value = $value - ($user_info[\'time_offset\'] + $modSettings[\'time_offset\']) * 3600; + else + $value = $cur_profile[\'date_registered\']; + + return true; + '), + ), + 'email_address' => array( + 'type' => 'text', + 'label' => $txt['email'], + 'subtext' => $txt['valid_email'], + 'log_change' => true, + 'permission' => 'profile_identity', + 'input_validate' => create_function('&$value', ' + global $context, $old_profile, $context, $profile_vars, $sourcedir, $modSettings; + + if (strtolower($value) == strtolower($old_profile[\'email_address\'])) + return false; + + $isValid = profileValidateEmail($value, $context[\'id_member\']); + + // Do they need to revalidate? If so schedule the function! + if ($isValid === true && !empty($modSettings[\'send_validation_onChange\']) && !allowedTo(\'moderate_forum\')) + { + require_once($sourcedir . \'/Subs-Members.php\'); + $profile_vars[\'validation_code\'] = generateValidationCode(); + $profile_vars[\'is_activated\'] = 2; + $context[\'profile_execute_on_save\'][] = \'profileSendActivation\'; + unset($context[\'profile_execute_on_save\'][\'reload_user\']); + } + + return $isValid; + '), + ), + 'gender' => array( + 'type' => 'select', + 'cast_type' => 'int', + 'options' => 'return array(0 => \'\', 1 => $txt[\'male\'], 2 => $txt[\'female\']);', + 'label' => $txt['gender'], + 'permission' => 'profile_extra', + ), + 'hide_email' => array( + 'type' => 'check', + 'value' => empty($cur_profile['hide_email']) ? true : false, + 'label' => $txt['allow_user_email'], + 'permission' => 'profile_identity', + 'input_validate' => create_function('&$value', ' + $value = $value == 0 ? 1 : 0; + + return true; + '), + ), + 'icq' => array( + 'type' => 'text', + 'label' => $txt['icq'], + 'subtext' => $txt['your_icq'], + 'size' => 24, + 'permission' => 'profile_extra', + // Need to make sure ICQ doesn't equal 0. + 'input_validate' => create_function('&$value', ' + if (empty($value)) + $value = \'\'; + else + $value = (int) $value; + return true; + '), + ), + // Selecting group membership is a complicated one so we treat it separate! + 'id_group' => array( + 'type' => 'callback', + 'callback_func' => 'group_manage', + 'permission' => 'manage_membergroups', + 'preload' => 'profileLoadGroups', + 'log_change' => true, + 'input_validate' => 'profileSaveGroups', + ), + 'id_theme' => array( + 'type' => 'callback', + 'callback_func' => 'theme_pick', + 'permission' => 'profile_extra', + 'enabled' => $modSettings['theme_allow'] || allowedTo('admin_forum'), + 'preload' => create_function('', ' + global $smcFunc, $context, $cur_profile, $txt; + + $request = $smcFunc[\'db_query\'](\'\', \' + SELECT value + FROM {db_prefix}themes + WHERE id_theme = {int:id_theme} + AND variable = {string:variable} + LIMIT 1\', array( + \'id_theme\' => $cur_profile[\'id_theme\'], + \'variable\' => \'name\', + ) + ); + list ($name) = $smcFunc[\'db_fetch_row\']($request); + $smcFunc[\'db_free_result\']($request); + + $context[\'member\'][\'theme\'] = array( + \'id\' => $cur_profile[\'id_theme\'], + \'name\' => empty($cur_profile[\'id_theme\']) ? $txt[\'theme_forum_default\'] : $name + ); + return true; + '), + 'input_validate' => create_function('&$value', ' + $value = (int) $value; + return true; + '), + ), + 'karma_good' => array( + 'type' => 'callback', + 'callback_func' => 'karma_modify', + 'permission' => 'admin_forum', + // Set karma_bad too! + 'input_validate' => create_function('&$value', ' + global $profile_vars, $cur_profile; + + $value = (int) $value; + if (isset($_POST[\'karma_bad\'])) + { + $profile_vars[\'karma_bad\'] = $_POST[\'karma_bad\'] != \'\' ? (int) $_POST[\'karma_bad\'] : 0; + $cur_profile[\'karma_bad\'] = $_POST[\'karma_bad\'] != \'\' ? (int) $_POST[\'karma_bad\'] : 0; + } + return true; + '), + 'preload' => create_function('', ' + global $context, $cur_profile; + + $context[\'member\'][\'karma\'][\'good\'] = $cur_profile[\'karma_good\']; + $context[\'member\'][\'karma\'][\'bad\'] = $cur_profile[\'karma_bad\']; + + return true; + '), + 'enabled' => !empty($modSettings['karmaMode']), + ), + 'lngfile' => array( + 'type' => 'select', + 'options' => 'return $context[\'profile_languages\'];', + 'label' => $txt['preferred_language'], + 'permission' => 'profile_identity', + 'preload' => 'profileLoadLanguages', + 'enabled' => !empty($modSettings['userLanguage']), + 'value' => empty($cur_profile['lngfile']) ? $language : $cur_profile['lngfile'], + 'input_validate' => create_function('&$value', ' + global $context, $cur_profile; + + // Load the languages. + profileLoadLanguages(); + + if (isset($context[\'profile_languages\'][$value])) + { + if ($context[\'user\'][\'is_owner\'] && empty($context[\'password_auth_failed\'])) + $_SESSION[\'language\'] = $value; + return true; + } + else + { + $value = $cur_profile[\'lngfile\']; + return false; + } + '), + ), + 'location' => array( + 'type' => 'text', + 'label' => $txt['location'], + 'log_change' => true, + 'size' => 50, + 'permission' => 'profile_extra', + ), + // The username is not always editable - so adjust it as such. + 'member_name' => array( + 'type' => allowedTo('admin_forum') && isset($_GET['changeusername']) ? 'text' : 'label', + 'label' => $txt['username'], + 'subtext' => allowedTo('admin_forum') && !isset($_GET['changeusername']) ? '(' . $txt['username_change'] . ')' : '', + 'log_change' => true, + 'permission' => 'profile_identity', + 'prehtml' => allowedTo('admin_forum') && isset($_GET['changeusername']) ? '
' . $txt['username_warning'] . '
' : '', + 'input_validate' => create_function('&$value', ' + global $sourcedir, $context, $user_info, $cur_profile; + + if (allowedTo(\'admin_forum\')) + { + // We\'ll need this... + require_once($sourcedir . \'/Subs-Auth.php\'); + + // Maybe they are trying to change their password as well? + $resetPassword = true; + if (isset($_POST[\'passwrd1\']) && $_POST[\'passwrd1\'] != \'\' && isset($_POST[\'passwrd2\']) && $_POST[\'passwrd1\'] == $_POST[\'passwrd2\'] && validatePassword($_POST[\'passwrd1\'], $value, array($cur_profile[\'real_name\'], $user_info[\'username\'], $user_info[\'name\'], $user_info[\'email\'])) == null) + $resetPassword = false; + + // Do the reset... this will send them an email too. + if ($resetPassword) + resetPassword($context[\'id_member\'], $value); + elseif ($value !== null) + { + validateUsername($context[\'id_member\'], $value); + updateMemberData($context[\'id_member\'], array(\'member_name\' => $value)); + } + } + return false; + '), + ), + 'msn' => array( + 'type' => 'text', + 'label' => $txt['msn'], + 'subtext' => $txt['msn_email_address'], + 'size' => 24, + 'permission' => 'profile_extra', + 'input_validate' => create_function('&$value', ' + global $cur_profile; + // Make sure the msn one is an email address, not something like \'none\' :P. + if ($value != \'\' && preg_match(\'~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\\\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~\', $value) == 0) + { + $value = $cur_profile[\'msn\']; + return false; + } + return true; + '), + ), + 'passwrd1' => array( + 'type' => 'password', + 'label' => $txt['choose_pass'], + 'subtext' => $txt['password_strength'], + 'size' => 20, + 'value' => '', + 'enabled' => empty($cur_profile['openid_uri']), + 'permission' => 'profile_identity', + 'save_key' => 'passwd', + // Note this will only work if passwrd2 also exists! + 'input_validate' => create_function('&$value', ' + global $sourcedir, $user_info, $smcFunc, $cur_profile; + + // If we didn\'t try it then ignore it! + if ($value == \'\') + return false; + + // Do the two entries for the password even match? + if (!isset($_POST[\'passwrd2\']) || $value != $_POST[\'passwrd2\']) + return \'bad_new_password\'; + + // Let\'s get the validation function into play... + require_once($sourcedir . \'/Subs-Auth.php\'); + $passwordErrors = validatePassword($value, $cur_profile[\'member_name\'], array($cur_profile[\'real_name\'], $user_info[\'username\'], $user_info[\'name\'], $user_info[\'email\'])); + + // Were there errors? + if ($passwordErrors != null) + return \'password_\' . $passwordErrors; + + // Set up the new password variable... ready for storage. + $value = sha1(strtolower($cur_profile[\'member_name\']) . un_htmlspecialchars($value)); + return true; + '), + ), + 'passwrd2' => array( + 'type' => 'password', + 'label' => $txt['verify_pass'], + 'enabled' => empty($cur_profile['openid_uri']), + 'size' => 20, + 'value' => '', + 'permission' => 'profile_identity', + 'is_dummy' => true, + ), + 'personal_text' => array( + 'type' => 'text', + 'label' => $txt['personal_text'], + 'log_change' => true, + 'input_attr' => array('maxlength="50"'), + 'size' => 50, + 'permission' => 'profile_extra', + ), + // This does ALL the pm settings + 'pm_prefs' => array( + 'type' => 'callback', + 'callback_func' => 'pm_settings', + 'permission' => 'pm_read', + 'preload' => create_function('', ' + global $context, $cur_profile; + + $context[\'display_mode\'] = $cur_profile[\'pm_prefs\'] & 3; + $context[\'send_email\'] = $cur_profile[\'pm_email_notify\']; + $context[\'receive_from\'] = !empty($cur_profile[\'pm_receive_from\']) ? $cur_profile[\'pm_receive_from\'] : 0; + + return true; + '), + 'input_validate' => create_function('&$value', ' + global $cur_profile, $profile_vars; + + // Simple validate and apply the two "sub settings" + $value = max(min($value, 2), 0); + + $cur_profile[\'pm_email_notify\'] = $profile_vars[\'pm_email_notify\'] = max(min((int) $_POST[\'pm_email_notify\'], 2), 0); + $cur_profile[\'pm_receive_from\'] = $profile_vars[\'pm_receive_from\'] = max(min((int) $_POST[\'pm_receive_from\'], 4), 0); + + return true; + '), + ), + 'posts' => array( + 'type' => 'int', + 'label' => $txt['profile_posts'], + 'log_change' => true, + 'size' => 7, + 'permission' => 'moderate_forum', + 'input_validate' => create_function('&$value', ' + $value = $value != \'\' ? strtr($value, array(\',\' => \'\', \'.\' => \'\', \' \' => \'\')) : 0; + return true; + '), + ), + 'real_name' => array( + 'type' => !empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum') ? 'text' : 'label', + 'label' => $txt['name'], + 'subtext' => $txt['display_name_desc'], + 'log_change' => true, + 'input_attr' => array('maxlength="60"'), + 'permission' => 'profile_identity', + 'enabled' => !empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum'), + 'input_validate' => create_function('&$value', ' + global $context, $smcFunc, $sourcedir, $cur_profile; + + $value = trim(preg_replace(\'~[\s]~\' . ($context[\'utf8\'] ? \'u\' : \'\'), \' \', $value)); + + if (trim($value) == \'\') + return \'no_name\'; + elseif ($smcFunc[\'strlen\']($value) > 60) + return \'name_too_long\'; + elseif ($cur_profile[\'real_name\'] != $value) + { + require_once($sourcedir . \'/Subs-Members.php\'); + if (isReservedName($value, $context[\'id_member\'])) + return \'name_taken\'; + } + return true; + '), + ), + 'secret_question' => array( + 'type' => 'text', + 'label' => $txt['secret_question'], + 'subtext' => $txt['secret_desc'], + 'size' => 50, + 'permission' => 'profile_identity', + ), + 'secret_answer' => array( + 'type' => 'text', + 'label' => $txt['secret_answer'], + 'subtext' => $txt['secret_desc2'], + 'size' => 20, + 'postinput' => '' . $txt['secret_why_blank'] . '', + 'value' => '', + 'permission' => 'profile_identity', + 'input_validate' => create_function('&$value', ' + $value = $value != \'\' ? md5($value) : \'\'; + return true; + '), + ), + 'signature' => array( + 'type' => 'callback', + 'callback_func' => 'signature_modify', + 'permission' => 'profile_extra', + 'enabled' => substr($modSettings['signature_settings'], 0, 1) == 1, + 'preload' => 'profileLoadSignatureData', + 'input_validate' => 'profileValidateSignature', + ), + 'show_online' => array( + 'type' => 'check', + 'label' => $txt['show_online'], + 'permission' => 'profile_identity', + 'enabled' => !empty($modSettings['allow_hideOnline']) || allowedTo('moderate_forum'), + ), + 'smiley_set' => array( + 'type' => 'callback', + 'callback_func' => 'smiley_pick', + 'enabled' => !empty($modSettings['smiley_sets_enable']), + 'permission' => 'profile_extra', + 'preload' => create_function('', ' + global $modSettings, $context, $txt, $cur_profile; + + $context[\'member\'][\'smiley_set\'][\'id\'] = empty($cur_profile[\'smiley_set\']) ? \'\' : $cur_profile[\'smiley_set\']; + $context[\'smiley_sets\'] = explode(\',\', \'none,,\' . $modSettings[\'smiley_sets_known\']); + $set_names = explode("\n", $txt[\'smileys_none\'] . "\n" . $txt[\'smileys_forum_board_default\'] . "\n" . $modSettings[\'smiley_sets_names\']); + foreach ($context[\'smiley_sets\'] as $i => $set) + { + $context[\'smiley_sets\'][$i] = array( + \'id\' => htmlspecialchars($set), + \'name\' => htmlspecialchars($set_names[$i]), + \'selected\' => $set == $context[\'member\'][\'smiley_set\'][\'id\'] + ); + + if ($context[\'smiley_sets\'][$i][\'selected\']) + $context[\'member\'][\'smiley_set\'][\'name\'] = $set_names[$i]; + } + return true; + '), + 'input_validate' => create_function('&$value', ' + global $modSettings; + + $smiley_sets = explode(\',\', $modSettings[\'smiley_sets_known\']); + if (!in_array($value, $smiley_sets) && $value != \'none\') + $value = \'\'; + return true; + '), + ), + // Pretty much a dummy entry - it populates all the theme settings. + 'theme_settings' => array( + 'type' => 'callback', + 'callback_func' => 'theme_settings', + 'permission' => 'profile_extra', + 'is_dummy' => true, + 'preload' => create_function('', ' + loadLanguage(\'Settings\'); + return true; + '), + ), + 'time_format' => array( + 'type' => 'callback', + 'callback_func' => 'timeformat_modify', + 'permission' => 'profile_extra', + 'preload' => create_function('', ' + global $context, $user_info, $txt, $cur_profile, $modSettings; + + $context[\'easy_timeformats\'] = array( + array(\'format\' => \'\', \'title\' => $txt[\'timeformat_default\']), + array(\'format\' => \'%B %d, %Y, %I:%M:%S %p\', \'title\' => $txt[\'timeformat_easy1\']), + array(\'format\' => \'%B %d, %Y, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy2\']), + array(\'format\' => \'%Y-%m-%d, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy3\']), + array(\'format\' => \'%d %B %Y, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy4\']), + array(\'format\' => \'%d-%m-%Y, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy5\']) + ); + + $context[\'member\'][\'time_format\'] = $cur_profile[\'time_format\']; + $context[\'current_forum_time\'] = timeformat(time() - $user_info[\'time_offset\'] * 3600, false); + $context[\'current_forum_time_js\'] = strftime(\'%Y,\' . ((int) strftime(\'%m\', time() + $modSettings[\'time_offset\'] * 3600) - 1) . \',%d,%H,%M,%S\', time() + $modSettings[\'time_offset\'] * 3600); + $context[\'current_forum_time_hour\'] = (int) strftime(\'%H\', forum_time(false)); + return true; + '), + ), + 'time_offset' => array( + 'type' => 'callback', + 'callback_func' => 'timeoffset_modify', + 'permission' => 'profile_extra', + 'preload' => create_function('', ' + global $context, $cur_profile; + $context[\'member\'][\'time_offset\'] = $cur_profile[\'time_offset\']; + return true; + '), + 'input_validate' => create_function('&$value', ' + // Validate the time_offset... + $value = (float) strtr($value, \',\', \'.\'); + + if ($value < -23.5 || $value > 23.5) + return \'bad_offset\'; + + return true; + '), + ), + 'usertitle' => array( + 'type' => 'text', + 'label' => $txt['custom_title'], + 'log_change' => true, + 'size' => 50, + 'permission' => 'profile_title', + 'enabled' => !empty($modSettings['titlesEnable']), + ), + 'website_title' => array( + 'type' => 'text', + 'label' => $txt['website_title'], + 'subtext' => $txt['include_website_url'], + 'size' => 50, + 'permission' => 'profile_extra', + 'link_with' => 'website', + ), + 'website_url' => array( + 'type' => 'text', + 'label' => $txt['website_url'], + 'subtext' => $txt['complete_url'], + 'size' => 50, + 'permission' => 'profile_extra', + // Fix the URL... + 'input_validate' => create_function('&$value', ' + + if (strlen(trim($value)) > 0 && strpos($value, \'://\') === false) + $value = \'http://\' . $value; + if (strlen($value) < 8 || (substr($value, 0, 7) !== \'http://\' && substr($value, 0, 8) !== \'https://\')) + $value = \'\'; + return true; + '), + 'link_with' => 'website', + ), + 'yim' => array( + 'type' => 'text', + 'label' => $txt['yim'], + 'subtext' => $txt['your_yim'], + 'size' => 24, + 'input_attr' => array('maxlength="32"'), + 'permission' => 'profile_extra', + ), + ); + + $disabled_fields = !empty($modSettings['disabled_profile_fields']) ? explode(',', $modSettings['disabled_profile_fields']) : array(); + // For each of the above let's take out the bits which don't apply - to save memory and security! + foreach ($profile_fields as $key => $field) + { + // Do we have permission to do this? + if (isset($field['permission']) && !allowedTo(($context['user']['is_owner'] ? array($field['permission'] . '_own', $field['permission'] . '_any') : $field['permission'] . '_any')) && !allowedTo($field['permission'])) + unset($profile_fields[$key]); + + // Is it enabled? + if (isset($field['enabled']) && !$field['enabled']) + unset($profile_fields[$key]); + + // Is it specifically disabled? + if (in_array($key, $disabled_fields) || (isset($field['link_with']) && in_array($field['link_with'], $disabled_fields))) + unset($profile_fields[$key]); + } +} + +// Setup the context for a page load! +function setupProfileContext($fields) +{ + global $profile_fields, $context, $cur_profile, $smcFunc, $txt; + + // Make sure we have this! + loadProfileFields(true); + + // First check for any linked sets. + foreach ($profile_fields as $key => $field) + if (isset($field['link_with']) && in_array($field['link_with'], $fields)) + $fields[] = $key; + + // Some default bits. + $context['profile_prehtml'] = ''; + $context['profile_posthtml'] = ''; + $context['profile_javascript'] = ''; + $context['profile_onsubmit_javascript'] = ''; + + $i = 0; + $last_type = ''; + foreach ($fields as $key => $field) + { + if (isset($profile_fields[$field])) + { + // Shortcut. + $cur_field = &$profile_fields[$field]; + + // Does it have a preload and does that preload succeed? + if (isset($cur_field['preload']) && !$cur_field['preload']()) + continue; + + // If this is anything but complex we need to do more cleaning! + if ($cur_field['type'] != 'callback' && $cur_field['type'] != 'hidden') + { + if (!isset($cur_field['label'])) + $cur_field['label'] = isset($txt[$field]) ? $txt[$field] : $field; + + // Everything has a value! + if (!isset($cur_field['value'])) + { + $cur_field['value'] = isset($cur_profile[$field]) ? $cur_profile[$field] : ''; + } + + // Any input attributes? + $cur_field['input_attr'] = !empty($cur_field['input_attr']) ? implode(',', $cur_field['input_attr']) : ''; + } + + // Was there an error with this field on posting? + if (isset($context['profile_errors'][$field])) + $cur_field['is_error'] = true; + + // Any javascript stuff? + if (!empty($cur_field['js_submit'])) + $context['profile_onsubmit_javascript'] .= $cur_field['js_submit']; + if (!empty($cur_field['js'])) + $context['profile_javascript'] .= $cur_field['js']; + + // Any template stuff? + if (!empty($cur_field['prehtml'])) + $context['profile_prehtml'] .= $cur_field['prehtml']; + if (!empty($cur_field['posthtml'])) + $context['profile_posthtml'] .= $cur_field['posthtml']; + + // Finally put it into context? + if ($cur_field['type'] != 'hidden') + { + $last_type = $cur_field['type']; + $context['profile_fields'][$field] = &$profile_fields[$field]; + } + } + // Bodge in a line break - without doing two in a row ;) + elseif ($field == 'hr' && $last_type != 'hr' && $last_type != '') + { + $last_type = 'hr'; + $context['profile_fields'][$i++]['type'] = 'hr'; + } + } + + // Free up some memory. + unset($profile_fields); +} + +// Save the profile changes. +function saveProfileFields() +{ + global $profile_fields, $profile_vars, $context, $old_profile, $post_errors, $sourcedir, $modSettings, $cur_profile, $smcFunc; + + // Load them up. + loadProfileFields(); + + // This makes things easier... + $old_profile = $cur_profile; + + // This allows variables to call activities when they save - by default just to reload their settings + $context['profile_execute_on_save'] = array(); + if ($context['user']['is_owner']) + $context['profile_execute_on_save']['reload_user'] = 'profileReloadUser'; + + // Assume we log nothing. + $context['log_changes'] = array(); + + // Cycle through the profile fields working out what to do! + foreach ($profile_fields as $key => $field) + { + if (!isset($_POST[$key]) || !empty($field['is_dummy'])) + continue; + + // What gets updated? + $db_key = isset($field['save_key']) ? $field['save_key'] : $key; + + // Right - we have something that is enabled, we can act upon and has a value posted to it. Does it have a validation function? + if (isset($field['input_validate'])) + { + $is_valid = $field['input_validate']($_POST[$key]); + // An error occured - set it as such! + if ($is_valid !== true) + { + // Is this an actual error? + if ($is_valid !== false) + { + $post_errors[$key] = $is_valid; + $profile_fields[$key]['is_error'] = $is_valid; + } + // Retain the old value. + $cur_profile[$key] = $_POST[$key]; + continue; + } + } + + // Are we doing a cast? + $field['cast_type'] = empty($field['cast_type']) ? $field['type'] : $field['cast_type']; + + // Finally, clean up certain types. + if ($field['cast_type'] == 'int') + $_POST[$key] = (int) $_POST[$key]; + elseif ($field['cast_type'] == 'float') + $_POST[$key] = (float) $_POST[$key]; + elseif ($field['cast_type'] == 'check') + $_POST[$key] = !empty($_POST[$key]) ? 1 : 0; + + // If we got here we're doing OK. + if ($field['type'] != 'hidden' && (!isset($old_profile[$key]) || $_POST[$key] != $old_profile[$key])) + { + // Set the save variable. + $profile_vars[$db_key] = $_POST[$key]; + // And update the user profile. + $cur_profile[$key] = $_POST[$key]; + + // Are we logging it? + if (!empty($field['log_change']) && isset($old_profile[$key])) + $context['log_changes'][$key] = array( + 'previous' => $old_profile[$key], + 'new' => $_POST[$key], + ); + } + + // Logging group changes are a bit different... + if ($key == 'id_group' && $field['log_change']) + { + profileLoadGroups(); + + // Any changes to primary group? + if ($_POST['id_group'] != $old_profile['id_group']) + { + $context['log_changes']['id_group'] = array( + 'previous' => !empty($old_profile[$key]) && isset($context['member_groups'][$old_profile[$key]]) ? $context['member_groups'][$old_profile[$key]]['name'] : '', + 'new' => !empty($_POST[$key]) && isset($context['member_groups'][$_POST[$key]]) ? $context['member_groups'][$_POST[$key]]['name'] : '', + ); + } + + // Prepare additional groups for comparison. + $additional_groups = array( + 'previous' => !empty($old_profile['additional_groups']) ? explode(',', $old_profile['additional_groups']) : array(), + 'new' => !empty($_POST['additional_groups']) ? array_diff($_POST['additional_groups'], array(0)) : array(), + ); + + sort($additional_groups['previous']); + sort($additional_groups['new']); + + // What about additional groups? + if ($additional_groups['previous'] != $additional_groups['new']) + { + foreach ($additional_groups as $type => $groups) + { + foreach ($groups as $id => $group) + { + if (isset($context['member_groups'][$group])) + $additional_groups[$type][$id] = $context['member_groups'][$group]['name']; + else + unset($additional_groups[$type][$id]); + } + $additional_groups[$type] = implode(', ', $additional_groups[$type]); + } + + $context['log_changes']['additional_groups'] = $additional_groups; + } + } + } + + //!!! Temporary + if ($context['user']['is_owner']) + $changeOther = allowedTo(array('profile_extra_any', 'profile_extra_own')); + else + $changeOther = allowedTo('profile_extra_any'); + if ($changeOther && empty($post_errors)) + { + makeThemeChanges($context['id_member'], isset($_POST['id_theme']) ? (int) $_POST['id_theme'] : $old_profile['id_theme']); + if (!empty($_REQUEST['sa'])) + makeCustomFieldChanges($context['id_member'], $_REQUEST['sa'], false); + } + + // Free memory! + unset($profile_fields); +} + +// Save the profile changes.... +function saveProfileChanges(&$profile_vars, &$post_errors, $memID) +{ + global $user_info, $txt, $modSettings, $user_profile; + global $context, $settings, $sourcedir; + global $smcFunc; + + // These make life easier.... + $old_profile = &$user_profile[$memID]; + + // Permissions... + if ($context['user']['is_owner']) + { + $changeIdentity = allowedTo(array('profile_identity_any', 'profile_identity_own')); + $changeOther = allowedTo(array('profile_extra_any', 'profile_extra_own')); + } + else + { + $changeIdentity = allowedTo('profile_identity_any'); + $changeOther = allowedTo('profile_extra_any'); + } + + // Arrays of all the changes - makes things easier. + $profile_bools = array( + 'notify_announcements', 'notify_send_body', + ); + $profile_ints = array( + 'notify_regularity', + 'notify_types', + ); + $profile_floats = array( + ); + $profile_strings = array( + 'buddy_list', + 'ignore_boards', + ); + + if (isset($_POST['sa']) && $_POST['sa'] == 'ignoreboards' && empty($_POST['ignore_brd'])) + $_POST['ignore_brd'] = array(); + + unset($_POST['ignore_boards']); // Whatever it is set to is a dirty fithy thing. Kinda like our minds. + if (isset($_POST['ignore_brd'])) + { + if (!is_array($_POST['ignore_brd'])) + $_POST['ignore_brd'] = array ($_POST['ignore_brd']); + + foreach ($_POST['ignore_brd'] as $k => $d) + { + $d = (int) $d; + if ($d != 0) + $_POST['ignore_brd'][$k] = $d; + else + unset($_POST['ignore_brd'][$k]); + } + $_POST['ignore_boards'] = implode(',', $_POST['ignore_brd']); + unset($_POST['ignore_brd']); + + } + + // Here's where we sort out all the 'other' values... + if ($changeOther) + { + makeThemeChanges($memID, isset($_POST['id_theme']) ? (int) $_POST['id_theme'] : $old_profile['id_theme']); + //makeAvatarChanges($memID, $post_errors); + makeNotificationChanges($memID); + if (!empty($_REQUEST['sa'])) + makeCustomFieldChanges($memID, $_REQUEST['sa'], false); + + foreach ($profile_bools as $var) + if (isset($_POST[$var])) + $profile_vars[$var] = empty($_POST[$var]) ? '0' : '1'; + foreach ($profile_ints as $var) + if (isset($_POST[$var])) + $profile_vars[$var] = $_POST[$var] != '' ? (int) $_POST[$var] : ''; + foreach ($profile_floats as $var) + if (isset($_POST[$var])) + $profile_vars[$var] = (float) $_POST[$var]; + foreach ($profile_strings as $var) + if (isset($_POST[$var])) + $profile_vars[$var] = $_POST[$var]; + } +} + +// Make any theme changes that are sent with the profile.. +function makeThemeChanges($memID, $id_theme) +{ + global $modSettings, $smcFunc, $context; + + $reservedVars = array( + 'actual_theme_url', + 'actual_images_url', + 'base_theme_dir', + 'base_theme_url', + 'default_images_url', + 'default_theme_dir', + 'default_theme_url', + 'default_template', + 'images_url', + 'number_recent_posts', + 'smiley_sets_default', + 'theme_dir', + 'theme_id', + 'theme_layers', + 'theme_templates', + 'theme_url', + ); + + // Can't change reserved vars. + if ((isset($_POST['options']) && array_intersect($_POST['options'], $reservedVars) != array()) || (isset($_POST['default_options']) && array_intersect($_POST['default_options'], $reservedVars) != array())) + fatal_lang_error('no_access', false); + + // Don't allow any overriding of custom fields with default or non-default options. + $request = $smcFunc['db_query']('', ' + SELECT col_name + FROM {db_prefix}custom_fields + WHERE active = {int:is_active}', + array( + 'is_active' => 1, + ) + ); + $custom_fields = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $custom_fields[] = $row['col_name']; + $smcFunc['db_free_result']($request); + + // These are the theme changes... + $themeSetArray = array(); + if (isset($_POST['options']) && is_array($_POST['options'])) + { + foreach ($_POST['options'] as $opt => $val) + { + if (in_array($opt, $custom_fields)) + continue; + + // These need to be controlled. + if ($opt == 'topics_per_page' || $opt == 'messages_per_page') + $val = max(0, min($val, 50)); + + $themeSetArray[] = array($memID, $id_theme, $opt, is_array($val) ? implode(',', $val) : $val); + } + } + + $erase_options = array(); + if (isset($_POST['default_options']) && is_array($_POST['default_options'])) + foreach ($_POST['default_options'] as $opt => $val) + { + if (in_array($opt, $custom_fields)) + continue; + + // These need to be controlled. + if ($opt == 'topics_per_page' || $opt == 'messages_per_page') + $val = max(0, min($val, 50)); + + $themeSetArray[] = array($memID, 1, $opt, is_array($val) ? implode(',', $val) : $val); + $erase_options[] = $opt; + } + + // If themeSetArray isn't still empty, send it to the database. + if (empty($context['password_auth_failed'])) + { + if (!empty($themeSetArray)) + { + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + $themeSetArray, + array('id_member', 'id_theme', 'variable') + ); + } + + if (!empty($erase_options)) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE id_theme != {int:id_theme} + AND variable IN ({array_string:erase_variables}) + AND id_member = {int:id_member}', + array( + 'id_theme' => 1, + 'id_member' => $memID, + 'erase_variables' => $erase_options + ) + ); + } + + $themes = explode(',', $modSettings['knownThemes']); + foreach ($themes as $t) + cache_put_data('theme_settings-' . $t . ':' . $memID, null, 60); + } +} + +// Make any notification changes that need to be made. +function makeNotificationChanges($memID) +{ + global $smcFunc; + + // Update the boards they are being notified on. + if (isset($_POST['edit_notify_boards']) && !empty($_POST['notify_boards'])) + { + // Make sure only integers are deleted. + foreach ($_POST['notify_boards'] as $index => $id) + $_POST['notify_boards'][$index] = (int) $id; + + // id_board = 0 is reserved for topic notifications. + $_POST['notify_boards'] = array_diff($_POST['notify_boards'], array(0)); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_notify + WHERE id_board IN ({array_int:board_list}) + AND id_member = {int:selected_member}', + array( + 'board_list' => $_POST['notify_boards'], + 'selected_member' => $memID, + ) + ); + } + + // We are editing topic notifications...... + elseif (isset($_POST['edit_notify_topics']) && !empty($_POST['notify_topics'])) + { + foreach ($_POST['notify_topics'] as $index => $id) + $_POST['notify_topics'][$index] = (int) $id; + + // Make sure there are no zeros left. + $_POST['notify_topics'] = array_diff($_POST['notify_topics'], array(0)); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_notify + WHERE id_topic IN ({array_int:topic_list}) + AND id_member = {int:selected_member}', + array( + 'topic_list' => $_POST['notify_topics'], + 'selected_member' => $memID, + ) + ); + } +} + +// Save any changes to the custom profile fields... +function makeCustomFieldChanges($memID, $area, $sanitize = true) +{ + global $context, $smcFunc, $user_profile, $user_info, $modSettings; + + if ($sanitize && isset($_POST['customfield'])) + $_POST['customfield'] = htmlspecialchars__recursive($_POST['customfield']); + + $where = $area == 'register' ? 'show_reg != 0' : 'show_profile = {string:area}'; + + // Load the fields we are saving too - make sure we save valid data (etc). + $request = $smcFunc['db_query']('', ' + SELECT col_name, field_name, field_desc, field_type, field_length, field_options, default_value, show_reg, mask, private + FROM {db_prefix}custom_fields + WHERE ' . $where . ' + AND active = {int:is_active}', + array( + 'is_active' => 1, + 'area' => $area, + ) + ); + $changes = array(); + $log_changes = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + /* This means don't save if: + - The user is NOT an admin. + - The data is not freely viewable and editable by users. + - The data is not invisible to users but editable by the owner (or if it is the user is not the owner) + - The area isn't registration, and if it is that the field is not suppossed to be shown there. + */ + if ($row['private'] != 0 && !allowedTo('admin_forum') && ($memID != $user_info['id'] || $row['private'] != 2) && ($area != 'register' || $row['show_reg'] == 0)) + continue; + + // Validate the user data. + if ($row['field_type'] == 'check') + $value = isset($_POST['customfield'][$row['col_name']]) ? 1 : 0; + elseif ($row['field_type'] == 'select' || $row['field_type'] == 'radio') + { + $value = $row['default_value']; + foreach (explode(',', $row['field_options']) as $k => $v) + if (isset($_POST['customfield'][$row['col_name']]) && $_POST['customfield'][$row['col_name']] == $k) + $value = $v; + } + // Otherwise some form of text! + else + { + $value = isset($_POST['customfield'][$row['col_name']]) ? $_POST['customfield'][$row['col_name']] : ''; + if ($row['field_length']) + $value = $smcFunc['substr']($value, 0, $row['field_length']); + + // Any masks? + if ($row['field_type'] == 'text' && !empty($row['mask']) && $row['mask'] != 'none') + { + //!!! We never error on this - just ignore it at the moment... + if ($row['mask'] == 'email' && (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $value) === 0 || strlen($value) > 255)) + $value = ''; + elseif ($row['mask'] == 'number') + { + $value = (int) $value; + } + elseif (substr($row['mask'], 0, 5) == 'regex' && preg_match(substr($row['mask'], 5), $value) === 0) + $value = ''; + } + } + + // Did it change? + if (!isset($user_profile[$memID]['options'][$row['col_name']]) || $user_profile[$memID]['options'][$row['col_name']] != $value) + { + $log_changes[] = array( + 'action' => 'customfield_' . $row['col_name'], + 'id_log' => 2, + 'log_time' => time(), + 'id_member' => $memID, + 'ip' => $user_info['ip'], + 'extra' => serialize(array('previous' => !empty($user_profile[$memID]['options'][$row['col_name']]) ? $user_profile[$memID]['options'][$row['col_name']] : '', 'new' => $value, 'applicator' => $user_info['id'])), + ); + $changes[] = array(1, $row['col_name'], $value, $memID); + $user_profile[$memID]['options'][$row['col_name']] = $value; + } + } + $smcFunc['db_free_result']($request); + + // Make those changes! + if (!empty($changes) && empty($context['password_auth_failed'])) + { + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534', 'id_member' => 'int'), + $changes, + array('id_theme', 'variable', 'id_member') + ); + if (!empty($log_changes) && !empty($modSettings['modlog_enabled'])) + $smcFunc['db_insert']('', + '{db_prefix}log_actions', + array( + 'action' => 'string', 'id_log' => 'int', 'log_time' => 'int', 'id_member' => 'int', 'ip' => 'string-16', + 'extra' => 'string-65534', + ), + $log_changes, + array('id_action') + ); + } +} + +// Show all the users buddies, as well as a add/delete interface. +function editBuddyIgnoreLists($memID) +{ + global $sourcedir, $context, $txt, $scripturl, $modSettings, $user_profile; + + // Do a quick check to ensure people aren't getting here illegally! + if (!$context['user']['is_owner'] || empty($modSettings['enable_buddylist'])) + fatal_lang_error('no_access', false); + + // Can we email the user direct? + $context['can_moderate_forum'] = allowedTo('moderate_forum'); + + $subActions = array( + 'buddies' => array('editBuddies', $txt['editBuddies']), + 'ignore' => array('editIgnoreList', $txt['editIgnoreList']), + ); + + $context['list_area'] = isset($_GET['sa']) && isset($subActions[$_GET['sa']]) ? $_GET['sa'] : 'buddies'; + + // Create the tabs for the template. + $context[$context['profile_menu_name']]['tab_data'] = array( + 'title' => $txt['editBuddyIgnoreLists'], + 'description' => $txt['buddy_ignore_desc'], + 'icon' => 'profile_sm.gif', + 'tabs' => array( + 'buddies' => array(), + 'ignore' => array(), + ), + ); + + // Pass on to the actual function. + $context['sub_template'] = $subActions[$context['list_area']][0]; + $subActions[$context['list_area']][0]($memID); +} + +// Show all the users buddies, as well as a add/delete interface. +function editBuddies($memID) +{ + global $txt, $scripturl, $modSettings; + global $context, $user_profile, $memberContext, $smcFunc; + + // For making changes! + $buddiesArray = explode(',', $user_profile[$memID]['buddy_list']); + foreach ($buddiesArray as $k => $dummy) + if ($dummy == '') + unset($buddiesArray[$k]); + + // Removing a buddy? + if (isset($_GET['remove'])) + { + checkSession('get'); + + // Heh, I'm lazy, do it the easy way... + foreach ($buddiesArray as $key => $buddy) + if ($buddy == (int) $_GET['remove']) + unset($buddiesArray[$key]); + + // Make the changes. + $user_profile[$memID]['buddy_list'] = implode(',', $buddiesArray); + updateMemberData($memID, array('buddy_list' => $user_profile[$memID]['buddy_list'])); + + // Redirect off the page because we don't like all this ugly query stuff to stick in the history. + redirectexit('action=profile;area=lists;sa=buddies;u=' . $memID); + } + elseif (isset($_POST['new_buddy'])) + { + // Prepare the string for extraction... + $_POST['new_buddy'] = strtr($smcFunc['htmlspecialchars']($_POST['new_buddy'], ENT_QUOTES), array('"' => '"')); + preg_match_all('~"([^"]+)"~', $_POST['new_buddy'], $matches); + $new_buddies = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_POST['new_buddy'])))); + + foreach ($new_buddies as $k => $dummy) + { + $new_buddies[$k] = strtr(trim($new_buddies[$k]), array('\'' => ''')); + + if (strlen($new_buddies[$k]) == 0 || in_array($new_buddies[$k], array($user_profile[$memID]['member_name'], $user_profile[$memID]['real_name']))) + unset($new_buddies[$k]); + } + + if (!empty($new_buddies)) + { + // Now find out the id_member of the buddy. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE member_name IN ({array_string:new_buddies}) OR real_name IN ({array_string:new_buddies}) + LIMIT {int:count_new_buddies}', + array( + 'new_buddies' => $new_buddies, + 'count_new_buddies' => count($new_buddies), + ) + ); + + // Add the new member to the buddies array. + while ($row = $smcFunc['db_fetch_assoc']($request)) + $buddiesArray[] = (int) $row['id_member']; + $smcFunc['db_free_result']($request); + + // Now update the current users buddy list. + $user_profile[$memID]['buddy_list'] = implode(',', $buddiesArray); + updateMemberData($memID, array('buddy_list' => $user_profile[$memID]['buddy_list'])); + } + + // Back to the buddy list! + redirectexit('action=profile;area=lists;sa=buddies;u=' . $memID); + } + + // Get all the users "buddies"... + $buddies = array(); + + if (!empty($buddiesArray)) + { + $result = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE id_member IN ({array_int:buddy_list}) + ORDER BY real_name + LIMIT {int:buddy_list_count}', + array( + 'buddy_list' => $buddiesArray, + 'buddy_list_count' => substr_count($user_profile[$memID]['buddy_list'], ',') + 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $buddies[] = $row['id_member']; + $smcFunc['db_free_result']($result); + } + + $context['buddy_count'] = count($buddies); + + // Load all the members up. + loadMemberData($buddies, false, 'profile'); + + // Setup the context for each buddy. + $context['buddies'] = array(); + foreach ($buddies as $buddy) + { + loadMemberContext($buddy); + $context['buddies'][$buddy] = $memberContext[$buddy]; + } +} + +// Allows the user to view their ignore list, as well as the option to manage members on it. +function editIgnoreList($memID) +{ + global $txt, $scripturl, $modSettings; + global $context, $user_profile, $memberContext, $smcFunc; + + // For making changes! + $ignoreArray = explode(',', $user_profile[$memID]['pm_ignore_list']); + foreach ($ignoreArray as $k => $dummy) + if ($dummy == '') + unset($ignoreArray[$k]); + + // Removing a member from the ignore list? + if (isset($_GET['remove'])) + { + checkSession('get'); + + // Heh, I'm lazy, do it the easy way... + foreach ($ignoreArray as $key => $id_remove) + if ($id_remove == (int) $_GET['remove']) + unset($ignoreArray[$key]); + + // Make the changes. + $user_profile[$memID]['pm_ignore_list'] = implode(',', $ignoreArray); + updateMemberData($memID, array('pm_ignore_list' => $user_profile[$memID]['pm_ignore_list'])); + + // Redirect off the page because we don't like all this ugly query stuff to stick in the history. + redirectexit('action=profile;area=lists;sa=ignore;u=' . $memID); + } + elseif (isset($_POST['new_ignore'])) + { + // Prepare the string for extraction... + $_POST['new_ignore'] = strtr($smcFunc['htmlspecialchars']($_POST['new_ignore'], ENT_QUOTES), array('"' => '"')); + preg_match_all('~"([^"]+)"~', $_POST['new_ignore'], $matches); + $new_entries = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_POST['new_ignore'])))); + + foreach ($new_entries as $k => $dummy) + { + $new_entries[$k] = strtr(trim($new_entries[$k]), array('\'' => ''')); + + if (strlen($new_entries[$k]) == 0 || in_array($new_entries[$k], array($user_profile[$memID]['member_name'], $user_profile[$memID]['real_name']))) + unset($new_entries[$k]); + } + + if (!empty($new_entries)) + { + // Now find out the id_member for the members in question. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE member_name IN ({array_string:new_entries}) OR real_name IN ({array_string:new_entries}) + LIMIT {int:count_new_entries}', + array( + 'new_entries' => $new_entries, + 'count_new_entries' => count($new_entries), + ) + ); + + // Add the new member to the buddies array. + while ($row = $smcFunc['db_fetch_assoc']($request)) + $ignoreArray[] = (int) $row['id_member']; + $smcFunc['db_free_result']($request); + + // Now update the current users buddy list. + $user_profile[$memID]['pm_ignore_list'] = implode(',', $ignoreArray); + updateMemberData($memID, array('pm_ignore_list' => $user_profile[$memID]['pm_ignore_list'])); + } + + // Back to the list of pityful people! + redirectexit('action=profile;area=lists;sa=ignore;u=' . $memID); + } + + // Initialise the list of members we're ignoring. + $ignored = array(); + + if (!empty($ignoreArray)) + { + $result = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE id_member IN ({array_int:ignore_list}) + ORDER BY real_name + LIMIT {int:ignore_list_count}', + array( + 'ignore_list' => $ignoreArray, + 'ignore_list_count' => substr_count($user_profile[$memID]['pm_ignore_list'], ',') + 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $ignored[] = $row['id_member']; + $smcFunc['db_free_result']($result); + } + + $context['ignore_count'] = count($ignored); + + // Load all the members up. + loadMemberData($ignored, false, 'profile'); + + // Setup the context for each buddy. + $context['ignore_list'] = array(); + foreach ($ignored as $ignore_member) + { + loadMemberContext($ignore_member); + $context['ignore_list'][$ignore_member] = $memberContext[$ignore_member]; + } +} + +function account($memID) +{ + global $context, $txt; + + loadThemeOptions($memID); + if (allowedTo(array('profile_identity_own', 'profile_identity_any'))) + loadCustomFields($memID, 'account'); + + $context['sub_template'] = 'edit_options'; + $context['page_desc'] = $txt['account_info']; + + setupProfileContext( + array( + 'member_name', 'real_name', 'date_registered', 'posts', 'lngfile', 'hr', + 'id_group', 'hr', + 'email_address', 'hide_email', 'show_online', 'hr', + 'passwrd1', 'passwrd2', 'hr', + 'secret_question', 'secret_answer', + ) + ); +} + +function forumProfile($memID) +{ + global $context, $user_profile, $user_info, $txt, $modSettings; + + loadThemeOptions($memID); + if (allowedTo(array('profile_extra_own', 'profile_extra_any'))) + loadCustomFields($memID, 'forumprofile'); + + $context['sub_template'] = 'edit_options'; + $context['page_desc'] = $txt['forumProfile_info']; + + setupProfileContext( + array( + 'avatar_choice', 'hr', 'personal_text', 'hr', + 'bday1', 'location', 'gender', 'hr', + 'icq', 'aim', 'msn', 'yim', 'hr', + 'usertitle', 'signature', 'hr', + 'karma_good', 'hr', + 'website_title', 'website_url', + ) + ); +} + +// Allow the edit of *someone elses* personal message settings. +function pmprefs($memID) +{ + global $sourcedir, $context, $txt, $scripturl; + + loadThemeOptions($memID); + loadCustomFields($memID, 'pmprefs'); + + $context['sub_template'] = 'edit_options'; + $context['page_desc'] = $txt['pm_settings_desc']; + + setupProfileContext( + array( + 'pm_prefs', + ) + ); +} + +// Recursive function to retrieve avatar files +function getAvatars($directory, $level) +{ + global $context, $txt, $modSettings; + + $result = array(); + + // Open the directory.. + $dir = dir($modSettings['avatar_directory'] . (!empty($directory) ? '/' : '') . $directory); + $dirs = array(); + $files = array(); + + if (!$dir) + return array(); + + while ($line = $dir->read()) + { + if (in_array($line, array('.', '..', 'blank.gif', 'index.php'))) + continue; + + if (is_dir($modSettings['avatar_directory'] . '/' . $directory . (!empty($directory) ? '/' : '') . $line)) + $dirs[] = $line; + else + $files[] = $line; + } + $dir->close(); + + // Sort the results... + natcasesort($dirs); + natcasesort($files); + + if ($level == 0) + { + $result[] = array( + 'filename' => 'blank.gif', + 'checked' => in_array($context['member']['avatar']['server_pic'], array('', 'blank.gif')), + 'name' => $txt['no_pic'], + 'is_dir' => false + ); + } + + foreach ($dirs as $line) + { + $tmp = getAvatars($directory . (!empty($directory) ? '/' : '') . $line, $level + 1); + if (!empty($tmp)) + $result[] = array( + 'filename' => htmlspecialchars($line), + 'checked' => strpos($context['member']['avatar']['server_pic'], $line . '/') !== false, + 'name' => '[' . htmlspecialchars(str_replace('_', ' ', $line)) . ']', + 'is_dir' => true, + 'files' => $tmp + ); + unset($tmp); + } + + foreach ($files as $line) + { + $filename = substr($line, 0, (strlen($line) - strlen(strrchr($line, '.')))); + $extension = substr(strrchr($line, '.'), 1); + + // Make sure it is an image. + if (strcasecmp($extension, 'gif') != 0 && strcasecmp($extension, 'jpg') != 0 && strcasecmp($extension, 'jpeg') != 0 && strcasecmp($extension, 'png') != 0 && strcasecmp($extension, 'bmp') != 0) + continue; + + $result[] = array( + 'filename' => htmlspecialchars($line), + 'checked' => $line == $context['member']['avatar']['server_pic'], + 'name' => htmlspecialchars(str_replace('_', ' ', $filename)), + 'is_dir' => false + ); + if ($level == 1) + $context['avatar_list'][] = $directory . '/' . $line; + } + + return $result; +} + +function theme($memID) +{ + global $txt, $context, $user_profile, $modSettings, $settings, $user_info, $smcFunc; + + loadThemeOptions($memID); + if (allowedTo(array('profile_extra_own', 'profile_extra_any'))) + loadCustomFields($memID, 'theme'); + + $context['sub_template'] = 'edit_options'; + $context['page_desc'] = $txt['theme_info']; + + setupProfileContext( + array( + 'id_theme', 'smiley_set', 'hr', + 'time_format', 'time_offset', 'hr', + 'theme_settings', + ) + ); +} + +// Changing authentication method? Only appropriate for people using OpenID. +function authentication($memID, $saving = false) +{ + global $context, $cur_profile, $sourcedir, $txt, $post_errors, $modSettings; + + loadLanguage('Login'); + + // We are saving? + if ($saving) + { + // Moving to password passed authentication? + if ($_POST['authenticate'] == 'passwd') + { + // Didn't enter anything? + if ($_POST['passwrd1'] == '') + $post_errors[] = 'no_password'; + // Do the two entries for the password even match? + elseif (!isset($_POST['passwrd2']) || $_POST['passwrd1'] != $_POST['passwrd2']) + $post_errors[] = 'bad_new_password'; + // Is it valid? + else + { + require_once($sourcedir . '/Subs-Auth.php'); + $passwordErrors = validatePassword($_POST['passwrd1'], $cur_profile['member_name'], array($cur_profile['real_name'], $cur_profile['email_address'])); + + // Were there errors? + if ($passwordErrors != null) + $post_errors[] = 'password_' . $passwordErrors; + } + + if (empty($post_errors)) + { + // Integration? + call_integration_hook('integrate_reset_pass', array($cur_profile['member_name'], $cur_profile['member_name'], $_POST['passwrd1'])); + + // Go then. + $passwd = sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd1'])); + + // Do the important bits. + updateMemberData($memID, array('openid_uri' => '', 'passwd' => $passwd)); + if ($context['user']['is_owner']) + setLoginCookie(60 * $modSettings['cookieTime'], $memID, sha1(sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd2'])) . $cur_profile['password_salt'])); + + redirectexit('action=profile;u=' . $memID); + } + + return true; + } + // Not right yet! + elseif ($_POST['authenticate'] == 'openid' && !empty($_POST['openid_identifier'])) + { + require_once($sourcedir . '/Subs-OpenID.php'); + $_POST['openid_identifier'] = smf_openID_canonize($_POST['openid_identifier']); + + if (smf_openid_member_exists($_POST['openid_identifier'])) + $post_errors[] = 'openid_in_use'; + elseif (empty($post_errors)) + { + // Authenticate using the new OpenID URI first to make sure they didn't make a mistake. + if ($context['user']['is_owner']) + { + $_SESSION['new_openid_uri'] = $_POST['openid_identifier']; + + smf_openID_validate($_POST['openid_identifier'], false, null, 'change_uri'); + } + else + updateMemberData($memID, array('openid_uri' => $_POST['openid_identifier'])); + } + } + } + + // Some stuff. + $context['member']['openid_uri'] = $cur_profile['openid_uri']; + $context['auth_method'] = empty($cur_profile['openid_uri']) ? 'password' : 'openid'; + $context['sub_template'] = 'authentication_method'; +} + +// Display the notifications and settings for changes. +function notification($memID) +{ + global $txt, $scripturl, $user_profile, $user_info, $context, $modSettings, $smcFunc, $sourcedir, $settings; + + // Gonna want this for the list. + require_once($sourcedir . '/Subs-List.php'); + + // Fine, start with the board list. + $listOptions = array( + 'id' => 'board_notification_list', + 'width' => '100%', + 'no_items_label' => $txt['notifications_boards_none'] . '

' . $txt['notifications_boards_howto'], + 'no_items_align' => 'left', + 'base_href' => $scripturl . '?action=profile;u=' . $memID . ';area=notification', + 'default_sort_col' => 'board_name', + 'get_items' => array( + 'function' => 'list_getBoardNotifications', + 'params' => array( + $memID, + ), + ), + 'columns' => array( + 'board_name' => array( + 'header' => array( + 'value' => $txt['notifications_boards'], + 'class' => 'lefttext first_th', + ), + 'data' => array( + 'function' => create_function('$board', ' + global $settings, $txt; + + $link = $board[\'link\']; + + if ($board[\'new\']) + $link .= \' \' . $txt[\'new\'] . \'\'; + + return $link; + '), + ), + 'sort' => array( + 'default' => 'name', + 'reverse' => 'name DESC', + ), + ), + 'delete' => array( + 'header' => array( + 'value' => '', + 'style' => 'width: 4%;', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id' => false, + ), + ), + 'style' => 'text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=profile;area=notification;save', + 'include_sort' => true, + 'include_start' => true, + 'hidden_fields' => array( + 'u' => $memID, + 'sa' => $context['menu_item_selected'], + $context['session_var'] => $context['session_id'], + ), + ), + 'additional_rows' => array( + array( + 'position' => 'bottom_of_list', + 'value' => '', + 'align' => 'right', + ), + ), + ); + + // Create the board notification list. + createList($listOptions); + + // Now do the topic notifications. + $listOptions = array( + 'id' => 'topic_notification_list', + 'width' => '100%', + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $txt['notifications_topics_none'] . '

' . $txt['notifications_topics_howto'], + 'no_items_align' => 'left', + 'base_href' => $scripturl . '?action=profile;u=' . $memID . ';area=notification', + 'default_sort_col' => 'last_post', + 'get_items' => array( + 'function' => 'list_getTopicNotifications', + 'params' => array( + $memID, + ), + ), + 'get_count' => array( + 'function' => 'list_getTopicNotificationCount', + 'params' => array( + $memID, + ), + ), + 'columns' => array( + 'subject' => array( + 'header' => array( + 'value' => $txt['notifications_topics'], + 'class' => 'lefttext first_th', + ), + 'data' => array( + 'function' => create_function('$topic', ' + global $settings, $txt; + + $link = $topic[\'link\']; + + if ($topic[\'new\']) + $link .= \' \' . $txt[\'new\'] . \'\'; + + $link .= \'
\' . $txt[\'in\'] . \' \' . $topic[\'board_link\'] . \'\'; + + return $link; + '), + ), + 'sort' => array( + 'default' => 'ms.subject', + 'reverse' => 'ms.subject DESC', + ), + ), + 'started_by' => array( + 'header' => array( + 'value' => $txt['started_by'], + 'class' => 'lefttext', + ), + 'data' => array( + 'db' => 'poster_link', + ), + 'sort' => array( + 'default' => 'real_name_col', + 'reverse' => 'real_name_col DESC', + ), + ), + 'last_post' => array( + 'header' => array( + 'value' => $txt['last_post'], + 'class' => 'lefttext', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s
' . $txt['by'] . ' %2$s
', + 'params' => array( + 'updated' => false, + 'poster_updated_link' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'ml.id_msg DESC', + 'reverse' => 'ml.id_msg', + ), + ), + 'delete' => array( + 'header' => array( + 'value' => '', + 'style' => 'width: 4%;', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'id' => false, + ), + ), + 'style' => 'text-align: center;', + ), + ), + ), + 'form' => array( + 'href' => $scripturl . '?action=profile;area=notification;save', + 'include_sort' => true, + 'include_start' => true, + 'hidden_fields' => array( + 'u' => $memID, + 'sa' => $context['menu_item_selected'], + $context['session_var'] => $context['session_id'], + ), + ), + 'additional_rows' => array( + array( + 'position' => 'bottom_of_list', + 'value' => '', + 'align' => 'right', + ), + ), + ); + + // Create the notification list. + createList($listOptions); + + // What options are set? + $context['member'] += array( + 'notify_announcements' => $user_profile[$memID]['notify_announcements'], + 'notify_send_body' => $user_profile[$memID]['notify_send_body'], + 'notify_types' => $user_profile[$memID]['notify_types'], + 'notify_regularity' => $user_profile[$memID]['notify_regularity'], + ); + + loadThemeOptions($memID); +} + +function list_getTopicNotificationCount($memID) +{ + global $smcFunc, $user_info, $context, $modSettings; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_notify AS ln' . (!$modSettings['postmod_active'] && $user_info['query_see_board'] === '1=1' ? '' : ' + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic)') . ($user_info['query_see_board'] === '1=1' ? '' : ' + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)') . ' + WHERE ln.id_member = {int:selected_member}' . ($user_info['query_see_board'] === '1=1' ? '' : ' + AND {query_see_board}') . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : ''), + array( + 'selected_member' => $memID, + 'is_approved' => 1, + ) + ); + list ($totalNotifications) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $totalNotifications; +} + +function list_getTopicNotifications($start, $items_per_page, $sort, $memID) +{ + global $smcFunc, $txt, $scripturl, $user_info, $context, $modSettings; + + // All the topics with notification on... + $request = $smcFunc['db_query']('', ' + SELECT + IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from, b.id_board, b.name, + t.id_topic, ms.subject, ms.id_member, IFNULL(mem.real_name, ms.poster_name) AS real_name_col, + ml.id_msg_modified, ml.poster_time, ml.id_member AS id_member_updated, + IFNULL(mem2.real_name, ml.poster_name) AS last_real_name + FROM {db_prefix}log_notify AS ln + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ') + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board AND {query_see_board}) + INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ms.id_member) + LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = ml.id_member) + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = b.id_board AND lmr.id_member = {int:current_member}) + WHERE ln.id_member = {int:selected_member} + ORDER BY {raw:sort} + LIMIT {int:offset}, {int:items_per_page}', + array( + 'current_member' => $user_info['id'], + 'is_approved' => 1, + 'selected_member' => $memID, + 'sort' => $sort, + 'offset' => $start, + 'items_per_page' => $items_per_page, + ) + ); + $notification_topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + censorText($row['subject']); + + $notification_topics[] = array( + 'id' => $row['id_topic'], + 'poster_link' => empty($row['id_member']) ? $row['real_name_col'] : '' . $row['real_name_col'] . '', + 'poster_updated_link' => empty($row['id_member_updated']) ? $row['last_real_name'] : '' . $row['last_real_name'] . '', + 'subject' => $row['subject'], + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'link' => '' . $row['subject'] . '', + 'new' => $row['new_from'] <= $row['id_msg_modified'], + 'new_from' => $row['new_from'], + 'updated' => timeformat($row['poster_time']), + 'new_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['new_from'] . '#new', + 'new_link' => '' . $row['subject'] . '', + 'board_link' => '' . $row['name'] . '', + ); + } + $smcFunc['db_free_result']($request); + + return $notification_topics; +} + +function list_getBoardNotifications($start, $items_per_page, $sort, $memID) +{ + global $smcFunc, $txt, $scripturl, $user_info; + + $request = $smcFunc['db_query']('', ' + SELECT b.id_board, b.name, IFNULL(lb.id_msg, 0) AS board_read, b.id_msg_updated + FROM {db_prefix}log_notify AS ln + INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board) + LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member}) + WHERE ln.id_member = {int:selected_member} + AND {query_see_board} + ORDER BY ' . $sort, + array( + 'current_member' => $user_info['id'], + 'selected_member' => $memID, + ) + ); + $notification_boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $notification_boards[] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'link' => '' . $row['name'] . '', + 'new' => $row['board_read'] < $row['id_msg_updated'] + ); + $smcFunc['db_free_result']($request); + + return $notification_boards; +} + +function loadThemeOptions($memID) +{ + global $context, $options, $cur_profile, $smcFunc; + + if (isset($_POST['default_options'])) + $_POST['options'] = isset($_POST['options']) ? $_POST['options'] + $_POST['default_options'] : $_POST['default_options']; + + if ($context['user']['is_owner']) + { + $context['member']['options'] = $options; + if (isset($_POST['options']) && is_array($_POST['options'])) + foreach ($_POST['options'] as $k => $v) + $context['member']['options'][$k] = $v; + } + else + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, variable, value + FROM {db_prefix}themes + WHERE id_theme IN (1, {int:member_theme}) + AND id_member IN (-1, {int:selected_member})', + array( + 'member_theme' => (int) $cur_profile['id_theme'], + 'selected_member' => $memID, + ) + ); + $temp = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['id_member'] == -1) + { + $temp[$row['variable']] = $row['value']; + continue; + } + + if (isset($_POST['options'][$row['variable']])) + $row['value'] = $_POST['options'][$row['variable']]; + $context['member']['options'][$row['variable']] = $row['value']; + } + $smcFunc['db_free_result']($request); + + // Load up the default theme options for any missing. + foreach ($temp as $k => $v) + { + if (!isset($context['member']['options'][$k])) + $context['member']['options'][$k] = $v; + } + } +} + +function ignoreboards($memID) +{ + global $txt, $user_info, $context, $modSettings, $smcFunc, $cur_profile; + + // Have the admins enabled this option? + if (empty($modSettings['allow_ignore_boards'])) + fatal_lang_error('ignoreboards_disallowed', 'user'); + + // Find all the boards this user is allowed to see. + $request = $smcFunc['db_query']('order_by_board_order', ' + SELECT b.id_cat, c.name AS cat_name, b.id_board, b.name, b.child_level, + '. (!empty($cur_profile['ignore_boards']) ? 'b.id_board IN ({array_int:ignore_boards})' : '0') . ' AS is_ignored + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE {query_see_board} + AND redirect = {string:empty_string}', + array( + 'ignore_boards' => !empty($cur_profile['ignore_boards']) ? explode(',', $cur_profile['ignore_boards']) : array(), + 'empty_string' => '', + ) + ); + $context['num_boards'] = $smcFunc['db_num_rows']($request); + $context['categories'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // This category hasn't been set up yet.. + if (!isset($context['categories'][$row['id_cat']])) + $context['categories'][$row['id_cat']] = array( + 'id' => $row['id_cat'], + 'name' => $row['cat_name'], + 'boards' => array() + ); + + // Set this board up, and let the template know when it's a child. (indent them..) + $context['categories'][$row['id_cat']]['boards'][$row['id_board']] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'child_level' => $row['child_level'], + 'selected' => $row['is_ignored'], + ); + } + $smcFunc['db_free_result']($request); + + // Now, let's sort the list of categories into the boards for templates that like that. + $temp_boards = array(); + foreach ($context['categories'] as $category) + { + // Include a list of boards per category for easy toggling. + $context['categories'][$category['id']]['child_ids'] = array_keys($category['boards']); + + $temp_boards[] = array( + 'name' => $category['name'], + 'child_ids' => array_keys($category['boards']) + ); + $temp_boards = array_merge($temp_boards, array_values($category['boards'])); + } + + $max_boards = ceil(count($temp_boards) / 2); + if ($max_boards == 1) + $max_boards = 2; + + // Now, alternate them so they can be shown left and right ;). + $context['board_columns'] = array(); + for ($i = 0; $i < $max_boards; $i++) + { + $context['board_columns'][] = $temp_boards[$i]; + if (isset($temp_boards[$i + $max_boards])) + $context['board_columns'][] = $temp_boards[$i + $max_boards]; + else + $context['board_columns'][] = array(); + } + + loadThemeOptions($memID); +} + +// Load all the languages for the profile. +function profileLoadLanguages() +{ + global $context, $modSettings, $settings, $cur_profile, $language, $smcFunc; + + $context['profile_languages'] = array(); + + // Get our languages! + getLanguages(true, true); + + // Setup our languages. + foreach ($context['languages'] as $lang) + { + $context['profile_languages'][$lang['filename']] = strtr($lang['name'], array('-utf8' => '')); + } + ksort($context['profile_languages']); + + // Return whether we should proceed with this. + return count($context['profile_languages']) > 1 ? true : false; +} + +// Load all the group info for the profile. +function profileLoadGroups() +{ + global $cur_profile, $txt, $context, $smcFunc, $user_settings; + + $context['member_groups'] = array( + 0 => array( + 'id' => 0, + 'name' => $txt['no_primary_membergroup'], + 'is_primary' => $cur_profile['id_group'] == 0, + 'can_be_additional' => false, + 'can_be_primary' => true, + ) + ); + $curGroups = explode(',', $cur_profile['additional_groups']); + + // Load membergroups, but only those groups the user can assign. + $request = $smcFunc['db_query']('', ' + SELECT group_name, id_group, hidden + FROM {db_prefix}membergroups + WHERE id_group != {int:moderator_group} + AND min_posts = {int:min_posts}' . (allowedTo('admin_forum') ? '' : ' + AND group_type != {int:is_protected}') . ' + ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name', + array( + 'moderator_group' => 3, + 'min_posts' => -1, + 'is_protected' => 1, + 'newbie_group' => 4, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // We should skip the administrator group if they don't have the admin_forum permission! + if ($row['id_group'] == 1 && !allowedTo('admin_forum')) + continue; + + $context['member_groups'][$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'is_primary' => $cur_profile['id_group'] == $row['id_group'], + 'is_additional' => in_array($row['id_group'], $curGroups), + 'can_be_additional' => true, + 'can_be_primary' => $row['hidden'] != 2, + ); + } + $smcFunc['db_free_result']($request); + + $context['member']['group_id'] = $user_settings['id_group']; + + return true; +} + +// Load key signature context data. +function profileLoadSignatureData() +{ + global $modSettings, $context, $txt, $cur_profile, $smcFunc; + + // Signature limits. + list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']); + $sig_limits = explode(',', $sig_limits); + + $context['signature_enabled'] = isset($sig_limits[0]) ? $sig_limits[0] : 0; + $context['signature_limits'] = array( + 'max_length' => isset($sig_limits[1]) ? $sig_limits[1] : 0, + 'max_lines' => isset($sig_limits[2]) ? $sig_limits[2] : 0, + 'max_images' => isset($sig_limits[3]) ? $sig_limits[3] : 0, + 'max_smileys' => isset($sig_limits[4]) ? $sig_limits[4] : 0, + 'max_image_width' => isset($sig_limits[5]) ? $sig_limits[5] : 0, + 'max_image_height' => isset($sig_limits[6]) ? $sig_limits[6] : 0, + 'max_font_size' => isset($sig_limits[7]) ? $sig_limits[7] : 0, + 'bbc' => !empty($sig_bbc) ? explode(',', $sig_bbc) : array(), + ); + // Kept this line in for backwards compatibility! + $context['max_signature_length'] = $context['signature_limits']['max_length']; + // Warning message for signature image limits? + $context['signature_warning'] = ''; + if ($context['signature_limits']['max_image_width'] && $context['signature_limits']['max_image_height']) + $context['signature_warning'] = sprintf($txt['profile_error_signature_max_image_size'], $context['signature_limits']['max_image_width'], $context['signature_limits']['max_image_height']); + elseif ($context['signature_limits']['max_image_width'] || $context['signature_limits']['max_image_height']) + $context['signature_warning'] = sprintf($txt['profile_error_signature_max_image_' . ($context['signature_limits']['max_image_width'] ? 'width' : 'height')], $context['signature_limits'][$context['signature_limits']['max_image_width'] ? 'max_image_width' : 'max_image_height']); + + $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new'); + + $context['member']['signature'] = empty($cur_profile['signature']) ? '' : str_replace(array('
', '<', '>', '"', '\''), array("\n", '<', '>', '"', '''), $cur_profile['signature']); + + return true; +} + +// Load avatar context data. +function profileLoadAvatarData() +{ + global $context, $cur_profile, $modSettings, $scripturl; + + $context['avatar_url'] = $modSettings['avatar_url']; + + // Default context. + $context['member']['avatar'] += array( + 'custom' => stristr($cur_profile['avatar'], 'http://') ? $cur_profile['avatar'] : 'http://', + 'selection' => $cur_profile['avatar'] == '' || stristr($cur_profile['avatar'], 'http://') ? '' : $cur_profile['avatar'], + 'id_attach' => $cur_profile['id_attach'], + 'filename' => $cur_profile['filename'], + 'allow_server_stored' => allowedTo('profile_server_avatar') || (!$context['user']['is_owner'] && allowedTo('profile_extra_any')), + 'allow_upload' => allowedTo('profile_upload_avatar') || (!$context['user']['is_owner'] && allowedTo('profile_extra_any')), + 'allow_external' => allowedTo('profile_remote_avatar') || (!$context['user']['is_owner'] && allowedTo('profile_extra_any')), + ); + + if ($cur_profile['avatar'] == '' && $cur_profile['id_attach'] > 0 && $context['member']['avatar']['allow_upload']) + { + $context['member']['avatar'] += array( + 'choice' => 'upload', + 'server_pic' => 'blank.gif', + 'external' => 'http://' + ); + $context['member']['avatar']['href'] = empty($cur_profile['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $cur_profile['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $cur_profile['filename']; + } + elseif (stristr($cur_profile['avatar'], 'http://') && $context['member']['avatar']['allow_external']) + $context['member']['avatar'] += array( + 'choice' => 'external', + 'server_pic' => 'blank.gif', + 'external' => $cur_profile['avatar'] + ); + elseif ($cur_profile['avatar'] != '' && file_exists($modSettings['avatar_directory'] . '/' . $cur_profile['avatar']) && $context['member']['avatar']['allow_server_stored']) + $context['member']['avatar'] += array( + 'choice' => 'server_stored', + 'server_pic' => $cur_profile['avatar'] == '' ? 'blank.gif' : $cur_profile['avatar'], + 'external' => 'http://' + ); + else + $context['member']['avatar'] += array( + 'choice' => 'none', + 'server_pic' => 'blank.gif', + 'external' => 'http://' + ); + + // Get a list of all the avatars. + if ($context['member']['avatar']['allow_server_stored']) + { + $context['avatar_list'] = array(); + $context['avatars'] = is_dir($modSettings['avatar_directory']) ? getAvatars('', 0) : array(); + } + else + $context['avatars'] = array(); + + // Second level selected avatar... + $context['avatar_selected'] = substr(strrchr($context['member']['avatar']['server_pic'], '/'), 1); + return true; +} + +// Save a members group. +function profileSaveGroups(&$value) +{ + global $profile_vars, $old_profile, $context, $smcFunc, $cur_profile; + + // Do we need to protect some groups? + if (!allowedTo('admin_forum')) + { + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}membergroups + WHERE group_type = {int:is_protected}', + array( + 'is_protected' => 1, + ) + ); + $protected_groups = array(1); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $protected_groups[] = $row['id_group']; + $smcFunc['db_free_result']($request); + + $protected_groups = array_unique($protected_groups); + } + + // The account page allows the change of your id_group - but not to a protected group! + if (empty($protected_groups) || count(array_intersect(array((int) $value, $old_profile['id_group']), $protected_groups)) == 0) + $value = (int) $value; + // ... otherwise it's the old group sir. + else + $value = $old_profile['id_group']; + + // Find the additional membergroups (if any) + if (isset($_POST['additional_groups']) && is_array($_POST['additional_groups'])) + { + $additional_groups = array(); + foreach ($_POST['additional_groups'] as $group_id) + { + $group_id = (int) $group_id; + if (!empty($group_id) && (empty($protected_groups) || !in_array($group_id, $protected_groups))) + $additional_groups[] = $group_id; + } + + // Put the protected groups back in there if you don't have permission to take them away. + $old_additional_groups = explode(',', $old_profile['additional_groups']); + foreach ($old_additional_groups as $group_id) + { + if (!empty($protected_groups) && in_array($group_id, $protected_groups)) + $additional_groups[] = $group_id; + } + + if (implode(',', $additional_groups) !== $old_profile['additional_groups']) + { + $profile_vars['additional_groups'] = implode(',', $additional_groups); + $cur_profile['additional_groups'] = implode(',', $additional_groups); + } + } + + // Too often, people remove delete their own account, or something. + if (in_array(1, explode(',', $old_profile['additional_groups'])) || $old_profile['id_group'] == 1) + { + $stillAdmin = $value == 1 || (isset($additional_groups) && in_array(1, $additional_groups)); + + // If they would no longer be an admin, look for any other... + if (!$stillAdmin) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0) + AND id_member != {int:selected_member} + LIMIT 1', + array( + 'admin_group' => 1, + 'selected_member' => $context['id_member'], + ) + ); + list ($another) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (empty($another)) + fatal_lang_error('at_least_one_admin', 'critical'); + } + } + + // If we are changing group status, update permission cache as necessary. + if ($value != $old_profile['id_group'] || isset($profile_vars['additional_groups'])) + { + if ($context['user']['is_owner']) + $_SESSION['mc']['time'] = 0; + else + updateSettings(array('settings_updated' => time())); + } + + return true; +} + +// The avatar is incredibly complicated, what with the options... and what not. +function profileSaveAvatarData(&$value) +{ + global $modSettings, $sourcedir, $smcFunc, $profile_vars, $cur_profile, $context; + + $memID = $context['id_member']; + if (empty($memID) && !empty($context['password_auth_failed'])) + return false; + + require_once($sourcedir . '/ManageAttachments.php'); + + // We need to know where we're going to be putting it.. + if (!empty($modSettings['custom_avatar_enabled'])) + { + $uploadDir = $modSettings['custom_avatar_dir']; + $id_folder = 1; + } + elseif (!empty($modSettings['currentAttachmentUploadDir'])) + { + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + + // Just use the current path for temp files. + $uploadDir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']]; + $id_folder = $modSettings['currentAttachmentUploadDir']; + } + else + { + $uploadDir = $modSettings['attachmentUploadDir']; + $id_folder = 1; + } + + $downloadedExternalAvatar = false; + if ($value == 'external' && allowedTo('profile_remote_avatar') && strtolower(substr($_POST['userpicpersonal'], 0, 7)) == 'http://' && strlen($_POST['userpicpersonal']) > 7 && !empty($modSettings['avatar_download_external'])) + { + if (!is_writable($uploadDir)) + fatal_lang_error('attachments_no_write', 'critical'); + + require_once($sourcedir . '/Subs-Package.php'); + + $url = parse_url($_POST['userpicpersonal']); + $contents = fetch_web_data('http://' . $url['host'] . (empty($url['port']) ? '' : ':' . $url['port']) . str_replace(' ', '%20', trim($url['path']))); + + if ($contents != false && $tmpAvatar = fopen($uploadDir . '/avatar_tmp_' . $memID, 'wb')) + { + fwrite($tmpAvatar, $contents); + fclose($tmpAvatar); + + $downloadedExternalAvatar = true; + $_FILES['attachment']['tmp_name'] = $uploadDir . '/avatar_tmp_' . $memID; + } + } + + if ($value == 'none') + { + $profile_vars['avatar'] = ''; + + // Reset the attach ID. + $cur_profile['id_attach'] = 0; + $cur_profile['attachment_type'] = 0; + $cur_profile['filename'] = ''; + + removeAttachments(array('id_member' => $memID)); + } + elseif ($value == 'server_stored' && allowedTo('profile_server_avatar')) + { + $profile_vars['avatar'] = strtr(empty($_POST['file']) ? (empty($_POST['cat']) ? '' : $_POST['cat']) : $_POST['file'], array('&' => '&')); + $profile_vars['avatar'] = preg_match('~^([\w _!@%*=\-#()\[\]&.,]+/)?[\w _!@%*=\-#()\[\]&.,]+$~', $profile_vars['avatar']) != 0 && preg_match('/\.\./', $profile_vars['avatar']) == 0 && file_exists($modSettings['avatar_directory'] . '/' . $profile_vars['avatar']) ? ($profile_vars['avatar'] == 'blank.gif' ? '' : $profile_vars['avatar']) : ''; + + // Clear current profile... + $cur_profile['id_attach'] = 0; + $cur_profile['attachment_type'] = 0; + $cur_profile['filename'] = ''; + + // Get rid of their old avatar. (if uploaded.) + removeAttachments(array('id_member' => $memID)); + } + elseif ($value == 'external' && allowedTo('profile_remote_avatar') && strtolower(substr($_POST['userpicpersonal'], 0, 7)) == 'http://' && empty($modSettings['avatar_download_external'])) + { + // We need these clean... + $cur_profile['id_attach'] = 0; + $cur_profile['attachment_type'] = 0; + $cur_profile['filename'] = ''; + + // Remove any attached avatar... + removeAttachments(array('id_member' => $memID)); + + $profile_vars['avatar'] = str_replace('%20', '', preg_replace('~action(?:=|%3d)(?!dlattach)~i', 'action-', $_POST['userpicpersonal'])); + + if ($profile_vars['avatar'] == 'http://' || $profile_vars['avatar'] == 'http:///') + $profile_vars['avatar'] = ''; + // Trying to make us do something we'll regret? + elseif (substr($profile_vars['avatar'], 0, 7) != 'http://') + return 'bad_avatar'; + // Should we check dimensions? + elseif (!empty($modSettings['avatar_max_height_external']) || !empty($modSettings['avatar_max_width_external'])) + { + // Now let's validate the avatar. + $sizes = url_image_size($profile_vars['avatar']); + + if (is_array($sizes) && (($sizes[0] > $modSettings['avatar_max_width_external'] && !empty($modSettings['avatar_max_width_external'])) || ($sizes[1] > $modSettings['avatar_max_height_external'] && !empty($modSettings['avatar_max_height_external'])))) + { + // Houston, we have a problem. The avatar is too large!! + if ($modSettings['avatar_action_too_large'] == 'option_refuse') + return 'bad_avatar'; + elseif ($modSettings['avatar_action_too_large'] == 'option_download_and_resize') + { + require_once($sourcedir . '/Subs-Graphics.php'); + if (downloadAvatar($profile_vars['avatar'], $memID, $modSettings['avatar_max_width_external'], $modSettings['avatar_max_height_external'])) + { + $profile_vars['avatar'] = ''; + $cur_profile['id_attach'] = $modSettings['new_avatar_data']['id']; + $cur_profile['filename'] = $modSettings['new_avatar_data']['filename']; + $cur_profile['attachment_type'] = $modSettings['new_avatar_data']['type']; + } + else + return 'bad_avatar'; + } + } + } + } + elseif (($value == 'upload' && allowedTo('profile_upload_avatar')) || $downloadedExternalAvatar) + { + if ((isset($_FILES['attachment']['name']) && $_FILES['attachment']['name'] != '') || $downloadedExternalAvatar) + { + // Get the dimensions of the image. + if (!$downloadedExternalAvatar) + { + if (!is_writable($uploadDir)) + fatal_lang_error('attachments_no_write', 'critical'); + + if (!move_uploaded_file($_FILES['attachment']['tmp_name'], $uploadDir . '/avatar_tmp_' . $memID)) + fatal_lang_error('attach_timeout', 'critical'); + + $_FILES['attachment']['tmp_name'] = $uploadDir . '/avatar_tmp_' . $memID; + } + + $sizes = @getimagesize($_FILES['attachment']['tmp_name']); + + // No size, then it's probably not a valid pic. + if ($sizes === false) + return 'bad_avatar'; + // Check whether the image is too large. + elseif ((!empty($modSettings['avatar_max_width_upload']) && $sizes[0] > $modSettings['avatar_max_width_upload']) || (!empty($modSettings['avatar_max_height_upload']) && $sizes[1] > $modSettings['avatar_max_height_upload'])) + { + if (!empty($modSettings['avatar_resize_upload'])) + { + // Attempt to chmod it. + @chmod($uploadDir . '/avatar_tmp_' . $memID, 0644); + + require_once($sourcedir . '/Subs-Graphics.php'); + if (!downloadAvatar($uploadDir . '/avatar_tmp_' . $memID, $memID, $modSettings['avatar_max_width_upload'], $modSettings['avatar_max_height_upload'])) + return 'bad_avatar'; + + // Reset attachment avatar data. + $cur_profile['id_attach'] = $modSettings['new_avatar_data']['id']; + $cur_profile['filename'] = $modSettings['new_avatar_data']['filename']; + $cur_profile['attachment_type'] = $modSettings['new_avatar_data']['type']; + } + else + return 'bad_avatar'; + } + elseif (is_array($sizes)) + { + // Now try to find an infection. + require_once($sourcedir . '/Subs-Graphics.php'); + if (!checkImageContents($_FILES['attachment']['tmp_name'], !empty($modSettings['avatar_paranoid']))) + { + // It's bad. Try to re-encode the contents? + if (empty($modSettings['avatar_reencode']) || (!reencodeImage($_FILES['attachment']['tmp_name'], $sizes[2]))) + return 'bad_avatar'; + // We were successful. However, at what price? + $sizes = @getimagesize($_FILES['attachment']['tmp_name']); + // Hard to believe this would happen, but can you bet? + if ($sizes === false) + return 'bad_avatar'; + } + + $extensions = array( + '1' => 'gif', + '2' => 'jpg', + '3' => 'png', + '6' => 'bmp' + ); + + $extension = isset($extensions[$sizes[2]]) ? $extensions[$sizes[2]] : 'bmp'; + $mime_type = 'image/' . ($extension === 'jpg' ? 'jpeg' : ($extension === 'bmp' ? 'x-ms-bmp' : $extension)); + $destName = 'avatar_' . $memID . '_' . time() . '.' . $extension; + list ($width, $height) = getimagesize($_FILES['attachment']['tmp_name']); + $file_hash = empty($modSettings['custom_avatar_enabled']) ? getAttachmentFilename($destName, false, null, true) : ''; + + // Remove previous attachments this member might have had. + removeAttachments(array('id_member' => $memID)); + + $smcFunc['db_insert']('', + '{db_prefix}attachments', + array( + 'id_member' => 'int', 'attachment_type' => 'int', 'filename' => 'string', 'file_hash' => 'string', 'fileext' => 'string', 'size' => 'int', + 'width' => 'int', 'height' => 'int', 'mime_type' => 'string', 'id_folder' => 'int', + ), + array( + $memID, (empty($modSettings['custom_avatar_enabled']) ? 0 : 1), $destName, $file_hash, $extension, filesize($_FILES['attachment']['tmp_name']), + (int) $width, (int) $height, $mime_type, $id_folder, + ), + array('id_attach') + ); + + $cur_profile['id_attach'] = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach'); + $cur_profile['filename'] = $destName; + $cur_profile['attachment_type'] = empty($modSettings['custom_avatar_enabled']) ? 0 : 1; + + $destinationPath = $uploadDir . '/' . (empty($file_hash) ? $destName : $cur_profile['id_attach'] . '_' . $file_hash); + if (!rename($_FILES['attachment']['tmp_name'], $destinationPath)) + { + // I guess a man can try. + removeAttachments(array('id_member' => $memID)); + fatal_lang_error('attach_timeout', 'critical'); + } + + // Attempt to chmod it. + @chmod($uploadDir . '/' . $destinationPath, 0644); + } + $profile_vars['avatar'] = ''; + + // Delete any temporary file. + if (file_exists($uploadDir . '/avatar_tmp_' . $memID)) + @unlink($uploadDir . '/avatar_tmp_' . $memID); + } + // Selected the upload avatar option and had one already uploaded before or didn't upload one. + else + $profile_vars['avatar'] = ''; + } + else + $profile_vars['avatar'] = ''; + + // Setup the profile variables so it shows things right on display! + $cur_profile['avatar'] = $profile_vars['avatar']; + + return false; +} + +// Validate the signature! +function profileValidateSignature(&$value) +{ + global $sourcedir, $modSettings, $smcFunc, $txt; + + require_once($sourcedir . '/Subs-Post.php'); + + // Admins can do whatever they hell they want! + if (!allowedTo('admin_forum')) + { + // Load all the signature limits. + list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']); + $sig_limits = explode(',', $sig_limits); + $disabledTags = !empty($sig_bbc) ? explode(',', $sig_bbc) : array(); + + $unparsed_signature = strtr(un_htmlspecialchars($value), array("\r" => '', ''' => '\'')); + // Too long? + if (!empty($sig_limits[1]) && $smcFunc['strlen']($unparsed_signature) > $sig_limits[1]) + { + $_POST['signature'] = trim(htmlspecialchars($smcFunc['substr']($unparsed_signature, 0, $sig_limits[1]), ENT_QUOTES)); + $txt['profile_error_signature_max_length'] = sprintf($txt['profile_error_signature_max_length'], $sig_limits[1]); + return 'signature_max_length'; + } + // Too many lines? + if (!empty($sig_limits[2]) && substr_count($unparsed_signature, "\n") >= $sig_limits[2]) + { + $txt['profile_error_signature_max_lines'] = sprintf($txt['profile_error_signature_max_lines'], $sig_limits[2]); + return 'signature_max_lines'; + } + // Too many images?! + if (!empty($sig_limits[3]) && (substr_count(strtolower($unparsed_signature), '[img') + substr_count(strtolower($unparsed_signature), ' $sig_limits[3]) + { + $txt['profile_error_signature_max_image_count'] = sprintf($txt['profile_error_signature_max_image_count'], $sig_limits[3]); + return 'signature_max_image_count'; + } + // What about too many smileys! + $smiley_parsed = $unparsed_signature; + parsesmileys($smiley_parsed); + $smiley_count = substr_count(strtolower($smiley_parsed), ' 0) + return 'signature_allow_smileys'; + elseif (!empty($sig_limits[4]) && $sig_limits[4] > 0 && $smiley_count > $sig_limits[4]) + { + $txt['profile_error_signature_max_smileys'] = sprintf($txt['profile_error_signature_max_smileys'], $sig_limits[4]); + return 'signature_max_smileys'; + } + // Maybe we are abusing font sizes? + if (!empty($sig_limits[7]) && preg_match_all('~\[size=([\d\.]+)?(px|pt|em|x-large|larger)~i', $unparsed_signature, $matches) !== false && isset($matches[2])) + { + foreach ($matches[1] as $ind => $size) + { + $limit_broke = 0; + // Attempt to allow all sizes of abuse, so to speak. + if ($matches[2][$ind] == 'px' && $size > $sig_limits[7]) + $limit_broke = $sig_limits[7] . 'px'; + elseif ($matches[2][$ind] == 'pt' && $size > ($sig_limits[7] * 0.75)) + $limit_broke = ((int) $sig_limits[7] * 0.75) . 'pt'; + elseif ($matches[2][$ind] == 'em' && $size > ((float) $sig_limits[7] / 16)) + $limit_broke = ((float) $sig_limits[7] / 16) . 'em'; + elseif ($matches[2][$ind] != 'px' && $matches[2][$ind] != 'pt' && $matches[2][$ind] != 'em' && $sig_limits[7] < 18) + $limit_broke = 'large'; + + if ($limit_broke) + { + $txt['profile_error_signature_max_font_size'] = sprintf($txt['profile_error_signature_max_font_size'], $limit_broke); + return 'signature_max_font_size'; + } + } + } + // The difficult one - image sizes! Don't error on this - just fix it. + if ((!empty($sig_limits[5]) || !empty($sig_limits[6]))) + { + // Get all BBC tags... + preg_match_all('~\[img(\s+width=([\d]+))?(\s+height=([\d]+))?(\s+width=([\d]+))?\s*\](?:
)*([^<">]+?)(?:
)*\[/img\]~i', $unparsed_signature, $matches); + // ... and all HTML ones. + preg_match_all('~~i', $unparsed_signature, $matches2, PREG_PATTERN_ORDER); + // And stick the HTML in the BBC. + if (!empty($matches2)) + { + foreach ($matches2[0] as $ind => $dummy) + { + $matches[0][] = $matches2[0][$ind]; + $matches[1][] = ''; + $matches[2][] = ''; + $matches[3][] = ''; + $matches[4][] = ''; + $matches[5][] = ''; + $matches[6][] = ''; + $matches[7][] = $matches2[1][$ind]; + } + } + + $replaces = array(); + // Try to find all the images! + if (!empty($matches)) + { + foreach ($matches[0] as $key => $image) + { + $width = -1; $height = -1; + + // Does it have predefined restraints? Width first. + if ($matches[6][$key]) + $matches[2][$key] = $matches[6][$key]; + if ($matches[2][$key] && $sig_limits[5] && $matches[2][$key] > $sig_limits[5]) + { + $width = $sig_limits[5]; + $matches[4][$key] = $matches[4][$key] * ($width / $matches[2][$key]); + } + elseif ($matches[2][$key]) + $width = $matches[2][$key]; + // ... and height. + if ($matches[4][$key] && $sig_limits[6] && $matches[4][$key] > $sig_limits[6]) + { + $height = $sig_limits[6]; + if ($width != -1) + $width = $width * ($height / $matches[4][$key]); + } + elseif ($matches[4][$key]) + $height = $matches[4][$key]; + + // If the dimensions are still not fixed - we need to check the actual image. + if (($width == -1 && $sig_limits[5]) || ($height == -1 && $sig_limits[6])) + { + $sizes = url_image_size($matches[7][$key]); + if (is_array($sizes)) + { + // Too wide? + if ($sizes[0] > $sig_limits[5] && $sig_limits[5]) + { + $width = $sig_limits[5]; + $sizes[1] = $sizes[1] * ($width / $sizes[0]); + } + // Too high? + if ($sizes[1] > $sig_limits[6] && $sig_limits[6]) + { + $height = $sig_limits[6]; + if ($width == -1) + $width = $sizes[0]; + $width = $width * ($height / $sizes[1]); + } + elseif ($width != -1) + $height = $sizes[1]; + } + } + + // Did we come up with some changes? If so remake the string. + if ($width != -1 || $height != -1) + $replaces[$image] = '[img' . ($width != -1 ? ' width=' . round($width) : '') . ($height != -1 ? ' height=' . round($height) : '') . ']' . $matches[7][$key] . '[/img]'; + } + if (!empty($replaces)) + $value = str_replace(array_keys($replaces), array_values($replaces), $value); + } + } + // Any disabled BBC? + $disabledSigBBC = implode('|', $disabledTags); + if (!empty($disabledSigBBC)) + { + if (preg_match('~\[(' . $disabledSigBBC . ')~i', $unparsed_signature, $matches) !== false && isset($matches[1])) + { + $disabledTags = array_unique($disabledTags); + $txt['profile_error_signature_disabled_bbc'] = sprintf($txt['profile_error_signature_disabled_bbc'], implode(', ', $disabledTags)); + return 'signature_disabled_bbc'; + } + } + } + + preparsecode($value); + return true; +} + +// Validate an email address. +function profileValidateEmail($email, $memID = 0) +{ + global $smcFunc, $context; + + $email = strtr($email, array(''' => '\'')); + + // Check the name and email for validity. + if (trim($email) == '') + return 'no_email'; + if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $email) == 0) + return 'bad_email'; + + // Email addresses should be and stay unique. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE ' . ($memID != 0 ? 'id_member != {int:selected_member} AND ' : '') . ' + email_address = {string:email_address} + LIMIT 1', + array( + 'selected_member' => $memID, + 'email_address' => $email, + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + return 'email_taken'; + $smcFunc['db_free_result']($request); + + return true; +} + +// Reload a users settings. +function profileReloadUser() +{ + global $sourcedir, $modSettings, $context, $cur_profile, $smcFunc, $profile_vars; + + // Log them back in - using the verify password as they must have matched and this one doesn't get changed by anyone! + if (isset($_POST['passwrd2']) && $_POST['passwrd2'] != '') + { + require_once($sourcedir . '/Subs-Auth.php'); + setLoginCookie(60 * $modSettings['cookieTime'], $context['id_member'], sha1(sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd2'])) . $cur_profile['password_salt'])); + } + + loadUserSettings(); + writeLog(); +} + +// Send the user a new activation email if they need to reactivate! +function profileSendActivation() +{ + global $sourcedir, $profile_vars, $txt, $context, $scripturl, $smcFunc, $cookiename, $cur_profile, $language, $modSettings; + + require_once($sourcedir . '/Subs-Post.php'); + + // Shouldn't happen but just in case. + if (empty($profile_vars['email_address'])) + return; + + $replacements = array( + 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $context['id_member'] . ';code=' . $profile_vars['validation_code'], + 'ACTIVATIONCODE' => $profile_vars['validation_code'], + 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $context['id_member'], + ); + + // Send off the email. + $emaildata = loadEmailTemplate('activate_reactivate', $replacements, empty($cur_profile['lngfile']) || empty($modSettings['userLanguage']) ? $language : $cur_profile['lngfile']); + sendmail($profile_vars['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + + // Log the user out. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_online + WHERE id_member = {int:selected_member}', + array( + 'selected_member' => $context['id_member'], + ) + ); + $_SESSION['log_time'] = 0; + $_SESSION['login_' . $cookiename] = serialize(array(0, '', 0)); + + if (isset($_COOKIE[$cookiename])) + $_COOKIE[$cookiename] = ''; + + loadUserSettings(); + + $context['user']['is_logged'] = false; + $context['user']['is_guest'] = true; + + // Send them to the done-with-registration-login screen. + loadTemplate('Register'); + + $context['page_title'] = $txt['profile']; + $context['sub_template'] = 'after'; + $context['title'] = $txt['activate_changed_email_title']; + $context['description'] = $txt['activate_changed_email_desc']; + + // We're gone! + obExit(); +} + +// Function to allow the user to choose group membership etc... +function groupMembership($memID) +{ + global $txt, $scripturl, $user_profile, $user_info, $context, $modSettings, $smcFunc; + + $curMember = $user_profile[$memID]; + $context['primary_group'] = $curMember['id_group']; + + // Can they manage groups? + $context['can_manage_membergroups'] = allowedTo('manage_membergroups'); + $context['can_manage_protected'] = allowedTo('admin_forum'); + $context['can_edit_primary'] = $context['can_manage_protected']; + $context['update_message'] = isset($_GET['msg']) && isset($txt['group_membership_msg_' . $_GET['msg']]) ? $txt['group_membership_msg_' . $_GET['msg']] : ''; + + // Get all the groups this user is a member of. + $groups = explode(',', $curMember['additional_groups']); + $groups[] = $curMember['id_group']; + + // Ensure the query doesn't croak! + if (empty($groups)) + $groups = array(0); + // Just to be sure... + foreach ($groups as $k => $v) + $groups[$k] = (int) $v; + + // Get all the membergroups they can join. + $request = $smcFunc['db_query']('', ' + SELECT mg.id_group, mg.group_name, mg.description, mg.group_type, mg.online_color, mg.hidden, + IFNULL(lgr.id_member, 0) AS pending + FROM {db_prefix}membergroups AS mg + LEFT JOIN {db_prefix}log_group_requests AS lgr ON (lgr.id_member = {int:selected_member} AND lgr.id_group = mg.id_group) + WHERE (mg.id_group IN ({array_int:group_list}) + OR mg.group_type > {int:nonjoin_group_id}) + AND mg.min_posts = {int:min_posts} + AND mg.id_group != {int:moderator_group} + ORDER BY group_name', + array( + 'group_list' => $groups, + 'selected_member' => $memID, + 'nonjoin_group_id' => 1, + 'min_posts' => -1, + 'moderator_group' => 3, + ) + ); + // This beast will be our group holder. + $context['groups'] = array( + 'member' => array(), + 'available' => array() + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Can they edit their primary group? + if (($row['id_group'] == $context['primary_group'] && $row['group_type'] > 1) || ($row['hidden'] != 2 && $context['primary_group'] == 0 && in_array($row['id_group'], $groups))) + $context['can_edit_primary'] = true; + + // If they can't manage (protected) groups, and it's not publically joinable or already assigned, they can't see it. + if (((!$context['can_manage_protected'] && $row['group_type'] == 1) || (!$context['can_manage_membergroups'] && $row['group_type'] == 0)) && $row['id_group'] != $context['primary_group']) + continue; + + $context['groups'][in_array($row['id_group'], $groups) ? 'member' : 'available'][$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'desc' => $row['description'], + 'color' => $row['online_color'], + 'type' => $row['group_type'], + 'pending' => $row['pending'], + 'is_primary' => $row['id_group'] == $context['primary_group'], + 'can_be_primary' => $row['hidden'] != 2, + // Anything more than this needs to be done through account settings for security. + 'can_leave' => $row['id_group'] != 1 && $row['group_type'] > 1 ? true : false, + ); + } + $smcFunc['db_free_result']($request); + + // Add registered members on the end. + $context['groups']['member'][0] = array( + 'id' => 0, + 'name' => $txt['regular_members'], + 'desc' => $txt['regular_members_desc'], + 'type' => 0, + 'is_primary' => $context['primary_group'] == 0 ? true : false, + 'can_be_primary' => true, + 'can_leave' => 0, + ); + + // No changing primary one unless you have enough groups! + if (count($context['groups']['member']) < 2) + $context['can_edit_primary'] = false; + + // In the special case that someone is requesting membership of a group, setup some special context vars. + if (isset($_REQUEST['request']) && isset($context['groups']['available'][(int) $_REQUEST['request']]) && $context['groups']['available'][(int) $_REQUEST['request']]['type'] == 2) + $context['group_request'] = $context['groups']['available'][(int) $_REQUEST['request']]; +} + +// This function actually makes all the group changes... +function groupMembership2($profile_vars, $post_errors, $memID) +{ + global $user_info, $sourcedir, $context, $user_profile, $modSettings, $txt, $smcFunc, $scripturl, $language; + + // Let's be extra cautious... + if (!$context['user']['is_owner'] || empty($modSettings['show_group_membership'])) + isAllowedTo('manage_membergroups'); + if (!isset($_REQUEST['gid']) && !isset($_POST['primary'])) + fatal_lang_error('no_access', false); + + checkSession(isset($_GET['gid']) ? 'get' : 'post'); + + $old_profile = &$user_profile[$memID]; + $context['can_manage_membergroups'] = allowedTo('manage_membergroups'); + $context['can_manage_protected'] = allowedTo('admin_forum'); + + // By default the new primary is the old one. + $newPrimary = $old_profile['id_group']; + $addGroups = array_flip(explode(',', $old_profile['additional_groups'])); + $canChangePrimary = $old_profile['id_group'] == 0 ? 1 : 0; + $changeType = isset($_POST['primary']) ? 'primary' : (isset($_POST['req']) ? 'request' : 'free'); + + // One way or another, we have a target group in mind... + $group_id = isset($_REQUEST['gid']) ? (int) $_REQUEST['gid'] : (int) $_POST['primary']; + $foundTarget = $changeType == 'primary' && $group_id == 0 ? true : false; + + // Sanity check!! + if ($group_id == 1) + isAllowedTo('admin_forum'); + // Protected groups too! + else + { + $request = $smcFunc['db_query']('', ' + SELECT group_type + FROM {db_prefix}membergroups + WHERE id_group = {int:current_group} + LIMIT {int:limit}', + array( + 'current_group' => $group_id, + 'limit' => 1, + ) + ); + list ($is_protected) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if ($is_protected == 1) + isAllowedTo('admin_forum'); + } + + // What ever we are doing, we need to determine if changing primary is possible! + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_type, hidden, group_name + FROM {db_prefix}membergroups + WHERE id_group IN ({int:group_list}, {int:current_group})', + array( + 'group_list' => $group_id, + 'current_group' => $old_profile['id_group'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Is this the new group? + if ($row['id_group'] == $group_id) + { + $foundTarget = true; + $group_name = $row['group_name']; + + // Does the group type match what we're doing - are we trying to request a non-requestable group? + if ($changeType == 'request' && $row['group_type'] != 2) + fatal_lang_error('no_access', false); + // What about leaving a requestable group we are not a member of? + elseif ($changeType == 'free' && $row['group_type'] == 2 && $old_profile['id_group'] != $row['id_group'] && !isset($addGroups[$row['id_group']])) + fatal_lang_error('no_access', false); + elseif ($changeType == 'free' && $row['group_type'] != 3 && $row['group_type'] != 2) + fatal_lang_error('no_access', false); + + // We can't change the primary group if this is hidden! + if ($row['hidden'] == 2) + $canChangePrimary = false; + } + + // If this is their old primary, can we change it? + if ($row['id_group'] == $old_profile['id_group'] && ($row['group_type'] > 1 || $context['can_manage_membergroups']) && $canChangePrimary !== false) + $canChangePrimary = 1; + + // If we are not doing a force primary move, don't do it automatically if current primary is not 0. + if ($changeType != 'primary' && $old_profile['id_group'] != 0) + $canChangePrimary = false; + + // If this is the one we are acting on, can we even act? + if ((!$context['can_manage_protected'] && $row['group_type'] == 1) || (!$context['can_manage_membergroups'] && $row['group_type'] == 0)) + $canChangePrimary = false; + } + $smcFunc['db_free_result']($request); + + // Didn't find the target? + if (!$foundTarget) + fatal_lang_error('no_access', false); + + // Final security check, don't allow users to promote themselves to admin. + if ($context['can_manage_membergroups'] && !allowedTo('admin_forum')) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(permission) + FROM {db_prefix}permissions + WHERE id_group = {int:selected_group} + AND permission = {string:admin_forum} + AND add_deny = {int:not_denied}', + array( + 'selected_group' => $group_id, + 'not_denied' => 1, + 'admin_forum' => 'admin_forum', + ) + ); + list ($disallow) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if ($disallow) + isAllowedTo('admin_forum'); + } + + // If we're requesting, add the note then return. + if ($changeType == 'request') + { + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}log_group_requests + WHERE id_member = {int:selected_member} + AND id_group = {int:selected_group}', + array( + 'selected_member' => $memID, + 'selected_group' => $group_id, + ) + ); + if ($smcFunc['db_num_rows']($request) != 0) + fatal_lang_error('profile_error_already_requested_group'); + $smcFunc['db_free_result']($request); + + // Log the request. + $smcFunc['db_insert']('', + '{db_prefix}log_group_requests', + array( + 'id_member' => 'int', 'id_group' => 'int', 'time_applied' => 'int', 'reason' => 'string-65534', + ), + array( + $memID, $group_id, time(), $_POST['reason'], + ), + array('id_request') + ); + + // Send an email to all group moderators etc. + require_once($sourcedir . '/Subs-Post.php'); + + // Do we have any group moderators? + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}group_moderators + WHERE id_group = {int:selected_group}', + array( + 'selected_group' => $group_id, + ) + ); + $moderators = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $moderators[] = $row['id_member']; + $smcFunc['db_free_result']($request); + + // Otherwise this is the backup! + if (empty($moderators)) + { + require_once($sourcedir . '/Subs-Members.php'); + $moderators = membersAllowedTo('manage_membergroups'); + } + + if (!empty($moderators)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, email_address, lngfile, member_name, mod_prefs + FROM {db_prefix}members + WHERE id_member IN ({array_int:moderator_list}) + AND notify_types != {int:no_notifications} + ORDER BY lngfile', + array( + 'moderator_list' => $moderators, + 'no_notifications' => 4, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Check whether they are interested. + if (!empty($row['mod_prefs'])) + { + list(,, $pref_binary) = explode('|', $row['mod_prefs']); + if (!($pref_binary & 4)) + continue; + } + + $replacements = array( + 'RECPNAME' => $row['member_name'], + 'APPYNAME' => $old_profile['member_name'], + 'GROUPNAME' => $group_name, + 'REASON' => $_POST['reason'], + 'MODLINK' => $scripturl . '?action=moderate;area=groups;sa=requests', + ); + + $emaildata = loadEmailTemplate('request_membership', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); + sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 2); + } + $smcFunc['db_free_result']($request); + } + + return $changeType; + } + // Otherwise we are leaving/joining a group. + elseif ($changeType == 'free') + { + // Are we leaving? + if ($old_profile['id_group'] == $group_id || isset($addGroups[$group_id])) + { + if ($old_profile['id_group'] == $group_id) + $newPrimary = 0; + else + unset($addGroups[$group_id]); + } + // ... if not, must be joining. + else + { + // Can we change the primary, and do we want to? + if ($canChangePrimary) + { + if ($old_profile['id_group'] != 0) + $addGroups[$old_profile['id_group']] = -1; + $newPrimary = $group_id; + } + // Otherwise it's an additional group... + else + $addGroups[$group_id] = -1; + } + } + // Finally, we must be setting the primary. + elseif ($canChangePrimary) + { + if ($old_profile['id_group'] != 0) + $addGroups[$old_profile['id_group']] = -1; + if (isset($addGroups[$group_id])) + unset($addGroups[$group_id]); + $newPrimary = $group_id; + } + + // Finally, we can make the changes! + foreach ($addGroups as $id => $dummy) + if (empty($id)) + unset($addGroups[$id]); + $addGroups = implode(',', array_flip($addGroups)); + + // Ensure that we don't cache permissions if the group is changing. + if ($context['user']['is_owner']) + $_SESSION['mc']['time'] = 0; + else + updateSettings(array('settings_updated' => time())); + + updateMemberData($memID, array('id_group' => $newPrimary, 'additional_groups' => $addGroups)); + + return $changeType; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Profile-View.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Profile-View.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1958 @@ + sprintf($txt['profile_of_username'], $memberContext[$memID]['name']), + 'can_send_pm' => allowedTo('pm_send'), + 'can_have_buddy' => allowedTo('profile_identity_own') && !empty($modSettings['enable_buddylist']), + 'can_issue_warning' => in_array('w', $context['admin_features']) && allowedTo('issue_warning') && $modSettings['warning_settings'][0] == 1, + ); + $context['member'] = &$memberContext[$memID]; + $context['can_view_warning'] = in_array('w', $context['admin_features']) && (allowedTo('issue_warning') && !$context['user']['is_owner']) || (!empty($modSettings['warning_show']) && ($modSettings['warning_show'] > 1 || $context['user']['is_owner'])); + + // Set a canonical URL for this page. + $context['canonical_url'] = $scripturl . '?action=profile;u=' . $memID; + + // Are there things we don't show? + $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array(); + + // See if they have broken any warning levels... + list ($modSettings['warning_enable'], $modSettings['user_limit']) = explode(',', $modSettings['warning_settings']); + if (!empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $context['member']['warning']) + $context['warning_status'] = $txt['profile_warning_is_muted']; + elseif (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $context['member']['warning']) + $context['warning_status'] = $txt['profile_warning_is_moderation']; + elseif (!empty($modSettings['warning_watch']) && $modSettings['warning_watch'] <= $context['member']['warning']) + $context['warning_status'] = $txt['profile_warning_is_watch']; + + // They haven't even been registered for a full day!? + $days_registered = (int) ((time() - $user_profile[$memID]['date_registered']) / (3600 * 24)); + if (empty($user_profile[$memID]['date_registered']) || $days_registered < 1) + $context['member']['posts_per_day'] = $txt['not_applicable']; + else + $context['member']['posts_per_day'] = comma_format($context['member']['real_posts'] / $days_registered, 3); + + // Set the age... + if (empty($context['member']['birth_date'])) + { + $context['member'] += array( + 'age' => $txt['not_applicable'], + 'today_is_birthday' => false + ); + } + else + { + list ($birth_year, $birth_month, $birth_day) = sscanf($context['member']['birth_date'], '%d-%d-%d'); + $datearray = getdate(forum_time()); + $context['member'] += array( + 'age' => $birth_year <= 4 ? $txt['not_applicable'] : $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1), + 'today_is_birthday' => $datearray['mon'] == $birth_month && $datearray['mday'] == $birth_day + ); + } + + if (allowedTo('moderate_forum')) + { + // Make sure it's a valid ip address; otherwise, don't bother... + if (preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $memberContext[$memID]['ip']) == 1 && empty($modSettings['disableHostnameLookup'])) + $context['member']['hostname'] = host_from_ip($memberContext[$memID]['ip']); + else + $context['member']['hostname'] = ''; + + $context['can_see_ip'] = true; + } + else + $context['can_see_ip'] = false; + + if (!empty($modSettings['who_enabled'])) + { + include_once($sourcedir . '/Who.php'); + $action = determineActions($user_profile[$memID]['url']); + + if ($action !== false) + $context['member']['action'] = $action; + } + + // If the user is awaiting activation, and the viewer has permission - setup some activation context messages. + if ($context['member']['is_activated'] % 10 != 1 && allowedTo('moderate_forum')) + { + $context['activate_type'] = $context['member']['is_activated']; + // What should the link text be? + $context['activate_link_text'] = in_array($context['member']['is_activated'], array(3, 4, 5, 13, 14, 15)) ? $txt['account_approve'] : $txt['account_activate']; + + // Should we show a custom message? + $context['activate_message'] = isset($txt['account_activate_method_' . $context['member']['is_activated'] % 10]) ? $txt['account_activate_method_' . $context['member']['is_activated'] % 10] : $txt['account_not_activated']; + } + + // Is the signature even enabled on this forum? + $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1; + + // How about, are they banned? + $context['member']['bans'] = array(); + if (allowedTo('moderate_forum')) + { + // Can they edit the ban? + $context['can_edit_ban'] = allowedTo('manage_bans'); + + $ban_query = array(); + $ban_query_vars = array( + 'time' => time(), + ); + $ban_query[] = 'id_member = ' . $context['member']['id']; + + // Valid IP? + if (preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $memberContext[$memID]['ip'], $ip_parts) == 1) + { + $ban_query[] = '((' . $ip_parts[1] . ' BETWEEN bi.ip_low1 AND bi.ip_high1) + AND (' . $ip_parts[2] . ' BETWEEN bi.ip_low2 AND bi.ip_high2) + AND (' . $ip_parts[3] . ' BETWEEN bi.ip_low3 AND bi.ip_high3) + AND (' . $ip_parts[4] . ' BETWEEN bi.ip_low4 AND bi.ip_high4))'; + + // Do we have a hostname already? + if (!empty($context['member']['hostname'])) + { + $ban_query[] = '({string:hostname} LIKE hostname)'; + $ban_query_vars['hostname'] = $context['member']['hostname']; + } + } + // Use '255.255.255.255' for 'unknown' - it's not valid anyway. + elseif ($memberContext[$memID]['ip'] == 'unknown') + $ban_query[] = '(bi.ip_low1 = 255 AND bi.ip_high1 = 255 + AND bi.ip_low2 = 255 AND bi.ip_high2 = 255 + AND bi.ip_low3 = 255 AND bi.ip_high3 = 255 + AND bi.ip_low4 = 255 AND bi.ip_high4 = 255)'; + + // Check their email as well... + if (strlen($context['member']['email']) != 0) + { + $ban_query[] = '({string:email} LIKE bi.email_address)'; + $ban_query_vars['email'] = $context['member']['email']; + } + + // So... are they banned? Dying to know! + $request = $smcFunc['db_query']('', ' + SELECT bg.id_ban_group, bg.name, bg.cannot_access, bg.cannot_post, bg.cannot_register, + bg.cannot_login, bg.reason + FROM {db_prefix}ban_items AS bi + INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group AND (bg.expire_time IS NULL OR bg.expire_time > {int:time})) + WHERE (' . implode(' OR ', $ban_query) . ')', + $ban_query_vars + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Work out what restrictions we actually have. + $ban_restrictions = array(); + foreach (array('access', 'register', 'login', 'post') as $type) + if ($row['cannot_' . $type]) + $ban_restrictions[] = $txt['ban_type_' . $type]; + + // No actual ban in place? + if (empty($ban_restrictions)) + continue; + + // Prepare the link for context. + $ban_explanation = sprintf($txt['user_cannot_due_to'], implode(', ', $ban_restrictions), '' . $row['name'] . ''); + + $context['member']['bans'][$row['id_ban_group']] = array( + 'reason' => empty($row['reason']) ? '' : '

' . $txt['ban_reason'] . ': ' . $row['reason'], + 'cannot' => array( + 'access' => !empty($row['cannot_access']), + 'register' => !empty($row['cannot_register']), + 'post' => !empty($row['cannot_post']), + 'login' => !empty($row['cannot_login']), + ), + 'explanation' => $ban_explanation, + ); + } + $smcFunc['db_free_result']($request); + } + + loadCustomFields($memID); +} + +// !!! This function needs to be split up properly. +// Show all posts by the current user +function showPosts($memID) +{ + global $txt, $user_info, $scripturl, $modSettings; + global $context, $user_profile, $sourcedir, $smcFunc, $board; + + // Some initial context. + $context['start'] = (int) $_REQUEST['start']; + $context['current_member'] = $memID; + + // Create the tabs for the template. + $context[$context['profile_menu_name']]['tab_data'] = array( + 'title' => $txt['showPosts'], + 'description' => $txt['showPosts_help'], + 'icon' => 'profile_sm.gif', + 'tabs' => array( + 'messages' => array( + ), + 'topics' => array( + ), + 'attach' => array( + ), + ), + ); + + // Set the page title + $context['page_title'] = $txt['showPosts'] . ' - ' . $user_profile[$memID]['real_name']; + + // Is the load average too high to allow searching just now? + if (!empty($context['load_average']) && !empty($modSettings['loadavg_show_posts']) && $context['load_average'] >= $modSettings['loadavg_show_posts']) + fatal_lang_error('loadavg_show_posts_disabled', false); + + // If we're specifically dealing with attachments use that function! + if (isset($_GET['sa']) && $_GET['sa'] == 'attach') + return showAttachments($memID); + + // Are we just viewing topics? + $context['is_topics'] = isset($_GET['sa']) && $_GET['sa'] == 'topics' ? true : false; + + // If just deleting a message, do it and then redirect back. + if (isset($_GET['delete']) && !$context['is_topics']) + { + checkSession('get'); + + // We need msg info for logging. + $request = $smcFunc['db_query']('', ' + SELECT subject, id_member, id_topic, id_board + FROM {db_prefix}messages + WHERE id_msg = {int:id_msg}', + array( + 'id_msg' => (int) $_GET['delete'], + ) + ); + $info = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Trying to remove a message that doesn't exist. + if (empty($info)) + redirectexit('action=profile;u=' . $memID . ';area=showposts;start=' . $_GET['start']); + + // We can be lazy, since removeMessage() will check the permissions for us. + require_once($sourcedir . '/RemoveTopic.php'); + removeMessage((int) $_GET['delete']); + + // Add it to the mod log. + if (allowedTo('delete_any') && (!allowedTo('delete_own') || $info[1] != $user_info['id'])) + logAction('delete', array('topic' => $info[2], 'subject' => $info[0], 'member' => $info[1], 'board' => $info[3])); + + // Back to... where we are now ;). + redirectexit('action=profile;u=' . $memID . ';area=showposts;start=' . $_GET['start']); + } + + // Default to 10. + if (empty($_REQUEST['viewscount']) || !is_numeric($_REQUEST['viewscount'])) + $_REQUEST['viewscount'] = '10'; + + if ($context['is_topics']) + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}topics AS t' . ($user_info['query_see_board'] == '1=1' ? '' : ' + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board AND {query_see_board})') . ' + WHERE t.id_member_started = {int:current_member}' . (!empty($board) ? ' + AND t.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : ' + AND t.approved = {int:is_approved}'), + array( + 'current_member' => $memID, + 'is_approved' => 1, + 'board' => $board, + ) + ); + else + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : ' + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . ' + WHERE m.id_member = {int:current_member}' . (!empty($board) ? ' + AND m.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : ' + AND m.approved = {int:is_approved}'), + array( + 'current_member' => $memID, + 'is_approved' => 1, + 'board' => $board, + ) + ); + list ($msgCount) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT MIN(id_msg), MAX(id_msg) + FROM {db_prefix}messages AS m + WHERE m.id_member = {int:current_member}' . (!empty($board) ? ' + AND m.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : ' + AND m.approved = {int:is_approved}'), + array( + 'current_member' => $memID, + 'is_approved' => 1, + 'board' => $board, + ) + ); + list ($min_msg_member, $max_msg_member) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $reverse = false; + $range_limit = ''; + $maxIndex = (int) $modSettings['defaultMaxMessages']; + + // Make sure the starting place makes sense and construct our friend the page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showposts' . ($context['is_topics'] ? ';sa=topics' : '') . (!empty($board) ? ';board=' . $board : ''), $context['start'], $msgCount, $maxIndex); + $context['current_page'] = $context['start'] / $maxIndex; + + // Reverse the query if we're past 50% of the pages for better performance. + $start = $context['start']; + $reverse = $_REQUEST['start'] > $msgCount / 2; + if ($reverse) + { + $maxIndex = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : (int) $modSettings['defaultMaxMessages']; + $start = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 || $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] ? 0 : $msgCount - $context['start'] - $modSettings['defaultMaxMessages']; + } + + // Guess the range of messages to be shown. + if ($msgCount > 1000) + { + $margin = floor(($max_msg_member - $min_msg_member) * (($start + $modSettings['defaultMaxMessages']) / $msgCount) + .1 * ($max_msg_member - $min_msg_member)); + // Make a bigger margin for topics only. + if ($context['is_topics']) + { + $margin *= 5; + $range_limit = $reverse ? 't.id_first_msg < ' . ($min_msg_member + $margin) : 't.id_first_msg > ' . ($max_msg_member - $margin); + } + else + $range_limit = $reverse ? 'm.id_msg < ' . ($min_msg_member + $margin) : 'm.id_msg > ' . ($max_msg_member - $margin); + } + + // Find this user's posts. The left join on categories somehow makes this faster, weird as it looks. + $looped = false; + while (true) + { + if ($context['is_topics']) + { + $request = $smcFunc['db_query']('', ' + SELECT + b.id_board, b.name AS bname, c.id_cat, c.name AS cname, t.id_member_started, t.id_first_msg, t.id_last_msg, + t.approved, m.body, m.smileys_enabled, m.subject, m.poster_time, m.id_topic, m.id_msg + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE t.id_member_started = {int:current_member}' . (!empty($board) ? ' + AND t.id_board = {int:board}' : '') . (empty($range_limit) ? '' : ' + AND ' . $range_limit) . ' + AND {query_see_board}' . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : ' + AND t.approved = {int:is_approved} AND m.approved = {int:is_approved}') . ' + ORDER BY t.id_first_msg ' . ($reverse ? 'ASC' : 'DESC') . ' + LIMIT ' . $start . ', ' . $maxIndex, + array( + 'current_member' => $memID, + 'is_approved' => 1, + 'board' => $board, + ) + ); + } + else + { + $request = $smcFunc['db_query']('', ' + SELECT + b.id_board, b.name AS bname, c.id_cat, c.name AS cname, m.id_topic, m.id_msg, + t.id_member_started, t.id_first_msg, t.id_last_msg, m.body, m.smileys_enabled, + m.subject, m.poster_time, m.approved + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE m.id_member = {int:current_member}' . (!empty($board) ? ' + AND b.id_board = {int:board}' : '') . (empty($range_limit) ? '' : ' + AND ' . $range_limit) . ' + AND {query_see_board}' . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : ' + AND t.approved = {int:is_approved} AND m.approved = {int:is_approved}') . ' + ORDER BY m.id_msg ' . ($reverse ? 'ASC' : 'DESC') . ' + LIMIT ' . $start . ', ' . $maxIndex, + array( + 'current_member' => $memID, + 'is_approved' => 1, + 'board' => $board, + ) + ); + } + + // Make sure we quit this loop. + if ($smcFunc['db_num_rows']($request) === $maxIndex || $looped) + break; + $looped = true; + $range_limit = ''; + } + + // Start counting at the number of the first message displayed. + $counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start']; + $context['posts'] = array(); + $board_ids = array('own' => array(), 'any' => array()); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Censor.... + censorText($row['body']); + censorText($row['subject']); + + // Do the code. + $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); + + // And the array... + $context['posts'][$counter += $reverse ? -1 : 1] = array( + 'body' => $row['body'], + 'counter' => $counter, + 'alternate' => $counter % 2, + 'category' => array( + 'name' => $row['cname'], + 'id' => $row['id_cat'] + ), + 'board' => array( + 'name' => $row['bname'], + 'id' => $row['id_board'] + ), + 'topic' => $row['id_topic'], + 'subject' => $row['subject'], + 'start' => 'msg' . $row['id_msg'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'id' => $row['id_msg'], + 'can_reply' => false, + 'can_mark_notify' => false, + 'can_delete' => false, + 'delete_possible' => ($row['id_first_msg'] != $row['id_msg'] || $row['id_last_msg'] == $row['id_msg']) && (empty($modSettings['edit_disable_time']) || $row['poster_time'] + $modSettings['edit_disable_time'] * 60 >= time()), + 'approved' => $row['approved'], + ); + + if ($user_info['id'] == $row['id_member_started']) + $board_ids['own'][$row['id_board']][] = $counter; + $board_ids['any'][$row['id_board']][] = $counter; + } + $smcFunc['db_free_result']($request); + + // All posts were retrieved in reverse order, get them right again. + if ($reverse) + $context['posts'] = array_reverse($context['posts'], true); + + // These are all the permissions that are different from board to board.. + if ($context['is_topics']) + $permissions = array( + 'own' => array( + 'post_reply_own' => 'can_reply', + ), + 'any' => array( + 'post_reply_any' => 'can_reply', + 'mark_any_notify' => 'can_mark_notify', + ) + ); + else + $permissions = array( + 'own' => array( + 'post_reply_own' => 'can_reply', + 'delete_own' => 'can_delete', + ), + 'any' => array( + 'post_reply_any' => 'can_reply', + 'mark_any_notify' => 'can_mark_notify', + 'delete_any' => 'can_delete', + ) + ); + + // For every permission in the own/any lists... + foreach ($permissions as $type => $list) + { + foreach ($list as $permission => $allowed) + { + // Get the boards they can do this on... + $boards = boardsAllowedTo($permission); + + // Hmm, they can do it on all boards, can they? + if (!empty($boards) && $boards[0] == 0) + $boards = array_keys($board_ids[$type]); + + // Now go through each board they can do the permission on. + foreach ($boards as $board_id) + { + // There aren't any posts displayed from this board. + if (!isset($board_ids[$type][$board_id])) + continue; + + // Set the permission to true ;). + foreach ($board_ids[$type][$board_id] as $counter) + $context['posts'][$counter][$allowed] = true; + } + } + } + + // Clean up after posts that cannot be deleted and quoted. + $quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])); + foreach ($context['posts'] as $counter => $dummy) + { + $context['posts'][$counter]['can_delete'] &= $context['posts'][$counter]['delete_possible']; + $context['posts'][$counter]['can_quote'] = $context['posts'][$counter]['can_reply'] && $quote_enabled; + } +} + +// Show all the attachments of a user. +function showAttachments($memID) +{ + global $txt, $user_info, $scripturl, $modSettings, $board; + global $context, $user_profile, $sourcedir, $smcFunc; + + // OBEY permissions! + $boardsAllowed = boardsAllowedTo('view_attachments'); + // Make sure we can't actually see anything... + if (empty($boardsAllowed)) + $boardsAllowed = array(-1); + + // Get the total number of attachments they have posted. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}attachments AS a + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board}) + WHERE a.attachment_type = {int:attachment_type} + AND a.id_msg != {int:no_message} + AND m.id_member = {int:current_member}' . (!empty($board) ? ' + AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? ' + AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : ' + AND m.approved = {int:is_approved}'), + array( + 'boards_list' => $boardsAllowed, + 'attachment_type' => 0, + 'no_message' => 0, + 'current_member' => $memID, + 'is_approved' => 1, + 'board' => $board, + ) + ); + list ($attachCount) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $maxIndex = (int) $modSettings['defaultMaxMessages']; + + // What about ordering? + $sortTypes = array( + 'filename' => 'a.filename', + 'downloads' => 'a.downloads', + 'subject' => 'm.subject', + 'posted' => 'm.poster_time', + ); + $context['sort_order'] = isset($_GET['sort']) && isset($sortTypes[$_GET['sort']]) ? $_GET['sort'] : 'posted'; + $context['sort_direction'] = isset($_GET['asc']) ? 'up' : 'down'; + + $sort = $sortTypes[$context['sort_order']]; + + // Let's get ourselves a lovely page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showposts;sa=attach;sort=' . $context['sort_order'] . ($context['sort_direction'] == 'up' ? ';asc' : ''), $context['start'], $attachCount, $maxIndex); + + // Retrieve some attachments. + $request = $smcFunc['db_query']('', ' + SELECT a.id_attach, a.id_msg, a.filename, a.downloads, a.approved, m.id_msg, m.id_topic, + m.id_board, m.poster_time, m.subject, b.name + FROM {db_prefix}attachments AS a + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board}) + WHERE a.attachment_type = {int:attachment_type} + AND a.id_msg != {int:no_message} + AND m.id_member = {int:current_member}' . (!empty($board) ? ' + AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? ' + AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : ' + AND m.approved = {int:is_approved}') . ' + ORDER BY {raw:sort} + LIMIT {int:offset}, {int:limit}', + array( + 'boards_list' => $boardsAllowed, + 'attachment_type' => 0, + 'no_message' => 0, + 'current_member' => $memID, + 'is_approved' => 1, + 'board' => $board, + 'sort' => $sort . ' ' . ($context['sort_direction'] == 'down' ? 'DESC' : 'ASC'), + 'offset' => $context['start'], + 'limit' => $maxIndex, + ) + ); + $context['attachments'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $row['subject'] = censorText($row['subject']); + + $context['attachments'][] = array( + 'id' => $row['id_attach'], + 'filename' => $row['filename'], + 'downloads' => $row['downloads'], + 'subject' => $row['subject'], + 'posted' => timeformat($row['poster_time']), + 'msg' => $row['id_msg'], + 'topic' => $row['id_topic'], + 'board' => $row['id_board'], + 'board_name' => $row['name'], + 'approved' => $row['approved'], + ); + } + $smcFunc['db_free_result']($request); +} + +function statPanel($memID) +{ + global $txt, $scripturl, $context, $user_profile, $user_info, $modSettings, $smcFunc; + + $context['page_title'] = $txt['statPanel_showStats'] . ' ' . $user_profile[$memID]['real_name']; + + // General user statistics. + $timeDays = floor($user_profile[$memID]['total_time_logged_in'] / 86400); + $timeHours = floor(($user_profile[$memID]['total_time_logged_in'] % 86400) / 3600); + $context['time_logged_in'] = ($timeDays > 0 ? $timeDays . $txt['totalTimeLogged2'] : '') . ($timeHours > 0 ? $timeHours . $txt['totalTimeLogged3'] : '') . floor(($user_profile[$memID]['total_time_logged_in'] % 3600) / 60) . $txt['totalTimeLogged4']; + $context['num_posts'] = comma_format($user_profile[$memID]['posts']); + + // Number of topics started. + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}topics + WHERE id_member_started = {int:current_member}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND id_board != {int:recycle_board}' : ''), + array( + 'current_member' => $memID, + 'recycle_board' => $modSettings['recycle_board'], + ) + ); + list ($context['num_topics']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Number polls started. + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}topics + WHERE id_member_started = {int:current_member}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND id_board != {int:recycle_board}' : '') . ' + AND id_poll != {int:no_poll}', + array( + 'current_member' => $memID, + 'recycle_board' => $modSettings['recycle_board'], + 'no_poll' => 0, + ) + ); + list ($context['num_polls']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Number polls voted in. + $result = $smcFunc['db_query']('distinct_poll_votes', ' + SELECT COUNT(DISTINCT id_poll) + FROM {db_prefix}log_polls + WHERE id_member = {int:current_member}', + array( + 'current_member' => $memID, + ) + ); + list ($context['num_votes']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Format the numbers... + $context['num_topics'] = comma_format($context['num_topics']); + $context['num_polls'] = comma_format($context['num_polls']); + $context['num_votes'] = comma_format($context['num_votes']); + + // Grab the board this member posted in most often. + $result = $smcFunc['db_query']('', ' + SELECT + b.id_board, MAX(b.name) AS name, MAX(b.num_posts) AS num_posts, COUNT(*) AS message_count + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE m.id_member = {int:current_member} + AND b.count_posts = {int:count_enabled} + AND {query_see_board} + GROUP BY b.id_board + ORDER BY message_count DESC + LIMIT 10', + array( + 'current_member' => $memID, + 'count_enabled' => 0, + ) + ); + $context['popular_boards'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $context['popular_boards'][$row['id_board']] = array( + 'id' => $row['id_board'], + 'posts' => $row['message_count'], + 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'link' => '' . $row['name'] . '', + 'posts_percent' => $user_profile[$memID]['posts'] == 0 ? 0 : ($row['message_count'] * 100) / $user_profile[$memID]['posts'], + 'total_posts' => $row['num_posts'], + 'total_posts_member' => $user_profile[$memID]['posts'], + ); + } + $smcFunc['db_free_result']($result); + + // Now get the 10 boards this user has most often participated in. + $result = $smcFunc['db_query']('profile_board_stats', ' + SELECT + b.id_board, MAX(b.name) AS name, b.num_posts, COUNT(*) AS message_count, + CASE WHEN COUNT(*) > MAX(b.num_posts) THEN 1 ELSE COUNT(*) / MAX(b.num_posts) END * 100 AS percentage + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE m.id_member = {int:current_member} + AND {query_see_board} + GROUP BY b.id_board, b.num_posts + ORDER BY percentage DESC + LIMIT 10', + array( + 'current_member' => $memID, + ) + ); + $context['board_activity'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $context['board_activity'][$row['id_board']] = array( + 'id' => $row['id_board'], + 'posts' => $row['message_count'], + 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'link' => '' . $row['name'] . '', + 'percent' => comma_format((float) $row['percentage'], 2), + 'posts_percent' => (float) $row['percentage'], + 'total_posts' => $row['num_posts'], + ); + } + $smcFunc['db_free_result']($result); + + // Posting activity by time. + $result = $smcFunc['db_query']('user_activity_by_time', ' + SELECT + HOUR(FROM_UNIXTIME(poster_time + {int:time_offset})) AS hour, + COUNT(*) AS post_count + FROM {db_prefix}messages + WHERE id_member = {int:current_member}' . ($modSettings['totalMessages'] > 100000 ? ' + AND id_topic > {int:top_ten_thousand_topics}' : '') . ' + GROUP BY hour', + array( + 'current_member' => $memID, + 'top_ten_thousand_topics' => $modSettings['totalTopics'] - 10000, + 'time_offset' => (($user_info['time_offset'] + $modSettings['time_offset']) * 3600), + ) + ); + $maxPosts = $realPosts = 0; + $context['posts_by_time'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Cast as an integer to remove the leading 0. + $row['hour'] = (int) $row['hour']; + + $maxPosts = max($row['post_count'], $maxPosts); + $realPosts += $row['post_count']; + + $context['posts_by_time'][$row['hour']] = array( + 'hour' => $row['hour'], + 'hour_format' => stripos($user_info['time_format'], '%p') === false ? $row['hour'] : date('g a', mktime($row['hour'])), + 'posts' => $row['post_count'], + 'posts_percent' => 0, + 'is_last' => $row['hour'] == 23, + ); + } + $smcFunc['db_free_result']($result); + + if ($maxPosts > 0) + for ($hour = 0; $hour < 24; $hour++) + { + if (!isset($context['posts_by_time'][$hour])) + $context['posts_by_time'][$hour] = array( + 'hour' => $hour, + 'hour_format' => stripos($user_info['time_format'], '%p') === false ? $hour : date('g a', mktime($hour)), + 'posts' => 0, + 'posts_percent' => 0, + 'relative_percent' => 0, + 'is_last' => $hour == 23, + ); + else + { + $context['posts_by_time'][$hour]['posts_percent'] = round(($context['posts_by_time'][$hour]['posts'] * 100) / $realPosts); + $context['posts_by_time'][$hour]['relative_percent'] = round(($context['posts_by_time'][$hour]['posts'] * 100) / $maxPosts); + } + } + + // Put it in the right order. + ksort($context['posts_by_time']); +} + +function tracking($memID) +{ + global $sourcedir, $context, $txt, $scripturl, $modSettings, $user_profile; + + $subActions = array( + 'activity' => array('trackActivity', $txt['trackActivity']), + 'ip' => array('TrackIP', $txt['trackIP']), + 'edits' => array('trackEdits', $txt['trackEdits']), + ); + + $context['tracking_area'] = isset($_GET['sa']) && isset($subActions[$_GET['sa']]) ? $_GET['sa'] : 'activity'; + + if (isset($types[$context['tracking_area']][1])) + require_once($sourcedir . '/' . $types[$context['tracking_area']][1]); + + // Create the tabs for the template. + $context[$context['profile_menu_name']]['tab_data'] = array( + 'title' => $txt['tracking'], + 'description' => $txt['tracking_description'], + 'icon' => 'profile_sm.gif', + 'tabs' => array( + 'activity' => array(), + 'ip' => array(), + 'edits' => array(), + ), + ); + + // Moderation must be on to track edits. + if (empty($modSettings['modlog_enabled'])) + unset($context[$context['profile_menu_name']]['tab_data']['edits']); + + // Set a page title. + $context['page_title'] = $txt['trackUser'] . ' - ' . $subActions[$context['tracking_area']][1] . ' - ' . $user_profile[$memID]['real_name']; + + // Pass on to the actual function. + $context['sub_template'] = $subActions[$context['tracking_area']][0]; + $subActions[$context['tracking_area']][0]($memID); +} + +function trackActivity($memID) +{ + global $scripturl, $txt, $modSettings, $sourcedir; + global $user_profile, $context, $smcFunc; + + // Verify if the user has sufficient permissions. + isAllowedTo('moderate_forum'); + + $context['last_ip'] = $user_profile[$memID]['member_ip']; + if ($context['last_ip'] != $user_profile[$memID]['member_ip2']) + $context['last_ip2'] = $user_profile[$memID]['member_ip2']; + $context['member']['name'] = $user_profile[$memID]['real_name']; + + // Set the options for the list component. + $listOptions = array( + 'id' => 'track_user_list', + 'title' => $txt['errors_by'] . ' ' . $context['member']['name'], + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $txt['no_errors_from_user'], + 'base_href' => $scripturl . '?action=profile;area=tracking;sa=user;u=' . $memID, + 'default_sort_col' => 'date', + 'get_items' => array( + 'function' => 'list_getUserErrors', + 'params' => array( + 'le.id_member = {int:current_member}', + array('current_member' => $memID), + ), + ), + 'get_count' => array( + 'function' => 'list_getUserErrorCount', + 'params' => array( + 'id_member = {int:current_member}', + array('current_member' => $memID), + ), + ), + 'columns' => array( + 'ip_address' => array( + 'header' => array( + 'value' => $txt['ip_address'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s', + 'params' => array( + 'ip' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'le.ip', + 'reverse' => 'le.ip DESC', + ), + ), + 'message' => array( + 'header' => array( + 'value' => $txt['message'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s
%2$s', + 'params' => array( + 'message' => false, + 'url' => false, + ), + ), + ), + ), + 'date' => array( + 'header' => array( + 'value' => $txt['date'], + ), + 'data' => array( + 'db' => 'time', + ), + 'sort' => array( + 'default' => 'le.id_error DESC', + 'reverse' => 'le.id_error', + ), + ), + ), + 'additional_rows' => array( + array( + 'position' => 'after_title', + 'value' => $txt['errors_desc'], + 'class' => 'smalltext', + 'style' => 'padding: 2ex;', + ), + ), + ); + + // Create the list for viewing. + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + // If this is a big forum, or a large posting user, let's limit the search. + if ($modSettings['totalMessages'] > 50000 && $user_profile[$memID]['posts'] > 500) + { + $request = $smcFunc['db_query']('', ' + SELECT MAX(id_msg) + FROM {db_prefix}messages AS m + WHERE m.id_member = {int:current_member}', + array( + 'current_member' => $memID, + ) + ); + list ($max_msg_member) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // There's no point worrying ourselves with messages made yonks ago, just get recent ones! + $min_msg_member = max(0, $max_msg_member - $user_profile[$memID]['posts'] * 3); + } + + // Default to at least the ones we know about. + $ips = array( + $user_profile[$memID]['member_ip'], + $user_profile[$memID]['member_ip2'], + ); + + // Get all IP addresses this user has used for his messages. + $request = $smcFunc['db_query']('', ' + SELECT poster_ip + FROM {db_prefix}messages + WHERE id_member = {int:current_member} + ' . (isset($min_msg_member) ? ' + AND id_msg >= {int:min_msg_member} AND id_msg <= {int:max_msg_member}' : '') . ' + GROUP BY poster_ip', + array( + 'current_member' => $memID, + 'min_msg_member' => !empty($min_msg_member) ? $min_msg_member : 0, + 'max_msg_member' => !empty($max_msg_member) ? $max_msg_member : 0, + ) + ); + $context['ips'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['ips'][] = '' . $row['poster_ip'] . ''; + $ips[] = $row['poster_ip']; + } + $smcFunc['db_free_result']($request); + + // Now also get the IP addresses from the error messages. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS error_count, ip + FROM {db_prefix}log_errors + WHERE id_member = {int:current_member} + GROUP BY ip', + array( + 'current_member' => $memID, + ) + ); + $context['error_ips'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['error_ips'][] = '' . $row['ip'] . ''; + $ips[] = $row['ip']; + } + $smcFunc['db_free_result']($request); + + // Find other users that might use the same IP. + $ips = array_unique($ips); + $context['members_in_range'] = array(); + if (!empty($ips)) + { + // Get member ID's which are in messages... + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.poster_ip IN ({array_string:ip_list}) + GROUP BY mem.id_member + HAVING mem.id_member != {int:current_member}', + array( + 'current_member' => $memID, + 'ip_list' => $ips, + ) + ); + $message_members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $message_members[] = $row['id_member']; + $smcFunc['db_free_result']($request); + + // Fetch their names, cause of the GROUP BY doesn't like giving us that normally. + if (!empty($message_members)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE id_member IN ({array_int:message_members})', + array( + 'message_members' => $message_members, + 'ip_list' => $ips, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['members_in_range'][$row['id_member']] = '' . $row['real_name'] . ''; + $smcFunc['db_free_result']($request); + } + + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE id_member != {int:current_member} + AND member_ip IN ({array_string:ip_list})', + array( + 'current_member' => $memID, + 'ip_list' => $ips, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['members_in_range'][$row['id_member']] = '' . $row['real_name'] . ''; + $smcFunc['db_free_result']($request); + } +} + +function list_getUserErrorCount($where, $where_vars = array()) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS error_count + FROM {db_prefix}log_errors + WHERE ' . $where, + $where_vars + ); + list ($count) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $count; +} + +function list_getUserErrors($start, $items_per_page, $sort, $where, $where_vars = array()) +{ + global $smcFunc, $txt, $scripturl; + + // Get a list of error messages from this ip (range). + $request = $smcFunc['db_query']('', ' + SELECT + le.log_time, le.ip, le.url, le.message, IFNULL(mem.id_member, 0) AS id_member, + IFNULL(mem.real_name, {string:guest_title}) AS display_name, mem.member_name + FROM {db_prefix}log_errors AS le + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = le.id_member) + WHERE ' . $where . ' + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array_merge($where_vars, array( + 'guest_title' => $txt['guest_title'], + )) + ); + $error_messages = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $error_messages[] = array( + 'ip' => $row['ip'], + 'member_link' => $row['id_member'] > 0 ? '' . $row['display_name'] . '' : $row['display_name'], + 'message' => strtr($row['message'], array('<span class="remove">' => '', '</span>' => '')), + 'url' => $row['url'], + 'time' => timeformat($row['log_time']), + 'timestamp' => forum_time(true, $row['log_time']), + ); + $smcFunc['db_free_result']($request); + + return $error_messages; +} + +function list_getIPMessageCount($where, $where_vars = array()) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS message_count + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE {query_see_board} AND ' . $where, + $where_vars + ); + list ($count) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $count; +} + +function list_getIPMessages($start, $items_per_page, $sort, $where, $where_vars = array()) +{ + global $smcFunc, $txt, $scripturl; + + // Get all the messages fitting this where clause. + // !!!SLOW This query is using a filesort. + $request = $smcFunc['db_query']('', ' + SELECT + m.id_msg, m.poster_ip, IFNULL(mem.real_name, m.poster_name) AS display_name, mem.id_member, + m.subject, m.poster_time, m.id_topic, m.id_board + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE {query_see_board} AND ' . $where . ' + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array_merge($where_vars, array( + )) + ); + $messages = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $messages[] = array( + 'ip' => $row['poster_ip'], + 'member_link' => empty($row['id_member']) ? $row['display_name'] : '' . $row['display_name'] . '', + 'board' => array( + 'id' => $row['id_board'], + 'href' => $scripturl . '?board=' . $row['id_board'] + ), + 'topic' => $row['id_topic'], + 'id' => $row['id_msg'], + 'subject' => $row['subject'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']) + ); + $smcFunc['db_free_result']($request); + + return $messages; +} + +function TrackIP($memID = 0) +{ + global $user_profile, $scripturl, $txt, $user_info, $modSettings, $sourcedir; + global $context, $smcFunc; + + // Can the user do this? + isAllowedTo('moderate_forum'); + + if ($memID == 0) + { + $context['ip'] = $user_info['ip']; + loadTemplate('Profile'); + loadLanguage('Profile'); + $context['sub_template'] = 'trackIP'; + $context['page_title'] = $txt['profile']; + $context['base_url'] = $scripturl . '?action=trackip'; + } + else + { + $context['ip'] = $user_profile[$memID]['member_ip']; + $context['base_url'] = $scripturl . '?action=profile;area=tracking;sa=ip;u=' . $memID; + } + + // Searching? + if (isset($_REQUEST['searchip'])) + $context['ip'] = trim($_REQUEST['searchip']); + + if (preg_match('/^\d{1,3}\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/', $context['ip']) == 0) + fatal_lang_error('invalid_tracking_ip', false); + + $ip_var = str_replace('*', '%', $context['ip']); + $ip_string = strpos($ip_var, '%') === false ? '= {string:ip_address}' : 'LIKE {string:ip_address}'; + + if (empty($context['tracking_area'])) + $context['page_title'] = $txt['trackIP'] . ' - ' . $context['ip']; + + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name AS display_name, member_ip + FROM {db_prefix}members + WHERE member_ip ' . $ip_string, + array( + 'ip_address' => $ip_var, + ) + ); + $context['ips'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['ips'][$row['member_ip']][] = '' . $row['display_name'] . ''; + $smcFunc['db_free_result']($request); + + ksort($context['ips']); + + // Gonna want this for the list. + require_once($sourcedir . '/Subs-List.php'); + + // Start with the user messages. + $listOptions = array( + 'id' => 'track_message_list', + 'title' => $txt['messages_from_ip'] . ' ' . $context['ip'], + 'start_var_name' => 'messageStart', + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $txt['no_messages_from_ip'], + 'base_href' => $context['base_url'] . ';searchip=' . $context['ip'], + 'default_sort_col' => 'date', + 'get_items' => array( + 'function' => 'list_getIPMessages', + 'params' => array( + 'm.poster_ip ' . $ip_string, + array('ip_address' => $ip_var), + ), + ), + 'get_count' => array( + 'function' => 'list_getIPMessageCount', + 'params' => array( + 'm.poster_ip ' . $ip_string, + array('ip_address' => $ip_var), + ), + ), + 'columns' => array( + 'ip_address' => array( + 'header' => array( + 'value' => $txt['ip_address'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s', + 'params' => array( + 'ip' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'INET_ATON(m.poster_ip)', + 'reverse' => 'INET_ATON(m.poster_ip) DESC', + ), + ), + 'poster' => array( + 'header' => array( + 'value' => $txt['poster'], + ), + 'data' => array( + 'db' => 'member_link', + ), + ), + 'subject' => array( + 'header' => array( + 'value' => $txt['subject'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%3$s', + 'params' => array( + 'topic' => false, + 'id' => false, + 'subject' => false, + ), + ), + ), + ), + 'date' => array( + 'header' => array( + 'value' => $txt['date'], + ), + 'data' => array( + 'db' => 'time', + ), + 'sort' => array( + 'default' => 'm.id_msg DESC', + 'reverse' => 'm.id_msg', + ), + ), + ), + 'additional_rows' => array( + array( + 'position' => 'after_title', + 'value' => $txt['messages_from_ip_desc'], + 'class' => 'smalltext', + 'style' => 'padding: 2ex;', + ), + ), + ); + + // Create the messages list. + createList($listOptions); + + // Set the options for the error lists. + $listOptions = array( + 'id' => 'track_user_list', + 'title' => $txt['errors_from_ip'] . ' ' . $context['ip'], + 'start_var_name' => 'errorStart', + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $txt['no_errors_from_ip'], + 'base_href' => $context['base_url'] . ';searchip=' . $context['ip'], + 'default_sort_col' => 'date2', + 'get_items' => array( + 'function' => 'list_getUserErrors', + 'params' => array( + 'le.ip ' . $ip_string, + array('ip_address' => $ip_var), + ), + ), + 'get_count' => array( + 'function' => 'list_getUserErrorCount', + 'params' => array( + 'ip ' . $ip_string, + array('ip_address' => $ip_var), + ), + ), + 'columns' => array( + 'ip_address2' => array( + 'header' => array( + 'value' => $txt['ip_address'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s', + 'params' => array( + 'ip' => false, + ), + ), + ), + 'sort' => array( + 'default' => 'INET_ATON(le.ip)', + 'reverse' => 'INET_ATON(le.ip) DESC', + ), + ), + 'display_name' => array( + 'header' => array( + 'value' => $txt['display_name'], + ), + 'data' => array( + 'db' => 'member_link', + ), + ), + 'message' => array( + 'header' => array( + 'value' => $txt['message'], + ), + 'data' => array( + 'sprintf' => array( + 'format' => '%1$s
%2$s', + 'params' => array( + 'message' => false, + 'url' => false, + ), + ), + ), + ), + 'date2' => array( + 'header' => array( + 'value' => $txt['date'], + ), + 'data' => array( + 'db' => 'time', + ), + 'sort' => array( + 'default' => 'le.id_error DESC', + 'reverse' => 'le.id_error', + ), + ), + ), + 'additional_rows' => array( + array( + 'position' => 'after_title', + 'value' => $txt['errors_from_ip_desc'], + 'class' => 'smalltext', + 'style' => 'padding: 2ex;', + ), + ), + ); + + // Create the error list. + createList($listOptions); + + $context['single_ip'] = strpos($context['ip'], '*') === false; + if ($context['single_ip']) + { + $context['whois_servers'] = array( + 'afrinic' => array( + 'name' => $txt['whois_afrinic'], + 'url' => 'http://www.afrinic.net/cgi-bin/whois?searchtext=' . $context['ip'], + 'range' => array(41, 154, 196), + ), + 'apnic' => array( + 'name' => $txt['whois_apnic'], + 'url' => 'http://wq.apnic.net/apnic-bin/whois.pl?searchtext=' . $context['ip'], + 'range' => array(58, 59, 60, 61, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 133, 150, 153, 163, 171, 202, 203, 210, 211, 218, 219, 220, 221, 222), + ), + 'arin' => array( + 'name' => $txt['whois_arin'], + 'url' => 'http://whois.arin.net/rest/ip/' . $context['ip'], + 'range' => array(7, 24, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 96, 97, 98, 99, + 128, 129, 130, 131, 132, 134, 135, 136, 137, 138, 139, 140, 142, 143, 144, 146, 147, 148, 149, + 152, 155, 156, 157, 158, 159, 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 172, 173, 174, + 192, 198, 199, 204, 205, 206, 207, 208, 209, 216), + ), + 'lacnic' => array( + 'name' => $txt['whois_lacnic'], + 'url' => 'http://lacnic.net/cgi-bin/lacnic/whois?query=' . $context['ip'], + 'range' => array(186, 187, 189, 190, 191, 200, 201), + ), + 'ripe' => array( + 'name' => $txt['whois_ripe'], + 'url' => 'http://www.db.ripe.net/whois?searchtext=' . $context['ip'], + 'range' => array(62, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 141, 145, 151, 188, 193, 194, 195, 212, 213, 217), + ), + ); + + foreach ($context['whois_servers'] as $whois) + { + // Strip off the "decimal point" and anything following... + if (in_array((int) $context['ip'], $whois['range'])) + $context['auto_whois_server'] = $whois; + } + } +} + +function trackEdits($memID) +{ + global $scripturl, $txt, $modSettings, $sourcedir, $context, $smcFunc; + + require_once($sourcedir . '/Subs-List.php'); + + // Get the names of any custom fields. + $request = $smcFunc['db_query']('', ' + SELECT col_name, field_name, bbc + FROM {db_prefix}custom_fields', + array( + ) + ); + $context['custom_field_titles'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['custom_field_titles']['customfield_' . $row['col_name']] = array( + 'title' => $row['field_name'], + 'parse_bbc' => $row['bbc'], + ); + $smcFunc['db_free_result']($request); + + // Set the options for the error lists. + $listOptions = array( + 'id' => 'edit_list', + 'title' => $txt['trackEdits'], + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $txt['trackEdit_no_edits'], + 'base_href' => $scripturl . '?action=profile;area=tracking;sa=edits;u=' . $memID, + 'default_sort_col' => 'time', + 'get_items' => array( + 'function' => 'list_getProfileEdits', + 'params' => array( + $memID, + ), + ), + 'get_count' => array( + 'function' => 'list_getProfileEditCount', + 'params' => array( + $memID, + ), + ), + 'columns' => array( + 'action' => array( + 'header' => array( + 'value' => $txt['trackEdit_action'], + ), + 'data' => array( + 'db' => 'action_text', + ), + ), + 'before' => array( + 'header' => array( + 'value' => $txt['trackEdit_before'], + ), + 'data' => array( + 'db' => 'before', + ), + ), + 'after' => array( + 'header' => array( + 'value' => $txt['trackEdit_after'], + ), + 'data' => array( + 'db' => 'after', + ), + ), + 'time' => array( + 'header' => array( + 'value' => $txt['date'], + ), + 'data' => array( + 'db' => 'time', + ), + 'sort' => array( + 'default' => 'id_action DESC', + 'reverse' => 'id_action', + ), + ), + 'applicator' => array( + 'header' => array( + 'value' => $txt['trackEdit_applicator'], + ), + 'data' => array( + 'db' => 'member_link', + ), + ), + ), + ); + + // Create the error list. + createList($listOptions); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'edit_list'; +} + +// How many edits? +function list_getProfileEditCount($memID) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS edit_count + FROM {db_prefix}log_actions + WHERE id_log = {int:log_type} + AND id_member = {int:owner}', + array( + 'log_type' => 2, + 'owner' => $memID, + ) + ); + list ($edit_count) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $edit_count; +} + +function list_getProfileEdits($start, $items_per_page, $sort, $memID) +{ + global $smcFunc, $txt, $scripturl, $context; + + // Get a list of error messages from this ip (range). + $request = $smcFunc['db_query']('', ' + SELECT + id_action, id_member, ip, log_time, action, extra + FROM {db_prefix}log_actions + WHERE id_log = {int:log_type} + AND id_member = {int:owner} + ORDER BY ' . $sort . ' + LIMIT ' . $start . ', ' . $items_per_page, + array( + 'log_type' => 2, + 'owner' => $memID, + ) + ); + $edits = array(); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $extra = @unserialize($row['extra']); + if (!empty($extra['applicator'])) + $members[] = $extra['applicator']; + + // Work out what the name of the action is. + if (isset($txt['trackEdit_action_' . $row['action']])) + $action_text = $txt['trackEdit_action_' . $row['action']]; + elseif (isset($txt[$row['action']])) + $action_text = $txt[$row['action']]; + // Custom field? + elseif (isset($context['custom_field_titles'][$row['action']])) + $action_text = $context['custom_field_titles'][$row['action']]['title']; + else + $action_text = $row['action']; + + // Parse BBC? + $parse_bbc = isset($context['custom_field_titles'][$row['action']]) && $context['custom_field_titles'][$row['action']]['parse_bbc'] ? true : false; + + $edits[] = array( + 'id' => $row['id_action'], + 'ip' => $row['ip'], + 'id_member' => !empty($extra['applicator']) ? $extra['applicator'] : 0, + 'member_link' => $txt['trackEdit_deleted_member'], + 'action' => $row['action'], + 'action_text' => $action_text, + 'before' => !empty($extra['previous']) ? ($parse_bbc ? parse_bbc($extra['previous']) : $extra['previous']) : '', + 'after' => !empty($extra['new']) ? ($parse_bbc ? parse_bbc($extra['new']) : $extra['new']) : '', + 'time' => timeformat($row['log_time']), + ); + } + $smcFunc['db_free_result']($request); + + // Get any member names. + if (!empty($members)) + { + $request = $smcFunc['db_query']('', ' + SELECT + id_member, real_name + FROM {db_prefix}members + WHERE id_member IN ({array_int:members})', + array( + 'members' => $members, + ) + ); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[$row['id_member']] = $row['real_name']; + $smcFunc['db_free_result']($request); + + foreach ($edits as $key => $value) + if (isset($members[$value['id_member']])) + $edits[$key]['member_link'] = '' . $members[$value['id_member']] . ''; + } + + return $edits; +} + +function showPermissions($memID) +{ + global $scripturl, $txt, $board, $modSettings; + global $user_profile, $context, $user_info, $sourcedir, $smcFunc; + + // Verify if the user has sufficient permissions. + isAllowedTo('manage_permissions'); + + loadLanguage('ManagePermissions'); + loadLanguage('Admin'); + loadTemplate('ManageMembers'); + + // Load all the permission profiles. + require_once($sourcedir . '/ManagePermissions.php'); + loadPermissionProfiles(); + + $context['member']['id'] = $memID; + $context['member']['name'] = $user_profile[$memID]['real_name']; + + $context['page_title'] = $txt['showPermissions']; + $board = empty($board) ? 0 : (int) $board; + $context['board'] = $board; + + // Determine which groups this user is in. + if (empty($user_profile[$memID]['additional_groups'])) + $curGroups = array(); + else + $curGroups = explode(',', $user_profile[$memID]['additional_groups']); + $curGroups[] = $user_profile[$memID]['id_group']; + $curGroups[] = $user_profile[$memID]['id_post_group']; + + // Load a list of boards for the jump box - except the defaults. + $request = $smcFunc['db_query']('order_by_board_order', ' + SELECT b.id_board, b.name, b.id_profile, b.member_groups, IFNULL(mods.id_member, 0) AS is_mod + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member}) + WHERE {query_see_board}', + array( + 'current_member' => $memID, + ) + ); + $context['boards'] = array(); + $context['no_access_boards'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (count(array_intersect($curGroups, explode(',', $row['member_groups']))) === 0 && !$row['is_mod']) + $context['no_access_boards'][] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'is_last' => false, + ); + elseif ($row['id_profile'] != 1 || $row['is_mod']) + $context['boards'][$row['id_board']] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'selected' => $board == $row['id_board'], + 'profile' => $row['id_profile'], + 'profile_name' => $context['profiles'][$row['id_profile']]['name'], + ); + } + $smcFunc['db_free_result']($request); + + if (!empty($context['no_access_boards'])) + $context['no_access_boards'][count($context['no_access_boards']) - 1]['is_last'] = true; + + $context['member']['permissions'] = array( + 'general' => array(), + 'board' => array() + ); + + // If you're an admin we know you can do everything, we might as well leave. + $context['member']['has_all_permissions'] = in_array(1, $curGroups); + if ($context['member']['has_all_permissions']) + return; + + $denied = array(); + + // Get all general permissions. + $result = $smcFunc['db_query']('', ' + SELECT p.permission, p.add_deny, mg.group_name, p.id_group + FROM {db_prefix}permissions AS p + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = p.id_group) + WHERE p.id_group IN ({array_int:group_list}) + ORDER BY p.add_deny DESC, p.permission, mg.min_posts, CASE WHEN mg.id_group < {int:newbie_group} THEN mg.id_group ELSE 4 END, mg.group_name', + array( + 'group_list' => $curGroups, + 'newbie_group' => 4, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // We don't know about this permission, it doesn't exist :P. + if (!isset($txt['permissionname_' . $row['permission']])) + continue; + + if (empty($row['add_deny'])) + $denied[] = $row['permission']; + + // Permissions that end with _own or _any consist of two parts. + if (in_array(substr($row['permission'], -4), array('_own', '_any')) && isset($txt['permissionname_' . substr($row['permission'], 0, -4)])) + $name = $txt['permissionname_' . substr($row['permission'], 0, -4)] . ' - ' . $txt['permissionname_' . $row['permission']]; + else + $name = $txt['permissionname_' . $row['permission']]; + + // Add this permission if it doesn't exist yet. + if (!isset($context['member']['permissions']['general'][$row['permission']])) + $context['member']['permissions']['general'][$row['permission']] = array( + 'id' => $row['permission'], + 'groups' => array( + 'allowed' => array(), + 'denied' => array() + ), + 'name' => $name, + 'is_denied' => false, + 'is_global' => true, + ); + + // Add the membergroup to either the denied or the allowed groups. + $context['member']['permissions']['general'][$row['permission']]['groups'][empty($row['add_deny']) ? 'denied' : 'allowed'][] = $row['id_group'] == 0 ? $txt['membergroups_members'] : $row['group_name']; + + // Once denied is always denied. + $context['member']['permissions']['general'][$row['permission']]['is_denied'] |= empty($row['add_deny']); + } + $smcFunc['db_free_result']($result); + + $request = $smcFunc['db_query']('', ' + SELECT + bp.add_deny, bp.permission, bp.id_group, mg.group_name' . (empty($board) ? '' : ', + b.id_profile, CASE WHEN mods.id_member IS NULL THEN 0 ELSE 1 END AS is_moderator') . ' + FROM {db_prefix}board_permissions AS bp' . (empty($board) ? '' : ' + INNER JOIN {db_prefix}boards AS b ON (b.id_board = {int:current_board}) + LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})') . ' + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = bp.id_group) + WHERE bp.id_profile = {raw:current_profile} + AND bp.id_group IN ({array_int:group_list}' . (empty($board) ? ')' : ', {int:moderator_group}) + AND (mods.id_member IS NOT NULL OR bp.id_group != {int:moderator_group})'), + array( + 'current_board' => $board, + 'group_list' => $curGroups, + 'current_member' => $memID, + 'current_profile' => empty($board) ? '1' : 'b.id_profile', + 'moderator_group' => 3, + ) + ); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // We don't know about this permission, it doesn't exist :P. + if (!isset($txt['permissionname_' . $row['permission']])) + continue; + + // The name of the permission using the format 'permission name' - 'own/any topic/event/etc.'. + if (in_array(substr($row['permission'], -4), array('_own', '_any')) && isset($txt['permissionname_' . substr($row['permission'], 0, -4)])) + $name = $txt['permissionname_' . substr($row['permission'], 0, -4)] . ' - ' . $txt['permissionname_' . $row['permission']]; + else + $name = $txt['permissionname_' . $row['permission']]; + + // Create the structure for this permission. + if (!isset($context['member']['permissions']['board'][$row['permission']])) + $context['member']['permissions']['board'][$row['permission']] = array( + 'id' => $row['permission'], + 'groups' => array( + 'allowed' => array(), + 'denied' => array() + ), + 'name' => $name, + 'is_denied' => false, + 'is_global' => empty($board), + ); + + $context['member']['permissions']['board'][$row['permission']]['groups'][empty($row['add_deny']) ? 'denied' : 'allowed'][$row['id_group']] = $row['id_group'] == 0 ? $txt['membergroups_members'] : $row['group_name']; + + $context['member']['permissions']['board'][$row['permission']]['is_denied'] |= empty($row['add_deny']); + } + $smcFunc['db_free_result']($request); +} + +// View a members warnings? +function viewWarning($memID) +{ + global $modSettings, $context, $sourcedir, $txt, $scripturl; + + // Firstly, can we actually even be here? + if (!allowedTo('issue_warning') && (empty($modSettings['warning_show']) || ($modSettings['warning_show'] == 1 && !$context['user']['is_owner']))) + fatal_lang_error('no_access', false); + + // Make sure things which are disabled stay disabled. + $modSettings['warning_watch'] = !empty($modSettings['warning_watch']) ? $modSettings['warning_watch'] : 110; + $modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110; + $modSettings['warning_mute'] = !empty($modSettings['warning_mute']) ? $modSettings['warning_mute'] : 110; + + // Let's use a generic list to get all the current warnings, and use the issue warnings grab-a-granny thing. + require_once($sourcedir . '/Subs-List.php'); + require_once($sourcedir . '/Profile-Actions.php'); + + $listOptions = array( + 'id' => 'view_warnings', + 'title' => $txt['profile_viewwarning_previous_warnings'], + 'items_per_page' => $modSettings['defaultMaxMessages'], + 'no_items_label' => $txt['profile_viewwarning_no_warnings'], + 'base_href' => $scripturl . '?action=profile;area=viewwarning;sa=user;u=' . $memID, + 'default_sort_col' => 'log_time', + 'get_items' => array( + 'function' => 'list_getUserWarnings', + 'params' => array( + $memID, + ), + ), + 'get_count' => array( + 'function' => 'list_getUserWarningCount', + 'params' => array( + $memID, + ), + ), + 'columns' => array( + 'log_time' => array( + 'header' => array( + 'value' => $txt['profile_warning_previous_time'], + ), + 'data' => array( + 'db' => 'time', + ), + 'sort' => array( + 'default' => 'lc.log_time DESC', + 'reverse' => 'lc.log_time', + ), + ), + 'reason' => array( + 'header' => array( + 'value' => $txt['profile_warning_previous_reason'], + 'style' => 'width: 50%', + ), + 'data' => array( + 'db' => 'reason', + ), + ), + 'level' => array( + 'header' => array( + 'value' => $txt['profile_warning_previous_level'], + ), + 'data' => array( + 'db' => 'counter', + ), + 'sort' => array( + 'default' => 'lc.counter DESC', + 'reverse' => 'lc.counter', + ), + ), + ), + 'additional_rows' => array( + array( + 'position' => 'after_title', + 'value' => $txt['profile_viewwarning_desc'], + 'class' => 'smalltext', + 'style' => 'padding: 2ex;', + ), + ), + ); + + // Create the list for viewing. + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + // Create some common text bits for the template. + $context['level_effects'] = array( + 0 => '', + $modSettings['warning_watch'] => $txt['profile_warning_effect_own_watched'], + $modSettings['warning_moderate'] => $txt['profile_warning_effect_own_moderated'], + $modSettings['warning_mute'] => $txt['profile_warning_effect_own_muted'], + ); + $context['current_level'] = 0; + foreach ($context['level_effects'] as $limit => $dummy) + if ($context['member']['warning'] >= $limit) + $context['current_level'] = $limit; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Profile.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Profile.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,770 @@ + array( + 'title' => $txt['profileInfo'], + 'areas' => array( + 'summary' => array( + 'label' => $txt['summary'], + 'file' => 'Profile-View.php', + 'function' => 'summary', + 'permission' => array( + 'own' => 'profile_view_own', + 'any' => 'profile_view_any', + ), + ), + 'statistics' => array( + 'label' => $txt['statPanel'], + 'file' => 'Profile-View.php', + 'function' => 'statPanel', + 'permission' => array( + 'own' => 'profile_view_own', + 'any' => 'profile_view_any', + ), + ), + 'showposts' => array( + 'label' => $txt['showPosts'], + 'file' => 'Profile-View.php', + 'function' => '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')), + 'attach' => array($txt['showAttachments'], array('profile_view_own', 'profile_view_any')), + ), + 'permission' => array( + 'own' => 'profile_view_own', + 'any' => 'profile_view_any', + ), + ), + 'permissions' => array( + 'label' => $txt['showPermissions'], + 'file' => 'Profile-View.php', + 'function' => 'showPermissions', + 'permission' => array( + 'own' => 'manage_permissions', + 'any' => 'manage_permissions', + ), + ), + 'tracking' => array( + 'label' => $txt['trackUser'], + 'file' => 'Profile-View.php', + 'function' => 'tracking', + 'subsections' => array( + 'activity' => array($txt['trackActivity'], 'moderate_forum'), + 'ip' => array($txt['trackIP'], 'moderate_forum'), + 'edits' => array($txt['trackEdits'], 'moderate_forum'), + ), + 'permission' => array( + 'own' => 'moderate_forum', + 'any' => 'moderate_forum', + ), + ), + 'viewwarning' => array( + 'label' => $txt['profile_view_warnings'], + 'enabled' => in_array('w', $context['admin_features']) && $modSettings['warning_settings'][0] == 1 && $cur_profile['warning'] && $context['user']['is_owner'] && !empty($modSettings['warning_show']), + 'file' => 'Profile-View.php', + 'function' => 'viewWarning', + 'permission' => array( + 'own' => 'profile_view_own', + 'any' => 'issue_warning', + ), + ), + ), + ), + 'edit_profile' => array( + 'title' => $txt['profileEdit'], + 'areas' => array( + 'account' => array( + 'label' => $txt['account'], + 'file' => 'Profile-Modify.php', + 'function' => 'account', + 'enabled' => $context['user']['is_admin'] || ($cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups']))), + 'sc' => 'post', + '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' => 'Profile-Modify.php', + 'function' => 'forumProfile', + 'sc' => 'post', + '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' => 'Profile-Modify.php', + 'function' => 'theme', + 'sc' => 'post', + 'permission' => array( + 'own' => array('profile_extra_any', 'profile_extra_own'), + 'any' => array('profile_extra_any'), + ), + ), + 'authentication' => array( + 'label' => $txt['authentication'], + 'file' => 'Profile-Modify.php', + 'function' => 'authentication', + 'enabled' => !empty($modSettings['enableOpenID']) || !empty($cur_profile['openid_uri']), + 'sc' => 'post', + '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['notification'], + 'file' => 'Profile-Modify.php', + 'function' => 'notification', + 'sc' => 'post', + 'permission' => array( + 'own' => array('profile_extra_any', 'profile_extra_own'), + 'any' => array('profile_extra_any'), + ), + ), + // Without profile_extra_own, settings are accessible from the PM section. + 'pmprefs' => array( + 'label' => $txt['pmprefs'], + 'file' => 'Profile-Modify.php', + 'function' => 'pmprefs', + 'enabled' => allowedTo(array('profile_extra_own', 'profile_extra_any')), + 'sc' => 'post', + 'permission' => array( + 'own' => array('pm_read'), + 'any' => array('profile_extra_any'), + ), + ), + 'ignoreboards' => array( + 'label' => $txt['ignoreboards'], + 'file' => 'Profile-Modify.php', + 'function' => 'ignoreboards', + 'enabled' => !empty($modSettings['allow_ignore_boards']), + 'sc' => 'post', + 'permission' => array( + 'own' => array('profile_extra_any', 'profile_extra_own'), + 'any' => array('profile_extra_any'), + ), + ), + 'lists' => array( + 'label' => $txt['editBuddyIgnoreLists'], + 'file' => 'Profile-Modify.php', + 'function' => 'editBuddyIgnoreLists', + 'enabled' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'], + 'sc' => 'post', + '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' => 'Profile-Modify.php', + 'function' => 'groupMembership', + 'enabled' => !empty($modSettings['show_group_membership']) && $context['user']['is_owner'], + 'sc' => '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']) && $modSettings['warning_settings'][0] == 1 && (!$context['user']['is_owner'] || $context['user']['is_admin']), + 'file' => 'Profile-Actions.php', + 'function' => 'issueWarning', + 'permission' => array( + 'own' => array('issue_warning'), + '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' => 'Profile-Actions.php', + 'function' => 'subscriptions', + 'enabled' => !empty($modSettings['paid_enabled']), + 'permission' => array( + 'own' => array('profile_view_own'), + 'any' => array('moderate_forum'), + ), + ), + 'deleteaccount' => array( + 'label' => $txt['deleteAccount'], + 'file' => 'Profile-Actions.php', + 'function' => 'deleteAccount', + 'sc' => 'post', + 'password' => true, + 'permission' => array( + 'own' => array('profile_remove_any', 'profile_remove_own'), + 'any' => array('profile_remove_any'), + ), + ), + 'activateaccount' => array( + 'file' => 'Profile-Actions.php', + 'function' => 'activateAccount', + 'sc' => 'get', + 'select' => 'summary', + 'permission' => array( + 'own' => array(), + 'any' => array('moderate_forum'), + ), + ), + ), + ), + ); + + // Let them modify profile areas easily. + call_integration_hook('integrate_profile_areas', array(&$profile_areas)); + + // Do some cleaning ready for the menu function. + $context['password_areas'] = array(); + $current_area = isset($_REQUEST['area']) ? $_REQUEST['area'] : ''; + + foreach ($profile_areas as $section_id => $section) + { + // Do a bit of spring cleaning so to speak. + foreach ($section['areas'] as $area_id => $area) + { + // If it said no permissions that meant it wasn't valid! + if (empty($area['permission'][$context['user']['is_owner'] ? 'own' : 'any'])) + $profile_areas[$section_id]['areas'][$area_id]['enabled'] = false; + // Otherwise pick the right set. + else + $profile_areas[$section_id]['areas'][$area_id]['permission'] = $area['permission'][$context['user']['is_owner'] ? 'own' : 'any']; + + // Password required - only if not on OpenID. + if (!empty($area['password'])) + $context['password_areas'][] = $area_id; + } + } + + // 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, + 'current_area' => $current_area, + 'extra_url_parameters' => array( + 'u' => $context['id_member'], + ), + ); + + // Actually create the menu! + $profile_include_data = createMenu($profile_areas, $menuOptions); + + // No menu means no access. + if (!$profile_include_data && (!$user_info['is_guest'] || validateSession())) + 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 every compromise the menu function in error! + $context['completed_save'] = false; + $security_checks = array(); + $found_area = false; + foreach ($profile_areas as $section_id => $section) + { + // Do a bit of spring cleaning so to speak. + foreach ($section['areas'] as $area_id => $area) + { + // Is this our area? + if ($current_area == $area_id) + { + // This can't happen - but is a security check. + if ((isset($section['enabled']) && $section['enabled'] == false) || (isset($area['enabled']) && $area['enabled'] == false)) + fatal_lang_error('no_access', false); + + // Are we saving data in a valid area? + if (isset($area['sc']) && isset($_REQUEST['save'])) + { + $security_checks['session'] = $area['sc']; + $context['completed_save'] = true; + } + + // Does this require session validating? + if (!empty($area['validate'])) + $security_checks['validate'] = true; + + // Permissions for good measure. + if (!empty($profile_include_data['permission'])) + $security_checks['permission'] = $profile_include_data['permission']; + + // Either way got something. + $found_area = true; + } + } + } + + // Oh dear, some serious security lapse is going on here... we'll put a stop to that! + if (!$found_area) + fatal_lang_error('no_access', false); + + // Release this now. + unset($profile_areas); + + // Now the context is setup have we got any security checks to carry out additional to that above? + if (isset($security_checks['session'])) + checkSession($security_checks['session']); + if (isset($security_checks['validate'])) + validateSession(); + if (isset($security_checks['permission'])) + isAllowedTo($security_checks['permission']); + + // File to include? + if (isset($profile_include_data['file'])) + require_once($sourcedir . '/' . $profile_include_data['file']); + + // Make sure that the area function does exist! + if (!isset($profile_include_data['function']) || !function_exists($profile_include_data['function'])) + { + destroyMenu(); + fatal_lang_error('no_access', false); + } + + // 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 and add the profile layer. + $context['sub_template'] = $profile_include_data['function']; + $context['template_layers'][] = 'profile'; + + // All the subactions that require a user password in order to validate. + $check_password = $context['user']['is_owner'] && in_array($profile_include_data['current_area'], $context['password_areas']); + $context['require_password'] = $check_password && empty($user_settings['openid_uri']); + + // If we're in wireless then we have a cut down template... + if (WIRELESS && $context['sub_template'] == 'summary' && WIRELESS_PROTOCOL != 'wap') + $context['sub_template'] = WIRELESS_PROTOCOL . '_profile'; + + // 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 ($context['completed_save']) + { + // If it's someone elses profile then validate the session. + if (!$context['user']['is_owner']) + validateSession(); + + // 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($sourcedir . '/Subs-OpenID.php'); + smf_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); + + // Bad password!!! + if (!$good_password && $user_info['passwd'] != sha1(strtolower($cur_profile['member_name']) . $_POST['oldpasswrd'])) + $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)) + activateAccount($memID); + } + elseif ($current_area == 'deleteaccount') + { + if (empty($post_errors)) + { + deleteAccount2($profile_vars, $post_errors, $memID); + redirectexit(); + } + } + elseif ($current_area == 'groupmembership' && empty($post_errors)) + { + $msg = groupMembership2($profile_vars, $post_errors, $memID); + + // 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 : '')); + } + // Authentication changes? + elseif ($current_area == 'authentication') + { + authentication($memID, true); + } + elseif (in_array($current_area, array('account', 'forumprofile', 'theme', 'pmprefs'))) + saveProfileFields(); + else + { + $force_redirect = true; + // Ensure we include this. + require_once($sourcedir . '/Profile-Modify.php'); + saveProfileChanges($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, + 'id_log' => 2, + 'log_time' => time(), + 'id_member' => $memID, + 'ip' => $user_info['ip'], + 'extra' => serialize(array_merge($v, array('applicator' => $user_info['id']))), + ); + $smcFunc['db_insert']('', + '{db_prefix}log_actions', + array( + 'action' => 'string', 'id_log' => 'int', 'log_time' => 'int', 'id_member' => 'int', 'ip' => 'string-16', + 'extra' => 'string-65534', + ), + $log_changes, + array('id_action') + ); + } + + // 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; + } + // If it's you then we should redirect upon save. + elseif (!empty($profile_vars) && $context['user']['is_owner']) + redirectexit('action=profile;area=' . $current_area . ';updated'); + elseif (!empty($force_redirect)) + redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=' . $current_area); + + // Call the appropriate subaction function. + $profile_include_data['function']($memID); + + // 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] : ''); +} + +// Load any custom fields for this area... no area means load all, 'summary' loads all public ones. +function loadCustomFields($memID, $area = 'summary') +{ + global $context, $txt, $user_profile, $smcFunc, $user_info, $settings, $scripturl; + + // Get the right restrictions in place... + $where = 'active = 1'; + if (!allowedTo('admin_forum') && $area != 'register') + { + // If it's the owner they can see two types of private fields, regardless. + if ($memID == $user_info['id']) + $where .= $area == 'summary' ? ' AND private < 3' : ' AND (private = 0 OR private = 2)'; + else + $where .= $area == 'summary' ? ' AND private < 2' : ' AND private = 0'; + } + + if ($area == 'register') + $where .= ' AND show_reg != 0'; + elseif ($area != 'summary') + $where .= ' AND show_profile = {string:area}'; + + // Load all the relevant fields - and data. + $request = $smcFunc['db_query']('', ' + SELECT + col_name, field_name, field_desc, field_type, field_length, field_options, + default_value, bbc, enclose, placement + FROM {db_prefix}custom_fields + WHERE ' . $where, + array( + 'area' => $area, + ) + ); + $context['custom_fields'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Shortcut. + $exists = $memID && isset($user_profile[$memID], $user_profile[$memID]['options'][$row['col_name']]); + $value = $exists ? $user_profile[$memID]['options'][$row['col_name']] : ''; + + // If this was submitted already then make the value the posted version. + if (isset($_POST['customfield']) && isset($_POST['customfield'][$row['col_name']])) + { + $value = $smcFunc['htmlspecialchars']($_POST['customfield'][$row['col_name']]); + if (in_array($row['field_type'], array('select', 'radio'))) + $value = ($options = explode(',', $row['field_options'])) && isset($options[$value]) ? $options[$value] : ''; + } + + // HTML for the input form. + $output_html = $value; + if ($row['field_type'] == 'check') + { + $true = (!$exists && $row['default_value']) || $value; + $input_html = ''; + $output_html = $true ? $txt['yes'] : $txt['no']; + } + elseif ($row['field_type'] == 'select') + { + $input_html = ''; + } + elseif ($row['field_type'] == 'radio') + { + $input_html = '
'; + $options = explode(',', $row['field_options']); + foreach ($options as $k => $v) + { + $true = (!$exists && $row['default_value'] == $v) || $value == $v; + $input_html .= '
'; + if ($true) + $output_html = $v; + } + $input_html .= '
'; + } + elseif ($row['field_type'] == 'text') + { + $input_html = ''; + } + else + { + @list ($rows, $cols) = @explode(',', $row['default_value']); + $input_html = ''; + } + + // Parse BBCode + if ($row['bbc']) + $output_html = parse_bbc($output_html); + elseif($row['field_type'] == 'textarea') + // Allow for newlines at least + $output_html = strtr($output_html, array("\n" => '
')); + + // Enclosing the user input within some other text? + if (!empty($row['enclose']) && !empty($output_html)) + $output_html = strtr($row['enclose'], array( + '{SCRIPTURL}' => $scripturl, + '{IMAGES_URL}' => $settings['images_url'], + '{DEFAULT_IMAGES_URL}' => $settings['default_images_url'], + '{INPUT}' => $output_html, + )); + + $context['custom_fields'][] = array( + 'name' => $row['field_name'], + 'desc' => $row['field_desc'], + 'type' => $row['field_type'], + 'input_html' => $input_html, + 'output_html' => $output_html, + 'placement' => $row['placement'], + 'colname' => $row['col_name'], + 'value' => $value, + ); + } + $smcFunc['db_free_result']($request); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/QueryString.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/QueryString.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,483 @@ + $value) + if (is_numeric($key)) + unset($_COOKIE[$key]); + + // Get the correct query string. It may be in an environment variable... + if (!isset($_SERVER['QUERY_STRING'])) + $_SERVER['QUERY_STRING'] = getenv('QUERY_STRING'); + + // It seems that sticking a URL after the query string is mighty common, well, it's evil - don't. + if (strpos($_SERVER['QUERY_STRING'], 'http') === 0) + { + header('HTTP/1.1 400 Bad Request'); + die; + } + + // Are we going to need to parse the ; out? + if ((strpos(@ini_get('arg_separator.input'), ';') === false || @version_compare(PHP_VERSION, '4.2.0') == -1) && !empty($_SERVER['QUERY_STRING'])) + { + // Get rid of the old one! You don't know where it's been! + $_GET = array(); + + // Was this redirected? If so, get the REDIRECT_QUERY_STRING. + // Do not urldecode() the querystring, unless you so much wish to break OpenID implementation. :) + $_SERVER['QUERY_STRING'] = substr($_SERVER['QUERY_STRING'], 0, 5) === 'url=/' ? $_SERVER['REDIRECT_QUERY_STRING'] : $_SERVER['QUERY_STRING']; + + // Replace ';' with '&' and '&something&' with '&something=&'. (this is done for compatibility...) + // !!! smflib + parse_str(preg_replace('/&(\w+)(?=&|$)/', '&$1=', strtr($_SERVER['QUERY_STRING'], array(';?' => '&', ';' => '&', '%00' => '', "\0" => ''))), $_GET); + + // Magic quotes still applies with parse_str - so clean it up. + if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes'])) + $_GET = $removeMagicQuoteFunction($_GET); + } + elseif (strpos(@ini_get('arg_separator.input'), ';') !== false) + { + if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes'])) + $_GET = $removeMagicQuoteFunction($_GET); + + // Search engines will send action=profile%3Bu=1, which confuses PHP. + foreach ($_GET as $k => $v) + { + if (is_string($v) && strpos($k, ';') !== false) + { + $temp = explode(';', $v); + $_GET[$k] = $temp[0]; + + for ($i = 1, $n = count($temp); $i < $n; $i++) + { + @list ($key, $val) = @explode('=', $temp[$i], 2); + if (!isset($_GET[$key])) + $_GET[$key] = $val; + } + } + + // This helps a lot with integration! + if (strpos($k, '?') === 0) + { + $_GET[substr($k, 1)] = $v; + unset($_GET[$k]); + } + } + } + + // There's no query string, but there is a URL... try to get the data from there. + if (!empty($_SERVER['REQUEST_URI'])) + { + // Remove the .html, assuming there is one. + if (substr($_SERVER['REQUEST_URI'], strrpos($_SERVER['REQUEST_URI'], '.'), 4) == '.htm') + $request = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '.')); + else + $request = $_SERVER['REQUEST_URI']; + + // !!! smflib. + // Replace 'index.php/a,b,c/d/e,f' with 'a=b,c&d=&e=f' and parse it into $_GET. + if (strpos($request, basename($scripturl) . '/') !== false) + { + parse_str(substr(preg_replace('/&(\w+)(?=&|$)/', '&$1=', strtr(preg_replace('~/([^,/]+),~', '/$1=', substr($request, strpos($request, basename($scripturl)) + strlen(basename($scripturl)))), '/', '&')), 1), $temp); + if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes'])) + $temp = $removeMagicQuoteFunction($temp); + $_GET += $temp; + } + } + + // If magic quotes is on we have some work... + if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0) + { + $_ENV = $removeMagicQuoteFunction($_ENV); + $_POST = $removeMagicQuoteFunction($_POST); + $_COOKIE = $removeMagicQuoteFunction($_COOKIE); + foreach ($_FILES as $k => $dummy) + if (isset($_FILES[$k]['name'])) + $_FILES[$k]['name'] = $removeMagicQuoteFunction($_FILES[$k]['name']); + } + + // Add entities to GET. This is kinda like the slashes on everything else. + $_GET = htmlspecialchars__recursive($_GET); + + // Let's not depend on the ini settings... why even have COOKIE in there, anyway? + $_REQUEST = $_POST + $_GET; + + // Make sure $board and $topic are numbers. + if (isset($_REQUEST['board'])) + { + // Make sure its a string and not something else like an array + $_REQUEST['board'] = (string) $_REQUEST['board']; + + // If there's a slash in it, we've got a start value! (old, compatible links.) + if (strpos($_REQUEST['board'], '/') !== false) + list ($_REQUEST['board'], $_REQUEST['start']) = explode('/', $_REQUEST['board']); + // Same idea, but dots. This is the currently used format - ?board=1.0... + elseif (strpos($_REQUEST['board'], '.') !== false) + list ($_REQUEST['board'], $_REQUEST['start']) = explode('.', $_REQUEST['board']); + // Now make absolutely sure it's a number. + $board = (int) $_REQUEST['board']; + $_REQUEST['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; + + // This is for "Who's Online" because it might come via POST - and it should be an int here. + $_GET['board'] = $board; + } + // Well, $board is going to be a number no matter what. + else + $board = 0; + + // If there's a threadid, it's probably an old YaBB SE link. Flow with it. + if (isset($_REQUEST['threadid']) && !isset($_REQUEST['topic'])) + $_REQUEST['topic'] = $_REQUEST['threadid']; + + // We've got topic! + if (isset($_REQUEST['topic'])) + { + // Make sure its a string and not something else like an array + $_REQUEST['topic'] = (string) $_REQUEST['topic']; + + // Slash means old, beta style, formatting. That's okay though, the link should still work. + if (strpos($_REQUEST['topic'], '/') !== false) + list ($_REQUEST['topic'], $_REQUEST['start']) = explode('/', $_REQUEST['topic']); + // Dots are useful and fun ;). This is ?topic=1.15. + elseif (strpos($_REQUEST['topic'], '.') !== false) + list ($_REQUEST['topic'], $_REQUEST['start']) = explode('.', $_REQUEST['topic']); + + $topic = (int) $_REQUEST['topic']; + + // Now make sure the online log gets the right number. + $_GET['topic'] = $topic; + } + else + $topic = 0; + + // There should be a $_REQUEST['start'], some at least. If you need to default to other than 0, use $_GET['start']. + if (empty($_REQUEST['start']) || $_REQUEST['start'] < 0 || (int) $_REQUEST['start'] > 2147473647) + $_REQUEST['start'] = 0; + + // The action needs to be a string and not an array or anything else + if (isset($_REQUEST['action'])) + $_REQUEST['action'] = (string) $_REQUEST['action']; + if (isset($_GET['action'])) + $_GET['action'] = (string) $_GET['action']; + + // Make sure we have a valid REMOTE_ADDR. + if (!isset($_SERVER['REMOTE_ADDR'])) + { + $_SERVER['REMOTE_ADDR'] = ''; + // A new magic variable to indicate we think this is command line. + $_SERVER['is_cli'] = true; + } + elseif (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER['REMOTE_ADDR']) === 0) + $_SERVER['REMOTE_ADDR'] = 'unknown'; + + // Try to calculate their most likely IP for those people behind proxies (And the like). + $_SERVER['BAN_CHECK_IP'] = $_SERVER['REMOTE_ADDR']; + + // Find the user's IP address. (but don't let it give you 'unknown'!) + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)) + { + // We have both forwarded for AND client IP... check the first forwarded for as the block - only switch if it's better that way. + if (strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.') && '.' . strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') == strrchr($_SERVER['HTTP_CLIENT_IP'], '.') && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)) + $_SERVER['BAN_CHECK_IP'] = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP']))); + else + $_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_CLIENT_IP']; + } + if (!empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)) + { + // Since they are in different blocks, it's probably reversed. + if (strtok($_SERVER['REMOTE_ADDR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.')) + $_SERVER['BAN_CHECK_IP'] = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP']))); + else + $_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_CLIENT_IP']; + } + elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) + { + // If there are commas, get the last one.. probably. + if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) + { + $ips = array_reverse(explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR'])); + + // Go through each IP... + foreach ($ips as $i => $ip) + { + // Make sure it's in a valid range... + if (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $ip) != 0 && preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) == 0) + continue; + + // Otherwise, we've got an IP! + $_SERVER['BAN_CHECK_IP'] = trim($ip); + break; + } + } + // Otherwise just use the only one. + elseif (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0) + $_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_X_FORWARDED_FOR']; + } + + // Make sure we know the URL of the current request. + if (empty($_SERVER['REQUEST_URI'])) + $_SERVER['REQUEST_URL'] = $scripturl . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''); + elseif (preg_match('~^([^/]+//[^/]+)~', $scripturl, $match) == 1) + $_SERVER['REQUEST_URL'] = $match[1] . $_SERVER['REQUEST_URI']; + else + $_SERVER['REQUEST_URL'] = $_SERVER['REQUEST_URI']; + + // And make sure HTTP_USER_AGENT is set. + $_SERVER['HTTP_USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? htmlspecialchars($smcFunc['db_unescape_string']($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES) : ''; + + // Some final checking. + if (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER['BAN_CHECK_IP']) === 0) + $_SERVER['BAN_CHECK_IP'] = ''; + if ($_SERVER['REMOTE_ADDR'] == 'unknown') + $_SERVER['REMOTE_ADDR'] = ''; +} + +// Adds slashes to the array/variable. Uses two underscores to guard against overloading. +function escapestring__recursive($var) +{ + global $smcFunc; + + if (!is_array($var)) + return $smcFunc['db_escape_string']($var); + + // Reindex the array with slashes. + $new_var = array(); + + // Add slashes to every element, even the indexes! + foreach ($var as $k => $v) + $new_var[$smcFunc['db_escape_string']($k)] = escapestring__recursive($v); + + return $new_var; +} + +// Adds html entities to the array/variable. Uses two underscores to guard against overloading. +function htmlspecialchars__recursive($var, $level = 0) +{ + global $smcFunc; + + if (!is_array($var)) + return isset($smcFunc['htmlspecialchars']) ? $smcFunc['htmlspecialchars']($var, ENT_QUOTES) : htmlspecialchars($var, ENT_QUOTES); + + // Add the htmlspecialchars to every element. + foreach ($var as $k => $v) + $var[$k] = $level > 25 ? null : htmlspecialchars__recursive($v, $level + 1); + + return $var; +} + +// Removes url stuff from the array/variable. Uses two underscores to guard against overloading. +function urldecode__recursive($var, $level = 0) +{ + if (!is_array($var)) + return urldecode($var); + + // Reindex the array... + $new_var = array(); + + // Add the htmlspecialchars to every element. + foreach ($var as $k => $v) + $new_var[urldecode($k)] = $level > 25 ? null : urldecode__recursive($v, $level + 1); + + return $new_var; +} +// Unescapes any array or variable. Two underscores for the normal reason. +function unescapestring__recursive($var) +{ + global $smcFunc; + + if (!is_array($var)) + return $smcFunc['db_unescape_string']($var); + + // Reindex the array without slashes, this time. + $new_var = array(); + + // Strip the slashes from every element. + foreach ($var as $k => $v) + $new_var[$smcFunc['db_unescape_string']($k)] = unescapestring__recursive($v); + + return $new_var; +} + +// Remove slashes recursively... +function stripslashes__recursive($var, $level = 0) +{ + if (!is_array($var)) + return stripslashes($var); + + // Reindex the array without slashes, this time. + $new_var = array(); + + // Strip the slashes from every element. + foreach ($var as $k => $v) + $new_var[stripslashes($k)] = $level > 25 ? null : stripslashes__recursive($v, $level + 1); + + return $new_var; +} + +// Trim a string including the HTML space, character 160. +function htmltrim__recursive($var, $level = 0) +{ + global $smcFunc; + + // Remove spaces (32), tabs (9), returns (13, 10, and 11), nulls (0), and hard spaces. (160) + if (!is_array($var)) + return isset($smcFunc) ? $smcFunc['htmltrim']($var) : trim($var, ' ' . "\t\n\r\x0B" . '\0' . "\xA0"); + + // Go through all the elements and remove the whitespace. + foreach ($var as $k => $v) + $var[$k] = $level > 25 ? null : htmltrim__recursive($v, $level + 1); + + return $var; +} + +// Clean up the XML to make sure it doesn't contain invalid characters. +function cleanXml($string) +{ + global $context; + + // http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char + return preg_replace('~[\x00-\x08\x0B\x0C\x0E-\x19' . ($context['utf8'] ? (@version_compare(PHP_VERSION, '4.3.3') != -1 ? '\x{FFFE}\x{FFFF}' : "\xED\xA0\x80-\xED\xBF\xBF\xEF\xBF\xBE\xEF\xBF\xBF") : '') . ']~' . ($context['utf8'] ? 'u' : ''), '', $string); +} + +function JavaScriptEscape($string) +{ + global $scripturl; + + return '\'' . strtr($string, array( + "\r" => '', + "\n" => '\\n', + "\t" => '\\t', + '\\' => '\\\\', + '\'' => '\\\'', + ' '<\' + \'/', + 'script' => 'scri\'+\'pt', + ' ' $scripturl . '\'+\'', + )) . '\''; +} + +// Rewrite URLs to include the session ID. +function ob_sessrewrite($buffer) +{ + global $scripturl, $modSettings, $user_info, $context; + + // If $scripturl is set to nothing, or the SID is not defined (SSI?) just quit. + if ($scripturl == '' || !defined('SID')) + return $buffer; + + // Do nothing if the session is cookied, or they are a crawler - guests are caught by redirectexit(). This doesn't work below PHP 4.3.0, because it makes the output buffer bigger. + // !!! smflib + if (empty($_COOKIE) && SID != '' && empty($context['browser']['possibly_robot']) && @version_compare(PHP_VERSION, '4.3.0') != -1) + $buffer = preg_replace('/"' . preg_quote($scripturl, '/') . '(?!\?' . preg_quote(SID, '/') . ')\\??/', '"' . $scripturl . '?' . SID . '&', $buffer); + // Debugging templates, are we? + elseif (isset($_GET['debug'])) + $buffer = preg_replace('/(? \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Recent.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Recent.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1323 @@ + 0 ? ' + AND b.id_board != {int:recycle_board}' : '') . ' + AND ml.approved = {int:is_approved} + ORDER BY b.id_msg_updated DESC + LIMIT 1', + array( + 'recycle_board' => $modSettings['recycle_board'], + 'is_approved' => 1, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + return array(); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Censor the subject and post... + censorText($row['subject']); + censorText($row['body']); + + $row['body'] = strip_tags(strtr(parse_bbc($row['body'], $row['smileys_enabled']), array('
' => ' '))); + if ($smcFunc['strlen']($row['body']) > 128) + $row['body'] = $smcFunc['substr']($row['body'], 0, 128) . '...'; + + // Send the data. + return array( + 'topic' => $row['id_topic'], + 'subject' => $row['subject'], + 'short_subject' => shorten_subject($row['subject'], 24), + 'preview' => $row['body'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new', + 'link' => '
' . $row['subject'] . '' + ); +} + +// Find the ten most recent posts. +function RecentPosts() +{ + global $txt, $scripturl, $user_info, $context, $modSettings, $sourcedir, $board, $smcFunc; + + loadTemplate('Recent'); + $context['page_title'] = $txt['recent_posts']; + + if (isset($_REQUEST['start']) && $_REQUEST['start'] > 95) + $_REQUEST['start'] = 95; + + $query_parameters = array(); + if (!empty($_REQUEST['c']) && empty($board)) + { + $_REQUEST['c'] = explode(',', $_REQUEST['c']); + foreach ($_REQUEST['c'] as $i => $c) + $_REQUEST['c'][$i] = (int) $c; + + if (count($_REQUEST['c']) == 1) + { + $request = $smcFunc['db_query']('', ' + SELECT name + FROM {db_prefix}categories + WHERE id_cat = {int:id_cat} + LIMIT 1', + array( + 'id_cat' => $_REQUEST['c'][0], + ) + ); + list ($name) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (empty($name)) + fatal_lang_error('no_access', false); + + $context['linktree'][] = array( + 'url' => $scripturl . '#c' . (int) $_REQUEST['c'], + 'name' => $name + ); + } + + $request = $smcFunc['db_query']('', ' + SELECT b.id_board, b.num_posts + FROM {db_prefix}boards AS b + WHERE b.id_cat IN ({array_int:category_list}) + AND {query_see_board}', + array( + 'category_list' => $_REQUEST['c'], + ) + ); + $total_cat_posts = 0; + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $boards[] = $row['id_board']; + $total_cat_posts += $row['num_posts']; + } + $smcFunc['db_free_result']($request); + + if (empty($boards)) + fatal_lang_error('error_no_boards_selected'); + + $query_this_board = 'b.id_board IN ({array_int:boards})'; + $query_parameters['boards'] = $boards; + + // If this category has a significant number of posts in it... + if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15) + { + $query_this_board .= ' + AND m.id_msg >= {int:max_id_msg}'; + $query_parameters['max_id_msg'] = max(0, $modSettings['maxMsgID'] - 400 - $_REQUEST['start'] * 7); + } + + $context['page_index'] = constructPageIndex($scripturl . '?action=recent;c=' . implode(',', $_REQUEST['c']), $_REQUEST['start'], min(100, $total_cat_posts), 10, false); + } + elseif (!empty($_REQUEST['boards'])) + { + $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); + foreach ($_REQUEST['boards'] as $i => $b) + $_REQUEST['boards'][$i] = (int) $b; + + $request = $smcFunc['db_query']('', ' + SELECT b.id_board, b.num_posts + FROM {db_prefix}boards AS b + WHERE b.id_board IN ({array_int:board_list}) + AND {query_see_board} + LIMIT {int:limit}', + array( + 'board_list' => $_REQUEST['boards'], + 'limit' => count($_REQUEST['boards']), + ) + ); + $total_posts = 0; + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $boards[] = $row['id_board']; + $total_posts += $row['num_posts']; + } + $smcFunc['db_free_result']($request); + + if (empty($boards)) + fatal_lang_error('error_no_boards_selected'); + + $query_this_board = 'b.id_board IN ({array_int:boards})'; + $query_parameters['boards'] = $boards; + + // If these boards have a significant number of posts in them... + if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12) + { + $query_this_board .= ' + AND m.id_msg >= {int:max_id_msg}'; + $query_parameters['max_id_msg'] = max(0, $modSettings['maxMsgID'] - 500 - $_REQUEST['start'] * 9); + } + + $context['page_index'] = constructPageIndex($scripturl . '?action=recent;boards=' . implode(',', $_REQUEST['boards']), $_REQUEST['start'], min(100, $total_posts), 10, false); + } + elseif (!empty($board)) + { + $request = $smcFunc['db_query']('', ' + SELECT num_posts + FROM {db_prefix}boards + WHERE id_board = {int:current_board} + LIMIT 1', + array( + 'current_board' => $board, + ) + ); + list ($total_posts) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $query_this_board = 'b.id_board = {int:board}'; + $query_parameters['board'] = $board; + + // If this board has a significant number of posts in it... + if ($total_posts > 80 && $total_posts > $modSettings['totalMessages'] / 10) + { + $query_this_board .= ' + AND m.id_msg >= {int:max_id_msg}'; + $query_parameters['max_id_msg'] = max(0, $modSettings['maxMsgID'] - 600 - $_REQUEST['start'] * 10); + } + + $context['page_index'] = constructPageIndex($scripturl . '?action=recent;board=' . $board . '.%1$d', $_REQUEST['start'], min(100, $total_posts), 10, true); + } + else + { + $query_this_board = '{query_wanna_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_board}' : ''). ' + AND m.id_msg >= {int:max_id_msg}'; + $query_parameters['max_id_msg'] = max(0, $modSettings['maxMsgID'] - 100 - $_REQUEST['start'] * 6); + $query_parameters['recycle_board'] = $modSettings['recycle_board']; + + // !!! This isn't accurate because we ignore the recycle bin. + $context['page_index'] = constructPageIndex($scripturl . '?action=recent', $_REQUEST['start'], min(100, $modSettings['totalMessages']), 10, false); + } + + $context['linktree'][] = array( + 'url' => $scripturl . '?action=recent' . (empty($board) ? (empty($_REQUEST['c']) ? '' : ';c=' . (int) $_REQUEST['c']) : ';board=' . $board . '.0'), + 'name' => $context['page_title'] + ); + + $key = 'recent-' . $user_info['id'] . '-' . md5(serialize(array_diff_key($query_parameters, array('max_id_msg' => 0)))) . '-' . (int) $_REQUEST['start']; + if (empty($modSettings['cache_enable']) || ($messages = cache_get_data($key, 120)) == null) + { + $done = false; + while (!$done) + { + // Find the 10 most recent messages they can *view*. + // !!!SLOW This query is really slow still, probably? + $request = $smcFunc['db_query']('', ' + SELECT m.id_msg + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE ' . $query_this_board . ' + AND m.approved = {int:is_approved} + ORDER BY m.id_msg DESC + LIMIT {int:offset}, {int:limit}', + array_merge($query_parameters, array( + 'is_approved' => 1, + 'offset' => $_REQUEST['start'], + 'limit' => 10, + )) + ); + // If we don't have 10 results, try again with an unoptimized version covering all rows, and cache the result. + if (isset($query_parameters['max_id_msg']) && $smcFunc['db_num_rows']($request) < 10) + { + $smcFunc['db_free_result']($request); + $query_this_board = str_replace('AND m.id_msg >= {int:max_id_msg}', '', $query_this_board); + $cache_results = true; + unset($query_parameters['max_id_msg']); + } + else + $done = true; + } + $messages = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $messages[] = $row['id_msg']; + $smcFunc['db_free_result']($request); + if (!empty($cache_results)) + cache_put_data($key, $messages, 120); + } + + // Nothing here... Or at least, nothing you can see... + if (empty($messages)) + { + $context['posts'] = array(); + return; + } + + // Get all the most recent posts. + $request = $smcFunc['db_query']('', ' + SELECT + m.id_msg, m.subject, m.smileys_enabled, m.poster_time, m.body, m.id_topic, t.id_board, b.id_cat, + b.name AS bname, c.name AS cname, t.num_replies, m.id_member, m2.id_member AS id_first_member, + IFNULL(mem2.real_name, m2.poster_name) AS first_poster_name, t.id_first_msg, + IFNULL(mem.real_name, m.poster_name) AS poster_name, t.id_last_msg + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + INNER JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + INNER JOIN {db_prefix}messages AS m2 ON (m2.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member) + WHERE m.id_msg IN ({array_int:message_list}) + ORDER BY m.id_msg DESC + LIMIT ' . count($messages), + array( + 'message_list' => $messages, + ) + ); + $counter = $_REQUEST['start'] + 1; + $context['posts'] = array(); + $board_ids = array('own' => array(), 'any' => array()); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Censor everything. + censorText($row['body']); + censorText($row['subject']); + + // BBC-atize the message. + $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); + + // And build the array. + $context['posts'][$row['id_msg']] = array( + 'id' => $row['id_msg'], + 'counter' => $counter++, + 'alternate' => $counter % 2, + 'category' => array( + 'id' => $row['id_cat'], + 'name' => $row['cname'], + 'href' => $scripturl . '#c' . $row['id_cat'], + 'link' => '' . $row['cname'] . '' + ), + 'board' => array( + 'id' => $row['id_board'], + 'name' => $row['bname'], + 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'link' => '' . $row['bname'] . '' + ), + 'topic' => $row['id_topic'], + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + 'link' => '' . $row['subject'] . '', + 'start' => $row['num_replies'], + 'subject' => $row['subject'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'first_poster' => array( + 'id' => $row['id_first_member'], + 'name' => $row['first_poster_name'], + 'href' => empty($row['id_first_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_first_member'], + 'link' => empty($row['id_first_member']) ? $row['first_poster_name'] : '' . $row['first_poster_name'] . '' + ), + 'poster' => array( + 'id' => $row['id_member'], + 'name' => $row['poster_name'], + 'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => empty($row['id_member']) ? $row['poster_name'] : '' . $row['poster_name'] . '' + ), + 'message' => $row['body'], + 'can_reply' => false, + 'can_mark_notify' => false, + 'can_delete' => false, + 'delete_possible' => ($row['id_first_msg'] != $row['id_msg'] || $row['id_last_msg'] == $row['id_msg']) && (empty($modSettings['edit_disable_time']) || $row['poster_time'] + $modSettings['edit_disable_time'] * 60 >= time()), + ); + + if ($user_info['id'] == $row['id_first_member']) + $board_ids['own'][$row['id_board']][] = $row['id_msg']; + $board_ids['any'][$row['id_board']][] = $row['id_msg']; + } + $smcFunc['db_free_result']($request); + + // There might be - and are - different permissions between any and own. + $permissions = array( + 'own' => array( + 'post_reply_own' => 'can_reply', + 'delete_own' => 'can_delete', + ), + 'any' => array( + 'post_reply_any' => 'can_reply', + 'mark_any_notify' => 'can_mark_notify', + 'delete_any' => 'can_delete', + ) + ); + + // Now go through all the permissions, looking for boards they can do it on. + foreach ($permissions as $type => $list) + { + foreach ($list as $permission => $allowed) + { + // They can do it on these boards... + $boards = boardsAllowedTo($permission); + + // If 0 is the only thing in the array, they can do it everywhere! + if (!empty($boards) && $boards[0] == 0) + $boards = array_keys($board_ids[$type]); + + // Go through the boards, and look for posts they can do this on. + foreach ($boards as $board_id) + { + // Hmm, they have permission, but there are no topics from that board on this page. + if (!isset($board_ids[$type][$board_id])) + continue; + + // Okay, looks like they can do it for these posts. + foreach ($board_ids[$type][$board_id] as $counter) + if ($type == 'any' || $context['posts'][$counter]['poster']['id'] == $user_info['id']) + $context['posts'][$counter][$allowed] = true; + } + } + } + + $quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])); + foreach ($context['posts'] as $counter => $dummy) + { + // Some posts - the first posts - can't just be deleted. + $context['posts'][$counter]['can_delete'] &= $context['posts'][$counter]['delete_possible']; + + // And some cannot be quoted... + $context['posts'][$counter]['can_quote'] = $context['posts'][$counter]['can_reply'] && $quote_enabled; + } +} + +// Find unread topics and replies. +function UnreadTopics() +{ + global $board, $txt, $scripturl, $sourcedir; + global $user_info, $context, $settings, $modSettings, $smcFunc, $options; + + // Guests can't have unread things, we don't know anything about them. + is_not_guest(); + + // Prefetching + lots of MySQL work = bad mojo. + if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') + { + ob_end_clean(); + header('HTTP/1.1 403 Forbidden'); + die; + } + + $context['showing_all_topics'] = isset($_GET['all']); + $context['start'] = (int) $_REQUEST['start']; + $context['topics_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) && !WIRELESS ? $options['topics_per_page'] : $modSettings['defaultMaxTopics']; + if ($_REQUEST['action'] == 'unread') + $context['page_title'] = $context['showing_all_topics'] ? $txt['unread_topics_all'] : $txt['unread_topics_visit']; + else + $context['page_title'] = $txt['unread_replies']; + + if ($context['showing_all_topics'] && !empty($context['load_average']) && !empty($modSettings['loadavg_allunread']) && $context['load_average'] >= $modSettings['loadavg_allunread']) + fatal_lang_error('loadavg_allunread_disabled', false); + elseif ($_REQUEST['action'] != 'unread' && !empty($context['load_average']) && !empty($modSettings['loadavg_unreadreplies']) && $context['load_average'] >= $modSettings['loadavg_unreadreplies']) + fatal_lang_error('loadavg_unreadreplies_disabled', false); + elseif (!$context['showing_all_topics'] && $_REQUEST['action'] == 'unread' && !empty($context['load_average']) && !empty($modSettings['loadavg_unread']) && $context['load_average'] >= $modSettings['loadavg_unread']) + fatal_lang_error('loadavg_unread_disabled', false); + + // Parameters for the main query. + $query_parameters = array(); + + // Are we specifying any specific board? + if (isset($_REQUEST['children']) && (!empty($board) || !empty($_REQUEST['boards']))) + { + $boards = array(); + + if (!empty($_REQUEST['boards'])) + { + $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); + foreach ($_REQUEST['boards'] as $b) + $boards[] = (int) $b; + } + + if (!empty($board)) + $boards[] = (int) $board; + + // The easiest thing is to just get all the boards they can see, but since we've specified the top of tree we ignore some of them + $request = $smcFunc['db_query']('', ' + SELECT b.id_board, b.id_parent + FROM {db_prefix}boards AS b + WHERE {query_wanna_see_board} + AND b.child_level > {int:no_child} + AND b.id_board NOT IN ({array_int:boards}) + ORDER BY child_level ASC + ', + array( + 'no_child' => 0, + 'boards' => $boards, + ) + ); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + if (in_array($row['id_parent'], $boards)) + $boards[] = $row['id_board']; + + $smcFunc['db_free_result']($request); + + if (empty($boards)) + fatal_lang_error('error_no_boards_selected'); + + $query_this_board = 'id_board IN ({array_int:boards})'; + $query_parameters['boards'] = $boards; + $context['querystring_board_limits'] = ';boards=' . implode(',', $boards) . ';start=%d'; + } + elseif (!empty($board)) + { + $query_this_board = 'id_board = {int:board}'; + $query_parameters['board'] = $board; + $context['querystring_board_limits'] = ';board=' . $board . '.%1$d'; + } + elseif (!empty($_REQUEST['boards'])) + { + $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); + foreach ($_REQUEST['boards'] as $i => $b) + $_REQUEST['boards'][$i] = (int) $b; + + $request = $smcFunc['db_query']('', ' + SELECT b.id_board + FROM {db_prefix}boards AS b + WHERE {query_see_board} + AND b.id_board IN ({array_int:board_list})', + array( + 'board_list' => $_REQUEST['boards'], + ) + ); + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards[] = $row['id_board']; + $smcFunc['db_free_result']($request); + + if (empty($boards)) + fatal_lang_error('error_no_boards_selected'); + + $query_this_board = 'id_board IN ({array_int:boards})'; + $query_parameters['boards'] = $boards; + $context['querystring_board_limits'] = ';boards=' . implode(',', $boards) . ';start=%1$d'; + } + elseif (!empty($_REQUEST['c'])) + { + $_REQUEST['c'] = explode(',', $_REQUEST['c']); + foreach ($_REQUEST['c'] as $i => $c) + $_REQUEST['c'][$i] = (int) $c; + + $see_board = isset($_REQUEST['action']) && $_REQUEST['action'] == 'unreadreplies' ? 'query_see_board' : 'query_wanna_see_board'; + $request = $smcFunc['db_query']('', ' + SELECT b.id_board + FROM {db_prefix}boards AS b + WHERE ' . $user_info[$see_board] . ' + AND b.id_cat IN ({array_int:id_cat})', + array( + 'id_cat' => $_REQUEST['c'], + ) + ); + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards[] = $row['id_board']; + $smcFunc['db_free_result']($request); + + if (empty($boards)) + fatal_lang_error('error_no_boards_selected'); + + $query_this_board = 'id_board IN ({array_int:boards})'; + $query_parameters['boards'] = $boards; + $context['querystring_board_limits'] = ';c=' . implode(',', $_REQUEST['c']) . ';start=%1$d'; + } + else + { + $see_board = isset($_REQUEST['action']) && $_REQUEST['action'] == 'unreadreplies' ? 'query_see_board' : 'query_wanna_see_board'; + // Don't bother to show deleted posts! + $request = $smcFunc['db_query']('', ' + SELECT b.id_board + FROM {db_prefix}boards AS b + WHERE ' . $user_info[$see_board] . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_board}' : ''), + array( + 'recycle_board' => (int) $modSettings['recycle_board'], + ) + ); + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards[] = $row['id_board']; + $smcFunc['db_free_result']($request); + + if (empty($boards)) + fatal_lang_error('error_no_boards_selected'); + + $query_this_board = 'id_board IN ({array_int:boards})'; + $query_parameters['boards'] = $boards; + $context['querystring_board_limits'] = ';start=%1$d'; + $context['no_board_limits'] = true; + } + + $sort_methods = array( + 'subject' => 'ms.subject', + 'starter' => 'IFNULL(mems.real_name, ms.poster_name)', + 'replies' => 't.num_replies', + 'views' => 't.num_views', + 'first_post' => 't.id_topic', + 'last_post' => 't.id_last_msg' + ); + + // The default is the most logical: newest first. + if (!isset($_REQUEST['sort']) || !isset($sort_methods[$_REQUEST['sort']])) + { + $context['sort_by'] = 'last_post'; + $_REQUEST['sort'] = 't.id_last_msg'; + $ascending = isset($_REQUEST['asc']); + + $context['querystring_sort_limits'] = $ascending ? ';asc' : ''; + } + // But, for other methods the default sort is ascending. + else + { + $context['sort_by'] = $_REQUEST['sort']; + $_REQUEST['sort'] = $sort_methods[$_REQUEST['sort']]; + $ascending = !isset($_REQUEST['desc']); + + $context['querystring_sort_limits'] = ';sort=' . $context['sort_by'] . ($ascending ? '' : ';desc'); + } + $context['sort_direction'] = $ascending ? 'up' : 'down'; + + if (!empty($_REQUEST['c']) && is_array($_REQUEST['c']) && count($_REQUEST['c']) == 1) + { + $request = $smcFunc['db_query']('', ' + SELECT name + FROM {db_prefix}categories + WHERE id_cat = {int:id_cat} + LIMIT 1', + array( + 'id_cat' => (int) $_REQUEST['c'][0], + ) + ); + list ($name) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['linktree'][] = array( + 'url' => $scripturl . '#c' . (int) $_REQUEST['c'][0], + 'name' => $name + ); + } + + $context['linktree'][] = array( + 'url' => $scripturl . '?action=' . $_REQUEST['action'] . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'], + 'name' => $_REQUEST['action'] == 'unread' ? $txt['unread_topics_visit'] : $txt['unread_replies'] + ); + + if ($context['showing_all_topics']) + $context['linktree'][] = array( + 'url' => $scripturl . '?action=' . $_REQUEST['action'] . ';all' . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'], + 'name' => $txt['unread_topics_all'] + ); + else + $txt['unread_topics_visit_none'] = strtr($txt['unread_topics_visit_none'], array('?action=unread;all' => '?action=unread;all' . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'])); + + if (WIRELESS) + $context['sub_template'] = WIRELESS_PROTOCOL . '_recent'; + else + { + loadTemplate('Recent'); + $context['sub_template'] = $_REQUEST['action'] == 'unread' ? 'unread' : 'replies'; + } + + // Setup the default topic icons... for checking they exist and the like ;) + $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless', 'clip'); + $context['icon_sources'] = array(); + foreach ($stable_icons as $icon) + $context['icon_sources'][$icon] = 'images_url'; + + $is_topics = $_REQUEST['action'] == 'unread'; + + // This part is the same for each query. + $select_clause = ' + ms.subject AS first_subject, ms.poster_time AS first_poster_time, ms.id_topic, t.id_board, b.name AS bname, + t.num_replies, t.num_views, ms.id_member AS id_first_member, ml.id_member AS id_last_member, + ml.poster_time AS last_poster_time, IFNULL(mems.real_name, ms.poster_name) AS first_poster_name, + IFNULL(meml.real_name, ml.poster_name) AS last_poster_name, ml.subject AS last_subject, + ml.icon AS last_icon, ms.icon AS first_icon, t.id_poll, t.is_sticky, t.locked, ml.modified_time AS last_modified_time, + IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from, SUBSTRING(ml.body, 1, 385) AS last_body, + SUBSTRING(ms.body, 1, 385) AS first_body, ml.smileys_enabled AS last_smileys, ms.smileys_enabled AS first_smileys, t.id_first_msg, t.id_last_msg'; + + if ($context['showing_all_topics']) + { + if (!empty($board)) + { + $request = $smcFunc['db_query']('', ' + SELECT MIN(id_msg) + FROM {db_prefix}log_mark_read + WHERE id_member = {int:current_member} + AND id_board = {int:current_board}', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + ) + ); + list ($earliest_msg) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + { + $request = $smcFunc['db_query']('', ' + SELECT MIN(lmr.id_msg) + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = b.id_board AND lmr.id_member = {int:current_member}) + WHERE {query_see_board}', + array( + 'current_member' => $user_info['id'], + ) + ); + list ($earliest_msg) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // This is needed in case of topics marked unread. + if (empty($earliest_msg)) + $earliest_msg = 0; + else + { + // Using caching, when possible, to ignore the below slow query. + if (isset($_SESSION['cached_log_time']) && $_SESSION['cached_log_time'][0] + 45 > time()) + $earliest_msg2 = $_SESSION['cached_log_time'][1]; + else + { + // This query is pretty slow, but it's needed to ensure nothing crucial is ignored. + $request = $smcFunc['db_query']('', ' + SELECT MIN(id_msg) + FROM {db_prefix}log_topics + WHERE id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + ) + ); + list ($earliest_msg2) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // In theory this could be zero, if the first ever post is unread, so fudge it ;) + if ($earliest_msg2 == 0) + $earliest_msg2 = -1; + + $_SESSION['cached_log_time'] = array(time(), $earliest_msg2); + } + + $earliest_msg = min($earliest_msg2, $earliest_msg); + } + } + + // !!! Add modified_time in for log_time check? + + if ($modSettings['totalMessages'] > 100000 && $context['showing_all_topics']) + { + $smcFunc['db_query']('', ' + DROP TABLE IF EXISTS {db_prefix}log_topics_unread', + array( + ) + ); + + // Let's copy things out of the log_topics table, to reduce searching. + $have_temp_table = $smcFunc['db_query']('', ' + CREATE TEMPORARY TABLE {db_prefix}log_topics_unread ( + PRIMARY KEY (id_topic) + ) + SELECT lt.id_topic, lt.id_msg + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic) + WHERE lt.id_member = {int:current_member} + AND t.' . $query_this_board . (empty($earliest_msg) ? '' : ' + AND t.id_last_msg > {int:earliest_msg}') . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : ''), + array_merge($query_parameters, array( + 'current_member' => $user_info['id'], + 'earliest_msg' => !empty($earliest_msg) ? $earliest_msg : 0, + 'is_approved' => 1, + 'db_error_skip' => true, + )) + ) !== false; + } + else + $have_temp_table = false; + + if ($context['showing_all_topics'] && $have_temp_table) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*), MIN(t.id_last_msg) + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member}) + WHERE t.' . $query_this_board . (!empty($earliest_msg) ? ' + AND t.id_last_msg > {int:earliest_msg}' : '') . ' + AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : ''), + array_merge($query_parameters, array( + 'current_member' => $user_info['id'], + 'earliest_msg' => !empty($earliest_msg) ? $earliest_msg : 0, + 'is_approved' => 1, + )) + ); + list ($num_topics, $min_message) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Make sure the starting place makes sense and construct the page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . $context['querystring_board_limits'] . $context['querystring_sort_limits'], $_REQUEST['start'], $num_topics, $context['topics_per_page'], true); + $context['current_page'] = (int) $_REQUEST['start'] / $context['topics_per_page']; + + $context['links'] = array( + 'first' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'] : '', + 'prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] - $context['topics_per_page']) . $context['querystring_sort_limits'] : '', + 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] + $context['topics_per_page']) . $context['querystring_sort_limits'] : '', + 'last' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], floor(($num_topics - 1) / $context['topics_per_page']) * $context['topics_per_page']) . $context['querystring_sort_limits'] : '', + 'up' => $scripturl, + ); + $context['page_info'] = array( + 'current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, + 'num_pages' => floor(($num_topics - 1) / $context['topics_per_page']) + 1 + ); + + if ($num_topics == 0) + { + // Mark the boards as read if there are no unread topics! + require_once($sourcedir . '/Subs-Boards.php'); + markBoardsRead(empty($boards) ? $board : $boards); + + $context['topics'] = array(); + if ($context['querystring_board_limits'] == ';start=%1$d') + $context['querystring_board_limits'] = ''; + else + $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']); + return; + } + else + $min_message = (int) $min_message; + + $request = $smcFunc['db_query']('substring', ' + SELECT ' . $select_clause . ' + FROM {db_prefix}messages AS ms + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ms.id_topic AND t.id_first_msg = ms.id_msg) + INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg) + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = ms.id_board) + LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member) + LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member) + LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member}) + WHERE b.' . $query_this_board . ' + AND t.id_last_msg >= {int:min_message} + AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? ' + AND ms.approved = {int:is_approved}' : '') . ' + ORDER BY {raw:sort} + LIMIT {int:offset}, {int:limit}', + array_merge($query_parameters, array( + 'current_member' => $user_info['id'], + 'min_message' => $min_message, + 'is_approved' => 1, + 'sort' => $_REQUEST['sort'] . ($ascending ? '' : ' DESC'), + 'offset' => $_REQUEST['start'], + 'limit' => $context['topics_per_page'], + )) + ); + } + elseif ($is_topics) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*), MIN(t.id_last_msg) + FROM {db_prefix}topics AS t' . (!empty($have_temp_table) ? ' + LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)' : ' + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})') . ' + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member}) + WHERE t.' . $query_this_board . ($context['showing_all_topics'] && !empty($earliest_msg) ? ' + AND t.id_last_msg > {int:earliest_msg}' : (!$context['showing_all_topics'] && empty($_SESSION['first_login']) ? ' + AND t.id_last_msg > {int:id_msg_last_visit}' : '')) . ' + AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : ''), + array_merge($query_parameters, array( + 'current_member' => $user_info['id'], + 'earliest_msg' => !empty($earliest_msg) ? $earliest_msg : 0, + 'id_msg_last_visit' => $_SESSION['id_msg_last_visit'], + 'is_approved' => 1, + )) + ); + list ($num_topics, $min_message) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Make sure the starting place makes sense and construct the page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . $context['querystring_board_limits'] . $context['querystring_sort_limits'], $_REQUEST['start'], $num_topics, $context['topics_per_page'], true); + $context['current_page'] = (int) $_REQUEST['start'] / $context['topics_per_page']; + + $context['links'] = array( + 'first' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'] : '', + 'prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] - $context['topics_per_page']) . $context['querystring_sort_limits'] : '', + 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] + $context['topics_per_page']) . $context['querystring_sort_limits'] : '', + 'last' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], floor(($num_topics - 1) / $context['topics_per_page']) * $context['topics_per_page']) . $context['querystring_sort_limits'] : '', + 'up' => $scripturl, + ); + $context['page_info'] = array( + 'current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, + 'num_pages' => floor(($num_topics - 1) / $context['topics_per_page']) + 1 + ); + + if ($num_topics == 0) + { + // Is this an all topics query? + if ($context['showing_all_topics']) + { + // Since there are no unread topics, mark the boards as read! + require_once($sourcedir . '/Subs-Boards.php'); + markBoardsRead(empty($boards) ? $board : $boards); + } + + $context['topics'] = array(); + if ($context['querystring_board_limits'] == ';start=%d') + $context['querystring_board_limits'] = ''; + else + $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']); + return; + } + else + $min_message = (int) $min_message; + + $request = $smcFunc['db_query']('substring', ' + SELECT ' . $select_clause . ' + FROM {db_prefix}messages AS ms + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ms.id_topic AND t.id_first_msg = ms.id_msg) + INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg) + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member) + LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)' . (!empty($have_temp_table) ? ' + LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)' : ' + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})') . ' + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member}) + WHERE t.' . $query_this_board . ' + AND t.id_last_msg >= {int:min_message} + AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < ml.id_msg' . ($modSettings['postmod_active'] ? ' + AND ms.approved = {int:is_approved}' : '') . ' + ORDER BY {raw:order} + LIMIT {int:offset}, {int:limit}', + array_merge($query_parameters, array( + 'current_member' => $user_info['id'], + 'min_message' => $min_message, + 'is_approved' => 1, + 'order' => $_REQUEST['sort'] . ($ascending ? '' : ' DESC'), + 'offset' => $_REQUEST['start'], + 'limit' => $context['topics_per_page'], + )) + ); + } + else + { + if ($modSettings['totalMessages'] > 100000) + { + $smcFunc['db_query']('', ' + DROP TABLE IF EXISTS {db_prefix}topics_posted_in', + array( + ) + ); + + $smcFunc['db_query']('', ' + DROP TABLE IF EXISTS {db_prefix}log_topics_posted_in', + array( + ) + ); + + $sortKey_joins = array( + 'ms.subject' => ' + INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)', + 'IFNULL(mems.real_name, ms.poster_name)' => ' + INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)', + ); + + // The main benefit of this temporary table is not that it's faster; it's that it avoids locks later. + $have_temp_table = $smcFunc['db_query']('', ' + CREATE TEMPORARY TABLE {db_prefix}topics_posted_in ( + id_topic mediumint(8) unsigned NOT NULL default {string:string_zero}, + id_board smallint(5) unsigned NOT NULL default {string:string_zero}, + id_last_msg int(10) unsigned NOT NULL default {string:string_zero}, + id_msg int(10) unsigned NOT NULL default {string:string_zero}, + PRIMARY KEY (id_topic) + ) + SELECT t.id_topic, t.id_board, t.id_last_msg, IFNULL(lmr.id_msg, 0) AS id_msg' . (!in_array($_REQUEST['sort'], array('t.id_last_msg', 't.id_topic')) ? ', ' . $_REQUEST['sort'] . ' AS sort_key' : '') . ' + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})' . (isset($sortKey_joins[$_REQUEST['sort']]) ? $sortKey_joins[$_REQUEST['sort']] : '') . ' + WHERE m.id_member = {int:current_member}' . (!empty($board) ? ' + AND t.id_board = {int:current_board}' : '') . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : '') . ' + GROUP BY m.id_topic', + array( + 'current_board' => $board, + 'current_member' => $user_info['id'], + 'is_approved' => 1, + 'string_zero' => '0', + 'db_error_skip' => true, + ) + ) !== false; + + // If that worked, create a sample of the log_topics table too. + if ($have_temp_table) + $have_temp_table = $smcFunc['db_query']('', ' + CREATE TEMPORARY TABLE {db_prefix}log_topics_posted_in ( + PRIMARY KEY (id_topic) + ) + SELECT lt.id_topic, lt.id_msg + FROM {db_prefix}log_topics AS lt + INNER JOIN {db_prefix}topics_posted_in AS pi ON (pi.id_topic = lt.id_topic) + WHERE lt.id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'db_error_skip' => true, + ) + ) !== false; + } + + if (!empty($have_temp_table)) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}topics_posted_in AS pi + LEFT JOIN {db_prefix}log_topics_posted_in AS lt ON (lt.id_topic = pi.id_topic) + WHERE pi.' . $query_this_board . ' + AND IFNULL(lt.id_msg, pi.id_msg) < pi.id_last_msg', + array_merge($query_parameters, array( + )) + ); + list ($num_topics) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + { + $request = $smcFunc['db_query']('unread_fetch_topic_count', ' + SELECT COUNT(DISTINCT t.id_topic), MIN(t.id_last_msg) + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic) + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member}) + WHERE t.' . $query_this_board . ' + AND m.id_member = {int:current_member} + AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : ''), + array_merge($query_parameters, array( + 'current_member' => $user_info['id'], + 'is_approved' => 1, + )) + ); + list ($num_topics, $min_message) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // Make sure the starting place makes sense and construct the page index. + $context['page_index'] = constructPageIndex($scripturl . '?action=' . $_REQUEST['action'] . $context['querystring_board_limits'] . $context['querystring_sort_limits'], $_REQUEST['start'], $num_topics, $context['topics_per_page'], true); + $context['current_page'] = (int) $_REQUEST['start'] / $context['topics_per_page']; + + $context['links'] = array( + 'first' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'] : '', + 'prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] - $context['topics_per_page']) . $context['querystring_sort_limits'] : '', + 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] + $context['topics_per_page']) . $context['querystring_sort_limits'] : '', + 'last' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], floor(($num_topics - 1) / $context['topics_per_page']) * $context['topics_per_page']) . $context['querystring_sort_limits'] : '', + 'up' => $scripturl, + ); + $context['page_info'] = array( + 'current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, + 'num_pages' => floor(($num_topics - 1) / $context['topics_per_page']) + 1 + ); + + if ($num_topics == 0) + { + $context['topics'] = array(); + if ($context['querystring_board_limits'] == ';start=%d') + $context['querystring_board_limits'] = ''; + else + $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']); + return; + } + + if (!empty($have_temp_table)) + $request = $smcFunc['db_query']('', ' + SELECT t.id_topic + FROM {db_prefix}topics_posted_in AS t + LEFT JOIN {db_prefix}log_topics_posted_in AS lt ON (lt.id_topic = t.id_topic) + WHERE t.' . $query_this_board . ' + AND IFNULL(lt.id_msg, t.id_msg) < t.id_last_msg + ORDER BY {raw:order} + LIMIT {int:offset}, {int:limit}', + array_merge($query_parameters, array( + 'order' => (in_array($_REQUEST['sort'], array('t.id_last_msg', 't.id_topic')) ? $_REQUEST['sort'] : 't.sort_key') . ($ascending ? '' : ' DESC'), + 'offset' => $_REQUEST['start'], + 'limit' => $context['topics_per_page'], + )) + ); + else + $request = $smcFunc['db_query']('unread_replies', ' + SELECT DISTINCT t.id_topic + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic AND m.id_member = {int:current_member})' . (strpos($_REQUEST['sort'], 'ms.') === false ? '' : ' + INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)') . (strpos($_REQUEST['sort'], 'mems.') === false ? '' : ' + LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)') . ' + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member}) + WHERE t.' . $query_this_board . ' + AND t.id_last_msg >= {int:min_message} + AND (IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0))) < t.id_last_msg + AND t.approved = {int:is_approved} + ORDER BY {raw:order} + LIMIT {int:offset}, {int:limit}', + array_merge($query_parameters, array( + 'current_member' => $user_info['id'], + 'min_message' => (int) $min_message, + 'is_approved' => 1, + 'order' => $_REQUEST['sort'] . ($ascending ? '' : ' DESC'), + 'offset' => $_REQUEST['start'], + 'limit' => $context['topics_per_page'], + 'sort' => $_REQUEST['sort'], + )) + ); + + $topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topics[] = $row['id_topic']; + $smcFunc['db_free_result']($request); + + // Sanity... where have you gone? + if (empty($topics)) + { + $context['topics'] = array(); + if ($context['querystring_board_limits'] == ';start=%d') + $context['querystring_board_limits'] = ''; + else + $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']); + return; + } + + $request = $smcFunc['db_query']('substring', ' + SELECT ' . $select_clause . ' + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS ms ON (ms.id_topic = t.id_topic AND ms.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member) + LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member) + LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member}) + LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member}) + WHERE t.id_topic IN ({array_int:topic_list}) + ORDER BY ' . $_REQUEST['sort'] . ($ascending ? '' : ' DESC') . ' + LIMIT ' . count($topics), + array( + 'current_member' => $user_info['id'], + 'topic_list' => $topics, + ) + ); + } + + $context['topics'] = array(); + $topic_ids = array(); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['id_poll'] > 0 && $modSettings['pollMode'] == '0') + continue; + + $topic_ids[] = $row['id_topic']; + + if (!empty($settings['message_index_preview'])) + { + // Limit them to 128 characters - do this FIRST because it's a lot of wasted censoring otherwise. + $row['first_body'] = strip_tags(strtr(parse_bbc($row['first_body'], $row['first_smileys'], $row['id_first_msg']), array('
' => ' '))); + if ($smcFunc['strlen']($row['first_body']) > 128) + $row['first_body'] = $smcFunc['substr']($row['first_body'], 0, 128) . '...'; + $row['last_body'] = strip_tags(strtr(parse_bbc($row['last_body'], $row['last_smileys'], $row['id_last_msg']), array('
' => ' '))); + if ($smcFunc['strlen']($row['last_body']) > 128) + $row['last_body'] = $smcFunc['substr']($row['last_body'], 0, 128) . '...'; + + // Censor the subject and message preview. + censorText($row['first_subject']); + censorText($row['first_body']); + + // Don't censor them twice! + if ($row['id_first_msg'] == $row['id_last_msg']) + { + $row['last_subject'] = $row['first_subject']; + $row['last_body'] = $row['first_body']; + } + else + { + censorText($row['last_subject']); + censorText($row['last_body']); + } + } + else + { + $row['first_body'] = ''; + $row['last_body'] = ''; + censorText($row['first_subject']); + + if ($row['id_first_msg'] == $row['id_last_msg']) + $row['last_subject'] = $row['first_subject']; + else + censorText($row['last_subject']); + } + + // Decide how many pages the topic should have. + $topic_length = $row['num_replies'] + 1; + $messages_per_page = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) && !WIRELESS ? $options['messages_per_page'] : $modSettings['defaultMaxMessages']; + if ($topic_length > $messages_per_page) + { + $tmppages = array(); + $tmpa = 1; + for ($tmpb = 0; $tmpb < $topic_length; $tmpb += $messages_per_page) + { + $tmppages[] = '' . $tmpa . ''; + $tmpa++; + } + // Show links to all the pages? + if (count($tmppages) <= 5) + $pages = '« ' . implode(' ', $tmppages); + // Or skip a few? + else + $pages = '« ' . $tmppages[0] . ' ' . $tmppages[1] . ' ... ' . $tmppages[count($tmppages) - 2] . ' ' . $tmppages[count($tmppages) - 1]; + + if (!empty($modSettings['enableAllMessages']) && $topic_length < $modSettings['enableAllMessages']) + $pages .= '  ' . $txt['all'] . ''; + $pages .= ' »'; + } + else + $pages = ''; + + // We need to check the topic icons exist... you can never be too sure! + if (empty($modSettings['messageIconChecks_disable'])) + { + // First icon first... as you'd expect. + if (!isset($context['icon_sources'][$row['first_icon']])) + $context['icon_sources'][$row['first_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['first_icon'] . '.gif') ? 'images_url' : 'default_images_url'; + // Last icon... last... duh. + if (!isset($context['icon_sources'][$row['last_icon']])) + $context['icon_sources'][$row['last_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['last_icon'] . '.gif') ? 'images_url' : 'default_images_url'; + } + + // And build the array. + $context['topics'][$row['id_topic']] = array( + 'id' => $row['id_topic'], + 'first_post' => array( + 'id' => $row['id_first_msg'], + 'member' => array( + 'name' => $row['first_poster_name'], + 'id' => $row['id_first_member'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_first_member'], + 'link' => !empty($row['id_first_member']) ? '' . $row['first_poster_name'] . '' : $row['first_poster_name'] + ), + 'time' => timeformat($row['first_poster_time']), + 'timestamp' => forum_time(true, $row['first_poster_time']), + 'subject' => $row['first_subject'], + 'preview' => $row['first_body'], + 'icon' => $row['first_icon'], + 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.gif', + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0;topicseen', + 'link' => '' . $row['first_subject'] . '' + ), + 'last_post' => array( + 'id' => $row['id_last_msg'], + 'member' => array( + 'name' => $row['last_poster_name'], + 'id' => $row['id_last_member'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_last_member'], + 'link' => !empty($row['id_last_member']) ? '' . $row['last_poster_name'] . '' : $row['last_poster_name'] + ), + 'time' => timeformat($row['last_poster_time']), + 'timestamp' => forum_time(true, $row['last_poster_time']), + 'subject' => $row['last_subject'], + 'preview' => $row['last_body'], + 'icon' => $row['last_icon'], + 'icon_url' => $settings[$context['icon_sources'][$row['last_icon']]] . '/post/' . $row['last_icon'] . '.gif', + 'href' => $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . ';topicseen#msg' . $row['id_last_msg'], + 'link' => '' . $row['last_subject'] . '' + ), + 'new_from' => $row['new_from'], + 'new_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['new_from'] . ';topicseen#new', + 'href' => $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['new_from']) . ';topicseen' . ($row['num_replies'] == 0 ? '' : 'new'), + 'link' => '' . $row['first_subject'] . '', + 'is_sticky' => !empty($modSettings['enableStickyTopics']) && !empty($row['is_sticky']), + 'is_locked' => !empty($row['locked']), + 'is_poll' => $modSettings['pollMode'] == '1' && $row['id_poll'] > 0, + 'is_hot' => $row['num_replies'] >= $modSettings['hotTopicPosts'], + 'is_very_hot' => $row['num_replies'] >= $modSettings['hotTopicVeryPosts'], + 'is_posted_in' => false, + 'icon' => $row['first_icon'], + 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.gif', + 'subject' => $row['first_subject'], + 'pages' => $pages, + 'replies' => comma_format($row['num_replies']), + 'views' => comma_format($row['num_views']), + 'board' => array( + 'id' => $row['id_board'], + 'name' => $row['bname'], + 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'link' => '' . $row['bname'] . '' + ) + ); + + determineTopicClass($context['topics'][$row['id_topic']]); + } + $smcFunc['db_free_result']($request); + + if ($is_topics && !empty($modSettings['enableParticipation']) && !empty($topic_ids)) + { + $result = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topic_list}) + AND id_member = {int:current_member} + GROUP BY id_topic + LIMIT {int:limit}', + array( + 'current_member' => $user_info['id'], + 'topic_list' => $topic_ids, + 'limit' => count($topic_ids), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (empty($context['topics'][$row['id_topic']]['is_posted_in'])) + { + $context['topics'][$row['id_topic']]['is_posted_in'] = true; + $context['topics'][$row['id_topic']]['class'] = 'my_' . $context['topics'][$row['id_topic']]['class']; + } + } + $smcFunc['db_free_result']($result); + } + + $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']); + $context['topics_to_mark'] = implode('-', $topic_ids); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Register.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Register.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,849 @@ + 1 && $context['require_agreement'] && !$context['registration_passed_agreement']) + $current_step = 1; + + // Show the user the right form. + $context['sub_template'] = $current_step == 1 ? 'registration_agreement' : 'registration_form'; + $context['page_title'] = $current_step == 1 ? $txt['registration_agreement'] : $txt['registration_form']; + + // Add the register chain to the link tree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=register', + 'name' => $txt['register'], + ); + + // If you have to agree to the agreement, it needs to be fetched from the file. + if ($context['require_agreement']) + { + // Have we got a localized one? + if (file_exists($boarddir . '/agreement.' . $user_info['language'] . '.txt')) + $context['agreement'] = parse_bbc(file_get_contents($boarddir . '/agreement.' . $user_info['language'] . '.txt'), true, 'agreement_' . $user_info['language']); + elseif (file_exists($boarddir . '/agreement.txt')) + $context['agreement'] = parse_bbc(file_get_contents($boarddir . '/agreement.txt'), true, 'agreement'); + else + $context['agreement'] = ''; + } + + if (!empty($modSettings['userLanguage'])) + { + $selectedLanguage = empty($_SESSION['language']) ? $language : $_SESSION['language']; + + // Do we have any languages? + if (empty($context['languages'])) + getLanguages(); + + // Try to find our selected language. + foreach ($context['languages'] as $key => $lang) + { + $context['languages'][$key]['name'] = strtr($lang['name'], array('-utf8' => '')); + + // Found it! + if ($selectedLanguage == $lang['filename']) + $context['languages'][$key]['selected'] = true; + } + } + + // Any custom fields we want filled in? + require_once($sourcedir . '/Profile.php'); + loadCustomFields(0, 'register'); + + // Or any standard ones? + if (!empty($modSettings['registration_fields'])) + { + require_once($sourcedir . '/Profile-Modify.php'); + + // Setup some important context. + loadLanguage('Profile'); + loadTemplate('Profile'); + + $context['user']['is_owner'] = true; + + // Here, and here only, emulate the permissions the user would have to do this. + $user_info['permissions'] = array_merge($user_info['permissions'], array('profile_account_own', 'profile_extra_own')); + $reg_fields = explode(',', $modSettings['registration_fields']); + + // We might have had some submissions on this front - go check. + foreach ($reg_fields as $field) + if (isset($_POST[$field])) + $cur_profile[$field] = $smcFunc['htmlspecialchars']($_POST[$field]); + + // Load all the fields in question. + setupProfileContext($reg_fields); + } + + // Generate a visual verification code to make sure the user is no bot. + if (!empty($modSettings['reg_verification'])) + { + require_once($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'register', + ); + $context['visual_verification'] = create_control_verification($verificationOptions); + $context['visual_verification_id'] = $verificationOptions['id']; + } + // Otherwise we have nothing to show. + else + $context['visual_verification'] = false; + + // Are they coming from an OpenID login attempt? + if (!empty($_SESSION['openid']['verified']) && !empty($_SESSION['openid']['openid_uri'])) + { + $context['openid'] = $_SESSION['openid']['openid_uri']; + $context['username'] = $smcFunc['htmlspecialchars'](!empty($_POST['user']) ? $_POST['user'] : $_SESSION['openid']['nickname']); + $context['email'] = $smcFunc['htmlspecialchars'](!empty($_POST['email']) ? $_POST['email'] : $_SESSION['openid']['email']); + } + // See whether we have some prefiled values. + else + { + $context += array( + 'openid' => isset($_POST['openid_identifier']) ? $_POST['openid_identifier'] : '', + 'username' => isset($_POST['user']) ? $smcFunc['htmlspecialchars']($_POST['user']) : '', + 'email' => isset($_POST['email']) ? $smcFunc['htmlspecialchars']($_POST['email']) : '', + ); + } + + // !!! Why isn't this a simple set operation? + // Were there any errors? + $context['registration_errors'] = array(); + if (!empty($reg_errors)) + foreach ($reg_errors as $error) + $context['registration_errors'][] = $error; +} + +// Actually register the member. +function Register2($verifiedOpenID = false) +{ + global $scripturl, $txt, $modSettings, $context, $sourcedir; + global $user_info, $options, $settings, $smcFunc; + + // Start collecting together any errors. + $reg_errors = array(); + + // Did we save some open ID fields? + if ($verifiedOpenID && !empty($context['openid_save_fields'])) + { + foreach ($context['openid_save_fields'] as $id => $value) + $_POST[$id] = $value; + } + + // You can't register if it's disabled. + if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 3) + fatal_lang_error('registration_disabled', false); + + // Things we don't do for people who have already confirmed their OpenID allegances via register. + if (!$verifiedOpenID) + { + // Well, if you don't agree, you can't register. + if (!empty($modSettings['requireAgreement']) && empty($_SESSION['registration_agreed'])) + redirectexit(); + + // Make sure they came from *somewhere*, have a session. + if (!isset($_SESSION['old_url'])) + redirectexit('action=register'); + + // Are they under age, and under age users are banned? + if (!empty($modSettings['coppaAge']) && empty($modSettings['coppaType']) && empty($_SESSION['skip_coppa'])) + { + // !!! This should be put in Errors, imho. + loadLanguage('Login'); + fatal_lang_error('under_age_registration_prohibited', false, array($modSettings['coppaAge'])); + } + + // Check whether the visual verification code was entered correctly. + if (!empty($modSettings['reg_verification'])) + { + require_once($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'register', + ); + $context['visual_verification'] = create_control_verification($verificationOptions, true); + + if (is_array($context['visual_verification'])) + { + loadLanguage('Errors'); + foreach ($context['visual_verification'] as $error) + $reg_errors[] = $txt['error_' . $error]; + } + } + } + + foreach ($_POST as $key => $value) + { + if (!is_array($_POST[$key])) + $_POST[$key] = htmltrim__recursive(str_replace(array("\n", "\r"), '', $_POST[$key])); + } + + // Collect all extra registration fields someone might have filled in. + $possible_strings = array( + 'website_url', 'website_title', + 'aim', 'yim', + 'location', 'birthdate', + 'time_format', + 'buddy_list', + 'pm_ignore_list', + 'smiley_set', + 'signature', 'personal_text', 'avatar', + 'lngfile', + 'secret_question', 'secret_answer', + ); + $possible_ints = array( + 'pm_email_notify', + 'notify_types', + 'icq', + 'gender', + 'id_theme', + ); + $possible_floats = array( + 'time_offset', + ); + $possible_bools = array( + 'notify_announcements', 'notify_regularity', 'notify_send_body', + 'hide_email', 'show_online', + ); + + if (isset($_POST['secret_answer']) && $_POST['secret_answer'] != '') + $_POST['secret_answer'] = md5($_POST['secret_answer']); + + // Needed for isReservedName() and registerMember(). + require_once($sourcedir . '/Subs-Members.php'); + + // Validation... even if we're not a mall. + if (isset($_POST['real_name']) && (!empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum'))) + { + $_POST['real_name'] = trim(preg_replace('~[\s]~' . ($context['utf8'] ? 'u' : ''), ' ', $_POST['real_name'])); + if (trim($_POST['real_name']) != '' && !isReservedName($_POST['real_name']) && $smcFunc['strlen']($_POST['real_name']) < 60) + $possible_strings[] = 'real_name'; + } + + if (isset($_POST['msn']) && preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['msn']) != 0) + $profile_strings[] = 'msn'; + + // Handle a string as a birthdate... + if (isset($_POST['birthdate']) && $_POST['birthdate'] != '') + $_POST['birthdate'] = strftime('%Y-%m-%d', strtotime($_POST['birthdate'])); + // Or birthdate parts... + elseif (!empty($_POST['bday1']) && !empty($_POST['bday2'])) + $_POST['birthdate'] = sprintf('%04d-%02d-%02d', empty($_POST['bday3']) ? 0 : (int) $_POST['bday3'], (int) $_POST['bday1'], (int) $_POST['bday2']); + + // By default assume email is hidden, only show it if we tell it to. + $_POST['hide_email'] = !empty($_POST['allow_email']) ? 0 : 1; + + // Validate the passed language file. + if (isset($_POST['lngfile']) && !empty($modSettings['userLanguage'])) + { + // Do we have any languages? + if (empty($context['languages'])) + getLanguages(); + + // Did we find it? + if (isset($context['languages'][$_POST['lngfile']])) + $_SESSION['language'] = $_POST['lngfile']; + else + unset($_POST['lngfile']); + } + else + unset($_POST['lngfile']); + + // Set the options needed for registration. + $regOptions = array( + 'interface' => 'guest', + 'username' => !empty($_POST['user']) ? $_POST['user'] : '', + 'email' => !empty($_POST['email']) ? $_POST['email'] : '', + 'password' => !empty($_POST['passwrd1']) ? $_POST['passwrd1'] : '', + 'password_check' => !empty($_POST['passwrd2']) ? $_POST['passwrd2'] : '', + 'openid' => !empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : '', + 'auth_method' => !empty($_POST['authenticate']) ? $_POST['authenticate'] : '', + 'check_reserved_name' => true, + 'check_password_strength' => true, + 'check_email_ban' => true, + 'send_welcome_email' => !empty($modSettings['send_welcomeEmail']), + 'require' => !empty($modSettings['coppaAge']) && !$verifiedOpenID && empty($_SESSION['skip_coppa']) ? 'coppa' : (empty($modSettings['registration_method']) ? 'nothing' : ($modSettings['registration_method'] == 1 ? 'activation' : 'approval')), + 'extra_register_vars' => array(), + 'theme_vars' => array(), + ); + + // Include the additional options that might have been filled in. + foreach ($possible_strings as $var) + if (isset($_POST[$var])) + $regOptions['extra_register_vars'][$var] = $smcFunc['htmlspecialchars']($_POST[$var], ENT_QUOTES); + foreach ($possible_ints as $var) + if (isset($_POST[$var])) + $regOptions['extra_register_vars'][$var] = (int) $_POST[$var]; + foreach ($possible_floats as $var) + if (isset($_POST[$var])) + $regOptions['extra_register_vars'][$var] = (float) $_POST[$var]; + foreach ($possible_bools as $var) + if (isset($_POST[$var])) + $regOptions['extra_register_vars'][$var] = empty($_POST[$var]) ? 0 : 1; + + // Registration options are always default options... + if (isset($_POST['default_options'])) + $_POST['options'] = isset($_POST['options']) ? $_POST['options'] + $_POST['default_options'] : $_POST['default_options']; + $regOptions['theme_vars'] = isset($_POST['options']) && is_array($_POST['options']) ? $_POST['options'] : array(); + + // Make sure they are clean, dammit! + $regOptions['theme_vars'] = htmlspecialchars__recursive($regOptions['theme_vars']); + + // If Quick Reply hasn't been set then set it to be shown but collapsed. + if (!isset($regOptions['theme_vars']['display_quick_reply'])) + $regOptions['theme_vars']['display_quick_reply'] = 1; + + // Check whether we have fields that simply MUST be displayed? + $request = $smcFunc['db_query']('', ' + SELECT col_name, field_name, field_type, field_length, mask, show_reg + FROM {db_prefix}custom_fields + WHERE active = {int:is_active}', + array( + 'is_active' => 1, + ) + ); + $custom_field_errors = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Don't allow overriding of the theme variables. + if (isset($regOptions['theme_vars'][$row['col_name']])) + unset($regOptions['theme_vars'][$row['col_name']]); + + // Not actually showing it then? + if (!$row['show_reg']) + continue; + + // Prepare the value! + $value = isset($_POST['customfield'][$row['col_name']]) ? trim($_POST['customfield'][$row['col_name']]) : ''; + + // We only care for text fields as the others are valid to be empty. + if (!in_array($row['field_type'], array('check', 'select', 'radio'))) + { + // Is it too long? + if ($row['field_length'] && $row['field_length'] < $smcFunc['strlen']($value)) + $custom_field_errors[] = array('custom_field_too_long', array($row['field_name'], $row['field_length'])); + + // Any masks to apply? + if ($row['field_type'] == 'text' && !empty($row['mask']) && $row['mask'] != 'none') + { + //!!! We never error on this - just ignore it at the moment... + if ($row['mask'] == 'email' && (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $value) === 0 || strlen($value) > 255)) + $custom_field_errors[] = array('custom_field_invalid_email', array($row['field_name'])); + elseif ($row['mask'] == 'number' && preg_match('~[^\d]~', $value)) + $custom_field_errors[] = array('custom_field_not_number', array($row['field_name'])); + elseif (substr($row['mask'], 0, 5) == 'regex' && preg_match(substr($row['mask'], 5), $value) === 0) + $custom_field_errors[] = array('custom_field_inproper_format', array($row['field_name'])); + } + } + + // Is this required but not there? + if (trim($value) == '' && $row['show_reg'] > 1) + $custom_field_errors[] = array('custom_field_empty', array($row['field_name'])); + } + $smcFunc['db_free_result']($request); + + // Process any errors. + if (!empty($custom_field_errors)) + { + loadLanguage('Errors'); + foreach ($custom_field_errors as $error) + $reg_errors[] = vsprintf($txt['error_' . $error[0]], $error[1]); + } + + // Lets check for other errors before trying to register the member. + if (!empty($reg_errors)) + { + $_REQUEST['step'] = 2; + return Register($reg_errors); + } + // If they're wanting to use OpenID we need to validate them first. + if (empty($_SESSION['openid']['verified']) && !empty($_POST['authenticate']) && $_POST['authenticate'] == 'openid') + { + // What do we need to save? + $save_variables = array(); + foreach ($_POST as $k => $v) + if (!in_array($k, array('sc', 'sesc', $context['session_var'], 'passwrd1', 'passwrd2', 'regSubmit'))) + $save_variables[$k] = $v; + + require_once($sourcedir . '/Subs-OpenID.php'); + smf_openID_validate($_POST['openid_identifier'], false, $save_variables); + } + // If we've come from OpenID set up some default stuff. + elseif ($verifiedOpenID || (!empty($_POST['openid_identifier']) && $_POST['authenticate'] == 'openid')) + { + $regOptions['username'] = !empty($_POST['user']) && trim($_POST['user']) != '' ? $_POST['user'] : $_SESSION['openid']['nickname']; + $regOptions['email'] = !empty($_POST['email']) && trim($_POST['email']) != '' ? $_POST['email'] : $_SESSION['openid']['email']; + $regOptions['auth_method'] = 'openid'; + $regOptions['openid'] = !empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : $_SESSION['openid']['openid_uri']; + } + + $memberID = registerMember($regOptions, true); + + // What there actually an error of some kind dear boy? + if (is_array($memberID)) + { + $reg_errors = array_merge($reg_errors, $memberID); + $_REQUEST['step'] = 2; + return Register($reg_errors); + } + + // Do our spam protection now. + spamProtection('register'); + + // We'll do custom fields after as then we get to use the helper function! + if (!empty($_POST['customfield'])) + { + require_once($sourcedir . '/Profile.php'); + require_once($sourcedir . '/Profile-Modify.php'); + makeCustomFieldChanges($memberID, 'register'); + } + + // If COPPA has been selected then things get complicated, setup the template. + if (!empty($modSettings['coppaAge']) && empty($_SESSION['skip_coppa'])) + redirectexit('action=coppa;member=' . $memberID); + // Basic template variable setup. + elseif (!empty($modSettings['registration_method'])) + { + loadTemplate('Register'); + + $context += array( + 'page_title' => $txt['register'], + 'title' => $txt['registration_successful'], + 'sub_template' => 'after', + 'description' => $modSettings['registration_method'] == 2 ? $txt['approval_after_registration'] : $txt['activate_after_registration'] + ); + } + else + { + call_integration_hook('integrate_activate', array($row['member_name'])); + + setLoginCookie(60 * $modSettings['cookieTime'], $memberID, sha1(sha1(strtolower($regOptions['username']) . $regOptions['password']) . $regOptions['register_vars']['password_salt'])); + + redirectexit('action=login2;sa=check;member=' . $memberID, $context['server']['needs_login_fix']); + } +} + +function Activate() +{ + global $context, $txt, $modSettings, $scripturl, $sourcedir, $smcFunc, $language; + + loadLanguage('Login'); + loadTemplate('Login'); + + if (empty($_REQUEST['u']) && empty($_POST['user'])) + { + if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == 3) + fatal_lang_error('no_access', false); + + $context['member_id'] = 0; + $context['sub_template'] = 'resend'; + $context['page_title'] = $txt['invalid_activation_resend']; + $context['can_activate'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] == 1; + $context['default_username'] = isset($_GET['user']) ? $_GET['user'] : ''; + + return; + } + + // Get the code from the database... + $request = $smcFunc['db_query']('', ' + SELECT id_member, validation_code, member_name, real_name, email_address, is_activated, passwd, lngfile + FROM {db_prefix}members' . (empty($_REQUEST['u']) ? ' + WHERE member_name = {string:email_address} OR email_address = {string:email_address}' : ' + WHERE id_member = {int:id_member}') . ' + LIMIT 1', + array( + 'id_member' => isset($_REQUEST['u']) ? (int) $_REQUEST['u'] : 0, + 'email_address' => isset($_POST['user']) ? $_POST['user'] : '', + ) + ); + + // Does this user exist at all? + if ($smcFunc['db_num_rows']($request) == 0) + { + $context['sub_template'] = 'retry_activate'; + $context['page_title'] = $txt['invalid_userid']; + $context['member_id'] = 0; + + return; + } + + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Change their email address? (they probably tried a fake one first :P.) + if (isset($_POST['new_email'], $_REQUEST['passwd']) && sha1(strtolower($row['member_name']) . $_REQUEST['passwd']) == $row['passwd'] && ($row['is_activated'] == 0 || $row['is_activated'] == 2)) + { + if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == 3) + fatal_lang_error('no_access', false); + + // !!! Separate the sprintf? + if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['new_email']) == 0) + fatal_error(sprintf($txt['valid_email_needed'], htmlspecialchars($_POST['new_email'])), false); + + // Make sure their email isn't banned. + isBannedEmail($_POST['new_email'], 'cannot_register', $txt['ban_register_prohibited']); + + // Ummm... don't even dare try to take someone else's email!! + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE email_address = {string:email_address} + LIMIT 1', + array( + 'email_address' => $_POST['new_email'], + ) + ); + // !!! Separate the sprintf? + if ($smcFunc['db_num_rows']($request) != 0) + fatal_lang_error('email_in_use', false, array(htmlspecialchars($_POST['new_email']))); + $smcFunc['db_free_result']($request); + + updateMemberData($row['id_member'], array('email_address' => $_POST['new_email'])); + $row['email_address'] = $_POST['new_email']; + + $email_change = true; + } + + // Resend the password, but only if the account wasn't activated yet. + if (!empty($_REQUEST['sa']) && $_REQUEST['sa'] == 'resend' && ($row['is_activated'] == 0 || $row['is_activated'] == 2) && (!isset($_REQUEST['code']) || $_REQUEST['code'] == '')) + { + require_once($sourcedir . '/Subs-Post.php'); + + $replacements = array( + 'REALNAME' => $row['real_name'], + 'USERNAME' => $row['member_name'], + 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $row['id_member'] . ';code=' . $row['validation_code'], + 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $row['id_member'], + 'ACTIVATIONCODE' => $row['validation_code'], + 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', + ); + + $emaildata = loadEmailTemplate('resend_activate_message', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); + + sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + + $context['page_title'] = $txt['invalid_activation_resend']; + + // This will ensure we don't actually get an error message if it works! + $context['error_title'] = ''; + + fatal_lang_error(!empty($email_change) ? 'change_email_success' : 'resend_email_success', false); + } + + // Quit if this code is not right. + if (empty($_REQUEST['code']) || $row['validation_code'] != $_REQUEST['code']) + { + if (!empty($row['is_activated'])) + fatal_lang_error('already_activated', false); + elseif ($row['validation_code'] == '') + { + loadLanguage('Profile'); + fatal_error($txt['registration_not_approved'] . ' ' . $txt['here'] . '.', false); + } + + $context['sub_template'] = 'retry_activate'; + $context['page_title'] = $txt['invalid_activation_code']; + $context['member_id'] = $row['id_member']; + + return; + } + + // Let the integration know that they've been activated! + call_integration_hook('integrate_activate', array($row['member_name'])); + + // Validation complete - update the database! + updateMemberData($row['id_member'], array('is_activated' => 1, 'validation_code' => '')); + + // Also do a proper member stat re-evaluation. + updateStats('member', false); + + if (!isset($_POST['new_email'])) + { + require_once($sourcedir . '/Subs-Post.php'); + + adminNotify('activation', $row['id_member'], $row['member_name']); + } + + $context += array( + 'page_title' => $txt['registration_successful'], + 'sub_template' => 'login', + 'default_username' => $row['member_name'], + 'default_password' => '', + 'never_expire' => false, + 'description' => $txt['activate_success'] + ); +} + +// This function will display the contact information for the forum, as well a form to fill in. +function CoppaForm() +{ + global $context, $modSettings, $txt, $smcFunc; + + loadLanguage('Login'); + loadTemplate('Register'); + + // No User ID?? + if (!isset($_GET['member'])) + fatal_lang_error('no_access', false); + + // Get the user details... + $request = $smcFunc['db_query']('', ' + SELECT member_name + FROM {db_prefix}members + WHERE id_member = {int:id_member} + AND is_activated = {int:is_coppa}', + array( + 'id_member' => (int) $_GET['member'], + 'is_coppa' => 5, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_access', false); + list ($username) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (isset($_GET['form'])) + { + // Some simple contact stuff for the forum. + $context['forum_contacts'] = (!empty($modSettings['coppaPost']) ? $modSettings['coppaPost'] . '

' : '') . (!empty($modSettings['coppaFax']) ? $modSettings['coppaFax'] . '
' : ''); + $context['forum_contacts'] = !empty($context['forum_contacts']) ? $context['forum_name_html_safe'] . '
' . $context['forum_contacts'] : ''; + + // Showing template? + if (!isset($_GET['dl'])) + { + // Shortcut for producing underlines. + $context['ul'] = '                          '; + $context['template_layers'] = array(); + $context['sub_template'] = 'coppa_form'; + $context['page_title'] = $txt['coppa_form_title']; + $context['coppa_body'] = str_replace(array('{PARENT_NAME}', '{CHILD_NAME}', '{USER_NAME}'), array($context['ul'], $context['ul'], $username), $txt['coppa_form_body']); + } + // Downloading. + else + { + // The data. + $ul = ' '; + $crlf = "\r\n"; + $data = $context['forum_contacts'] . $crlf . $txt['coppa_form_address'] . ':' . $crlf . $txt['coppa_form_date'] . ':' . $crlf . $crlf . $crlf . $txt['coppa_form_body']; + $data = str_replace(array('{PARENT_NAME}', '{CHILD_NAME}', '{USER_NAME}', '
', '
'), array($ul, $ul, $username, $crlf, $crlf), $data); + + // Send the headers. + header('Connection: close'); + header('Content-Disposition: attachment; filename="approval.txt"'); + header('Content-Type: ' . ($context['browser']['is_ie'] || $context['browser']['is_opera'] ? 'application/octetstream' : 'application/octet-stream')); + header('Content-Length: ' . count($data)); + + echo $data; + obExit(false); + } + } + else + { + $context += array( + 'page_title' => $txt['coppa_title'], + 'sub_template' => 'coppa', + ); + + $context['coppa'] = array( + 'body' => str_replace('{MINIMUM_AGE}', $modSettings['coppaAge'], $txt['coppa_after_registration']), + 'many_options' => !empty($modSettings['coppaPost']) && !empty($modSettings['coppaFax']), + 'post' => empty($modSettings['coppaPost']) ? '' : $modSettings['coppaPost'], + 'fax' => empty($modSettings['coppaFax']) ? '' : $modSettings['coppaFax'], + 'phone' => empty($modSettings['coppaPhone']) ? '' : str_replace('{PHONE_NUMBER}', $modSettings['coppaPhone'], $txt['coppa_send_by_phone']), + 'id' => $_GET['member'], + ); + } +} + +// Show the verification code or let it hear. +function VerificationCode() +{ + global $sourcedir, $modSettings, $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("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B"); + } + + // Show a window that will play the verification code. + 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'; + $context['template_layers'] = array(); + + obExit(); + } + + // If we have GD, try the nice code. + elseif (empty($_REQUEST['format'])) + { + require_once($sourcedir . '/Subs-Graphics.php'); + + if (in_array('gd', get_loaded_extensions()) && !showCodeImage($code)) + header('HTTP/1.1 400 Bad Request'); + + // Otherwise just show a pre-defined letter. + 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("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B"); + } + } + // You must be up to no good. + else + { + header('Content-Type: image/gif'); + die("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B"); + } + } + + elseif ($_REQUEST['format'] === '.wav') + { + require_once($sourcedir . '/Subs-Sound.php'); + + if (!createWaveFile($code)) + header('HTTP/1.1 400 Bad Request'); + } + + // We all die one day... + die(); +} + +// See if a username already exists. +function RegisterCheckUsername() +{ + global $sourcedir, $smcFunc, $context, $txt; + + // This is XML! + loadTemplate('Xml'); + $context['sub_template'] = 'check_username'; + $context['checked_username'] = isset($_GET['username']) ? $_GET['username'] : ''; + $context['valid_username'] = true; + + // Clean it up like mother would. + $context['checked_username'] = preg_replace('~[\t\n\r\x0B\0' . ($context['utf8'] ? ($context['server']['complex_preg_chars'] ? '\x{A0}' : "\xC2\xA0") : '\xA0') . ']+~' . ($context['utf8'] ? 'u' : ''), ' ', $context['checked_username']); + if ($smcFunc['strlen']($context['checked_username']) > 25) + $context['checked_username'] = $smcFunc['htmltrim']($smcFunc['substr']($context['checked_username'], 0, 25)); + + // Only these characters are permitted. + if (preg_match('~[<>&"\'=\\\]~', preg_replace('~&#(?:\\d{1,7}|x[0-9a-fA-F]{1,6});~', '', $context['checked_username'])) != 0 || $context['checked_username'] == '_' || $context['checked_username'] == '|' || strpos($context['checked_username'], '[code') !== false || strpos($context['checked_username'], '[/code') !== false) + $context['valid_username'] = false; + + if (stristr($context['checked_username'], $txt['guest_title']) !== false) + $context['valid_username'] = false; + + if (trim($context['checked_username']) == '') + $context['valid_username'] = false; + else + { + require_once($sourcedir . '/Subs-Members.php'); + $context['valid_username'] &= isReservedName($context['checked_username'], 0, false, false) ? 0 : 1; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Reminder.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Reminder.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,398 @@ + 'RemindPick', + 'secret2' => 'SecretAnswer2', + 'setpassword' =>'setPassword', + 'setpassword2' =>'setPassword2' + ); + + // Any subaction? If none, fall through to the main template, which will ask for one. + if (isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']])) + $subActions[$_REQUEST['sa']](); +} + +// Pick a reminder type. +function RemindPick() +{ + global $context, $txt, $scripturl, $sourcedir, $user_info, $webmaster_email, $smcFunc, $language, $modSettings; + + checkSession(); + + // Coming with a known ID? + if (!empty($_REQUEST['uid'])) + { + $where = 'id_member = {int:id_member}'; + $where_params['id_member'] = (int) $_REQUEST['uid']; + } + elseif (isset($_POST['user']) && $_POST['user'] != '') + { + $where = 'member_name = {string:member_name}'; + $where_params['member_name'] = $_POST['user']; + $where_params['email_address'] = $_POST['user']; + } + + // You must enter a username/email address. + if (empty($where)) + fatal_lang_error('username_no_exist', false); + + // Find the user! + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, member_name, email_address, is_activated, validation_code, lngfile, openid_uri, secret_question + FROM {db_prefix}members + WHERE ' . $where . ' + LIMIT 1', + array_merge($where_params, array( + )) + ); + // Maybe email? + if ($smcFunc['db_num_rows']($request) == 0 && empty($_REQUEST['uid'])) + { + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, member_name, email_address, is_activated, validation_code, lngfile, openid_uri, secret_question + FROM {db_prefix}members + WHERE email_address = {string:email_address} + LIMIT 1', + array_merge($where_params, array( + )) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_user_with_email', false); + } + + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $context['account_type'] = !empty($row['openid_uri']) ? 'openid' : 'password'; + + // If the user isn't activated/approved, give them some feedback on what to do next. + if ($row['is_activated'] != 1) + { + // Awaiting approval... + if (trim($row['validation_code']) == '') + fatal_error($txt['registration_not_approved'] . ' ' . $txt['here'] . '.', false); + else + fatal_error($txt['registration_not_activated'] . ' ' . $txt['here'] . '.', false); + } + + // You can't get emailed if you have no email address. + $row['email_address'] = trim($row['email_address']); + if ($row['email_address'] == '') + fatal_error($txt['no_reminder_email'] . '
' . $txt['send_email'] . ' webmaster ' . $txt['to_ask_password'] . '.'); + + // If they have no secret question then they can only get emailed the item, or they are requesting the email, send them an email. + if (empty($row['secret_question']) || (isset($_POST['reminder_type']) && $_POST['reminder_type'] == 'email')) + { + // Randomly generate a new password, with only alpha numeric characters that is a max length of 10 chars. + require_once($sourcedir . '/Subs-Members.php'); + $password = generateValidationCode(); + + require_once($sourcedir . '/Subs-Post.php'); + $replacements = array( + 'REALNAME' => $row['real_name'], + 'REMINDLINK' => $scripturl . '?action=reminder;sa=setpassword;u=' . $row['id_member'] . ';code=' . $password, + 'IP' => $user_info['ip'], + 'MEMBERNAME' => $row['member_name'], + 'OPENID' => $row['openid_uri'], + ); + + $emaildata = loadEmailTemplate('forgot_' . $context['account_type'], $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); + $context['description'] = $txt['reminder_' . (!empty($row['openid_uri']) ? 'openid_' : '') . 'sent']; + + // If they were using OpenID simply email them their OpenID identity. + sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + if (empty($row['openid_uri'])) + // Set the password in the database. + updateMemberData($row['id_member'], array('validation_code' => substr(md5($password), 0, 10))); + + // Set up the template. + $context['sub_template'] = 'sent'; + + // Dont really. + return; + } + // Otherwise are ready to answer the question? + elseif (isset($_POST['reminder_type']) && $_POST['reminder_type'] == 'secret') + { + return SecretAnswerInput(); + } + + // No we're here setup the context for template number 2! + $context['sub_template'] = 'reminder_pick'; + $context['current_member'] = array( + 'id' => $row['id_member'], + 'name' => $row['member_name'], + ); +} + +// Set your new password +function setPassword() +{ + global $txt, $context; + + loadLanguage('Login'); + + // You need a code! + if (!isset($_REQUEST['code'])) + fatal_lang_error('no_access', false); + + // Fill the context array. + $context += array( + 'page_title' => $txt['reminder_set_password'], + 'sub_template' => 'set_password', + 'code' => $_REQUEST['code'], + 'memID' => (int) $_REQUEST['u'] + ); +} + +function setPassword2() +{ + global $context, $txt, $modSettings, $smcFunc, $sourcedir; + + checkSession(); + + if (empty($_POST['u']) || !isset($_POST['passwrd1']) || !isset($_POST['passwrd2'])) + fatal_lang_error('no_access', false); + + $_POST['u'] = (int) $_POST['u']; + + if ($_POST['passwrd1'] != $_POST['passwrd2']) + fatal_lang_error('passwords_dont_match', false); + + if ($_POST['passwrd1'] == '') + fatal_lang_error('no_password', false); + + loadLanguage('Login'); + + // Get the code as it should be from the database. + $request = $smcFunc['db_query']('', ' + SELECT validation_code, member_name, email_address, passwd_flood + FROM {db_prefix}members + WHERE id_member = {int:id_member} + AND is_activated = {int:is_activated} + AND validation_code != {string:blank_string} + LIMIT 1', + array( + 'id_member' => $_POST['u'], + 'is_activated' => 1, + 'blank_string' => '', + ) + ); + + // Does this user exist at all? + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('invalid_userid', false); + + list ($realCode, $username, $email, $flood_value) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Is the password actually valid? + require_once($sourcedir . '/Subs-Auth.php'); + $passwordError = validatePassword($_POST['passwrd1'], $username, array($email)); + + // What - it's not? + if ($passwordError != null) + fatal_lang_error('profile_error_password_' . $passwordError, false); + + require_once($sourcedir . '/LogInOut.php'); + + // Quit if this code is not right. + if (empty($_POST['code']) || substr($realCode, 0, 10) !== substr(md5($_POST['code']), 0, 10)) + { + // Stop brute force attacks like this. + validatePasswordFlood($_POST['u'], $flood_value, false); + + fatal_error($txt['invalid_activation_code'], false); + } + + // Just in case, flood control. + validatePasswordFlood($_POST['u'], $flood_value, true); + + // User validated. Update the database! + updateMemberData($_POST['u'], array('validation_code' => '', 'passwd' => sha1(strtolower($username) . $_POST['passwrd1']))); + + call_integration_hook('integrate_reset_pass', array($username, $username, $_POST['passwrd1'])); + + loadTemplate('Login'); + $context += array( + 'page_title' => $txt['reminder_password_set'], + 'sub_template' => 'login', + 'default_username' => $username, + 'default_password' => $_POST['passwrd1'], + 'never_expire' => false, + 'description' => $txt['reminder_password_set'] + ); +} + +// Get the secret answer. +function SecretAnswerInput() +{ + global $txt, $context, $smcFunc; + + checkSession(); + + // Strings for the register auto javascript clever stuffy wuffy. + loadLanguage('Login'); + + // Check they entered something... + if (empty($_REQUEST['uid'])) + fatal_lang_error('username_no_exist', false); + + // Get the stuff.... + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, member_name, secret_question, openid_uri + FROM {db_prefix}members + WHERE id_member = {int:id_member} + LIMIT 1', + array( + 'id_member' => (int) $_REQUEST['uid'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('username_no_exist', false); + + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $context['account_type'] = !empty($row['openid_uri']) ? 'openid' : 'password'; + + // If there is NO secret question - then throw an error. + if (trim($row['secret_question']) == '') + fatal_lang_error('registration_no_secret_question', false); + + // Ask for the answer... + $context['remind_user'] = $row['id_member']; + $context['remind_type'] = ''; + $context['secret_question'] = $row['secret_question']; + + $context['sub_template'] = 'ask'; +} + +function SecretAnswer2() +{ + global $txt, $context, $modSettings, $smcFunc, $sourcedir; + + checkSession(); + + // Hacker? How did you get this far without an email or username? + if (empty($_REQUEST['uid'])) + fatal_lang_error('username_no_exist', false); + + loadLanguage('Login'); + + // Get the information from the database. + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, member_name, secret_answer, secret_question, openid_uri, email_address + FROM {db_prefix}members + WHERE id_member = {int:id_member} + LIMIT 1', + array( + 'id_member' => $_REQUEST['uid'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('username_no_exist', false); + + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Check if the secret answer is correct. + if ($row['secret_question'] == '' || $row['secret_answer'] == '' || md5($_POST['secret_answer']) != $row['secret_answer']) + { + log_error(sprintf($txt['reminder_error'], $row['member_name']), 'user'); + fatal_lang_error('incorrect_answer', false); + } + + // If it's OpenID this is where the music ends. + if (!empty($row['openid_uri'])) + { + $context['sub_template'] = 'sent'; + $context['description'] = sprintf($txt['reminder_openid_is'], $row['openid_uri']); + return; + } + + // You can't use a blank one! + if (strlen(trim($_POST['passwrd1'])) === 0) + fatal_lang_error('no_password', false); + + // They have to be the same too. + if ($_POST['passwrd1'] != $_POST['passwrd2']) + fatal_lang_error('passwords_dont_match', false); + + // Make sure they have a strong enough password. + require_once($sourcedir . '/Subs-Auth.php'); + $passwordError = validatePassword($_POST['passwrd1'], $row['member_name'], array($row['email_address'])); + + // Invalid? + if ($passwordError != null) + fatal_lang_error('profile_error_password_' . $passwordError, false); + + // Alright, so long as 'yer sure. + updateMemberData($row['id_member'], array('passwd' => sha1(strtolower($row['member_name']) . $_POST['passwrd1']))); + + call_integration_hook('integrate_reset_pass', array($row['member_name'], $row['member_name'], $_POST['passwrd1'])); + + // Tell them it went fine. + loadTemplate('Login'); + $context += array( + 'page_title' => $txt['reminder_password_set'], + 'sub_template' => 'login', + 'default_username' => $row['member_name'], + 'default_password' => $_POST['passwrd1'], + 'never_expire' => false, + 'description' => $txt['reminder_password_set'] + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/RemoveTopic.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/RemoveTopic.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1461 @@ + $topic, + ) + ); + list ($starter, $subject, $approved) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if ($starter == $user_info['id'] && !allowedTo('remove_any')) + isAllowedTo('remove_own'); + else + isAllowedTo('remove_any'); + + // Can they see the topic? + if ($modSettings['postmod_active'] && !$approved && $starter != $user_info['id']) + isAllowedTo('approve_posts'); + + // Notify people that this topic has been removed. + sendNotifications($topic, 'remove'); + + removeTopics($topic); + + // Note, only log topic ID in native form if it's not gone forever. + if (allowedTo('remove_any') || (allowedTo('remove_own') && $starter == $user_info['id'])) + logAction('remove', array((empty($modSettings['recycle_enable']) || $modSettings['recycle_board'] != $board ? 'topic' : 'old_topic_id') => $topic, 'subject' => $subject, 'member' => $starter, 'board' => $board)); + + redirectexit('board=' . $board . '.0'); +} + +// Remove just a single post. +function DeleteMessage() +{ + global $user_info, $topic, $board, $modSettings, $smcFunc; + + checkSession('get'); + + $_REQUEST['msg'] = (int) $_REQUEST['msg']; + + // Is $topic set? + if (empty($topic) && isset($_REQUEST['topic'])) + $topic = (int) $_REQUEST['topic']; + + $request = $smcFunc['db_query']('', ' + SELECT t.id_member_started, m.id_member, m.subject, m.poster_time, m.approved + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = {int:id_msg} AND m.id_topic = {int:current_topic}) + WHERE t.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + 'id_msg' => $_REQUEST['msg'], + ) + ); + list ($starter, $poster, $subject, $post_time, $approved) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Verify they can see this! + if ($modSettings['postmod_active'] && !$approved && !empty($poster) && $poster != $user_info['id']) + isAllowedTo('approve_posts'); + + if ($poster == $user_info['id']) + { + if (!allowedTo('delete_own')) + { + if ($starter == $user_info['id'] && !allowedTo('delete_any')) + isAllowedTo('delete_replies'); + elseif (!allowedTo('delete_any')) + isAllowedTo('delete_own'); + } + elseif (!allowedTo('delete_any') && ($starter != $user_info['id'] || !allowedTo('delete_replies')) && !empty($modSettings['edit_disable_time']) && $post_time + $modSettings['edit_disable_time'] * 60 < time()) + fatal_lang_error('modify_post_time_passed', false); + } + elseif ($starter == $user_info['id'] && !allowedTo('delete_any')) + isAllowedTo('delete_replies'); + else + isAllowedTo('delete_any'); + + // If the full topic was removed go back to the board. + $full_topic = removeMessage($_REQUEST['msg']); + + if (allowedTo('delete_any') && (!allowedTo('delete_own') || $poster != $user_info['id'])) + logAction('delete', array('topic' => $topic, 'subject' => $subject, 'member' => $poster, 'board' => $board)); + + // We want to redirect back to recent action. + if (isset($_REQUEST['recent'])) + redirectexit('action=recent'); + elseif (isset($_REQUEST['profile'], $_REQUEST['start'], $_REQUEST['u'])) + redirectexit('action=profile;u=' . $_REQUEST['u'] . ';area=showposts;start=' . $_REQUEST['start']); + elseif ($full_topic) + redirectexit('board=' . $board . '.0'); + else + redirectexit('topic=' . $topic . '.' . $_REQUEST['start']); +} + +// So long as you are sure... all old posts will be gone. +function RemoveOldTopics2() +{ + global $modSettings, $smcFunc; + + isAllowedTo('admin_forum'); + checkSession('post', 'admin'); + + // No boards at all? Forget it then :/. + if (empty($_POST['boards'])) + redirectexit('action=admin;area=maintain;sa=topics'); + + // This should exist, but we can make sure. + $_POST['delete_type'] = isset($_POST['delete_type']) ? $_POST['delete_type'] : 'nothing'; + + // Custom conditions. + $condition = ''; + $condition_params = array( + 'boards' => array_keys($_POST['boards']), + 'poster_time' => time() - 3600 * 24 * $_POST['maxdays'], + ); + + // Just moved notice topics? + if ($_POST['delete_type'] == 'moved') + { + $condition .= ' + AND m.icon = {string:icon} + AND t.locked = {int:locked}'; + $condition_params['icon'] = 'moved'; + $condition_params['locked'] = 1; + } + // Otherwise, maybe locked topics only? + elseif ($_POST['delete_type'] == 'locked') + { + $condition .= ' + AND t.locked = {int:locked}'; + $condition_params['locked'] = 1; + } + + // Exclude stickies? + if (isset($_POST['delete_old_not_sticky'])) + { + $condition .= ' + AND t.is_sticky = {int:is_sticky}'; + $condition_params['is_sticky'] = 0; + } + + // All we're gonna do here is grab the id_topic's and send them to removeTopics(). + $request = $smcFunc['db_query']('', ' + SELECT t.id_topic + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_last_msg) + WHERE + m.poster_time < {int:poster_time}' . $condition . ' + AND t.id_board IN ({array_int:boards})', + $condition_params + ); + $topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topics[] = $row['id_topic']; + $smcFunc['db_free_result']($request); + + removeTopics($topics, false, true); + + // Log an action into the moderation log. + logAction('pruned', array('days' => $_POST['maxdays'])); + + redirectexit('action=admin;area=maintain;sa=topics;done=purgeold'); +} + +// Removes the passed id_topic's. (permissions are NOT checked here!) +function removeTopics($topics, $decreasePostCount = true, $ignoreRecycling = false) +{ + global $sourcedir, $modSettings, $smcFunc; + + // Nothing to do? + if (empty($topics)) + return; + // Only a single topic. + elseif (is_numeric($topics)) + $topics = array($topics); + + // Decrease the post counts. + if ($decreasePostCount) + { + $requestMembers = $smcFunc['db_query']('', ' + SELECT m.id_member, COUNT(*) AS posts + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + WHERE m.id_topic IN ({array_int:topics}) + AND m.icon != {string:recycled} + AND b.count_posts = {int:do_count_posts} + AND m.approved = {int:is_approved} + GROUP BY m.id_member', + array( + 'do_count_posts' => 0, + 'recycled' => 'recycled', + 'topics' => $topics, + 'is_approved' => 1, + ) + ); + if ($smcFunc['db_num_rows']($requestMembers) > 0) + { + while ($rowMembers = $smcFunc['db_fetch_assoc']($requestMembers)) + updateMemberData($rowMembers['id_member'], array('posts' => 'posts - ' . $rowMembers['posts'])); + } + $smcFunc['db_free_result']($requestMembers); + } + + // Recycle topics that aren't in the recycle board... + if (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 && !$ignoreRecycling) + { + $request = $smcFunc['db_query']('', ' + SELECT id_topic, id_board, unapproved_posts, approved + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:topics}) + AND id_board != {int:recycle_board} + LIMIT ' . count($topics), + array( + 'recycle_board' => $modSettings['recycle_board'], + 'topics' => $topics, + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + { + // Get topics that will be recycled. + $recycleTopics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + $recycleTopics[] = $row['id_topic']; + + // Set the id_previous_board for this topic - and make it not sticky. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET id_previous_board = {int:id_previous_board}, is_sticky = {int:not_sticky} + WHERE id_topic = {int:id_topic}', + array( + 'id_previous_board' => $row['id_board'], + 'id_topic' => $row['id_topic'], + 'not_sticky' => 0, + ) + ); + } + $smcFunc['db_free_result']($request); + + // Mark recycled topics as recycled. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET icon = {string:recycled} + WHERE id_topic IN ({array_int:recycle_topics})', + array( + 'recycle_topics' => $recycleTopics, + 'recycled' => 'recycled', + ) + ); + + // Move the topics to the recycle board. + require_once($sourcedir . '/MoveTopic.php'); + moveTopics($recycleTopics, $modSettings['recycle_board']); + + // Close reports that are being recycled. + require_once($sourcedir . '/ModerationCenter.php'); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_reported + SET closed = {int:is_closed} + WHERE id_topic IN ({array_int:recycle_topics})', + array( + 'recycle_topics' => $recycleTopics, + 'is_closed' => 1, + ) + ); + + updateSettings(array('last_mod_report_action' => time())); + recountOpenReports(); + + // Topics that were recycled don't need to be deleted, so subtract them. + $topics = array_diff($topics, $recycleTopics); + } + else + $smcFunc['db_free_result']($request); + } + + // Still topics left to delete? + if (empty($topics)) + return; + + $adjustBoards = array(); + + // Find out how many posts we are deleting. + $request = $smcFunc['db_query']('', ' + SELECT id_board, approved, COUNT(*) AS num_topics, SUM(unapproved_posts) AS unapproved_posts, + SUM(num_replies) AS num_replies + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:topics}) + GROUP BY id_board, approved', + array( + 'topics' => $topics, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($adjustBoards[$row['id_board']]['num_posts'])) + { + $adjustBoards[$row['id_board']] = array( + 'num_posts' => 0, + 'num_topics' => 0, + 'unapproved_posts' => 0, + 'unapproved_topics' => 0, + 'id_board' => $row['id_board'] + ); + } + // Posts = (num_replies + 1) for each approved topic. + $adjustBoards[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? $row['num_topics'] : 0); + $adjustBoards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts']; + + // Add the topics to the right type. + if ($row['approved']) + $adjustBoards[$row['id_board']]['num_topics'] += $row['num_topics']; + else + $adjustBoards[$row['id_board']]['unapproved_topics'] += $row['num_topics']; + } + $smcFunc['db_free_result']($request); + + // Decrease the posts/topics... + foreach ($adjustBoards as $stats) + { + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET + num_posts = CASE WHEN {int:num_posts} > num_posts THEN 0 ELSE num_posts - {int:num_posts} END, + num_topics = CASE WHEN {int:num_topics} > num_topics THEN 0 ELSE num_topics - {int:num_topics} END, + unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END, + unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END + WHERE id_board = {int:id_board}', + array( + 'id_board' => $stats['id_board'], + 'num_posts' => $stats['num_posts'], + 'num_topics' => $stats['num_topics'], + 'unapproved_posts' => $stats['unapproved_posts'], + 'unapproved_topics' => $stats['unapproved_topics'], + ) + ); + } + + // Remove Polls. + $request = $smcFunc['db_query']('', ' + SELECT id_poll + FROM {db_prefix}topics + WHERE id_topic IN ({array_int:topics}) + AND id_poll > {int:no_poll} + LIMIT ' . count($topics), + array( + 'no_poll' => 0, + 'topics' => $topics, + ) + ); + $polls = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $polls[] = $row['id_poll']; + $smcFunc['db_free_result']($request); + + if (!empty($polls)) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}polls + WHERE id_poll IN ({array_int:polls})', + array( + 'polls' => $polls, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}poll_choices + WHERE id_poll IN ({array_int:polls})', + array( + 'polls' => $polls, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_polls + WHERE id_poll IN ({array_int:polls})', + array( + 'polls' => $polls, + ) + ); + } + + // Get rid of the attachment, if it exists. + require_once($sourcedir . '/ManageAttachments.php'); + $attachmentQuery = array( + 'attachment_type' => 0, + 'id_topic' => $topics, + ); + removeAttachments($attachmentQuery, 'messages'); + + // Delete possible search index entries. + if (!empty($modSettings['search_custom_index_config'])) + { + $customIndexSettings = unserialize($modSettings['search_custom_index_config']); + + $words = array(); + $messages = array(); + $request = $smcFunc['db_query']('', ' + SELECT id_msg, body + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topics})', + array( + 'topics' => $topics, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + $words = array_merge($words, text2words($row['body'], $customIndexSettings['bytes_per_word'], true)); + $messages[] = $row['id_msg']; + } + $smcFunc['db_free_result']($request); + $words = array_unique($words); + + if (!empty($words) && !empty($messages)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_search_words + WHERE id_word IN ({array_int:word_list}) + AND id_msg IN ({array_int:message_list})', + array( + 'word_list' => $words, + 'message_list' => $messages, + ) + ); + } + + // Delete anything related to the topic. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topics})', + array( + 'topics' => $topics, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}calendar + WHERE id_topic IN ({array_int:topics})', + array( + 'topics' => $topics, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_topics + WHERE id_topic IN ({array_int:topics})', + array( + 'topics' => $topics, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_notify + WHERE id_topic IN ({array_int:topics})', + array( + 'topics' => $topics, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}topics + WHERE id_topic IN ({array_int:topics})', + array( + 'topics' => $topics, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_search_subjects + WHERE id_topic IN ({array_int:topics})', + array( + 'topics' => $topics, + ) + ); + + // Update the totals... + updateStats('message'); + updateStats('topic'); + updateSettings(array( + 'calendar_updated' => time(), + )); + + require_once($sourcedir . '/Subs-Post.php'); + $updates = array(); + foreach ($adjustBoards as $stats) + $updates[] = $stats['id_board']; + updateLastMessages($updates); +} + +// Remove a specific message (including permission checks). +function removeMessage($message, $decreasePostCount = true) +{ + global $board, $sourcedir, $modSettings, $user_info, $smcFunc, $context; + + if (empty($message) || !is_numeric($message)) + return false; + + $request = $smcFunc['db_query']('', ' + SELECT + m.id_member, m.icon, m.poster_time, m.subject,' . (empty($modSettings['search_custom_index_config']) ? '' : ' m.body,') . ' + m.approved, t.id_topic, t.id_first_msg, t.id_last_msg, t.num_replies, t.id_board, + t.id_member_started AS id_member_poster, + b.count_posts + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE m.id_msg = {int:id_msg} + LIMIT 1', + array( + 'id_msg' => $message, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + return false; + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + if (empty($board) || $row['id_board'] != $board) + { + $delete_any = boardsAllowedTo('delete_any'); + + if (!in_array(0, $delete_any) && !in_array($row['id_board'], $delete_any)) + { + $delete_own = boardsAllowedTo('delete_own'); + $delete_own = in_array(0, $delete_own) || in_array($row['id_board'], $delete_own); + $delete_replies = boardsAllowedTo('delete_replies'); + $delete_replies = in_array(0, $delete_replies) || in_array($row['id_board'], $delete_replies); + + if ($row['id_member'] == $user_info['id']) + { + if (!$delete_own) + { + if ($row['id_member_poster'] == $user_info['id']) + { + if (!$delete_replies) + fatal_lang_error('cannot_delete_replies', 'permission'); + } + else + fatal_lang_error('cannot_delete_own', 'permission'); + } + elseif (($row['id_member_poster'] != $user_info['id'] || !$delete_replies) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time()) + fatal_lang_error('modify_post_time_passed', false); + } + elseif ($row['id_member_poster'] == $user_info['id']) + { + if (!$delete_replies) + fatal_lang_error('cannot_delete_replies', 'permission'); + } + else + fatal_lang_error('cannot_delete_any', 'permission'); + } + + // Can't delete an unapproved message, if you can't see it! + if ($modSettings['postmod_active'] && !$row['approved'] && $row['id_member'] != $user_info['id'] && !(in_array(0, $delete_any) || in_array($row['id_board'], $delete_any))) + { + $approve_posts = boardsAllowedTo('approve_posts'); + if (!in_array(0, $approve_posts) && !in_array($row['id_board'], $approve_posts)) + return false; + } + } + else + { + // Check permissions to delete this message. + if ($row['id_member'] == $user_info['id']) + { + if (!allowedTo('delete_own')) + { + if ($row['id_member_poster'] == $user_info['id'] && !allowedTo('delete_any')) + isAllowedTo('delete_replies'); + elseif (!allowedTo('delete_any')) + isAllowedTo('delete_own'); + } + elseif (!allowedTo('delete_any') && ($row['id_member_poster'] != $user_info['id'] || !allowedTo('delete_replies')) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time()) + fatal_lang_error('modify_post_time_passed', false); + } + elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('delete_any')) + isAllowedTo('delete_replies'); + else + isAllowedTo('delete_any'); + + if ($modSettings['postmod_active'] && !$row['approved'] && $row['id_member'] != $user_info['id'] && !allowedTo('delete_own')) + isAllowedTo('approve_posts'); + } + + // Close any moderation reports for this message. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_reported + SET closed = {int:is_closed} + WHERE id_msg = {int:id_msg}', + array( + 'is_closed' => 1, + 'id_msg' => $message, + ) + ); + if ($smcFunc['db_affected_rows']() != 0) + { + require_once($sourcedir . '/ModerationCenter.php'); + updateSettings(array('last_mod_report_action' => time())); + recountOpenReports(); + } + + // Delete the *whole* topic, but only if the topic consists of one message. + if ($row['id_first_msg'] == $message) + { + if (empty($board) || $row['id_board'] != $board) + { + $remove_any = boardsAllowedTo('remove_any'); + $remove_any = in_array(0, $remove_any) || in_array($row['id_board'], $remove_any); + if (!$remove_any) + { + $remove_own = boardsAllowedTo('remove_own'); + $remove_own = in_array(0, $remove_own) || in_array($row['id_board'], $remove_own); + } + + if ($row['id_member'] != $user_info['id'] && !$remove_any) + fatal_lang_error('cannot_remove_any', 'permission'); + elseif (!$remove_any && !$remove_own) + fatal_lang_error('cannot_remove_own', 'permission'); + } + else + { + // Check permissions to delete a whole topic. + if ($row['id_member'] != $user_info['id']) + isAllowedTo('remove_any'); + elseif (!allowedTo('remove_any')) + isAllowedTo('remove_own'); + } + + // ...if there is only one post. + if (!empty($row['num_replies'])) + fatal_lang_error('delFirstPost', false); + + removeTopics($row['id_topic']); + return true; + } + + // Deleting a recycled message can not lower anyone's post count. + if ($row['icon'] == 'recycled') + $decreasePostCount = false; + + // This is the last post, update the last post on the board. + if ($row['id_last_msg'] == $message) + { + // Find the last message, set it, and decrease the post count. + $request = $smcFunc['db_query']('', ' + SELECT id_msg, id_member + FROM {db_prefix}messages + WHERE id_topic = {int:id_topic} + AND id_msg != {int:id_msg} + ORDER BY ' . ($modSettings['postmod_active'] ? 'approved DESC, ' : '') . 'id_msg DESC + LIMIT 1', + array( + 'id_topic' => $row['id_topic'], + 'id_msg' => $message, + ) + ); + $row2 = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET + id_last_msg = {int:id_last_msg}, + id_member_updated = {int:id_member_updated}' . (!$modSettings['postmod_active'] || $row['approved'] ? ', + num_replies = CASE WHEN num_replies = {int:no_replies} THEN 0 ELSE num_replies - 1 END' : ', + unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . ' + WHERE id_topic = {int:id_topic}', + array( + 'id_last_msg' => $row2['id_msg'], + 'id_member_updated' => $row2['id_member'], + 'no_replies' => 0, + 'no_unapproved' => 0, + 'id_topic' => $row['id_topic'], + ) + ); + } + // Only decrease post counts. + else + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET ' . ($row['approved'] ? ' + num_replies = CASE WHEN num_replies = {int:no_replies} THEN 0 ELSE num_replies - 1 END' : ' + unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . ' + WHERE id_topic = {int:id_topic}', + array( + 'no_replies' => 0, + 'no_unapproved' => 0, + 'id_topic' => $row['id_topic'], + ) + ); + + // Default recycle to false. + $recycle = false; + + // If recycle topics has been set, make a copy of this message in the recycle board. + // Make sure we're not recycling messages that are already on the recycle board. + if (!empty($modSettings['recycle_enable']) && $row['id_board'] != $modSettings['recycle_board'] && $row['icon'] != 'recycled') + { + // Check if the recycle board exists and if so get the read status. + $request = $smcFunc['db_query']('', ' + SELECT (IFNULL(lb.id_msg, 0) >= b.id_msg_updated) AS is_seen, id_last_msg + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member}) + WHERE b.id_board = {int:recycle_board}', + array( + 'current_member' => $user_info['id'], + 'recycle_board' => $modSettings['recycle_board'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('recycle_no_valid_board'); + list ($isRead, $last_board_msg) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Is there an existing topic in the recycle board to group this post with? + $request = $smcFunc['db_query']('', ' + SELECT id_topic, id_first_msg, id_last_msg + FROM {db_prefix}topics + WHERE id_previous_topic = {int:id_previous_topic} + AND id_board = {int:recycle_board}', + array( + 'id_previous_topic' => $row['id_topic'], + 'recycle_board' => $modSettings['recycle_board'], + ) + ); + list ($id_recycle_topic, $first_topic_msg, $last_topic_msg) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Insert a new topic in the recycle board if $id_recycle_topic is empty. + if (empty($id_recycle_topic)) + $smcFunc['db_insert']('', + '{db_prefix}topics', + array( + 'id_board' => 'int', 'id_member_started' => 'int', 'id_member_updated' => 'int', 'id_first_msg' => 'int', + 'id_last_msg' => 'int', 'unapproved_posts' => 'int', 'approved' => 'int', 'id_previous_topic' => 'int', + ), + array( + $modSettings['recycle_board'], $row['id_member'], $row['id_member'], $message, + $message, 0, 1, $row['id_topic'], + ), + array('id_topic') + ); + + // Capture the ID of the new topic... + $topicID = empty($id_recycle_topic) ? $smcFunc['db_insert_id']('{db_prefix}topics', 'id_topic') : $id_recycle_topic; + + // If the topic creation went successful, move the message. + if ($topicID > 0) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET + id_topic = {int:id_topic}, + id_board = {int:recycle_board}, + icon = {string:recycled}, + approved = {int:is_approved} + WHERE id_msg = {int:id_msg}', + array( + 'id_topic' => $topicID, + 'recycle_board' => $modSettings['recycle_board'], + 'id_msg' => $message, + 'recycled' => 'recycled', + 'is_approved' => 1, + ) + ); + + // Take any reported posts with us... + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_reported + SET + id_topic = {int:id_topic}, + id_board = {int:recycle_board} + WHERE id_msg = {int:id_msg}', + array( + 'id_topic' => $topicID, + 'recycle_board' => $modSettings['recycle_board'], + 'id_msg' => $message, + ) + ); + + // Mark recycled topic as read. + if (!$user_info['is_guest']) + $smcFunc['db_insert']('replace', + '{db_prefix}log_topics', + array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), + array($topicID, $user_info['id'], $modSettings['maxMsgID']), + array('id_topic', 'id_member') + ); + + // Mark recycle board as seen, if it was marked as seen before. + if (!empty($isRead) && !$user_info['is_guest']) + $smcFunc['db_insert']('replace', + '{db_prefix}log_boards', + array('id_board' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), + array($modSettings['recycle_board'], $user_info['id'], $modSettings['maxMsgID']), + array('id_board', 'id_member') + ); + + // Add one topic and post to the recycle bin board. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET + num_topics = num_topics + {int:num_topics_inc}, + num_posts = num_posts + 1' . + ($message > $last_board_msg ? ', id_last_msg = {int:id_merged_msg}' : '') . ' + WHERE id_board = {int:recycle_board}', + array( + 'num_topics_inc' => empty($id_recycle_topic) ? 1 : 0, + 'recycle_board' => $modSettings['recycle_board'], + 'id_merged_msg' => $message, + ) + ); + + // Lets increase the num_replies, and the first/last message ID as appropriate. + if (!empty($id_recycle_topic)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET num_replies = num_replies + 1' . + ($message > $last_topic_msg ? ', id_last_msg = {int:id_merged_msg}' : '') . + ($message < $first_topic_msg ? ', id_first_msg = {int:id_merged_msg}' : '') . ' + WHERE id_topic = {int:id_recycle_topic}', + array( + 'id_recycle_topic' => $id_recycle_topic, + 'id_merged_msg' => $message, + ) + ); + + // Make sure this message isn't getting deleted later on. + $recycle = true; + + // Make sure we update the search subject index. + updateStats('subject', $topicID, $row['subject']); + } + + // If it wasn't approved don't keep it in the queue. + if (!$row['approved']) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}approval_queue + WHERE id_msg = {int:id_msg} + AND id_attach = {int:id_attach}', + array( + 'id_msg' => $message, + 'id_attach' => 0, + ) + ); + } + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET ' . ($row['approved'] ? ' + num_posts = CASE WHEN num_posts = {int:no_posts} THEN 0 ELSE num_posts - 1 END' : ' + unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . ' + WHERE id_board = {int:id_board}', + array( + 'no_posts' => 0, + 'no_unapproved' => 0, + 'id_board' => $row['id_board'], + ) + ); + + // If the poster was registered and the board this message was on incremented + // the member's posts when it was posted, decrease his or her post count. + if (!empty($row['id_member']) && $decreasePostCount && empty($row['count_posts']) && $row['approved']) + updateMemberData($row['id_member'], array('posts' => '-')); + + // Only remove posts if they're not recycled. + if (!$recycle) + { + // Remove the message! + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}messages + WHERE id_msg = {int:id_msg}', + array( + 'id_msg' => $message, + ) + ); + + if (!empty($modSettings['search_custom_index_config'])) + { + $customIndexSettings = unserialize($modSettings['search_custom_index_config']); + $words = text2words($row['body'], $customIndexSettings['bytes_per_word'], true); + if (!empty($words)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_search_words + WHERE id_word IN ({array_int:word_list}) + AND id_msg = {int:id_msg}', + array( + 'word_list' => $words, + 'id_msg' => $message, + ) + ); + } + + // Delete attachment(s) if they exist. + require_once($sourcedir . '/ManageAttachments.php'); + $attachmentQuery = array( + 'attachment_type' => 0, + 'id_msg' => $message, + ); + removeAttachments($attachmentQuery); + } + + // Update the pesky statistics. + updateStats('message'); + updateStats('topic'); + updateSettings(array( + 'calendar_updated' => time(), + )); + + // And now to update the last message of each board we messed with. + require_once($sourcedir . '/Subs-Post.php'); + if ($recycle) + updateLastMessages(array($row['id_board'], $modSettings['recycle_board'])); + else + updateLastMessages($row['id_board']); + + return false; +} + +function RestoreTopic() +{ + global $context, $smcFunc, $modSettings, $sourcedir; + + // Check session. + checkSession('get'); + + // Is recycled board enabled? + if (empty($modSettings['recycle_enable'])) + fatal_lang_error('restored_disabled', 'critical'); + + // Can we be in here? + isAllowedTo('move_any', $modSettings['recycle_board']); + + // We need this file. + require_once($sourcedir . '/MoveTopic.php'); + + $unfound_messages = array(); + $topics_to_restore = array(); + + // Restoring messages? + if (!empty($_REQUEST['msgs'])) + { + $msgs = explode(',', $_REQUEST['msgs']); + foreach ($msgs as $k => $msg) + $msgs[$k] = (int) $msg; + + // Get the id_previous_board and id_previous_topic. + $request = $smcFunc['db_query']('', ' + SELECT m.id_topic, m.id_msg, m.id_board, m.subject, m.id_member, t.id_previous_board, t.id_previous_topic, + t.id_first_msg, b.count_posts, IFNULL(pt.id_board, 0) AS possible_prev_board + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + LEFT JOIN {db_prefix}topics AS pt ON (pt.id_topic = t.id_previous_topic) + WHERE m.id_msg IN ({array_int:messages})', + array( + 'messages' => $msgs, + ) + ); + + $actioned_messages = array(); + $previous_topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Restoring the first post means topic. + if ($row['id_msg'] == $row['id_first_msg'] && $row['id_previous_topic'] == $row['id_topic']) + { + $topics_to_restore[] = $row['id_topic']; + continue; + } + // Don't know where it's going? + if (empty($row['id_previous_topic'])) + { + $unfound_messages[$row['id_msg']] = $row['subject']; + continue; + } + + $previous_topics[] = $row['id_previous_topic']; + if (empty($actioned_messages[$row['id_previous_topic']])) + $actioned_messages[$row['id_previous_topic']] = array( + 'msgs' => array(), + 'count_posts' => $row['count_posts'], + 'subject' => $row['subject'], + 'previous_board' => $row['id_previous_board'], + 'possible_prev_board' => $row['possible_prev_board'], + 'current_topic' => $row['id_topic'], + 'current_board' => $row['id_board'], + 'members' => array(), + ); + + $actioned_messages[$row['id_previous_topic']]['msgs'][$row['id_msg']] = $row['subject']; + if ($row['id_member']) + $actioned_messages[$row['id_previous_topic']]['members'][] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + + // Check for topics we are going to fully restore. + foreach ($actioned_messages as $topic => $data) + if (in_array($topic, $topics_to_restore)) + unset($actioned_messages[$topic]); + + // Load any previous topics to check they exist. + if (!empty($previous_topics)) + { + $request = $smcFunc['db_query']('', ' + SELECT t.id_topic, t.id_board, m.subject + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE t.id_topic IN ({array_int:previous_topics})', + array( + 'previous_topics' => $previous_topics, + ) + ); + $previous_topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $previous_topics[$row['id_topic']] = array( + 'board' => $row['id_board'], + 'subject' => $row['subject'], + ); + $smcFunc['db_free_result']($request); + } + + // Restore each topic. + $messages = array(); + foreach ($actioned_messages as $topic => $data) + { + // If we have topics we are going to restore the whole lot ignore them. + if (in_array($topic, $topics_to_restore)) + { + unset($actioned_messages[$topic]); + continue; + } + + // Move the posts back then! + if (isset($previous_topics[$topic])) + { + mergePosts(array_keys($data['msgs']), $data['current_topic'], $topic); + // Log em. + logAction('restore_posts', array('topic' => $topic, 'subject' => $previous_topics[$topic]['subject'], 'board' => empty($data['previous_board']) ? $data['possible_prev_board'] : $data['previous_board'])); + $messages = array_merge(array_keys($data['msgs']), $messages); + } + else + { + foreach ($data['msgs'] as $msg) + $unfound_messages[$msg['id']] = $msg['subject']; + } + } + + // Put the icons back. + if (!empty($messages)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET icon = {string:icon} + WHERE id_msg IN ({array_int:messages})', + array( + 'icon' => 'xx', + 'messages' => $messages, + ) + ); + } + + // Now any topics? + if (!empty($_REQUEST['topics'])) + { + $topics = explode(',', $_REQUEST['topics']); + foreach ($topics as $key => $id) + $topics_to_restore[] = (int) $id; + } + + if (!empty($topics_to_restore)) + { + // Lets get the data for these topics. + $request = $smcFunc['db_query']('', ' + SELECT t.id_topic, t.id_previous_board, t.id_board, t.id_first_msg, m.subject + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE t.id_topic IN ({array_int:topics})', + array( + 'topics' => $topics_to_restore, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // We can only restore if the previous board is set. + if (empty($row['id_previous_board'])) + { + $unfound_messages[$row['id_first_msg']] = $row['subject']; + continue; + } + + // Ok we got here so me move them from here to there. + moveTopics($row['id_topic'], $row['id_previous_board']); + + // Lets remove the recycled icon. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET icon = {string:icon} + WHERE id_topic = {int:id_topic}', + array( + 'icon' => 'xx', + 'id_topic' => $row['id_topic'], + ) + ); + + // Lets see if the board that we are returning to has post count enabled. + $request2 = $smcFunc['db_query']('', ' + SELECT count_posts + FROM {db_prefix}boards + WHERE id_board = {int:board}', + array( + 'board' => $row['id_previous_board'], + ) + ); + list ($count_posts) = $smcFunc['db_fetch_row']($request2); + $smcFunc['db_free_result']($request2); + + if (empty($count_posts)) + { + // Lets get the members that need their post count restored. + $request2 = $smcFunc['db_query']('', ' + SELECT id_member, COUNT(id_msg) AS post_count + FROM {db_prefix}messages + WHERE id_topic = {int:topic} + AND approved = {int:is_approved} + GROUP BY id_member', + array( + 'topic' => $row['id_topic'], + 'is_approved' => 1, + ) + ); + + while ($member = $smcFunc['db_fetch_assoc']($request2)) + updateMemberData($member['id_member'], array('posts' => 'posts + ' . $member['post_count'])); + $smcFunc['db_free_result']($request2); + } + + // Log it. + logAction('restore_topic', array('topic' => $row['id_topic'], 'board' => $row['id_board'], 'board_to' => $row['id_previous_board'])); + } + $smcFunc['db_free_result']($request); + } + + // Didn't find some things? + if (!empty($unfound_messages)) + fatal_lang_error('restore_not_found', false, array(implode('
', $unfound_messages))); + + // Just send them to the index if they get here. + redirectexit(); +} + +// Take a load of messages from one place and stick them in a topic. +function mergePosts($msgs = array(), $from_topic, $target_topic) +{ + global $context, $smcFunc, $modSettings, $sourcedir; + + //!!! This really needs to be rewritten to take a load of messages from ANY topic, it's also inefficient. + + // Is it an array? + if (!is_array($msgs)) + $msgs = array($msgs); + + // Lets make sure they are int. + foreach ($msgs as $key => $msg) + $msgs[$key] = (int) $msg; + + // Get the source information. + $request = $smcFunc['db_query']('', ' + SELECT t.id_board, t.id_first_msg, t.num_replies, t.unapproved_posts + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE t.id_topic = {int:from_topic}', + array( + 'from_topic' => $from_topic, + ) + ); + list ($from_board, $from_first_msg, $from_replies, $from_unapproved_posts) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Get some target topic and board stats. + $request = $smcFunc['db_query']('', ' + SELECT t.id_board, t.id_first_msg, t.num_replies, t.unapproved_posts, b.count_posts + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE t.id_topic = {int:target_topic}', + array( + 'target_topic' => $target_topic, + ) + ); + list ($target_board, $target_first_msg, $target_replies, $target_unapproved_posts, $count_posts) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Lets see if the board that we are returning to has post count enabled. + if (empty($count_posts)) + { + // Lets get the members that need their post count restored. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}messages + WHERE id_msg IN ({array_int:messages}) + AND approved = {int:is_approved}', + array( + 'messages' => $msgs, + 'is_approved' => 1, + ) + ); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + updateMemberData($row['id_member'], array('posts' => '+')); + } + + // Time to move the messages. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET + id_topic = {int:target_topic}, + id_board = {int:target_board}, + icon = {string:icon} + WHERE id_msg IN({array_int:msgs})', + array( + 'target_topic' => $target_topic, + 'target_board' => $target_board, + 'icon' => $target_board == $modSettings['recycle_board'] ? 'recycled' : 'xx', + 'msgs' => $msgs, + ) + ); + + // Fix the id_first_msg and id_last_msg for the target topic. + $target_topic_data = array( + 'num_replies' => 0, + 'unapproved_posts' => 0, + 'id_first_msg' => 9999999999, + ); + $request = $smcFunc['db_query']('', ' + SELECT MIN(id_msg) AS id_first_msg, MAX(id_msg) AS id_last_msg, COUNT(*) AS message_count, approved + FROM {db_prefix}messages + WHERE id_topic = {int:target_topic} + GROUP BY id_topic, approved + ORDER BY approved ASC + LIMIT 2', + array( + 'target_topic' => $target_topic, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['id_first_msg'] < $target_topic_data['id_first_msg']) + $target_topic_data['id_first_msg'] = $row['id_first_msg']; + $target_topic_data['id_last_msg'] = $row['id_last_msg']; + if (!$row['approved']) + $target_topic_data['unapproved_posts'] = $row['message_count']; + else + $target_topic_data['num_replies'] = max(0, $row['message_count'] - 1); + } + $smcFunc['db_free_result']($request); + + // We have a new post count for the board. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET + num_posts = num_posts + {int:diff_replies}, + unapproved_posts = unapproved_posts + {int:diff_unapproved_posts} + WHERE id_board = {int:target_board}', + array( + 'diff_replies' => $target_topic_data['num_replies'] - $target_replies, // Lets keep in mind that the first message in a topic counts towards num_replies in a board. + 'diff_unapproved_posts' => $target_topic_data['unapproved_posts'] - $target_unapproved_posts, + 'target_board' => $target_board, + ) + ); + + // In some cases we merged the only post in a topic so the topic data is left behind in the topic table. + $request = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}messages + WHERE id_topic = {int:from_topic}', + array( + 'from_topic' => $from_topic, + ) + ); + + // Remove the topic if it doesn't have any messages. + $topic_exists = true; + if ($smcFunc['db_num_rows']($request) == 0) + { + removeTopics($from_topic, false, true); + $topic_exists = false; + } + $smcFunc['db_free_result']($request); + + // Recycled topic. + if ($topic_exists == true) + { + // Fix the id_first_msg and id_last_msg for the source topic. + $source_topic_data = array( + 'num_replies' => 0, + 'unapproved_posts' => 0, + 'id_first_msg' => 9999999999, + ); + $request = $smcFunc['db_query']('', ' + SELECT MIN(id_msg) AS id_first_msg, MAX(id_msg) AS id_last_msg, COUNT(*) AS message_count, approved, subject + FROM {db_prefix}messages + WHERE id_topic = {int:from_topic} + GROUP BY id_topic, approved + ORDER BY approved ASC + LIMIT 2', + array( + 'from_topic' => $from_topic, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['id_first_msg'] < $source_topic_data['id_first_msg']) + $source_topic_data['id_first_msg'] = $row['id_first_msg']; + $source_topic_data['id_last_msg'] = $row['id_last_msg']; + if (!$row['approved']) + $source_topic_data['unapproved_posts'] = $row['message_count']; + else + $source_topic_data['num_replies'] = max(0, $row['message_count'] - 1); + } + $smcFunc['db_free_result']($request); + + // Update the topic details for the source topic. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET + id_first_msg = {int:id_first_msg}, + id_last_msg = {int:id_last_msg}, + num_replies = {int:num_replies}, + unapproved_posts = {int:unapproved_posts} + WHERE id_topic = {int:from_topic}', + array( + 'id_first_msg' => $source_topic_data['id_first_msg'], + 'id_last_msg' => $source_topic_data['id_last_msg'], + 'num_replies' => $source_topic_data['num_replies'], + 'unapproved_posts' => $source_topic_data['unapproved_posts'], + 'from_topic' => $from_topic, + ) + ); + + // We have a new post count for the source board. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET + num_posts = num_posts + {int:diff_replies}, + unapproved_posts = unapproved_posts + {int:diff_unapproved_posts} + WHERE id_board = {int:from_board}', + array( + 'diff_replies' => $source_topic_data['num_replies'] - $from_replies, // Lets keep in mind that the first message in a topic counts towards num_replies in a board. + 'diff_unapproved_posts' => $source_topic_data['unapproved_posts'] - $from_unapproved_posts, + 'from_board' => $from_board, + ) + ); + } + + // Finally get around to updating the destination topic, now all indexes etc on the source are fixed. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET + id_first_msg = {int:id_first_msg}, + id_last_msg = {int:id_last_msg}, + num_replies = {int:num_replies}, + unapproved_posts = {int:unapproved_posts} + WHERE id_topic = {int:target_topic}', + array( + 'id_first_msg' => $target_topic_data['id_first_msg'], + 'id_last_msg' => $target_topic_data['id_last_msg'], + 'num_replies' => $target_topic_data['num_replies'], + 'unapproved_posts' => $target_topic_data['unapproved_posts'], + 'target_topic' => $target_topic, + ) + ); + + // Need it to update some stats. + require_once($sourcedir . '/Subs-Post.php'); + + // Update stats. + updateStats('topic'); + updateStats('message'); + + // Subject cache? + $cache_updates = array(); + if ($target_first_msg != $target_topic_data['id_first_msg']) + $cache_updates[] = $target_topic_data['id_first_msg']; + if (!empty($source_topic_data['id_first_msg']) && $from_first_msg != $source_topic_data['id_first_msg']) + $cache_updates[] = $source_topic_data['id_first_msg']; + + if (!empty($cache_updates)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_topic, subject + FROM {db_prefix}messages + WHERE id_msg IN ({array_int:first_messages})', + array( + 'first_messages' => $cache_updates, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + updateStats('subject', $row['id_topic'], $row['subject']); + $smcFunc['db_free_result']($request); + } + + updateLastMessages(array($from_board, $target_board)); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/RepairBoards.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/RepairBoards.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1644 @@ + $txt['maintain_title'], + 'help' => '', + 'description' => $txt['maintain_info'], + 'tabs' => array(), + ); + + // Start displaying errors without fixing them. + if (isset($_GET['fixErrors'])) + checkSession('get'); + + // Will want this. + loadForumTests(); + + // Giant if/else. The first displays the forum errors if a variable is not set and asks + // if you would like to continue, the other fixes the errors. + if (!isset($_GET['fixErrors'])) + { + $context['error_search'] = true; + $context['repair_errors'] = array(); + $context['to_fix'] = findForumErrors(); + + if (!empty($context['to_fix'])) + { + $_SESSION['repairboards_to_fix'] = $context['to_fix']; + $_SESSION['repairboards_to_fix2'] = null; + + if (empty($context['repair_errors'])) + $context['repair_errors'][] = '???'; + } + } + else + { + $context['error_search'] = false; + $context['to_fix'] = isset($_SESSION['repairboards_to_fix']) ? $_SESSION['repairboards_to_fix'] : array(); + + require_once($sourcedir . '/Subs-Boards.php'); + + // Get the MySQL version for future reference. + $mysql_version = $smcFunc['db_server_info']($db_connection); + + // Actually do the fix. + findForumErrors(true); + + // Note that we've changed everything possible ;) + updateSettings(array( + 'settings_updated' => time(), + )); + updateStats('message'); + updateStats('topic'); + updateSettings(array( + 'calendar_updated' => time(), + )); + + if (!empty($salvageBoardID)) + { + $context['redirect_to_recount'] = true; + } + + $_SESSION['repairboards_to_fix'] = null; + $_SESSION['repairboards_to_fix2'] = null; + } +} + +function pauseRepairProcess($to_fix, $current_step_description, $max_substep = 0, $force = false) +{ + global $context, $txt, $time_start, $db_temp_cache, $db_cache; + + // More time, I need more time! + @set_time_limit(600); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + // Errr, wait. How much time has this taken already? + if (!$force && time() - array_sum(explode(' ', $time_start)) < 3) + return; + + // Restore the query cache if interested. + if (!empty($db_temp_cache)) + $db_cache = $db_temp_cache; + + $context['continue_get_data'] = '?action=admin;area=repairboards' . (isset($_GET['fixErrors']) ? ';fixErrors' : '') . ';step=' . $_GET['step'] . ';substep=' . $_GET['substep'] . ';' . $context['session_var'] . '=' . $context['session_id']; + $context['page_title'] = $txt['not_done_title']; + $context['continue_post_data'] = ''; + $context['continue_countdown'] = '2'; + $context['sub_template'] = 'not_done'; + + // Change these two if more steps are added! + if (empty($max_substep)) + $context['continue_percent'] = round(($_GET['step'] * 100) / $context['total_steps']); + else + $context['continue_percent'] = round((($_GET['step'] + ($_GET['substep'] / $max_substep)) * 100) / $context['total_steps']); + + // Never more than 100%! + $context['continue_percent'] = min($context['continue_percent'], 100); + + // What about substeps? + $context['substep_enabled'] = $max_substep != 0; + $context['substep_title'] = sprintf($txt['repair_currently_' . (isset($_GET['fixErrors']) ? 'fixing' : 'checking')], (isset($txt['repair_operation_' . $current_step_description]) ? $txt['repair_operation_' . $current_step_description] : $current_step_description)); + $context['substep_continue_percent'] = $max_substep == 0 ? 0 : round(($_GET['substep'] * 100) / $max_substep, 1); + + $_SESSION['repairboards_to_fix'] = $to_fix; + $_SESSION['repairboards_to_fix2'] = $context['repair_errors']; + + obExit(); +} + +// Load up all the tests we might want to do ;) +function loadForumTests() +{ + global $smcFunc, $errorTests; + + /* Here this array is defined like so: + string check_query: Query to be executed when testing if errors exist. + string check_type: Defines how it knows if a problem was found. If set to count looks for the first variable from check_query + being > 0. Anything else it looks for some results. If not set assumes you want results. + string fix_it_query: When doing fixes if an error was detected this query is executed to "fix" it. + string fix_query: The query to execute to get data when doing a fix. If not set check_query is used again. + array fix_collect: This array is used if the fix is basically gathering all broken ids and then doing something with it. + - string index: The value returned from the main query and passed to the processing function. + - process: A function passed an array of ids to execute the fix on. + function fix_processing: + Function called for each row returned from fix_query to execute whatever fixes are required. + function fix_full_processing: + As above but does the while loop and everything itself - except the freeing. + array force_fix: If this is set then the error types included within this array will also be assumed broken. + Note: At the moment only processes these if they occur after the primary error in the array. + */ + + // This great array contains all of our error checks, fixes, etc etc etc. + $errorTests = array( + // Make a last-ditch-effort check to get rid of topics with zeros.. + 'zero_topics' => array( + 'check_query' => ' + SELECT COUNT(*) + FROM {db_prefix}topics + WHERE id_topic = 0', + 'check_type' => 'count', + 'fix_it_query' => ' + UPDATE {db_prefix}topics + SET id_topic = NULL + WHERE id_topic = 0', + 'message' => 'repair_zero_ids', + ), + // ... and same with messages. + 'zero_messages' => array( + 'check_query' => ' + SELECT COUNT(*) + FROM {db_prefix}messages + WHERE id_msg = 0', + 'check_type' => 'count', + 'fix_it_query' => ' + UPDATE {db_prefix}messages + SET id_msg = NULL + WHERE id_msg = 0', + 'message' => 'repair_zero_ids', + ), + // Find messages that don't have existing topics. + 'missing_topics' => array( + 'substeps' => array( + 'step_size' => 1000, + 'step_max' => ' + SELECT MAX(id_topic) + FROM {db_prefix}messages' + ), + 'check_query' => ' + SELECT m.id_topic, m.id_msg + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + WHERE m.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND t.id_topic IS NULL + ORDER BY m.id_topic, m.id_msg', + 'fix_query' => ' + SELECT + m.id_board, m.id_topic, MIN(m.id_msg) AS myid_first_msg, MAX(m.id_msg) AS myid_last_msg, + COUNT(*) - 1 AS my_num_replies + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + WHERE t.id_topic IS NULL + GROUP BY m.id_topic, m.id_board', + 'fix_processing' => create_function('$row', ' + global $smcFunc, $salvageBoardID; + + // Only if we don\'t have a reasonable idea of where to put it. + if ($row[\'id_board\'] == 0) + { + createSalvageArea(); + $row[\'id_board\'] = (int) $salvageBoardID; + } + + // Make sure that no topics claim the first/last message as theirs. + $smcFunc[\'db_query\'](\'\', \' + UPDATE {db_prefix}topics + SET id_first_msg = 0 + WHERE id_first_msg = {int:id_first_msg}\', + array( + \'id_first_msg\' => $row[\'myid_first_msg\'], + ) + ); + $smcFunc[\'db_query\'](\'\', \' + UPDATE {db_prefix}topics + SET id_last_msg = 0 + WHERE id_last_msg = {int:id_last_msg}\', + array( + \'id_last_msg\' => $row[\'myid_last_msg\'], + ) + ); + + $memberStartedID = (int) getMsgMemberID($row[\'myid_first_msg\']); + $memberUpdatedID = (int) getMsgMemberID($row[\'myid_last_msg\']); + + $smcFunc[\'db_insert\'](\'\', + \'{db_prefix}topics\', + array( + \'id_board\' => \'int\', + \'id_member_started\' => \'int\', + \'id_member_updated\' => \'int\', + \'id_first_msg\' => \'int\', + \'id_last_msg\' => \'int\', + \'num_replies\' => \'int\' + ), + array( + $row[\'id_board\'], + $memberStartedID, + $memberUpdatedID, + $row[\'myid_first_msg\'], + $row[\'myid_last_msg\'], + $row[\'my_num_replies\'] + ), + array(\'id_topic\') + ); + + $newTopicID = $smcFunc[\'db_insert_id\']("{db_prefix}topics", \'id_topic\'); + + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}messages + SET id_topic = $newTopicID, id_board = $row[id_board] + WHERE id_topic = $row[id_topic]", + array( + ) + ); + '), + 'force_fix' => array('stats_topics'), + 'messages' => array('repair_missing_topics', 'id_msg', 'id_topic'), + ), + // Find topics with no messages. + 'missing_messages' => array( + 'substeps' => array( + 'step_size' => 1000, + 'step_max' => ' + SELECT MAX(id_topic) + FROM {db_prefix}topics' + ), + 'check_query' => ' + SELECT t.id_topic, COUNT(m.id_msg) AS num_msg + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic) + WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY t.id_topic + HAVING COUNT(m.id_msg) = 0', + // Remove all topics that have zero messages in the messages table. + 'fix_collect' => array( + 'index' => 'id_topic', + 'process' => create_function('$topics', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}topics + WHERE id_topic IN ({array_int:topics})", + array( + \'topics\' => $topics + ) + ); + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_topics + WHERE id_topic IN ({array_int:topics})", + array( + \'topics\' => $topics + ) + ); + '), + ), + 'messages' => array('repair_missing_messages', 'id_topic'), + ), + 'polls_missing_topics' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_poll) + FROM {db_prefix}polls' + ), + 'check_query' => ' + SELECT p.id_poll, p.id_member, p.poster_name, t.id_board + FROM {db_prefix}polls AS p + LEFT JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll) + WHERE p.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND t.id_poll IS NULL', + 'fix_processing' => create_function('$row', ' + global $smcFunc, $salvageBoardID, $txt; + + // Only if we don\'t have a reasonable idea of where to put it. + if ($row[\'id_board\'] == 0) + { + createSalvageArea(); + $row[\'id_board\'] = (int) $salvageBoardID; + } + + $row[\'poster_name\'] = !empty($row[\'poster_name\']) ? $row[\'poster_name\'] : $txt[\'guest\']; + + $smcFunc[\'db_insert\'](\'\', + \'{db_prefix}messages\', + array( + \'id_board\' => \'int\', + \'id_topic\' => \'int\', + \'poster_time\' => \'int\', + \'id_member\' => \'int\', + \'subject\' => \'string-255\', + \'poster_name\' => \'string-255\', + \'poster_email\' => \'string-255\', + \'poster_ip\' => \'string-16\', + \'smileys_enabled\' => \'int\', + \'body\' => \'string-65534\', + \'icon\' => \'string-16\', + \'approved\' => \'int\', + ), + array( + $row[\'id_board\'], + 0, + time(), + $row[\'id_member\'], + $txt[\'salvaged_poll_topic_name\'], + $row[\'poster_name\'], + \'\', + \'127.0.0.1\', + 1, + $txt[\'salvaged_poll_message_body\'], + \'xx\', + 1, + ), + array(\'id_topic\') + ); + + $newMessageID = $smcFunc[\'db_insert_id\']("{db_prefix}messages", \'id_msg\'); + + $smcFunc[\'db_insert\'](\'\', + \'{db_prefix}topics\', + array( + \'id_board\' => \'int\', + \'id_poll\' => \'int\', + \'id_member_started\' => \'int\', + \'id_member_updated\' => \'int\', + \'id_first_msg\' => \'int\', + \'id_last_msg\' => \'int\', + \'num_replies\' => \'int\', + ), + array( + $row[\'id_board\'], + $row[\'id_poll\'], + $row[\'id_member\'], + $row[\'id_member\'], + $newMessageID, + $newMessageID, + 0, + ), + array(\'id_topic\') + ); + + $newTopicID = $smcFunc[\'db_insert_id\']("{db_prefix}topics", \'id_topic\'); + + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}messages + SET id_topic = $newTopicID, id_board = $row[id_board] + WHERE id_msg = $newMessageID", + array( + ) + ); + + updateStats(\'subject\', $newTopicID, $txt[\'salvaged_poll_topic_name\']); + + '), + 'force_fix' => array('stats_topics'), + 'messages' => array('repair_polls_missing_topics', 'id_poll', 'id_topic'), + ), + 'stats_topics' => array( + 'substeps' => array( + 'step_size' => 200, + 'step_max' => ' + SELECT MAX(id_topic) + FROM {db_prefix}topics' + ), + 'check_query' => ' + SELECT + t.id_topic, t.id_first_msg, t.id_last_msg, + CASE WHEN MIN(ma.id_msg) > 0 THEN + CASE WHEN MIN(mu.id_msg) > 0 THEN + CASE WHEN MIN(mu.id_msg) < MIN(ma.id_msg) THEN MIN(mu.id_msg) ELSE MIN(ma.id_msg) END ELSE + MIN(ma.id_msg) END ELSE + MIN(mu.id_msg) END AS myid_first_msg, + CASE WHEN MAX(ma.id_msg) > 0 THEN MAX(ma.id_msg) ELSE MIN(mu.id_msg) END AS myid_last_msg, + t.approved, mf.approved, mf.approved AS firstmsg_approved + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = 1) + LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = 0) + LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) + WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY t.id_topic, t.id_first_msg, t.id_last_msg, t.approved, mf.approved + ORDER BY t.id_topic', + 'fix_processing' => create_function('$row', ' + global $smcFunc; + $row[\'firstmsg_approved\'] = (int) $row[\'firstmsg_approved\']; + $row[\'myid_first_msg\'] = (int) $row[\'myid_first_msg\']; + $row[\'myid_last_msg\'] = (int) $row[\'myid_last_msg\']; + + // Not really a problem? + if ($row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'approved\'] == $row[\'firstmsg_approved\']) + return false; + + $memberStartedID = (int) getMsgMemberID($row[\'myid_first_msg\']); + $memberUpdatedID = (int) getMsgMemberID($row[\'myid_last_msg\']); + + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}topics + SET id_first_msg = $row[myid_first_msg], + id_member_started = $memberStartedID, id_last_msg = $row[myid_last_msg], + id_member_updated = $memberUpdatedID, approved = $row[firstmsg_approved] + WHERE id_topic = $row[id_topic]", + array( + ) + ); + '), + 'message_function' => create_function('$row', ' + global $txt, $context; + + // A pretend error? + if ($row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'approved\'] == $row[\'firstmsg_approved\']) + return false; + + if ($row[\'id_first_msg\'] != $row[\'myid_first_msg\']) + $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_1\'], $row[\'id_topic\'], $row[\'id_first_msg\']); + if ($row[\'id_last_msg\'] != $row[\'myid_last_msg\']) + $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_2\'], $row[\'id_topic\'], $row[\'id_last_msg\']); + if ($row[\'approved\'] != $row[\'firstmsg_approved\']) + $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_5\'], $row[\'id_topic\']); + + return true; + '), + ), + // Find topics with incorrect num_replies. + 'stats_topics2' => array( + 'substeps' => array( + 'step_size' => 300, + 'step_max' => ' + SELECT MAX(id_topic) + FROM {db_prefix}topics' + ), + 'check_query' => ' + SELECT + t.id_topic, t.num_replies, mf.approved, + CASE WHEN COUNT(ma.id_msg) > 0 THEN CASE WHEN mf.approved > 0 THEN COUNT(ma.id_msg) - 1 ELSE COUNT(ma.id_msg) END ELSE 0 END AS my_num_replies + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = 1) + LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) + WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY t.id_topic, t.num_replies, mf.approved + ORDER BY t.id_topic', + 'fix_processing' => create_function('$row', ' + global $smcFunc; + $row[\'my_num_replies\'] = (int) $row[\'my_num_replies\']; + + // Not really a problem? + if ($row[\'my_num_replies\'] == $row[\'num_replies\']) + return false; + + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}topics + SET num_replies = $row[my_num_replies] + WHERE id_topic = $row[id_topic]", + array( + ) + ); + '), + 'message_function' => create_function('$row', ' + global $txt, $context; + + // Just joking? + if ($row[\'my_num_replies\'] == $row[\'num_replies\']) + return false; + + if ($row[\'num_replies\'] != $row[\'my_num_replies\']) + $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_3\'], $row[\'id_topic\'], $row[\'num_replies\']); + + return true; + '), + ), + // Find topics with incorrect unapproved_posts. + 'stats_topics3' => array( + 'substeps' => array( + 'step_size' => 1000, + 'step_max' => ' + SELECT MAX(id_topic) + FROM {db_prefix}topics' + ), + 'check_query' => ' + SELECT + t.id_topic, t.unapproved_posts, COUNT(mu.id_msg) AS my_unapproved_posts + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = 0) + WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY t.id_topic, t.unapproved_posts + HAVING unapproved_posts != COUNT(mu.id_msg) + ORDER BY t.id_topic', + 'fix_processing' => create_function('$row', ' + global $smcFunc; + $row[\'my_unapproved_posts\'] = (int) $row[\'my_unapproved_posts\']; + + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}topics + SET unapproved_posts = $row[my_unapproved_posts] + WHERE id_topic = $row[id_topic]", + array( + ) + ); + '), + 'messages' => array('repair_stats_topics_4', 'id_topic', 'unapproved_posts'), + ), + // Find topics with nonexistent boards. + 'missing_boards' => array( + 'substeps' => array( + 'step_size' => 1000, + 'step_max' => ' + SELECT MAX(id_topic) + FROM {db_prefix}topics' + ), + 'check_query' => ' + SELECT t.id_topic, t.id_board + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE b.id_board IS NULL + AND t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + ORDER BY t.id_board, t.id_topic', + 'fix_query' => ' + SELECT t.id_board, COUNT(*) AS my_num_topics, COUNT(m.id_msg) AS my_num_posts + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + LEFT JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic) + WHERE b.id_board IS NULL + AND t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY t.id_board', + 'fix_processing' => create_function('$row', ' + global $smcFunc, $salvageCatID; + createSalvageArea(); + + $row[\'my_num_topics\'] = (int) $row[\'my_num_topics\']; + $row[\'my_num_posts\'] = (int) $row[\'my_num_posts\']; + + $smcFunc[\'db_insert\'](\'\', + \'{db_prefix}boards\', + array(\'id_cat\' => \'int\', \'name\' => \'string\', \'description\' => \'string\', \'num_topics\' => \'int\', \'num_posts\' => \'int\', \'member_groups\' => \'string\'), + array($salvageCatID, \'Salvaged board\', \'\', $row[\'my_num_topics\'], $row[\'my_num_posts\'], \'1\'), + array(\'id_board\') + ); + $newBoardID = $smcFunc[\'db_insert_id\'](\'{db_prefix}boards\', \'id_board\'); + + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}topics + SET id_board = $newBoardID + WHERE id_board = $row[id_board]", + array( + ) + ); + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}messages + SET id_board = $newBoardID + WHERE id_board = $row[id_board]", + array( + ) + ); + '), + 'messages' => array('repair_missing_boards', 'id_topic', 'id_board'), + ), + // Find boards with nonexistent categories. + 'missing_categories' => array( + 'check_query' => ' + SELECT b.id_board, b.id_cat + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE c.id_cat IS NULL + ORDER BY b.id_cat, b.id_board', + 'fix_collect' => array( + 'index' => 'id_cat', + 'process' => create_function('$cats', ' + global $smcFunc, $salvageCatID; + createSalvageArea(); + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}boards + SET id_cat = $salvageCatID + WHERE id_cat IN ({array_int:categories})", + array( + \'categories\' => $cats + ) + ); + '), + ), + 'messages' => array('repair_missing_categories', 'id_board', 'id_cat'), + ), + // Find messages with nonexistent members. + 'missing_posters' => array( + 'substeps' => array( + 'step_size' => 2000, + 'step_max' => ' + SELECT MAX(id_msg) + FROM {db_prefix}messages' + ), + 'check_query' => ' + SELECT m.id_msg, m.id_member + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE mem.id_member IS NULL + AND m.id_member != 0 + AND m.id_msg BETWEEN {STEP_LOW} AND {STEP_HIGH} + ORDER BY m.id_msg', + // Last step-make sure all non-guest posters still exist. + 'fix_collect' => array( + 'index' => 'id_msg', + 'process' => create_function('$msgs', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}messages + SET id_member = 0 + WHERE id_msg IN ({array_int:msgs})", + array( + \'msgs\' => $msgs + ) + ); + '), + ), + 'messages' => array('repair_missing_posters', 'id_msg', 'id_member'), + ), + // Find boards with nonexistent parents. + 'missing_parents' => array( + 'check_query' => ' + SELECT b.id_board, b.id_parent + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}boards AS p ON (p.id_board = b.id_parent) + WHERE b.id_parent != 0 + AND (p.id_board IS NULL OR p.id_board = b.id_board) + ORDER BY b.id_parent, b.id_board', + 'fix_collect' => array( + 'index' => 'id_parent', + 'process' => create_function('$parents', ' + global $smcFunc, $salvageBoardID, $salvageCatID; + createSalvageArea(); + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}boards + SET id_parent = $salvageBoardID, id_cat = $salvageCatID, child_level = 1 + WHERE id_parent IN ({array_int:parents})", + array( + \'parents\' => $parents + ) + ); + '), + ), + 'messages' => array('repair_missing_parents', 'id_board', 'id_parent'), + ), + 'missing_polls' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_poll) + FROM {db_prefix}topics' + ), + 'check_query' => ' + SELECT t.id_poll, t.id_topic + FROM {db_prefix}topics AS t + LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll) + WHERE t.id_poll != 0 + AND t.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND p.id_poll IS NULL', + 'fix_collect' => array( + 'index' => 'id_poll', + 'process' => create_function('$polls', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}topics + SET id_poll = 0 + WHERE id_poll IN ({array_int:polls})", + array( + \'polls\' => $polls + ) + ); + '), + ), + 'messages' => array('repair_missing_polls', 'id_topic', 'id_poll'), + ), + 'missing_calendar_topics' => array( + 'substeps' => array( + 'step_size' => 1000, + 'step_max' => ' + SELECT MAX(id_topic) + FROM {db_prefix}calendar' + ), + 'check_query' => ' + SELECT cal.id_topic, cal.id_event + FROM {db_prefix}calendar AS cal + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic) + WHERE cal.id_topic != 0 + AND cal.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND t.id_topic IS NULL + ORDER BY cal.id_topic', + 'fix_collect' => array( + 'index' => 'id_topic', + 'process' => create_function('$events', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', \' + UPDATE {db_prefix}calendar + SET id_topic = 0, id_board = 0 + WHERE id_topic IN ({array_int:events})\', + array( + \'events\' => $events + ) + ); + '), + ), + 'messages' => array('repair_missing_calendar_topics', 'id_event', 'id_topic'), + ), + 'missing_log_topics' => array( + 'substeps' => array( + 'step_size' => 150, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}log_topics' + ), + 'check_query' => ' + SELECT lt.id_topic + FROM {db_prefix}log_topics AS lt + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lt.id_topic) + WHERE t.id_topic IS NULL + AND lt.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}', + 'fix_collect' => array( + 'index' => 'id_topic', + 'process' => create_function('$topics', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_topics + WHERE id_topic IN ({array_int:topics})", + array( + \'topics\' => $topics + ) + ); + '), + ), + 'messages' => array('repair_missing_log_topics', 'id_topic'), + ), + 'missing_log_topics_members' => array( + 'substeps' => array( + 'step_size' => 150, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}log_topics' + ), + 'check_query' => ' + SELECT lt.id_member + FROM {db_prefix}log_topics AS lt + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lt.id_member) + WHERE mem.id_member IS NULL + AND lt.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY lt.id_member', + 'fix_collect' => array( + 'index' => 'id_member', + 'process' => create_function('$members', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_topics + WHERE id_member IN ({array_int:members})", + array( + \'members\' => $members + ) + ); + '), + ), + 'messages' => array('repair_missing_log_topics_members', 'id_member'), + ), + 'missing_log_boards' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}log_boards' + ), + 'check_query' => ' + SELECT lb.id_board + FROM {db_prefix}log_boards AS lb + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lb.id_board) + WHERE b.id_board IS NULL + AND lb.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY lb.id_board', + 'fix_collect' => array( + 'index' => 'id_board', + 'process' => create_function('$boards', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_boards + WHERE id_board IN ({array_int:boards})", + array( + \'boards\' => $boards + ) + ); + '), + ), + 'messages' => array('repair_missing_log_boards', 'id_board'), + ), + 'missing_log_boards_members' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}log_boards' + ), + 'check_query' => ' + SELECT lb.id_member + FROM {db_prefix}log_boards AS lb + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lb.id_member) + WHERE mem.id_member IS NULL + AND lb.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY lb.id_member', + 'fix_collect' => array( + 'index' => 'id_member', + 'process' => create_function('$members', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_boards + WHERE id_member IN ({array_int:members})", + array( + \'members\' => $members + ) + ); + '), + ), + 'messages' => array('repair_missing_log_boards_members', 'id_member'), + ), + 'missing_log_mark_read' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}log_mark_read' + ), + 'check_query' => ' + SELECT lmr.id_board + FROM {db_prefix}log_mark_read AS lmr + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lmr.id_board) + WHERE b.id_board IS NULL + AND lmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY lmr.id_board', + 'fix_collect' => array( + 'index' => 'id_board', + 'process' => create_function('$boards', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_mark_read + WHERE id_board IN ({array_int:boards})", + array( + \'boards\' => $boards + ) + ); + '), + ), + 'messages' => array('repair_missing_log_mark_read', 'id_board'), + ), + 'missing_log_mark_read_members' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}log_mark_read' + ), + 'check_query' => ' + SELECT lmr.id_member + FROM {db_prefix}log_mark_read AS lmr + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lmr.id_member) + WHERE mem.id_member IS NULL + AND lmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY lmr.id_member', + 'fix_collect' => array( + 'index' => 'id_member', + 'process' => create_function('$members', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_mark_read + WHERE id_member IN ({array_int:members})", + array( + \'members\' => $members + ) + ); + '), + ), + 'messages' => array('repair_missing_log_mark_read_members', 'id_member'), + ), + 'missing_pms' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_pm) + FROM {db_prefix}pm_recipients' + ), + 'check_query' => ' + SELECT pmr.id_pm + FROM {db_prefix}pm_recipients AS pmr + LEFT JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm) + WHERE pm.id_pm IS NULL + AND pmr.id_pm BETWEEN {STEP_LOW} AND {STEP_HIGH} + GROUP BY pmr.id_pm', + 'fix_collect' => array( + 'index' => 'id_pm', + 'process' => create_function('$pms', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}pm_recipients + WHERE id_pm IN ({array_int:pms})", + array( + \'pms\' => $pms + ) + ); + '), + ), + 'messages' => array('repair_missing_pms', 'id_pm'), + ), + 'missing_recipients' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}pm_recipients' + ), + 'check_query' => ' + SELECT pmr.id_member + FROM {db_prefix}pm_recipients AS pmr + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pmr.id_member) + WHERE pmr.id_member != 0 + AND pmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND mem.id_member IS NULL + GROUP BY pmr.id_member', + 'fix_collect' => array( + 'index' => 'id_member', + 'process' => create_function('$members', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}pm_recipients + WHERE id_member IN ({array_int:members})", + array( + \'members\' => $members + ) + ); + '), + ), + 'messages' => array('repair_missing_recipients', 'id_member'), + ), + 'missing_senders' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_pm) + FROM {db_prefix}personal_messages' + ), + 'check_query' => ' + SELECT pm.id_pm, pm.id_member_from + FROM {db_prefix}personal_messages AS pm + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from) + WHERE pm.id_member_from != 0 + AND pm.id_pm BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND mem.id_member IS NULL', + 'fix_collect' => array( + 'index' => 'id_pm', + 'process' => create_function('$guestMessages', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + UPDATE {db_prefix}personal_messages + SET id_member_from = 0 + WHERE id_pm IN ({array_int:guestMessages})", + array( + \'guestMessages\' => $guestMessages + )); + '), + ), + 'messages' => array('repair_missing_senders', 'id_pm', 'id_member_from'), + ), + 'missing_notify_members' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}log_notify' + ), + 'check_query' => ' + SELECT ln.id_member + FROM {db_prefix}log_notify AS ln + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member) + WHERE ln.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND mem.id_member IS NULL + GROUP BY ln.id_member', + 'fix_collect' => array( + 'index' => 'id_member', + 'process' => create_function('$members', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_notify + WHERE id_member IN ({array_int:members})", + array( + \'members\' => $members + ) + ); + '), + ), + 'messages' => array('repair_missing_notify_members', 'id_member'), + ), + 'missing_cached_subject' => array( + 'substeps' => array( + 'step_size' => 100, + 'step_max' => ' + SELECT MAX(id_topic) + FROM {db_prefix}topics' + ), + 'check_query' => ' + SELECT t.id_topic, fm.subject + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS fm ON (fm.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}log_search_subjects AS lss ON (lss.id_topic = t.id_topic) + WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND lss.id_topic IS NULL', + 'fix_full_processing' => create_function('$result', ' + global $smcFunc; + + $inserts = array(); + while ($row = $smcFunc[\'db_fetch_assoc\']($result)) + { + foreach (text2words($row[\'subject\']) as $word) + $inserts[] = array($word, $row[\'id_topic\']); + if (count($inserts) > 500) + { + $smcFunc[\'db_insert\'](\'ignore\', + "{db_prefix}log_search_subjects", + array(\'word\' => \'string\', \'id_topic\' => \'int\'), + $inserts, + array(\'word\', \'id_topic\') + ); + $inserts = array(); + } + + } + + if (!empty($inserts)) + $smcFunc[\'db_insert\'](\'ignore\', + "{db_prefix}log_search_subjects", + array(\'word\' => \'string\', \'id_topic\' => \'int\'), + $inserts, + array(\'word\', \'id_topic\') + ); + '), + 'message_function' => create_function('$row', ' + global $txt, $context; + + if (count(text2words($row[\'subject\'])) != 0) + { + $context[\'repair_errors\'][] = sprintf($txt[\'repair_missing_cached_subject\'], $row[\'id_topic\']); + return true; + } + + return false; + '), + ), + 'missing_topic_for_cache' => array( + 'substeps' => array( + 'step_size' => 50, + 'step_max' => ' + SELECT MAX(id_topic) + FROM {db_prefix}log_search_subjects' + ), + 'check_query' => ' + SELECT lss.id_topic, lss.word + FROM {db_prefix}log_search_subjects AS lss + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lss.id_topic) + WHERE lss.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND t.id_topic IS NULL', + 'fix_collect' => array( + 'index' => 'id_topic', + 'process' => create_function('$deleteTopics', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_search_subjects + WHERE id_topic IN ({array_int:deleteTopics})", + array( + \'deleteTopics\' => $deleteTopics + ) + ); + '), + ), + 'messages' => array('repair_missing_topic_for_cache', 'word'), + ), + 'missing_member_vote' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}log_polls' + ), + 'check_query' => ' + SELECT lp.id_poll, lp.id_member + FROM {db_prefix}log_polls AS lp + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lp.id_member) + WHERE lp.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND lp.id_member > 0 + AND mem.id_member IS NULL', + 'fix_collect' => array( + 'index' => 'id_member', + 'process' => create_function('$members', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_polls + WHERE id_member IN ({array_int:members})", + array( + \'members\' => $members + ) + ); + '), + ), + 'messages' => array('repair_missing_log_poll_member', 'id_poll', 'id_member'), + ), + 'missing_log_poll_vote' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_poll) + FROM {db_prefix}log_polls' + ), + 'check_query' => ' + SELECT lp.id_poll, lp.id_member + FROM {db_prefix}log_polls AS lp + LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = lp.id_poll) + WHERE lp.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND p.id_poll IS NULL', + 'fix_collect' => array( + 'index' => 'id_poll', + 'process' => create_function('$polls', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_polls + WHERE id_poll IN ({array_int:polls})", + array( + \'polls\' => $polls + ) + ); + '), + ), + 'messages' => array('repair_missing_log_poll_vote', 'id_member', 'id_poll'), + ), + 'report_missing_comments' => array( + 'substeps' => array( + 'step_size' => 500, + 'step_max' => ' + SELECT MAX(id_report) + FROM {db_prefix}log_reported' + ), + 'check_query' => ' + SELECT lr.id_report, lr.subject + FROM {db_prefix}log_reported AS lr + LEFT JOIN {db_prefix}log_reported_comments AS lrc ON (lrc.id_report = lr.id_report) + WHERE lr.id_report BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND lrc.id_report IS NULL', + 'fix_collect' => array( + 'index' => 'id_report', + 'process' => create_function('$reports', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_reported + WHERE id_report IN ({array_int:reports})", + array( + \'reports\' => $reports + ) + ); + '), + ), + 'messages' => array('repair_report_missing_comments', 'id_report', 'subject'), + ), + 'comments_missing_report' => array( + 'substeps' => array( + 'step_size' => 200, + 'step_max' => ' + SELECT MAX(id_report) + FROM {db_prefix}log_reported_comments' + ), + 'check_query' => ' + SELECT lrc.id_report, lrc.membername + FROM {db_prefix}log_reported_comments AS lrc + LEFT JOIN {db_prefix}log_reported AS lr ON (lr.id_report = lrc.id_report) + WHERE lrc.id_report BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND lr.id_report IS NULL', + 'fix_collect' => array( + 'index' => 'id_report', + 'process' => create_function('$reports', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_reported_comments + WHERE id_report IN ({array_int:reports})", + array( + \'reports\' => $reports + ) + ); + '), + ), + 'messages' => array('repair_comments_missing_report', 'id_report', 'membername'), + ), + 'group_request_missing_member' => array( + 'substeps' => array( + 'step_size' => 200, + 'step_max' => ' + SELECT MAX(id_member) + FROM {db_prefix}log_group_requests' + ), + 'check_query' => ' + SELECT lgr.id_member + FROM {db_prefix}log_group_requests AS lgr + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member) + WHERE lgr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND mem.id_member IS NULL + GROUP BY lgr.id_member', + 'fix_collect' => array( + 'index' => 'id_member', + 'process' => create_function('$members', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', " + DELETE FROM {db_prefix}log_group_requests + WHERE id_member IN ({array_int:members})", + array( + \'members\' => $members + ) + ); + '), + ), + 'messages' => array('repair_group_request_missing_member', 'id_member'), + ), + 'group_request_missing_group' => array( + 'substeps' => array( + 'step_size' => 200, + 'step_max' => ' + SELECT MAX(id_group) + FROM {db_prefix}log_group_requests' + ), + 'check_query' => ' + SELECT lgr.id_group + FROM {db_prefix}log_group_requests AS lgr + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group) + WHERE lgr.id_group BETWEEN {STEP_LOW} AND {STEP_HIGH} + AND mg.id_group IS NULL + GROUP BY lgr.id_group', + 'fix_collect' => array( + 'index' => 'id_group', + 'process' => create_function('$groups', ' + global $smcFunc; + $smcFunc[\'db_query\'](\'\', \' + DELETE FROM {db_prefix}log_group_requests + WHERE id_group IN ({array_int:groups})\', + array( + \'groups\' => $groups + ) + ); + '), + ), + 'messages' => array('repair_group_request_missing_group', 'id_group'), + ), + ); +} + +function findForumErrors($do_fix = false) +{ + global $context, $txt, $smcFunc, $errorTests, $db_cache, $db_temp_cache; + + // This may take some time... + @set_time_limit(600); + + $to_fix = !empty($_SESSION['repairboards_to_fix']) ? $_SESSION['repairboards_to_fix'] : array(); + $context['repair_errors'] = isset($_SESSION['repairboards_to_fix2']) ? $_SESSION['repairboards_to_fix2'] : array(); + + $_GET['step'] = empty($_GET['step']) ? 0 : (int) $_GET['step']; + $_GET['substep'] = empty($_GET['substep']) ? 0 : (int) $_GET['substep']; + + // Don't allow the cache to get too full. + $db_temp_cache = $db_cache; + $db_cache = ''; + + $context['total_steps'] = count($errorTests); + + // For all the defined error types do the necessary tests. + $current_step = -1; + $total_queries = 0; + foreach ($errorTests as $error_type => $test) + { + $current_step++; + + // Already done this? + if ($_GET['step'] > $current_step) + continue; + + // If we're fixing it but it ain't broke why try? + if ($do_fix && !in_array($error_type, $to_fix)) + { + $_GET['step']++; + continue; + } + + // Has it got substeps? + if (isset($test['substeps'])) + { + $step_size = isset($test['substeps']['step_size']) ? $test['substeps']['step_size'] : 100; + $request = $smcFunc['db_query']('', + $test['substeps']['step_max'], + array( + ) + ); + list ($step_max) = $smcFunc['db_fetch_row']($request); + + $total_queries++; + $smcFunc['db_free_result']($request); + } + + // We in theory keep doing this... the substeps. + $done = false; + while (!$done) + { + // Make sure there's at least one ID to test. + if (isset($test['substeps']) && empty($step_max)) + break; + + // What is the testing query (Changes if we are testing or fixing) + if (!$do_fix) + $test_query = 'check_query'; + else + $test_query = isset($test['fix_query']) ? 'fix_query' : 'check_query'; + + // Do the test... + $request = $smcFunc['db_query']('', + isset($test['substeps']) ? strtr($test[$test_query], array('{STEP_LOW}' => $_GET['substep'], '{STEP_HIGH}' => $_GET['substep'] + $step_size - 1)) : $test[$test_query], + array( + ) + ); + $needs_fix = false; + + // Does it need a fix? + if (!empty($test['check_type']) && $test['check_type'] == 'count') + list ($needs_fix) = $smcFunc['db_fetch_row']($request); + else + $needs_fix = $smcFunc['db_num_rows']($request); + + $total_queries++; + + if ($needs_fix) + { + // What about a message to the user? + if (!$do_fix) + { + // Assume need to fix. + $found_errors = true; + + if (isset($test['message'])) + $context['repair_errors'][] = $txt[$test['message']]; + + // One per row! + elseif (isset($test['messages'])) + { + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $variables = $test['messages']; + foreach ($variables as $k => $v) + { + if ($k == 0 && isset($txt[$v])) + $variables[$k] = $txt[$v]; + elseif ($k > 0 && isset($row[$v])) + $variables[$k] = $row[$v]; + } + $context['repair_errors'][] = call_user_func_array('sprintf', $variables); + } + } + + // A function to process? + elseif (isset($test['message_function'])) + { + // Find out if there are actually errors. + $found_errors = false; + while ($row = $smcFunc['db_fetch_assoc']($request)) + $found_errors |= $test['message_function']($row); + } + + // Actually have something to fix? + if ($found_errors) + $to_fix[] = $error_type; + } + + // We want to fix, we need to fix - so work out what exactly to do! + else + { + // Are we simply getting a collection of ids? + if (isset($test['fix_collect'])) + { + $ids = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $ids[] = $row[$test['fix_collect']['index']]; + if (!empty($ids)) + { + // Fix it! + $test['fix_collect']['process']($ids); + } + } + + // Simply executing a fix it query? + elseif (isset($test['fix_it_query'])) + $smcFunc['db_query']('', + $test['fix_it_query'], + array( + ) + ); + + // Do we have some processing to do? + elseif (isset($test['fix_processing'])) + { + while ($row = $smcFunc['db_fetch_assoc']($request)) + $test['fix_processing']($row); + } + + // What about the full set of processing? + elseif (isset($test['fix_full_processing'])) + $test['fix_full_processing']($request); + + // Do we have other things we need to fix as a result? + if (!empty($test['force_fix'])) + { + foreach ($test['force_fix'] as $item) + if (!in_array($item, $to_fix)) + $to_fix[] = $item; + } + } + } + + // Free the result. + $smcFunc['db_free_result']($request); + // Keep memory down. + $db_cache = ''; + + // Are we done yet? + if (isset($test['substeps'])) + { + $_GET['substep'] += $step_size; + // Not done? + if ($_GET['substep'] <= $step_max) + { + pauseRepairProcess($to_fix, $error_type, $step_max); + } + else + $done = true; + } + else + $done = true; + + // Don't allow more than 1000 queries at a time. + if ($total_queries >= 1000) + pauseRepairProcess($to_fix, $error_type, $step_max, true); + } + + // Keep going. + $_GET['step']++; + $_GET['substep'] = 0; + + $to_fix = array_unique($to_fix); + + // If we're doing fixes and this needed a fix and we're all done then don't do it again. + if ($do_fix) + { + $key = array_search($error_type, $to_fix); + if ($key !== false && isset($to_fix[$key])) + unset($to_fix[$key]); + } + + // Are we done? + pauseRepairProcess($to_fix, $error_type); + } + + // Restore the cache. + $db_cache = $db_temp_cache; + + return $to_fix; +} + +// Create a salvage area for repair purposes. +function createSalvageArea() +{ + global $txt, $language, $salvageBoardID, $salvageCatID, $smcFunc; + static $createOnce = false; + + // Have we already created it? + if ($createOnce) + return; + else + $createOnce = true; + + // Back to the forum's default language. + loadLanguage('Admin', $language); + + // Check to see if a 'Salvage Category' exists, if not => insert one. + $result = $smcFunc['db_query']('', ' + SELECT id_cat + FROM {db_prefix}categories + WHERE name = {string:cat_name} + LIMIT 1', + array( + 'cat_name' => $txt['salvaged_category_name'], + ) + ); + if ($smcFunc['db_num_rows']($result) != 0) + list ($salvageCatID) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + if (empty($salveageCatID)) + { + $smcFunc['db_insert']('', + '{db_prefix}categories', + array('name' => 'string-255', 'cat_order' => 'int'), + array($txt['salvaged_category_name'], -1), + array('id_cat') + ); + + if ($smcFunc['db_affected_rows']() <= 0) + { + loadLanguage('Admin'); + fatal_lang_error('salvaged_category_error', false); + } + + $salvageCatID = $smcFunc['db_insert_id']('{db_prefix}categories', 'id_cat'); + } + + // Check to see if a 'Salvage Board' exists, if not => insert one. + $result = $smcFunc['db_query']('', ' + SELECT id_board + FROM {db_prefix}boards + WHERE id_cat = {int:id_cat} + AND name = {string:board_name} + LIMIT 1', + array( + 'id_cat' => $salvageCatID, + 'board_name' => $txt['salvaged_board_name'], + ) + ); + if ($smcFunc['db_num_rows']($result) != 0) + list ($salvageBoardID) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + if (empty($salvageBoardID)) + { + $smcFunc['db_insert']('', + '{db_prefix}boards', + array('name' => 'string-255', 'description' => 'string-255', 'id_cat' => 'int', 'member_groups' => 'string', 'board_order' => 'int', 'redirect' => 'string'), + array($txt['salvaged_board_name'], $txt['salvaged_board_description'], $salvageCatID, '1', -1, ''), + array('id_board') + ); + + if ($smcFunc['db_affected_rows']() <= 0) + { + loadLanguage('Admin'); + fatal_lang_error('salvaged_board_error', false); + } + + $salvageBoardID = $smcFunc['db_insert_id']('{db_prefix}boards', 'id_board'); + } + + $smcFunc['db_query']('alter_table_boards', ' + ALTER TABLE {db_prefix}boards + ORDER BY board_order', + array( + ) + ); + + // Restore the user's language. + loadLanguage('Admin'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Reports.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Reports.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,940 @@ + 'BoardReport', + 'board_perms' => 'BoardPermissionsReport', + 'member_groups' => 'MemberGroupsReport', + 'group_perms' => 'GroupPermissionsReport', + 'staff' => 'StaffReport', + ); + + $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] : $type['id'], + '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']; + + // 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) + $context['template_layers'] = $reportTemplates[$_REQUEST['st']]['layers']; + } + + // 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']); + // Now generate the data. + $context['report_types'][$context['report_type']]['function'](); + + // Finish the tables before exiting - this is to help the templates a little more. + finishTables(); +} + +// Standard report about what settings the boards have. +function BoardReport() +{ + global $context, $txt, $sourcedir, $smcFunc; + + // Load the permission profiles. + require_once($sourcedir . '/ManagePermissions.php'); + loadLanguage('ManagePermissions'); + loadPermissionProfiles(); + + // Get every moderator. + $request = $smcFunc['db_query']('', ' + SELECT mods.id_board, mods.id_member, mem.real_name + FROM {db_prefix}moderators AS mods + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)', + array( + ) + ); + $moderators = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $moderators[$row['id_board']][] = $row['real_name']; + $smcFunc['db_free_result']($request); + + // Get all the possible membergroups! + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name, online_color + FROM {db_prefix}membergroups', + array( + ) + ); + $groups = array(-1 => $txt['guest_title'], 0 => $txt['full_member']); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $groups[$row['id_group']] = empty($row['online_color']) ? $row['group_name'] : '' . $row['group_name'] . ''; + $smcFunc['db_free_result']($request); + + // All the fields we'll show. + $boardSettings = array( + 'category' => $txt['board_category'], + 'parent' => $txt['board_parent'], + 'num_topics' => $txt['board_num_topics'], + 'num_posts' => $txt['board_num_posts'], + 'count_posts' => $txt['board_count_posts'], + 'theme' => $txt['board_theme'], + 'override_theme' => $txt['board_override_theme'], + 'profile' => $txt['board_profile'], + 'moderators' => $txt['board_moderators'], + 'groups' => $txt['board_groups'], + ); + + // Do it in columns, it's just easier. + setKeys('cols'); + + // Go through each board! + $request = $smcFunc['db_query']('order_by_board_order', ' + SELECT b.id_board, b.name, b.num_posts, b.num_topics, b.count_posts, b.member_groups, b.override_theme, b.id_profile, + c.name AS cat_name, IFNULL(par.name, {string:text_none}) AS parent_name, IFNULL(th.value, {string:text_none}) AS theme_name + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + LEFT JOIN {db_prefix}boards AS par ON (par.id_board = b.id_parent) + LEFT JOIN {db_prefix}themes AS th ON (th.id_theme = b.id_theme AND th.variable = {string:name})', + array( + 'name' => 'name', + 'text_none' => $txt['none'], + ) + ); + $boards = array(0 => array('name' => $txt['global_boards'])); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Each board has it's own table. + newTable($row['name'], '', 'left', 'auto', 'left', 200, 'left'); + + // First off, add in the side key. + addData($boardSettings); + + // Format the profile name. + $profile_name = $context['profiles'][$row['id_profile']]['name']; + + // Create the main data array. + $boardData = array( + 'category' => $row['cat_name'], + 'parent' => $row['parent_name'], + 'num_posts' => $row['num_posts'], + 'num_topics' => $row['num_topics'], + 'count_posts' => empty($row['count_posts']) ? $txt['yes'] : $txt['no'], + 'theme' => $row['theme_name'], + 'profile' => $profile_name, + 'override_theme' => $row['override_theme'] ? $txt['yes'] : $txt['no'], + 'moderators' => empty($moderators[$row['id_board']]) ? $txt['none'] : implode(', ', $moderators[$row['id_board']]), + ); + + // Work out the membergroups who can access it. + $allowedGroups = explode(',', $row['member_groups']); + foreach ($allowedGroups as $key => $group) + { + if (isset($groups[$group])) + $allowedGroups[$key] = $groups[$group]; + else + unset($allowedGroups[$key]); + } + $boardData['groups'] = implode(', ', $allowedGroups); + + // Next add the main data. + addData($boardData); + } + $smcFunc['db_free_result']($request); +} + +// Generate a report on the current permissions by board and membergroup. +function BoardPermissionsReport() +{ + global $context, $txt, $modSettings, $smcFunc; + + // Get as much memory as possible as this can be big. + @ini_set('memory_limit', '256M'); + + if (isset($_REQUEST['boards'])) + { + if (!is_array($_REQUEST['boards'])) + $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); + foreach ($_REQUEST['boards'] as $k => $dummy) + $_REQUEST['boards'][$k] = (int) $dummy; + + $board_clause = 'id_board IN ({array_int:boards})'; + } + else + $board_clause = '1=1'; + + if (isset($_REQUEST['groups'])) + { + if (!is_array($_REQUEST['groups'])) + $_REQUEST['groups'] = explode(',', $_REQUEST['groups']); + foreach ($_REQUEST['groups'] as $k => $dummy) + $_REQUEST['groups'][$k] = (int) $dummy; + + $group_clause = 'id_group IN ({array_int:groups})'; + } + else + $group_clause = '1=1'; + + // Fetch all the board names. + $request = $smcFunc['db_query']('', ' + SELECT id_board, name, id_profile + FROM {db_prefix}boards + WHERE ' . $board_clause . ' + ORDER BY id_board', + array( + 'boards' => isset($_REQUEST['boards']) ? $_REQUEST['boards'] : array(), + ) + ); + $profiles = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $boards[$row['id_board']] = array( + 'name' => $row['name'], + 'profile' => $row['id_profile'], + ); + $profiles[] = $row['id_profile']; + } + $smcFunc['db_free_result']($request); + + // Get all the possible membergroups, except admin! + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name + FROM {db_prefix}membergroups + WHERE ' . $group_clause . ' + AND id_group != {int:admin_group}' . (empty($modSettings['permission_enable_postgroups']) ? ' + AND min_posts = {int:min_posts}' : '') . ' + ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name', + array( + 'admin_group' => 1, + 'min_posts' => -1, + 'newbie_group' => 4, + 'groups' => isset($_REQUEST['groups']) ? $_REQUEST['groups'] : array(), + ) + ); + if (!isset($_REQUEST['groups']) || in_array(-1, $_REQUEST['groups']) || in_array(0, $_REQUEST['groups'])) + $member_groups = array('col' => '', -1 => $txt['membergroups_guests'], 0 => $txt['membergroups_members']); + else + $member_groups = array('col' => ''); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $member_groups[$row['id_group']] = $row['group_name']; + $smcFunc['db_free_result']($request); + + // Make sure that every group is represented - plus in rows! + setKeys('rows', $member_groups); + + // Cache every permission setting, to make sure we don't miss any allows. + $permissions = array(); + $board_permissions = array(); + $request = $smcFunc['db_query']('', ' + SELECT id_profile, id_group, add_deny, permission + FROM {db_prefix}board_permissions + WHERE id_profile IN ({array_int:profile_list}) + AND ' . $group_clause . (empty($modSettings['permission_enable_deny']) ? ' + AND add_deny = {int:not_deny}' : '') . ' + ORDER BY id_profile, permission', + array( + 'profile_list' => $profiles, + 'not_deny' => 1, + 'groups' => isset($_REQUEST['groups']) ? $_REQUEST['groups'] : array(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + foreach ($boards as $id => $board) + if ($board['profile'] == $row['id_profile']) + $board_permissions[$id][$row['id_group']][$row['permission']] = $row['add_deny']; + + // Make sure we get every permission. + if (!isset($permissions[$row['permission']])) + { + // This will be reused on other boards. + $permissions[$row['permission']] = array( + 'title' => isset($txt['board_perms_name_' . $row['permission']]) ? $txt['board_perms_name_' . $row['permission']] : $row['permission'], + ); + } + } + $smcFunc['db_free_result']($request); + + // Now cycle through the board permissions array... lots to do ;) + foreach ($board_permissions as $board => $groups) + { + // Create the table for this board first. + newTable($boards[$board]['name'], 'x', 'all', 100, 'center', 200, 'left'); + + // Add the header row - shows all the membergroups. + addData($member_groups); + + // Add the separator. + addSeparator($txt['board_perms_permission']); + + // Here cycle through all the detected permissions. + foreach ($permissions as $ID_PERM => $perm_info) + { + // Is this identical to the global? + $identicalGlobal = $board == 0 ? false : true; + + // Default data for this row. + $curData = array('col' => $perm_info['title']); + + // Now cycle each membergroup in this set of permissions. + foreach ($member_groups as $id_group => $name) + { + // Don't overwrite the key column! + if ($id_group === 'col') + continue; + + $group_permissions = isset($groups[$id_group]) ? $groups[$id_group] : array(); + + // Do we have any data for this group? + if (isset($group_permissions[$ID_PERM])) + { + // Set the data for this group to be the local permission. + $curData[$id_group] = $group_permissions[$ID_PERM]; + } + // Otherwise means it's set to disallow.. + else + { + $curData[$id_group] = 'x'; + } + + // Now actually make the data for the group look right. + if (empty($curData[$id_group])) + $curData[$id_group] = '' . $txt['board_perms_deny'] . ''; + elseif ($curData[$id_group] == 1) + $curData[$id_group] = '' . $txt['board_perms_allow'] . ''; + else + $curData[$id_group] = 'x'; + + // Embolden those permissions different from global (makes it a lot easier!) + if (@$board_permissions[0][$id_group][$ID_PERM] != @$group_permissions[$ID_PERM]) + $curData[$id_group] = '' . $curData[$id_group] . ''; + } + + // Now add the data for this permission. + addData($curData); + } + } +} + +// Show what the membergroups are made of. +function MemberGroupsReport() +{ + global $context, $txt, $settings, $modSettings, $smcFunc; + + // Fetch all the board names. + $request = $smcFunc['db_query']('', ' + SELECT id_board, name, member_groups, id_profile + FROM {db_prefix}boards', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (trim($row['member_groups']) == '') + $groups = array(1); + else + $groups = array_merge(array(1), explode(',', $row['member_groups'])); + + $boards[$row['id_board']] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'profile' => $row['id_profile'], + 'groups' => $groups, + ); + } + $smcFunc['db_free_result']($request); + + // Standard settings. + $mgSettings = array( + 'name' => '', + '#sep#1' => $txt['member_group_settings'], + 'color' => $txt['member_group_color'], + 'min_posts' => $txt['member_group_min_posts'], + 'max_messages' => $txt['member_group_max_messages'], + 'stars' => $txt['member_group_stars'], + '#sep#2' => $txt['member_group_access'], + ); + + // Add on the boards! + foreach ($boards as $board) + $mgSettings['board_' . $board['id']] = $board['name']; + + // Add all the membergroup settings, plus we'll be adding in columns! + setKeys('cols', $mgSettings); + + // Only one table this time! + newTable($txt['gr_type_member_groups'], '-', 'all', 100, 'center', 200, 'left'); + + // Get the shaded column in. + addData($mgSettings); + + // Now start cycling the membergroups! + $request = $smcFunc['db_query']('', ' + SELECT mg.id_group, mg.group_name, mg.online_color, mg.min_posts, mg.max_messages, mg.stars, + CASE WHEN bp.permission IS NOT NULL OR mg.id_group = {int:admin_group} THEN 1 ELSE 0 END AS can_moderate + FROM {db_prefix}membergroups AS mg + LEFT JOIN {db_prefix}board_permissions AS bp ON (bp.id_group = mg.id_group AND bp.id_profile = {int:default_profile} AND bp.permission = {string:moderate_board}) + ORDER BY mg.min_posts, CASE WHEN mg.id_group < {int:newbie_group} THEN mg.id_group ELSE 4 END, mg.group_name', + array( + 'admin_group' => 1, + 'default_profile' => 1, + 'newbie_group' => 4, + 'moderate_board' => 'moderate_board', + ) + ); + + // Cache them so we get regular members too. + $rows = array( + array( + 'id_group' => -1, + 'group_name' => $txt['membergroups_guests'], + 'online_color' => '', + 'min_posts' => -1, + 'max_messages' => null, + 'stars' => '' + ), + array( + 'id_group' => 0, + 'group_name' => $txt['membergroups_members'], + 'online_color' => '', + 'min_posts' => -1, + 'max_messages' => null, + 'stars' => '' + ), + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $rows[] = $row; + $smcFunc['db_free_result']($request); + + foreach ($rows as $row) + { + $row['stars'] = explode('#', $row['stars']); + + $group = array( + 'name' => $row['group_name'], + 'color' => empty($row['online_color']) ? '-' : '' . $row['online_color'] . '', + 'min_posts' => $row['min_posts'] == -1 ? 'N/A' : $row['min_posts'], + 'max_messages' => $row['max_messages'], + 'stars' => !empty($row['stars'][0]) && !empty($row['stars'][1]) ? str_repeat('*', $row['stars'][0]) : '', + ); + + // Board permissions. + foreach ($boards as $board) + $group['board_' . $board['id']] = in_array($row['id_group'], $board['groups']) ? '' . $txt['board_perms_allow'] . '' : 'x'; + + addData($group); + } +} + +// Show the large variety of group permissions assigned to each membergroup. +function GroupPermissionsReport() +{ + global $context, $txt, $modSettings, $smcFunc; + + if (isset($_REQUEST['groups'])) + { + if (!is_array($_REQUEST['groups'])) + $_REQUEST['groups'] = explode(',', $_REQUEST['groups']); + foreach ($_REQUEST['groups'] as $k => $dummy) + $_REQUEST['groups'][$k] = (int) $dummy; + $_REQUEST['groups'] = array_diff($_REQUEST['groups'], array(3)); + + $clause = 'id_group IN ({array_int:groups})'; + } + else + $clause = 'id_group != {int:moderator_group}'; + + // Get all the possible membergroups, except admin! + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name + FROM {db_prefix}membergroups + WHERE ' . $clause . ' + AND id_group != {int:admin_group}' . (empty($modSettings['permission_enable_postgroups']) ? ' + AND min_posts = {int:min_posts}' : '') . ' + ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name', + array( + 'admin_group' => 1, + 'min_posts' => -1, + 'newbie_group' => 4, + 'moderator_group' => 3, + 'groups' => isset($_REQUEST['groups']) ? $_REQUEST['groups'] : array(), + ) + ); + if (!isset($_REQUEST['groups']) || in_array(-1, $_REQUEST['groups']) || in_array(0, $_REQUEST['groups'])) + $groups = array('col' => '', -1 => $txt['membergroups_guests'], 0 => $txt['membergroups_members']); + else + $groups = array('col' => ''); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $groups[$row['id_group']] = $row['group_name']; + $smcFunc['db_free_result']($request); + + // Make sure that every group is represented! + setKeys('rows', $groups); + + // Create the table first. + newTable($txt['gr_type_group_perms'], '-', 'all', 100, 'center', 200, 'left'); + + // Show all the groups + addData($groups); + + // Add a separator + addSeparator($txt['board_perms_permission']); + + // Now the big permission fetch! + $request = $smcFunc['db_query']('', ' + SELECT id_group, add_deny, permission + FROM {db_prefix}permissions + WHERE ' . $clause . (empty($modSettings['permission_enable_deny']) ? ' + AND add_deny = {int:not_denied}' : '') . ' + ORDER BY permission', + array( + 'not_denied' => 1, + 'moderator_group' => 3, + 'groups' => isset($_REQUEST['groups']) ? $_REQUEST['groups'] : array(), + ) + ); + $lastPermission = null; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If this is a new permission flush the last row. + if ($row['permission'] != $lastPermission) + { + // Send the data! + if ($lastPermission !== null) + addData($curData); + + // Add the permission name in the left column. + $curData = array('col' => isset($txt['group_perms_name_' . $row['permission']]) ? $txt['group_perms_name_' . $row['permission']] : $row['permission']); + + $lastPermission = $row['permission']; + } + + // Good stuff - add the permission to the list! + if ($row['add_deny']) + $curData[$row['id_group']] = '' . $txt['board_perms_allow'] . ''; + else + $curData[$row['id_group']] = '' . $txt['board_perms_deny'] . ''; + } + $smcFunc['db_free_result']($request); + + // Flush the last data! + addData($curData); +} + +// Report for showing all the forum staff members - quite a feat! +function StaffReport() +{ + global $sourcedir, $context, $txt, $smcFunc; + + require_once($sourcedir . '/Subs-Members.php'); + + // Fetch all the board names. + $request = $smcFunc['db_query']('', ' + SELECT id_board, name + FROM {db_prefix}boards', + array( + ) + ); + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards[$row['id_board']] = $row['name']; + $smcFunc['db_free_result']($request); + + // Get every moderator. + $request = $smcFunc['db_query']('', ' + SELECT mods.id_board, mods.id_member + FROM {db_prefix}moderators AS mods', + array( + ) + ); + $moderators = array(); + $local_mods = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $moderators[$row['id_member']][] = $row['id_board']; + $local_mods[$row['id_member']] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + + // Get a list of global moderators (i.e. members with moderation powers). + $global_mods = array_intersect(membersAllowedTo('moderate_board', 0), membersAllowedTo('approve_posts', 0), membersAllowedTo('remove_any', 0), membersAllowedTo('modify_any', 0)); + + // How about anyone else who is special? + $allStaff = array_merge(membersAllowedTo('admin_forum'), membersAllowedTo('manage_membergroups'), membersAllowedTo('manage_permissions'), $local_mods, $global_mods); + + // Make sure everyone is there once - no admin less important than any other! + $allStaff = array_unique($allStaff); + + // This is a bit of a cop out - but we're protecting their forum, really! + if (count($allStaff) > 300) + fatal_lang_error('report_error_too_many_staff'); + + // Get all the possible membergroups! + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name, online_color + FROM {db_prefix}membergroups', + array( + ) + ); + $groups = array(0 => $txt['full_member']); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $groups[$row['id_group']] = empty($row['online_color']) ? $row['group_name'] : '' . $row['group_name'] . ''; + $smcFunc['db_free_result']($request); + + // All the fields we'll show. + $staffSettings = array( + 'position' => $txt['report_staff_position'], + 'moderates' => $txt['report_staff_moderates'], + 'posts' => $txt['report_staff_posts'], + 'last_login' => $txt['report_staff_last_login'], + ); + + // Do it in columns, it's just easier. + setKeys('cols'); + + // Get each member! + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, id_group, posts, last_login + FROM {db_prefix}members + WHERE id_member IN ({array_int:staff_list}) + ORDER BY real_name', + array( + 'staff_list' => $allStaff, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Each member gets their own table!. + newTable($row['real_name'], '', 'left', 'auto', 'left', 200, 'center'); + + // First off, add in the side key. + addData($staffSettings); + + // Create the main data array. + $staffData = array( + 'position' => isset($groups[$row['id_group']]) ? $groups[$row['id_group']] : $groups[0], + 'posts' => $row['posts'], + 'last_login' => timeformat($row['last_login']), + 'moderates' => array(), + ); + + // What do they moderate? + if (in_array($row['id_member'], $global_mods)) + $staffData['moderates'] = '' . $txt['report_staff_all_boards'] . ''; + elseif (isset($moderators[$row['id_member']])) + { + // Get the names + foreach ($moderators[$row['id_member']] as $board) + if (isset($boards[$board])) + $staffData['moderates'][] = $boards[$board]; + + $staffData['moderates'] = implode(', ', $staffData['moderates']); + } + else + $staffData['moderates'] = '' . $txt['report_staff_no_boards'] . ''; + + // Next add the main data. + addData($staffData); + } + $smcFunc['db_free_result']($request); +} + +// This function creates a new table of data, most functions will only use it once. +function newTable($title = '', $default_value = '', $shading = 'all', $width_normal = 'auto', $align_normal = 'center', $width_shaded = 'auto', $align_shaded = 'auto') +{ + global $context; + + // Set the table count if needed. + if (empty($context['table_count'])) + $context['table_count'] = 0; + + // Create the table! + $context['tables'][$context['table_count']] = array( + 'title' => $title, + 'default_value' => $default_value, + 'shading' => array( + 'left' => $shading == 'all' || $shading == 'left', + 'top' => $shading == 'all' || $shading == 'top', + ), + 'width' => array( + 'normal' => $width_normal, + 'shaded' => $width_shaded, + ), + 'align' => array( + 'normal' => $align_normal, + 'shaded' => $align_shaded, + ), + 'data' => array(), + ); + + $context['current_table'] = $context['table_count']; + + // Increment the count... + $context['table_count']++; +} + +// Add an extra slice of data to the table +function addData($inc_data, $custom_table = null) +{ + global $context; + + // No tables? Create one even though we are probably already in a bad state! + if (empty($context['table_count'])) + newTable(); + + // Specific table? + if ($custom_table !== null && !isset($context['tables'][$custom_table])) + return false; + elseif ($custom_table !== null) + $table = $custom_table; + else + $table = $context['current_table']; + + // If we have keys, sanitise the data... + if (!empty($context['keys'])) + { + // Basically, check every key exists! + foreach ($context['keys'] as $key => $dummy) + { + $data[$key] = array( + 'v' => empty($inc_data[$key]) ? $context['tables'][$table]['default_value'] : $inc_data[$key], + ); + // Special "hack" the adding separators when doing data by column. + if (substr($key, 0, 5) == '#sep#') + $data[$key]['separator'] = true; + } + } + else + { + $data = $inc_data; + foreach ($data as $key => $value) + { + $data[$key] = array( + 'v' => $value, + ); + if (substr($key, 0, 5) == '#sep#') + $data[$key]['separator'] = true; + } + } + + // Is it by row? + if (empty($context['key_method']) || $context['key_method'] == 'rows') + { + // Add the data! + $context['tables'][$table]['data'][] = $data; + } + // Otherwise, tricky! + else + { + foreach ($data as $key => $item) + $context['tables'][$table]['data'][$key][] = $item; + } +} + +// Add a separator row, only really used when adding data by rows. +function addSeparator($title = '', $custom_table = null) +{ + global $context; + + // No tables - return? + if (empty($context['table_count'])) + return; + + // Specific table? + if ($custom_table !== null && !isset($context['tables'][$table])) + return false; + elseif ($custom_table !== null) + $table = $custom_table; + else + $table = $context['current_table']; + + // Plumb in the separator + $context['tables'][$table]['data'][] = array(0 => array( + 'separator' => true, + 'v' => $title + )); +} + +// This does the necessary count of table data before displaying them. +function finishTables() +{ + global $context; + + if (empty($context['tables'])) + return; + + // Loop through each table counting up some basic values, to help with the templating. + foreach ($context['tables'] as $id => $table) + { + $context['tables'][$id]['id'] = $id; + $context['tables'][$id]['row_count'] = count($table['data']); + $curElement = current($table['data']); + $context['tables'][$id]['column_count'] = count($curElement); + + // Work out the rough width - for templates like the print template. Without this we might get funny tables. + if ($table['shading']['left'] && $table['width']['shaded'] != 'auto' && $table['width']['normal'] != 'auto') + $context['tables'][$id]['max_width'] = $table['width']['shaded'] + ($context['tables'][$id]['column_count'] - 1) * $table['width']['normal']; + elseif ($table['width']['normal'] != 'auto') + $context['tables'][$id]['max_width'] = $context['tables'][$id]['column_count'] * $table['width']['normal']; + else + $context['tables'][$id]['max_width'] = 'auto'; + } +} + +// Set the keys in use by the tables - these ensure entries MUST exist if the data isn't sent. +function setKeys($method = 'rows', $keys = array(), $reverse = false) +{ + global $context; + + // Do we want to use the keys of the keys as the keys? :P + if ($reverse) + $context['keys'] = array_flip($keys); + else + $context['keys'] = $keys; + + // Rows or columns? + $context['key_method'] = $method == 'rows' ? 'rows' : 'cols'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ScheduledTasks.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ScheduledTasks.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1643 @@ + 0, + 'current_time' => time(), + ) + ); + if ($smcFunc['db_num_rows']($request) != 0) + { + // The two important things really... + $row = $smcFunc['db_fetch_assoc']($request); + + // When should this next be run? + $next_time = next_time($row['time_regularity'], $row['time_unit'], $row['time_offset']); + + // How long in seconds it the gap? + $duration = $row['time_regularity']; + if ($row['time_unit'] == 'm') + $duration *= 60; + elseif ($row['time_unit'] == 'h') + $duration *= 3600; + elseif ($row['time_unit'] == 'd') + $duration *= 86400; + elseif ($row['time_unit'] == 'w') + $duration *= 604800; + + // If we were really late running this task actually skip the next one. + if (time() + ($duration / 2) > $next_time) + $next_time += $duration; + + // Update it now, so no others run this! + $smcFunc['db_query']('', ' + UPDATE {db_prefix}scheduled_tasks + SET next_time = {int:next_time} + WHERE id_task = {int:id_task} + AND next_time = {int:current_next_time}', + array( + 'next_time' => $next_time, + 'id_task' => $row['id_task'], + 'current_next_time' => $row['next_time'], + ) + ); + $affected_rows = $smcFunc['db_affected_rows'](); + + // The function must exist or we are wasting our time, plus do some timestamp checking, and database check! + if (function_exists('scheduled_' . $row['task']) && (!isset($_GET['ts']) || $_GET['ts'] == $row['next_time']) && $affected_rows) + { + ignore_user_abort(true); + + // Do the task... + $completed = call_user_func('scheduled_' . $row['task']); + + // Log that we did it ;) + if ($completed) + { + $total_time = round(array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)), 3); + $smcFunc['db_insert']('', + '{db_prefix}log_scheduled_tasks', + array( + 'id_task' => 'int', 'time_run' => 'int', 'time_taken' => 'float', + ), + array( + $row['id_task'], time(), (int) $total_time, + ), + array() + ); + } + } + } + $smcFunc['db_free_result']($request); + + // Get the next timestamp right. + $request = $smcFunc['db_query']('', ' + SELECT next_time + FROM {db_prefix}scheduled_tasks + WHERE disabled = {int:not_disabled} + ORDER BY next_time ASC + LIMIT 1', + array( + 'not_disabled' => 0, + ) + ); + // No new task scheduled yet? + if ($smcFunc['db_num_rows']($request) === 0) + $nextEvent = time() + 86400; + else + list ($nextEvent) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + updateSettings(array('next_task_time' => $nextEvent)); + } + + // Shall we return? + if (!isset($_GET['scheduled'])) + return true; + + // Finally, send some stuff... + header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Content-Type: image/gif'); + die("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B"); +} + +// Function to sending out approval notices to moderators etc. +function scheduled_approval_notification() +{ + global $scripturl, $modSettings, $mbname, $txt, $sourcedir, $smcFunc; + + // Grab all the items awaiting approval and sort type then board - clear up any things that are no longer relevant. + $request = $smcFunc['db_query']('', ' + SELECT aq.id_msg, aq.id_attach, aq.id_event, m.id_topic, m.id_board, m.subject, t.id_first_msg, + b.id_profile + FROM {db_prefix}approval_queue AS aq + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = aq.id_msg) + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)', + array( + ) + ); + $notices = array(); + $profiles = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If this is no longer around we'll ignore it. + if (empty($row['id_topic'])) + continue; + + // What type is it? + if ($row['id_first_msg'] && $row['id_first_msg'] == $row['id_msg']) + $type = 'topic'; + elseif ($row['id_attach']) + $type = 'attach'; + else + $type = 'msg'; + + // Add it to the array otherwise. + $notices[$row['id_board']][$type][] = array( + 'subject' => $row['subject'], + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], + ); + + // Store the profile for a bit later. + $profiles[$row['id_board']] = $row['id_profile']; + } + $smcFunc['db_free_result']($request); + + // Delete it all! + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}approval_queue', + array( + ) + ); + + // If nothing quit now. + if (empty($notices)) + return true; + + // Now we need to think about finding out *who* can approve - this is hard! + + // First off, get all the groups with this permission and sort by board. + $request = $smcFunc['db_query']('', ' + SELECT id_group, id_profile, add_deny + FROM {db_prefix}board_permissions + WHERE permission = {string:approve_posts} + AND id_profile IN ({array_int:profile_list})', + array( + 'profile_list' => $profiles, + 'approve_posts' => 'approve_posts', + ) + ); + $perms = array(); + $addGroups = array(1); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Sorry guys, but we have to ignore guests AND members - it would be too many otherwise. + if ($row['id_group'] < 2) + continue; + + $perms[$row['id_profile']][$row['add_deny'] ? 'add' : 'deny'][] = $row['id_group']; + + // Anyone who can access has to be considered. + if ($row['add_deny']) + $addGroups[] = $row['id_group']; + } + $smcFunc['db_free_result']($request); + + // Grab the moderators if they have permission! + $mods = array(); + $members = array(); + if (in_array(2, $addGroups)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, id_board + FROM {db_prefix}moderators', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $mods[$row['id_member']][$row['id_board']] = true; + // Make sure they get included in the big loop. + $members[] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + } + + // Come along one and all... until we reject you ;) + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name, email_address, lngfile, id_group, additional_groups, mod_prefs + FROM {db_prefix}members + WHERE id_group IN ({array_int:additional_group_list}) + OR FIND_IN_SET({raw:additional_group_list_implode}, additional_groups) != 0' . (empty($members) ? '' : ' + OR id_member IN ({array_int:member_list})') . ' + ORDER BY lngfile', + array( + 'additional_group_list' => $addGroups, + 'member_list' => $members, + 'additional_group_list_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $addGroups), + ) + ); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Check whether they are interested. + if (!empty($row['mod_prefs'])) + { + list(,, $pref_binary) = explode('|', $row['mod_prefs']); + if (!($pref_binary & 4)) + continue; + } + + $members[$row['id_member']] = array( + 'id' => $row['id_member'], + 'groups' => array_merge(explode(',', $row['additional_groups']), array($row['id_group'])), + 'language' => $row['lngfile'], + 'email' => $row['email_address'], + 'name' => $row['real_name'], + ); + } + $smcFunc['db_free_result']($request); + + // Get the mailing stuff. + require_once($sourcedir . '/Subs-Post.php'); + // Need the below for loadLanguage to work! + loadEssentialThemeData(); + + // Finally, loop through each member, work out what they can do, and send it. + foreach ($members as $id => $member) + { + $emailbody = ''; + + // Load the language file as required. + if (empty($current_language) || $current_language != $member['language']) + $current_language = loadLanguage('EmailTemplates', $member['language'], false); + + // Loop through each notice... + foreach ($notices as $board => $notice) + { + $access = false; + + // Can they mod in this board? + if (isset($mods[$id][$board])) + $access = true; + + // Do the group check... + if (!$access && isset($perms[$profiles[$board]]['add'])) + { + // They can access?! + if (array_intersect($perms[$profiles[$board]]['add'], $member['groups'])) + $access = true; + + // If they have deny rights don't consider them! + if (isset($perms[$profiles[$board]]['deny'])) + if (array_intersect($perms[$profiles[$board]]['deny'], $member['groups'])) + $access = false; + } + + // Finally, fix it for admins! + if (in_array(1, $member['groups'])) + $access = true; + + // If they can't access it then give it a break! + if (!$access) + continue; + + foreach ($notice as $type => $items) + { + // Build up the top of this section. + $emailbody .= $txt['scheduled_approval_email_' . $type] . "\n" . + '------------------------------------------------------' . "\n"; + + foreach ($items as $item) + $emailbody .= $item['subject'] . ' - ' . $item['href'] . "\n"; + + $emailbody .= "\n"; + } + } + + if ($emailbody == '') + continue; + + $replacements = array( + 'REALNAME' => $member['name'], + 'BODY' => $emailbody, + ); + + $emaildata = loadEmailTemplate('scheduled_approval', $replacements, $current_language); + + // Send the actual email. + sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 2); + } + + // All went well! + return true; +} + +// Do some daily cleaning up. +function scheduled_daily_maintenance() +{ + global $smcFunc, $modSettings, $sourcedir, $db_type; + + // First clean out the cache. + clean_cache(); + + // If warning decrement is enabled and we have people who have not had a new warning in 24 hours, lower their warning level. + list (, , $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']); + if ($modSettings['warning_decrement']) + { + // Find every member who has a warning level... + $request = $smcFunc['db_query']('', ' + SELECT id_member, warning + FROM {db_prefix}members + WHERE warning > {int:no_warning}', + array( + 'no_warning' => 0, + ) + ); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[$row['id_member']] = $row['warning']; + $smcFunc['db_free_result']($request); + + // Have some members to check? + if (!empty($members)) + { + // Find out when they were last warned. + $request = $smcFunc['db_query']('', ' + SELECT id_recipient, MAX(log_time) AS last_warning + FROM {db_prefix}log_comments + WHERE id_recipient IN ({array_int:member_list}) + AND comment_type = {string:warning} + GROUP BY id_recipient', + array( + 'member_list' => array_keys($members), + 'warning' => 'warning', + ) + ); + $member_changes = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // More than 24 hours ago? + if ($row['last_warning'] <= time() - 86400) + $member_changes[] = array( + 'id' => $row['id_recipient'], + 'warning' => $members[$row['id_recipient']] >= $modSettings['warning_decrement'] ? $members[$row['id_recipient']] - $modSettings['warning_decrement'] : 0, + ); + } + $smcFunc['db_free_result']($request); + + // Have some members to change? + if (!empty($member_changes)) + foreach ($member_changes as $change) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET warning = {int:warning} + WHERE id_member = {int:id_member}', + array( + 'warning' => $change['warning'], + 'id_member' => $change['id'], + ) + ); + } + } + + // Do any spider stuff. + if (!empty($modSettings['spider_mode']) && $modSettings['spider_mode'] > 1) + { + require_once($sourcedir . '/ManageSearchEngines.php'); + consolidateSpiderStats(); + } + + // Check the database version - for some buggy MySQL version. + $server_version = $smcFunc['db_server_info'](); + if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51'))) + updateSettings(array('db_mysql_group_by_fix' => '1')); + elseif (!empty($modSettings['db_mysql_group_by_fix'])) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}settings + WHERE variable = {string:mysql_fix}', + array( + 'mysql_fix' => 'db_mysql_group_by_fix', + ) + ); + + // Regenerate the Diffie-Hellman keys if OpenID is enabled. + if (!empty($modSettings['enableOpenID'])) + { + require_once($sourcedir . '/Subs-OpenID.php'); + smf_openID_setup_DH(true); + } + elseif (!empty($modSettings['dh_keys'])) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}settings + WHERE variable = {string:dh_keys}', + array( + 'dh_keys' => 'dh_keys', + ) + ); + + // Log we've done it... + return true; +} + +// Auto optimize the database? +function scheduled_auto_optimize() +{ + global $modSettings, $smcFunc, $db_prefix, $db_type; + + // By default do it now! + $delay = false; + + // As a kind of hack, if the server load is too great delay, but only by a bit! + if (!empty($modSettings['load_average']) && !empty($modSettings['loadavg_auto_opt']) && $modSettings['load_average'] >= $modSettings['loadavg_auto_opt']) + $delay = true; + + // Otherwise are we restricting the number of people online for this? + if (!empty($modSettings['autoOptMaxOnline'])) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_online', + array( + ) + ); + list ($dont_do_it) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if ($dont_do_it > $modSettings['autoOptMaxOnline']) + $delay = true; + } + + // If we are gonna delay, do so now! + if ($delay) + return false; + + db_extend(); + + // Get all the tables. + $tables = $smcFunc['db_list_tables'](false, $db_prefix . '%'); + + // Actually do the optimisation. + if ($db_type == 'sqlite') + $smcFunc['db_optimize_table']($table[0]); + else + foreach ($tables as $table) + $smcFunc['db_optimize_table']($table); + + // Return for the log... + return true; +} + +// Send out a daily email of all subscribed topics. +function scheduled_daily_digest() +{ + global $is_weekly, $txt, $mbname, $scripturl, $sourcedir, $smcFunc, $context, $modSettings; + + // We'll want this... + require_once($sourcedir . '/Subs-Post.php'); + loadEssentialThemeData(); + + $is_weekly = !empty($is_weekly) ? 1 : 0; + + // Right - get all the notification data FIRST. + $request = $smcFunc['db_query']('', ' + SELECT ln.id_topic, COALESCE(t.id_board, ln.id_board) AS id_board, mem.email_address, mem.member_name, mem.notify_types, + mem.lngfile, mem.id_member + FROM {db_prefix}log_notify AS ln + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member) + LEFT JOIN {db_prefix}topics AS t ON (ln.id_topic != {int:empty_topic} AND t.id_topic = ln.id_topic) + WHERE mem.notify_regularity = {int:notify_regularity} + AND mem.is_activated = {int:is_activated}', + array( + 'empty_topic' => 0, + 'notify_regularity' => $is_weekly ? '3' : '2', + 'is_activated' => 1, + ) + ); + $members = array(); + $langs = array(); + $notify = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($members[$row['id_member']])) + { + $members[$row['id_member']] = array( + 'email' => $row['email_address'], + 'name' => $row['member_name'], + 'id' => $row['id_member'], + 'notifyMod' => $row['notify_types'] < 3 ? true : false, + 'lang' => $row['lngfile'], + ); + $langs[$row['lngfile']] = $row['lngfile']; + } + + // Store this useful data! + $boards[$row['id_board']] = $row['id_board']; + if ($row['id_topic']) + $notify['topics'][$row['id_topic']][] = $row['id_member']; + else + $notify['boards'][$row['id_board']][] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + + if (empty($boards)) + return true; + + // Just get the board names. + $request = $smcFunc['db_query']('', ' + SELECT id_board, name + FROM {db_prefix}boards + WHERE id_board IN ({array_int:board_list})', + array( + 'board_list' => $boards, + ) + ); + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards[$row['id_board']] = $row['name']; + $smcFunc['db_free_result']($request); + + if (empty($boards)) + return true; + + // Get the actual topics... + $request = $smcFunc['db_query']('', ' + SELECT ld.note_type, t.id_topic, t.id_board, t.id_member_started, m.id_msg, m.subject, + b.name AS board_name + FROM {db_prefix}log_digest AS ld + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ld.id_topic + AND t.id_board IN ({array_int:board_list})) + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE ' . ($is_weekly ? 'ld.daily != {int:daily_value}' : 'ld.daily IN (0, 2)'), + array( + 'board_list' => array_keys($boards), + 'daily_value' => 2, + ) + ); + $types = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($types[$row['note_type']][$row['id_board']])) + $types[$row['note_type']][$row['id_board']] = array( + 'lines' => array(), + 'name' => $row['board_name'], + 'id' => $row['id_board'], + ); + + if ($row['note_type'] == 'reply') + { + if (isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']])) + $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['count']++; + else + $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array( + 'id' => $row['id_topic'], + 'subject' => un_htmlspecialchars($row['subject']), + 'count' => 1, + ); + } + elseif ($row['note_type'] == 'topic') + { + if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']])) + $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array( + 'id' => $row['id_topic'], + 'subject' => un_htmlspecialchars($row['subject']), + ); + } + else + { + if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']])) + $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array( + 'id' => $row['id_topic'], + 'subject' => un_htmlspecialchars($row['subject']), + 'starter' => $row['id_member_started'], + ); + } + + $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array(); + if (!empty($notify['topics'][$row['id_topic']])) + $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['topics'][$row['id_topic']]); + if (!empty($notify['boards'][$row['id_board']])) + $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['boards'][$row['id_board']]); + } + $smcFunc['db_free_result']($request); + + if (empty($types)) + return true; + + // Let's load all the languages into a cache thingy. + $langtxt = array(); + foreach ($langs as $lang) + { + loadLanguage('Post', $lang); + loadLanguage('index', $lang); + loadLanguage('EmailTemplates', $lang); + $langtxt[$lang] = array( + 'subject' => $txt['digest_subject_' . ($is_weekly ? 'weekly' : 'daily')], + 'char_set' => $txt['lang_character_set'], + 'intro' => sprintf($txt['digest_intro_' . ($is_weekly ? 'weekly' : 'daily')], $mbname), + 'new_topics' => $txt['digest_new_topics'], + 'topic_lines' => $txt['digest_new_topics_line'], + 'new_replies' => $txt['digest_new_replies'], + 'mod_actions' => $txt['digest_mod_actions'], + 'replies_one' => $txt['digest_new_replies_one'], + 'replies_many' => $txt['digest_new_replies_many'], + 'sticky' => $txt['digest_mod_act_sticky'], + 'lock' => $txt['digest_mod_act_lock'], + 'unlock' => $txt['digest_mod_act_unlock'], + 'remove' => $txt['digest_mod_act_remove'], + 'move' => $txt['digest_mod_act_move'], + 'merge' => $txt['digest_mod_act_merge'], + 'split' => $txt['digest_mod_act_split'], + 'bye' => $txt['regards_team'], + ); + } + + // Right - send out the silly things - this will take quite some space! + $emails = array(); + foreach ($members as $mid => $member) + { + // Right character set! + $context['character_set'] = empty($modSettings['global_character_set']) ? $langtxt[$lang]['char_set'] : $modSettings['global_character_set']; + + // Do the start stuff! + $email = array( + 'subject' => $mbname . ' - ' . $langtxt[$lang]['subject'], + 'body' => $member['name'] . ',' . "\n\n" . $langtxt[$lang]['intro'] . "\n" . $scripturl . '?action=profile;area=notification;u=' . $member['id'] . "\n", + 'email' => $member['email'], + ); + + // All new topics? + if (isset($types['topic'])) + { + $titled = false; + foreach ($types['topic'] as $id => $board) + foreach ($board['lines'] as $topic) + if (in_array($mid, $topic['members'])) + { + if (!$titled) + { + $email['body'] .= "\n" . $langtxt[$lang]['new_topics'] . ':' . "\n" . '-----------------------------------------------'; + $titled = true; + } + $email['body'] .= "\n" . sprintf($langtxt[$lang]['topic_lines'], $topic['subject'], $board['name']); + } + if ($titled) + $email['body'] .= "\n"; + } + + // What about replies? + if (isset($types['reply'])) + { + $titled = false; + foreach ($types['reply'] as $id => $board) + foreach ($board['lines'] as $topic) + if (in_array($mid, $topic['members'])) + { + if (!$titled) + { + $email['body'] .= "\n" . $langtxt[$lang]['new_replies'] . ':' . "\n" . '-----------------------------------------------'; + $titled = true; + } + $email['body'] .= "\n" . ($topic['count'] == 1 ? sprintf($langtxt[$lang]['replies_one'], $topic['subject']) : sprintf($langtxt[$lang]['replies_many'], $topic['count'], $topic['subject'])); + } + + if ($titled) + $email['body'] .= "\n"; + } + + // Finally, moderation actions! + $titled = false; + foreach ($types as $note_type => $type) + { + if ($note_type == 'topic' || $note_type == 'reply') + continue; + + foreach ($type as $id => $board) + foreach ($board['lines'] as $topic) + if (in_array($mid, $topic['members'])) + { + if (!$titled) + { + $email['body'] .= "\n" . $langtxt[$lang]['mod_actions'] . ':' . "\n" . '-----------------------------------------------'; + $titled = true; + } + $email['body'] .= "\n" . sprintf($langtxt[$lang][$note_type], $topic['subject']); + } + + } + if ($titled) + $email['body'] .= "\n"; + + // Then just say our goodbyes! + $email['body'] .= "\n\n" . $txt['regards_team']; + + // Send it - low priority! + sendmail($email['email'], $email['subject'], $email['body'], null, null, false, 4); + } + + // Clean up... + if ($is_weekly) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_digest + WHERE daily != {int:not_daily}', + array( + 'not_daily' => 0, + ) + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_digest + SET daily = {int:daily_value} + WHERE daily = {int:not_daily}', + array( + 'daily_value' => 2, + 'not_daily' => 0, + ) + ); + } + else + { + // Clear any only weekly ones, and stop us from sending daily again. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_digest + WHERE daily = {int:daily_value}', + array( + 'daily_value' => 2, + ) + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_digest + SET daily = {int:both_value} + WHERE daily = {int:no_value}', + array( + 'both_value' => 1, + 'no_value' => 0, + ) + ); + } + + // Just in case the member changes their settings mark this as sent. + $members = array_keys($members); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_notify + SET sent = {int:is_sent} + WHERE id_member IN ({array_int:member_list})', + array( + 'member_list' => $members, + 'is_sent' => 1, + ) + ); + + // Log we've done it... + return true; +} + +// Like the daily stuff - just seven times less regular ;) +function scheduled_weekly_digest() +{ + global $is_weekly; + + // We just pass through to the daily function - avoid duplication! + $is_weekly = true; + return scheduled_daily_digest(); +} + +// Send a bunch of emails from the mail queue. +function ReduceMailQueue($number = false, $override_limit = false, $force_send = false) +{ + global $modSettings, $smcFunc, $sourcedir; + + // Are we intending another script to be sending out the queue? + if (!empty($modSettings['mail_queue_use_cron']) && empty($force_send)) + return false; + + // By default send 5 at once. + if (!$number) + $number = empty($modSettings['mail_quantity']) ? 5 : $modSettings['mail_quantity']; + + // If we came with a timestamp, and that doesn't match the next event, then someone else has beaten us. + if (isset($_GET['ts']) && $_GET['ts'] != $modSettings['mail_next_send'] && empty($force_send)) + return false; + + // By default move the next sending on by 10 seconds, and require an affected row. + if (!$override_limit) + { + $delay = !empty($modSettings['mail_queue_delay']) ? $modSettings['mail_queue_delay'] : (!empty($modSettings['mail_limit']) && $modSettings['mail_limit'] < 5 ? 10 : 5); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}settings + SET value = {string:next_mail_send} + WHERE variable = {string:mail_next_send} + AND value = {string:last_send}', + array( + 'next_mail_send' => time() + $delay, + 'mail_next_send' => 'mail_next_send', + 'last_send' => $modSettings['mail_next_send'], + ) + ); + if ($smcFunc['db_affected_rows']() == 0) + return false; + $modSettings['mail_next_send'] = time() + $delay; + } + + // If we're not overriding how many are we allow to send? + if (!$override_limit && !empty($modSettings['mail_limit'])) + { + list ($mt, $mn) = @explode('|', $modSettings['mail_recent']); + + // Nothing worth noting... + if (empty($mn) || $mt < time() - 60) + { + $mt = time(); + $mn = $number; + } + // Otherwise we have a few more we can spend? + elseif ($mn < $modSettings['mail_limit']) + { + $mn += $number; + } + // No more I'm afraid, return! + else + return false; + + // Reflect that we're about to send some, do it now to be safe. + updateSettings(array('mail_recent' => $mt . '|' . $mn)); + } + + // Now we know how many we're sending, let's send them. + $request = $smcFunc['db_query']('', ' + SELECT /*!40001 SQL_NO_CACHE */ id_mail, recipient, body, subject, headers, send_html + FROM {db_prefix}mail_queue + ORDER BY priority ASC, id_mail ASC + LIMIT ' . $number, + array( + ) + ); + $ids = array(); + $emails = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // We want to delete these from the database ASAP, so just get the data and go. + $ids[] = $row['id_mail']; + $emails[] = array( + 'to' => $row['recipient'], + 'body' => $row['body'], + 'subject' => $row['subject'], + 'headers' => $row['headers'], + 'send_html' => $row['send_html'], + ); + } + $smcFunc['db_free_result']($request); + + // Delete, delete, delete!!! + if (!empty($ids)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}mail_queue + WHERE id_mail IN ({array_int:mail_list})', + array( + 'mail_list' => $ids, + ) + ); + + // Don't believe we have any left? + if (count($ids) < $number) + { + // Only update the setting if no-one else has beaten us to it. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}settings + SET value = {string:no_send} + WHERE variable = {string:mail_next_send} + AND value = {string:last_mail_send}', + array( + 'no_send' => '0', + 'mail_next_send' => 'mail_next_send', + 'last_mail_send' => $modSettings['mail_next_send'], + ) + ); + } + + if (empty($ids)) + return false; + + if (!empty($modSettings['mail_type']) && $modSettings['smtp_host'] != '') + require_once($sourcedir . '/Subs-Post.php'); + + // Send each email, yea! + $failed_emails = array(); + foreach ($emails as $key => $email) + { + if (empty($modSettings['mail_type']) || $modSettings['smtp_host'] == '') + { + $email['subject'] = strtr($email['subject'], array("\r" => '', "\n" => '')); + if (!empty($modSettings['mail_strip_carriage'])) + { + $email['body'] = strtr($email['body'], array("\r" => '')); + $email['headers'] = strtr($email['headers'], array("\r" => '')); + } + + // No point logging a specific error here, as we have no language. PHP error is helpful anyway... + $result = mail(strtr($email['to'], array("\r" => '', "\n" => '')), $email['subject'], $email['body'], $email['headers']); + + // Try to stop a timeout, this would be bad... + @set_time_limit(300); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + } + else + $result = smtp_mail(array($email['to']), $email['subject'], $email['body'], $email['send_html'] ? $email['headers'] : 'Mime-Version: 1.0' . "\r\n" . $email['headers']); + + // Hopefully it sent? + if (!$result) + $failed_emails[] = array($email['to'], $email['body'], $email['subject'], $email['headers'], $email['send_html']); + } + + // Any emails that didn't send? + if (!empty($failed_emails)) + { + // Update the failed attempts check. + $smcFunc['db_insert']('replace', + '{db_prefix}settings', + array('variable' => 'string', 'value' => 'string'), + array('mail_failed_attempts', empty($modSettings['mail_failed_attempts']) ? 1 : ++$modSettings['mail_failed_attempts']), + array('variable') + ); + + // If we have failed to many times, tell mail to wait a bit and try again. + if ($modSettings['mail_failed_attempts'] > 5) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}settings + SET value = {string:mail_next_send} + WHERE variable = {string:next_mail_send} + AND value = {string:last_send}', + array( + 'next_mail_send' => time() + 60, + 'mail_next_send' => 'mail_next_send', + 'last_send' => $modSettings['mail_next_send'], + )); + + // Add our email back to the queue, manually. + $smcFunc['db_insert']('insert', + '{db_prefix}mail_queue', + array('recipient' => 'string', 'body' => 'string', 'subject' => 'string', 'headers' => 'string', 'send_html' => 'string'), + $failed_emails, + array('id_mail') + ); + + return false; + } + // We where unable to send the email, clear our failed attempts. + elseif (!empty($modSettings['mail_failed_attempts'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}settings + SET value = {string:zero} + WHERE variable = {string:mail_failed_attempts}', + array( + 'zero' => '0', + 'mail_failed_attempts' => 'mail_failed_attempts', + )); + + // Had something to send... + return true; +} + +// Calculate the next time the passed tasks should be triggered. +function CalculateNextTrigger($tasks = array(), $forceUpdate = false) +{ + global $modSettings, $smcFunc; + + $task_query = ''; + if (!is_array($tasks)) + $tasks = array($tasks); + + // Actually have something passed? + if (!empty($tasks)) + { + if (!isset($tasks[0]) || is_numeric($tasks[0])) + $task_query = ' AND id_task IN ({array_int:tasks})'; + else + $task_query = ' AND task IN ({array_string:tasks})'; + } + $nextTaskTime = empty($tasks) ? time() + 86400 : $modSettings['next_task_time']; + + // Get the critical info for the tasks. + $request = $smcFunc['db_query']('', ' + SELECT id_task, next_time, time_offset, time_regularity, time_unit + FROM {db_prefix}scheduled_tasks + WHERE disabled = {int:no_disabled} + ' . $task_query, + array( + 'no_disabled' => 0, + 'tasks' => $tasks, + ) + ); + $tasks = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $next_time = next_time($row['time_regularity'], $row['time_unit'], $row['time_offset']); + + // Only bother moving the task if it's out of place or we're forcing it! + if ($forceUpdate || $next_time < $row['next_time'] || $row['next_time'] < time()) + $tasks[$row['id_task']] = $next_time; + else + $next_time = $row['next_time']; + + // If this is sooner than the current next task, make this the next task. + if ($next_time < $nextTaskTime) + $nextTaskTime = $next_time; + } + $smcFunc['db_free_result']($request); + + // Now make the changes! + foreach ($tasks as $id => $time) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}scheduled_tasks + SET next_time = {int:next_time} + WHERE id_task = {int:id_task}', + array( + 'next_time' => $time, + 'id_task' => $id, + ) + ); + + // If the next task is now different update. + if ($modSettings['next_task_time'] != $nextTaskTime) + updateSettings(array('next_task_time' => $nextTaskTime)); +} + +// Simply returns a time stamp of the next instance of these time parameters. +function next_time($regularity, $unit, $offset) +{ + // Just in case! + if ($regularity == 0) + $regularity = 2; + + $curHour = date('H', time()); + $curMin = date('i', time()); + $next_time = 9999999999; + + // If the unit is minutes only check regularity in minutes. + if ($unit == 'm') + { + $off = date('i', $offset); + + // If it's now just pretend it ain't, + if ($off == $curMin) + $next_time = time() + $regularity; + else + { + // Make sure that the offset is always in the past. + $off = $off > $curMin ? $off - 60 : $off; + + while ($off <= $curMin) + $off += $regularity; + + // Now we know when the time should be! + $next_time = time() + 60 * ($off - $curMin); + } + } + // Otherwise, work out what the offset would be with todays date. + else + { + $next_time = mktime(date('H', $offset), date('i', $offset), 0, date('m'), date('d'), date('Y')); + + // Make the time offset in the past! + if ($next_time > time()) + { + $next_time -= 86400; + } + + // Default we'll jump in hours. + $applyOffset = 3600; + // 24 hours = 1 day. + if ($unit == 'd') + $applyOffset = 86400; + // Otherwise a week. + if ($unit == 'w') + $applyOffset = 604800; + + $applyOffset *= $regularity; + + // Just add on the offset. + while ($next_time <= time()) + { + $next_time += $applyOffset; + } + } + + return $next_time; +} + +// This loads the bare minimum data to allow us to load language files! +function loadEssentialThemeData() +{ + global $settings, $modSettings, $smcFunc, $mbname, $context, $sourcedir; + + // Get all the default theme variables. + $result = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE id_member = {int:no_member} + AND id_theme IN (1, {int:theme_guests})', + array( + 'no_member' => 0, + 'theme_guests' => $modSettings['theme_guests'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $settings[$row['variable']] = $row['value']; + + // Is this the default theme? + if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1') + $settings['default_' . $row['variable']] = $row['value']; + } + $smcFunc['db_free_result']($result); + + // Check we have some directories setup. + if (empty($settings['template_dirs'])) + { + $settings['template_dirs'] = array($settings['theme_dir']); + + // Based on theme (if there is one). + if (!empty($settings['base_theme_dir'])) + $settings['template_dirs'][] = $settings['base_theme_dir']; + + // Lastly the default theme. + if ($settings['theme_dir'] != $settings['default_theme_dir']) + $settings['template_dirs'][] = $settings['default_theme_dir']; + } + + // Assume we want this. + $context['forum_name'] = $mbname; + + // Check loadLanguage actually exists! + if (!function_exists('loadLanguage')) + { + require_once($sourcedir . '/Load.php'); + require_once($sourcedir . '/Subs.php'); + } + + loadLanguage('index+Modifications'); +} + +function scheduled_fetchSMfiles() +{ + global $sourcedir, $txt, $language, $settings, $forum_version, $modSettings, $smcFunc; + + // What files do we want to get + $request = $smcFunc['db_query']('', ' + SELECT id_file, filename, path, parameters + FROM {db_prefix}admin_info_files', + array( + ) + ); + + $js_files = array(); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $js_files[$row['id_file']] = array( + 'filename' => $row['filename'], + 'path' => $row['path'], + 'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)), + ); + } + + $smcFunc['db_free_result']($request); + + // We're gonna need fetch_web_data() to pull this off. + require_once($sourcedir . '/Subs-Package.php'); + + // Just in case we run into a problem. + loadEssentialThemeData(); + loadLanguage('Errors', $language, false); + + foreach ($js_files as $ID_FILE => $file) + { + // Create the url + $server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.simplemachines.org' : ''; + $url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : ''); + + // Get the file + $file_data = fetch_web_data($url); + + // If we got an error - give up - the site might be down. + if ($file_data === false) + { + log_error(sprintf($txt['st_cannot_retrieve_file'], $url)); + return false; + } + + // Save the file to the database. + $smcFunc['db_query']('substring', ' + UPDATE {db_prefix}admin_info_files + SET data = SUBSTRING({string:file_data}, 1, 65534) + WHERE id_file = {int:id_file}', + array( + 'id_file' => $ID_FILE, + 'file_data' => $file_data, + ) + ); + } + return true; +} + +function scheduled_birthdayemails() +{ + global $modSettings, $sourcedir, $mbname, $txt, $smcFunc, $birthdayEmails; + + // Need this in order to load the language files. + loadEssentialThemeData(); + + // Going to need this to send the emails. + require_once($sourcedir . '/Subs-Post.php'); + + $greeting = isset($modSettings['birthday_email']) ? $modSettings['birthday_email'] : 'happy_birthday'; + + // Get the month and day of today. + $month = date('n'); // Month without leading zeros. + $day = date('j'); // Day without leading zeros. + + // So who are the lucky ones? Don't include those who are banned and those who don't want them. + $result = $smcFunc['db_query']('', ' + SELECT id_member, real_name, lngfile, email_address + FROM {db_prefix}members + WHERE is_activated < 10 + AND MONTH(birthdate) = {int:month} + AND DAYOFMONTH(birthdate) = {int:day} + AND notify_announcements = {int:notify_announcements} + AND YEAR(birthdate) > {int:year}', + array( + 'notify_announcements' => 1, + 'year' => 1, + 'month' => $month, + 'day' => $day, + ) + ); + + // Group them by languages. + $birthdays = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (!isset($birthdays[$row['lngfile']])) + $birthdays[$row['lngfile']] = array(); + $birthdays[$row['lngfile']][$row['id_member']] = array( + 'name' => $row['real_name'], + 'email' => $row['email_address'] + ); + } + $smcFunc['db_free_result']($result); + + // Send out the greetings! + foreach ($birthdays as $lang => $recps) + { + // We need to do some shuffling to make this work properly. + loadLanguage('EmailTemplates', $lang); + $txt['emails']['happy_birthday'] = $birthdayEmails[$greeting]; + + foreach ($recps as $recp) + { + $replacements = array( + 'REALNAME' => $recp['name'], + ); + + $emaildata = loadEmailTemplate('happy_birthday', $replacements, $lang, false); + + sendmail($recp['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 4); + + // Try to stop a timeout, this would be bad... + @set_time_limit(300); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + } + } + + // Flush the mail queue, just in case. + AddMailQueue(true); + + return true; +} + +function scheduled_weekly_maintenance() +{ + global $modSettings, $smcFunc; + + // Delete some settings that needn't be set if they are otherwise empty. + $emptySettings = array( + 'warning_mute', 'warning_moderate', 'warning_watch', 'warning_show', 'disableCustomPerPage', 'spider_mode', 'spider_group', + 'paid_currency_code', 'paid_currency_symbol', 'paid_email_to', 'paid_email', 'paid_enabled', 'paypal_email', + 'search_enable_captcha', 'search_floodcontrol_time', 'show_spider_online', + ); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}settings + WHERE variable IN ({array_string:setting_list}) + AND (value = {string:zero_value} OR value = {string:blank_value})', + array( + 'zero_value' => '0', + 'blank_value' => '', + 'setting_list' => $emptySettings, + ) + ); + + // Some settings we never want to keep - they are just there for temporary purposes. + $deleteAnywaySettings = array( + 'attachment_full_notified', + ); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}settings + WHERE variable IN ({array_string:setting_list})', + array( + 'setting_list' => $deleteAnywaySettings, + ) + ); + + // Ok should we prune the logs? + if (!empty($modSettings['pruningOptions'])) + { + if (!empty($modSettings['pruningOptions']) && strpos($modSettings['pruningOptions'], ',') !== false) + list ($modSettings['pruneErrorLog'], $modSettings['pruneModLog'], $modSettings['pruneBanLog'], $modSettings['pruneReportLog'], $modSettings['pruneScheduledTaskLog'], $modSettings['pruneSpiderHitLog']) = explode(',', $modSettings['pruningOptions']); + + if (!empty($modSettings['pruneErrorLog'])) + { + // Figure out when our cutoff time is. 1 day = 86400 seconds. + $t = time() - $modSettings['pruneErrorLog'] * 86400; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_errors + WHERE log_time < {int:log_time}', + array( + 'log_time' => $t, + ) + ); + } + + if (!empty($modSettings['pruneModLog'])) + { + // Figure out when our cutoff time is. 1 day = 86400 seconds. + $t = time() - $modSettings['pruneModLog'] * 86400; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_actions + WHERE log_time < {int:log_time} + AND id_log = {int:moderation_log}', + array( + 'log_time' => $t, + 'moderation_log' => 1, + ) + ); + } + + if (!empty($modSettings['pruneBanLog'])) + { + // Figure out when our cutoff time is. 1 day = 86400 seconds. + $t = time() - $modSettings['pruneBanLog'] * 86400; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_banned + WHERE log_time < {int:log_time}', + array( + 'log_time' => $t, + ) + ); + } + + if (!empty($modSettings['pruneReportLog'])) + { + // Figure out when our cutoff time is. 1 day = 86400 seconds. + $t = time() - $modSettings['pruneReportLog'] * 86400; + + // This one is more complex then the other logs. First we need to figure out which reports are too old. + $reports = array(); + $result = $smcFunc['db_query']('', ' + SELECT id_report + FROM {db_prefix}log_reported + WHERE time_started < {int:time_started}', + array( + 'time_started' => $t, + ) + ); + + while ($row = $smcFunc['db_fetch_row']($result)) + $reports[] = $row[0]; + + $smcFunc['db_free_result']($result); + + if (!empty($reports)) + { + // Now delete the reports... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_reported + WHERE id_report IN ({array_int:report_list})', + array( + 'report_list' => $reports, + ) + ); + // And delete the comments for those reports... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_reported_comments + WHERE id_report IN ({array_int:report_list})', + array( + 'report_list' => $reports, + ) + ); + } + } + + if (!empty($modSettings['pruneScheduledTaskLog'])) + { + // Figure out when our cutoff time is. 1 day = 86400 seconds. + $t = time() - $modSettings['pruneScheduledTaskLog'] * 86400; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_scheduled_tasks + WHERE time_run < {int:time_run}', + array( + 'time_run' => $t, + ) + ); + } + + if (!empty($modSettings['pruneSpiderHitLog'])) + { + // Figure out when our cutoff time is. 1 day = 86400 seconds. + $t = time() - $modSettings['pruneSpiderHitLog'] * 86400; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_spider_hits + WHERE log_time < {int:log_time}', + array( + 'log_time' => $t, + ) + ); + } + } + + // Get rid of any paid subscriptions that were never actioned. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_subscribed + WHERE end_time = {int:no_end_time} + AND status = {int:not_active} + AND start_time < {int:start_time} + AND payments_pending < {int:payments_pending}', + array( + 'no_end_time' => 0, + 'not_active' => 0, + 'start_time' => time() - 60, + 'payments_pending' => 1, + ) + ); + + // Some OS's don't seem to clean out their sessions. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}sessions + WHERE last_update < {int:last_update}', + array( + 'last_update' => time() - 86400, + ) + ); + + return true; +} + +// Perform the standard checks on expiring/near expiring subscriptions. +function scheduled_paid_subscriptions() +{ + global $txt, $sourcedir, $scripturl, $smcFunc, $modSettings, $language; + + // Start off by checking for removed subscriptions. + $request = $smcFunc['db_query']('', ' + SELECT id_subscribe, id_member + FROM {db_prefix}log_subscribed + WHERE status = {int:is_active} + AND end_time < {int:time_now}', + array( + 'is_active' => 1, + 'time_now' => time(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + require_once($sourcedir . '/ManagePaid.php'); + removeSubscription($row['id_subscribe'], $row['id_member']); + } + $smcFunc['db_free_result']($request); + + // Get all those about to expire that have not had a reminder sent. + $request = $smcFunc['db_query']('', ' + SELECT ls.id_sublog, m.id_member, m.member_name, m.email_address, m.lngfile, s.name, ls.end_time + FROM {db_prefix}log_subscribed AS ls + INNER JOIN {db_prefix}subscriptions AS s ON (s.id_subscribe = ls.id_subscribe) + INNER JOIN {db_prefix}members AS m ON (m.id_member = ls.id_member) + WHERE ls.status = {int:is_active} + AND ls.reminder_sent = {int:reminder_sent} + AND s.reminder > {int:reminder_wanted} + AND ls.end_time < ({int:time_now} + s.reminder * 86400)', + array( + 'is_active' => 1, + 'reminder_sent' => 0, + 'reminder_wanted' => 0, + 'time_now' => time(), + ) + ); + $subs_reminded = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If this is the first one load the important bits. + if (empty($subs_reminded)) + { + require_once($sourcedir . '/Subs-Post.php'); + // Need the below for loadLanguage to work! + loadEssentialThemeData(); + } + + $subs_reminded[] = $row['id_sublog']; + + $replacements = array( + 'PROFILE_LINK' => $scripturl . '?action=profile;area=subscriptions;u=' . $row['id_member'], + 'REALNAME' => $row['member_name'], + 'SUBSCRIPTION' => $row['name'], + 'END_DATE' => strip_tags(timeformat($row['end_time'])), + ); + + $emaildata = loadEmailTemplate('paid_subscription_reminder', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); + + // Send the actual email. + sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 2); + } + $smcFunc['db_free_result']($request); + + // Mark the reminder as sent. + if (!empty($subs_reminded)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET reminder_sent = {int:reminder_sent} + WHERE id_sublog IN ({array_int:subscription_list})', + array( + 'subscription_list' => $subs_reminded, + 'reminder_sent' => 1, + ) + ); + + return true; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Search.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Search.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2105 @@ + 'SMF 2.0', + // This is the minimum version of SMF that an API could have been written for to work. (strtr to stop accidentally updating version on release) + 'search_version' => strtr('SMF 2+0=Beta=2', array('+' => '.', '=' => ' ')), +); + +// Ask the user what they want to search for. +function PlushSearch1() +{ + global $txt, $scripturl, $modSettings, $user_info, $context, $smcFunc, $sourcedir; + + // Is the load average too high to allow searching just now? + if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search']) + fatal_lang_error('loadavg_search_disabled', false); + + loadLanguage('Search'); + // Don't load this in XML mode. + if (!isset($_REQUEST['xml'])) + loadTemplate('Search'); + + // Check the user's permissions. + isAllowedTo('search_posts'); + + // Link tree.... + $context['linktree'][] = array( + 'url' => $scripturl . '?action=search', + 'name' => $txt['search'] + ); + + // This is hard coded maximum string length. + $context['search_string_limit'] = 100; + + $context['require_verification'] = $user_info['is_guest'] && !empty($modSettings['search_enable_captcha']) && empty($_SESSION['ss_vv_passed']); + if ($context['require_verification']) + { + require_once($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'search', + ); + $context['require_verification'] = create_control_verification($verificationOptions); + $context['visual_verification_id'] = $verificationOptions['id']; + } + + // If you got back from search2 by using the linktree, you get your original search parameters back. + if (isset($_REQUEST['params'])) + { + // Due to IE's 2083 character limit, we have to compress long search strings + $temp_params = base64_decode(str_replace(array('-', '_', '.'), array('+', '/', '='), $_REQUEST['params'])); + // Test for gzuncompress failing + $temp_params2 = @gzuncompress($temp_params); + $temp_params = explode('|"|', !empty($temp_params2) ? $temp_params2 : $temp_params); + + $context['search_params'] = array(); + foreach ($temp_params as $i => $data) + { + @list ($k, $v) = explode('|\'|', $data); + $context['search_params'][$k] = $v; + } + if (isset($context['search_params']['brd'])) + $context['search_params']['brd'] = $context['search_params']['brd'] == '' ? array() : explode(',', $context['search_params']['brd']); + } + + if (isset($_REQUEST['search'])) + $context['search_params']['search'] = un_htmlspecialchars($_REQUEST['search']); + + if (isset($context['search_params']['search'])) + $context['search_params']['search'] = $smcFunc['htmlspecialchars']($context['search_params']['search']); + if (isset($context['search_params']['userspec'])) + $context['search_params']['userspec'] = htmlspecialchars($context['search_params']['userspec']); + if (!empty($context['search_params']['searchtype'])) + $context['search_params']['searchtype'] = 2; + if (!empty($context['search_params']['minage'])) + $context['search_params']['minage'] = (int) $context['search_params']['minage']; + if (!empty($context['search_params']['maxage'])) + $context['search_params']['maxage'] = (int) $context['search_params']['maxage']; + + $context['search_params']['show_complete'] = !empty($context['search_params']['show_complete']); + $context['search_params']['subject_only'] = !empty($context['search_params']['subject_only']); + + // Load the error text strings if there were errors in the search. + if (!empty($context['search_errors'])) + { + loadLanguage('Errors'); + $context['search_errors']['messages'] = array(); + foreach ($context['search_errors'] as $search_error => $dummy) + { + if ($search_error === 'messages') + continue; + + $context['search_errors']['messages'][] = $txt['error_' . $search_error]; + } + } + + // Find all the boards this user is allowed to see. + $request = $smcFunc['db_query']('order_by_board_order', ' + SELECT b.id_cat, c.name AS cat_name, b.id_board, b.name, b.child_level + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE {query_see_board} + AND redirect = {string:empty_string}', + array( + 'empty_string' => '', + ) + ); + $context['num_boards'] = $smcFunc['db_num_rows']($request); + $context['boards_check_all'] = true; + $context['categories'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // This category hasn't been set up yet.. + if (!isset($context['categories'][$row['id_cat']])) + $context['categories'][$row['id_cat']] = array( + 'id' => $row['id_cat'], + 'name' => $row['cat_name'], + 'boards' => array() + ); + + // Set this board up, and let the template know when it's a child. (indent them..) + $context['categories'][$row['id_cat']]['boards'][$row['id_board']] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'child_level' => $row['child_level'], + 'selected' => (empty($context['search_params']['brd']) && (empty($modSettings['recycle_enable']) || $row['id_board'] != $modSettings['recycle_board']) && !in_array($row['id_board'], $user_info['ignoreboards'])) || (!empty($context['search_params']['brd']) && in_array($row['id_board'], $context['search_params']['brd'])) + ); + + // If a board wasn't checked that probably should have been ensure the board selection is selected, yo! + if (!$context['categories'][$row['id_cat']]['boards'][$row['id_board']]['selected'] && (empty($modSettings['recycle_enable']) || $row['id_board'] != $modSettings['recycle_board'])) + $context['boards_check_all'] = false; + } + $smcFunc['db_free_result']($request); + + // Now, let's sort the list of categories into the boards for templates that like that. + $temp_boards = array(); + foreach ($context['categories'] as $category) + { + $temp_boards[] = array( + 'name' => $category['name'], + 'child_ids' => array_keys($category['boards']) + ); + $temp_boards = array_merge($temp_boards, array_values($category['boards'])); + + // Include a list of boards per category for easy toggling. + $context['categories'][$category['id']]['child_ids'] = array_keys($category['boards']); + } + + $max_boards = ceil(count($temp_boards) / 2); + if ($max_boards == 1) + $max_boards = 2; + + // Now, alternate them so they can be shown left and right ;). + $context['board_columns'] = array(); + for ($i = 0; $i < $max_boards; $i++) + { + $context['board_columns'][] = $temp_boards[$i]; + if (isset($temp_boards[$i + $max_boards])) + $context['board_columns'][] = $temp_boards[$i + $max_boards]; + else + $context['board_columns'][] = array(); + } + + if (!empty($_REQUEST['topic'])) + { + $context['search_params']['topic'] = (int) $_REQUEST['topic']; + $context['search_params']['show_complete'] = true; + } + if (!empty($context['search_params']['topic'])) + { + $context['search_params']['topic'] = (int) $context['search_params']['topic']; + + $context['search_topic'] = array( + 'id' => $context['search_params']['topic'], + 'href' => $scripturl . '?topic=' . $context['search_params']['topic'] . '.0', + ); + + $request = $smcFunc['db_query']('', ' + SELECT ms.subject + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg) + WHERE t.id_topic = {int:search_topic_id} + AND {query_see_board}' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved_true}' : '') . ' + LIMIT 1', + array( + 'is_approved_true' => 1, + 'search_topic_id' => $context['search_params']['topic'], + ) + ); + + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('topic_gone', false); + + list ($context['search_topic']['subject']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $context['search_topic']['link'] = '' . $context['search_topic']['subject'] . ''; + } + + // Simple or not? + $context['simple_search'] = isset($context['search_params']['advanced']) ? empty($context['search_params']['advanced']) : !empty($modSettings['simpleSearch']) && !isset($_REQUEST['advanced']); + $context['page_title'] = $txt['set_parameters']; +} + +// Gather the results and show them. +function PlushSearch2() +{ + global $scripturl, $modSettings, $sourcedir, $txt, $db_connection; + global $user_info, $context, $options, $messages_request, $boards_can; + global $excludedWords, $participants, $smcFunc, $search_versions, $searchAPI; + + if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search']) + fatal_lang_error('loadavg_search_disabled', false); + + // No, no, no... this is a bit hard on the server, so don't you go prefetching it! + if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') + { + ob_end_clean(); + header('HTTP/1.1 403 Forbidden'); + die; + } + + $weight_factors = array( + 'frequency', + 'age', + 'length', + 'subject', + 'first_message', + 'sticky', + ); + + $weight = array(); + $weight_total = 0; + foreach ($weight_factors as $weight_factor) + { + $weight[$weight_factor] = empty($modSettings['search_weight_' . $weight_factor]) ? 0 : (int) $modSettings['search_weight_' . $weight_factor]; + $weight_total += $weight[$weight_factor]; + } + + // Zero weight. Weightless :P. + if (empty($weight_total)) + fatal_lang_error('search_invalid_weights'); + + // These vars don't require an interface, they're just here for tweaking. + $recentPercentage = 0.30; + $humungousTopicPosts = 200; + $maxMembersToSearch = 500; + $maxMessageResults = empty($modSettings['search_max_results']) ? 0 : $modSettings['search_max_results'] * 5; + + // Start with no errors. + $context['search_errors'] = array(); + + // Number of pages hard maximum - normally not set at all. + $modSettings['search_max_results'] = empty($modSettings['search_max_results']) ? 200 * $modSettings['search_results_per_page'] : (int) $modSettings['search_max_results']; + // Maximum length of the string. + $context['search_string_limit'] = 100; + + loadLanguage('Search'); + if (!isset($_REQUEST['xml'])) + loadTemplate('Search'); + //If we're doing XML we need to use the results template regardless really. + else + $context['sub_template'] = 'results'; + + // Are you allowed? + isAllowedTo('search_posts'); + + require_once($sourcedir . '/Display.php'); + require_once($sourcedir . '/Subs-Package.php'); + + // Search has a special database set. + db_extend('search'); + + // Load up the search API we are going to use. + $modSettings['search_index'] = empty($modSettings['search_index']) ? 'standard' : $modSettings['search_index']; + if (!file_exists($sourcedir . '/SearchAPI-' . ucwords($modSettings['search_index']) . '.php')) + fatal_lang_error('search_api_missing'); + loadClassFile('SearchAPI-' . ucwords($modSettings['search_index']) . '.php'); + + // Create an instance of the search API and check it is valid for this version of SMF. + $search_class_name = $modSettings['search_index'] . '_search'; + $searchAPI = new $search_class_name(); + if (!$searchAPI || ($searchAPI->supportsMethod('isValid') && !$searchAPI->isValid()) || !matchPackageVersion($search_versions['forum_version'], $searchAPI->min_smf_version . '-' . $searchAPI->version_compatible)) + { + // Log the error. + loadLanguage('Errors'); + log_error(sprintf($txt['search_api_not_compatible'], 'SearchAPI-' . ucwords($modSettings['search_index']) . '.php'), 'critical'); + + loadClassFile('SearchAPI-Standard.php'); + $searchAPI = new standard_search(); + } + + // $search_params will carry all settings that differ from the default search parameters. + // That way, the URLs involved in a search page will be kept as short as possible. + $search_params = array(); + + if (isset($_REQUEST['params'])) + { + // Due to IE's 2083 character limit, we have to compress long search strings + $temp_params = base64_decode(str_replace(array('-', '_', '.'), array('+', '/', '='), $_REQUEST['params'])); + // Test for gzuncompress failing + $temp_params2 = @gzuncompress($temp_params); + $temp_params = explode('|"|', (!empty($temp_params2) ? $temp_params2 : $temp_params)); + + foreach ($temp_params as $i => $data) + { + @list ($k, $v) = explode('|\'|', $data); + $search_params[$k] = $v; + } + if (isset($search_params['brd'])) + $search_params['brd'] = empty($search_params['brd']) ? array() : explode(',', $search_params['brd']); + } + + // Store whether simple search was used (needed if the user wants to do another query). + if (!isset($search_params['advanced'])) + $search_params['advanced'] = empty($_REQUEST['advanced']) ? 0 : 1; + + // 1 => 'allwords' (default, don't set as param) / 2 => 'anywords'. + if (!empty($search_params['searchtype']) || (!empty($_REQUEST['searchtype']) && $_REQUEST['searchtype'] == 2)) + $search_params['searchtype'] = 2; + + // Minimum age of messages. Default to zero (don't set param in that case). + if (!empty($search_params['minage']) || (!empty($_REQUEST['minage']) && $_REQUEST['minage'] > 0)) + $search_params['minage'] = !empty($search_params['minage']) ? (int) $search_params['minage'] : (int) $_REQUEST['minage']; + + // Maximum age of messages. Default to infinite (9999 days: param not set). + if (!empty($search_params['maxage']) || (!empty($_REQUEST['maxage']) && $_REQUEST['maxage'] < 9999)) + $search_params['maxage'] = !empty($search_params['maxage']) ? (int) $search_params['maxage'] : (int) $_REQUEST['maxage']; + + // Searching a specific topic? + if (!empty($_REQUEST['topic'])) + { + $search_params['topic'] = (int) $_REQUEST['topic']; + $search_params['show_complete'] = true; + } + elseif (!empty($search_params['topic'])) + $search_params['topic'] = (int) $search_params['topic']; + + if (!empty($search_params['minage']) || !empty($search_params['maxage'])) + { + $request = $smcFunc['db_query']('', ' + SELECT ' . (empty($search_params['maxage']) ? '0, ' : 'IFNULL(MIN(id_msg), -1), ') . (empty($search_params['minage']) ? '0' : 'IFNULL(MAX(id_msg), -1)') . ' + FROM {db_prefix}messages + WHERE 1=1' . ($modSettings['postmod_active'] ? ' + AND approved = {int:is_approved_true}' : '') . (empty($search_params['minage']) ? '' : ' + AND poster_time <= {int:timestamp_minimum_age}') . (empty($search_params['maxage']) ? '' : ' + AND poster_time >= {int:timestamp_maximum_age}'), + array( + 'timestamp_minimum_age' => empty($search_params['minage']) ? 0 : time() - 86400 * $search_params['minage'], + 'timestamp_maximum_age' => empty($search_params['maxage']) ? 0 : time() - 86400 * $search_params['maxage'], + 'is_approved_true' => 1, + ) + ); + list ($minMsgID, $maxMsgID) = $smcFunc['db_fetch_row']($request); + if ($minMsgID < 0 || $maxMsgID < 0) + $context['search_errors']['no_messages_in_time_frame'] = true; + $smcFunc['db_free_result']($request); + } + + // Default the user name to a wildcard matching every user (*). + if (!empty($search_params['userspec']) || (!empty($_REQUEST['userspec']) && $_REQUEST['userspec'] != '*')) + $search_params['userspec'] = isset($search_params['userspec']) ? $search_params['userspec'] : $_REQUEST['userspec']; + + // If there's no specific user, then don't mention it in the main query. + if (empty($search_params['userspec'])) + $userQuery = ''; + else + { + $userString = strtr($smcFunc['htmlspecialchars']($search_params['userspec'], ENT_QUOTES), array('"' => '"')); + $userString = strtr($userString, array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_')); + + preg_match_all('~"([^"]+)"~', $userString, $matches); + $possible_users = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $userString))); + + for ($k = 0, $n = count($possible_users); $k < $n; $k++) + { + $possible_users[$k] = trim($possible_users[$k]); + + if (strlen($possible_users[$k]) == 0) + unset($possible_users[$k]); + } + + // Create a list of database-escaped search names. + $realNameMatches = array(); + foreach ($possible_users as $possible_user) + $realNameMatches[] = $smcFunc['db_quote']( + '{string:possible_user}', + array( + 'possible_user' => $possible_user + ) + ); + + // Retrieve a list of possible members. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE {raw:match_possible_users}', + array( + 'match_possible_users' => 'real_name LIKE ' . implode(' OR real_name LIKE ', $realNameMatches), + ) + ); + // Simply do nothing if there're too many members matching the criteria. + if ($smcFunc['db_num_rows']($request) > $maxMembersToSearch) + $userQuery = ''; + elseif ($smcFunc['db_num_rows']($request) == 0) + { + $userQuery = $smcFunc['db_quote']( + 'm.id_member = {int:id_member_guest} AND ({raw:match_possible_guest_names})', + array( + 'id_member_guest' => 0, + 'match_possible_guest_names' => 'm.poster_name LIKE ' . implode(' OR m.poster_name LIKE ', $realNameMatches), + ) + ); + } + else + { + $memberlist = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $memberlist[] = $row['id_member']; + $userQuery = $smcFunc['db_quote']( + '(m.id_member IN ({array_int:matched_members}) OR (m.id_member = {int:id_member_guest} AND ({raw:match_possible_guest_names})))', + array( + 'matched_members' => $memberlist, + 'id_member_guest' => 0, + 'match_possible_guest_names' => 'm.poster_name LIKE ' . implode(' OR m.poster_name LIKE ', $realNameMatches), + ) + ); + } + $smcFunc['db_free_result']($request); + } + + // If the boards were passed by URL (params=), temporarily put them back in $_REQUEST. + if (!empty($search_params['brd']) && is_array($search_params['brd'])) + $_REQUEST['brd'] = $search_params['brd']; + + // Ensure that brd is an array. + if (!empty($_REQUEST['brd']) && !is_array($_REQUEST['brd'])) + $_REQUEST['brd'] = strpos($_REQUEST['brd'], ',') !== false ? explode(',', $_REQUEST['brd']) : array($_REQUEST['brd']); + + // Make sure all boards are integers. + if (!empty($_REQUEST['brd'])) + foreach ($_REQUEST['brd'] as $id => $brd) + $_REQUEST['brd'][$id] = (int) $brd; + + // Special case for boards: searching just one topic? + if (!empty($search_params['topic'])) + { + $request = $smcFunc['db_query']('', ' + SELECT b.id_board + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE t.id_topic = {int:search_topic_id} + AND {query_see_board}' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved_true}' : '') . ' + LIMIT 1', + array( + 'search_topic_id' => $search_params['topic'], + 'is_approved_true' => 1, + ) + ); + + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('topic_gone', false); + + $search_params['brd'] = array(); + list ($search_params['brd'][0]) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + // Select all boards you've selected AND are allowed to see. + elseif ($user_info['is_admin'] && (!empty($search_params['advanced']) || !empty($_REQUEST['brd']))) + $search_params['brd'] = empty($_REQUEST['brd']) ? array() : $_REQUEST['brd']; + else + { + $see_board = empty($search_params['advanced']) ? 'query_wanna_see_board' : 'query_see_board'; + $request = $smcFunc['db_query']('', ' + SELECT b.id_board + FROM {db_prefix}boards AS b + WHERE {raw:boards_allowed_to_see} + AND redirect = {string:empty_string}' . (empty($_REQUEST['brd']) ? (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_board_id}' : '') : ' + AND b.id_board IN ({array_int:selected_search_boards})'), + array( + 'boards_allowed_to_see' => $user_info[$see_board], + 'empty_string' => '', + 'selected_search_boards' => empty($_REQUEST['brd']) ? array() : $_REQUEST['brd'], + 'recycle_board_id' => $modSettings['recycle_board'], + ) + ); + $search_params['brd'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $search_params['brd'][] = $row['id_board']; + $smcFunc['db_free_result']($request); + + // This error should pro'bly only happen for hackers. + if (empty($search_params['brd'])) + $context['search_errors']['no_boards_selected'] = true; + } + + if (count($search_params['brd']) != 0) + { + foreach ($search_params['brd'] as $k => $v) + $search_params['brd'][$k] = (int) $v; + + // If we've selected all boards, this parameter can be left empty. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}boards + WHERE redirect = {string:empty_string}', + array( + 'empty_string' => '', + ) + ); + list ($num_boards) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (count($search_params['brd']) == $num_boards) + $boardQuery = ''; + elseif (count($search_params['brd']) == $num_boards - 1 && !empty($modSettings['recycle_board']) && !in_array($modSettings['recycle_board'], $search_params['brd'])) + $boardQuery = '!= ' . $modSettings['recycle_board']; + else + $boardQuery = 'IN (' . implode(', ', $search_params['brd']) . ')'; + } + else + $boardQuery = ''; + + $search_params['show_complete'] = !empty($search_params['show_complete']) || !empty($_REQUEST['show_complete']); + $search_params['subject_only'] = !empty($search_params['subject_only']) || !empty($_REQUEST['subject_only']); + + $context['compact'] = !$search_params['show_complete']; + + // Get the sorting parameters right. Default to sort by relevance descending. + $sort_columns = array( + 'relevance', + 'num_replies', + 'id_msg', + ); + if (empty($search_params['sort']) && !empty($_REQUEST['sort'])) + list ($search_params['sort'], $search_params['sort_dir']) = array_pad(explode('|', $_REQUEST['sort']), 2, ''); + $search_params['sort'] = !empty($search_params['sort']) && in_array($search_params['sort'], $sort_columns) ? $search_params['sort'] : 'relevance'; + if (!empty($search_params['topic']) && $search_params['sort'] === 'num_replies') + $search_params['sort'] = 'id_msg'; + + // Sorting direction: descending unless stated otherwise. + $search_params['sort_dir'] = !empty($search_params['sort_dir']) && $search_params['sort_dir'] == 'asc' ? 'asc' : 'desc'; + + // Determine some values needed to calculate the relevance. + $minMsg = (int) ((1 - $recentPercentage) * $modSettings['maxMsgID']); + $recentMsg = $modSettings['maxMsgID'] - $minMsg; + + // *** Parse the search query + + // Unfortunately, searching for words like this is going to be slow, so we're blacklisting them. + // !!! Setting to add more here? + // !!! Maybe only blacklist if they are the only word, or "any" is used? + $blacklisted_words = array('img', 'url', 'quote', 'www', 'http', 'the', 'is', 'it', 'are', 'if'); + + // What are we searching for? + if (empty($search_params['search'])) + { + if (isset($_GET['search'])) + $search_params['search'] = un_htmlspecialchars($_GET['search']); + elseif (isset($_POST['search'])) + $search_params['search'] = $_POST['search']; + else + $search_params['search'] = ''; + } + + // Nothing?? + if (!isset($search_params['search']) || $search_params['search'] == '') + $context['search_errors']['invalid_search_string'] = true; + // Too long? + elseif ($smcFunc['strlen']($search_params['search']) > $context['search_string_limit']) + { + $context['search_errors']['string_too_long'] = true; + $txt['error_string_too_long'] = sprintf($txt['error_string_too_long'], $context['search_string_limit']); + } + + // Change non-word characters into spaces. + $stripped_query = preg_replace('~(?:[\x0B\0' . ($context['utf8'] ? ($context['server']['complex_preg_chars'] ? '\x{A0}' : "\xC2\xA0") : '\xA0') . '\t\r\s\n(){}\\[\\]<>!@$%^*.,:+=`\~\?/\\\\]+|&(?:amp|lt|gt|quot);)+~' . ($context['utf8'] ? 'u' : ''), ' ', $search_params['search']); + + // Make the query lower case. It's gonna be case insensitive anyway. + $stripped_query = un_htmlspecialchars($smcFunc['strtolower']($stripped_query)); + + // This (hidden) setting will do fulltext searching in the most basic way. + if (!empty($modSettings['search_simple_fulltext'])) + $stripped_query = strtr($stripped_query, array('"' => '')); + + $no_regexp = preg_match('~&#(?:\d{1,7}|x[0-9a-fA-F]{1,6});~', $stripped_query) === 1; + + // Extract phrase parts first (e.g. some words "this is a phrase" some more words.) + preg_match_all('/(?:^|\s)([-]?)"([^"]+)"(?:$|\s)/', $stripped_query, $matches, PREG_PATTERN_ORDER); + $phraseArray = $matches[2]; + + // Remove the phrase parts and extract the words. + $wordArray = explode(' ', preg_replace('~(?:^|\s)(?:[-]?)"(?:[^"]+)"(?:$|\s)~' . ($context['utf8'] ? 'u' : ''), ' ', $search_params['search'])); + + // A minus sign in front of a word excludes the word.... so... + $excludedWords = array(); + $excludedIndexWords = array(); + $excludedSubjectWords = array(); + $excludedPhrases = array(); + + // .. first, we check for things like -"some words", but not "-some words". + foreach ($matches[1] as $index => $word) + if ($word === '-') + { + if (($word = trim($phraseArray[$index], '-_\' ')) !== '' && !in_array($word, $blacklisted_words)) + $excludedWords[] = $word; + unset($phraseArray[$index]); + } + + // Now we look for -test, etc.... normaller. + foreach ($wordArray as $index => $word) + if (strpos(trim($word), '-') === 0) + { + if (($word = trim($word, '-_\' ')) !== '' && !in_array($word, $blacklisted_words)) + $excludedWords[] = $word; + unset($wordArray[$index]); + } + + // The remaining words and phrases are all included. + $searchArray = array_merge($phraseArray, $wordArray); + + // Trim everything and make sure there are no words that are the same. + foreach ($searchArray as $index => $value) + { + // Skip anything practically empty. + if (($searchArray[$index] = trim($value, '-_\' ')) === '') + unset($searchArray[$index]); + // Skip blacklisted words. Make sure to note we skipped them in case we end up with nothing. + elseif (in_array($searchArray[$index], $blacklisted_words)) + { + $foundBlackListedWords = true; + unset($searchArray[$index]); + } + // Don't allow very, very short words. + elseif ($smcFunc['strlen']($value) < 2) + { + $context['search_errors']['search_string_small_words'] = true; + unset($searchArray[$index]); + } + else + $searchArray[$index] = $searchArray[$index]; + } + $searchArray = array_slice(array_unique($searchArray), 0, 10); + + // Create an array of replacements for highlighting. + $context['mark'] = array(); + foreach ($searchArray as $word) + $context['mark'][$word] = '' . $word . ''; + + // Initialize two arrays storing the words that have to be searched for. + $orParts = array(); + $searchWords = array(); + + // Make sure at least one word is being searched for. + if (empty($searchArray)) + $context['search_errors']['invalid_search_string' . (!empty($foundBlackListedWords) ? '_blacklist' : '')] = true; + // All words/sentences must match. + elseif (empty($search_params['searchtype'])) + $orParts[0] = $searchArray; + // Any word/sentence must match. + else + foreach ($searchArray as $index => $value) + $orParts[$index] = array($value); + + // Don't allow duplicate error messages if one string is too short. + if (isset($context['search_errors']['search_string_small_words'], $context['search_errors']['invalid_search_string'])) + unset($context['search_errors']['invalid_search_string']); + // Make sure the excluded words are in all or-branches. + foreach ($orParts as $orIndex => $andParts) + foreach ($excludedWords as $word) + $orParts[$orIndex][] = $word; + + // Determine the or-branches and the fulltext search words. + foreach ($orParts as $orIndex => $andParts) + { + $searchWords[$orIndex] = array( + 'indexed_words' => array(), + 'words' => array(), + 'subject_words' => array(), + 'all_words' => array(), + ); + + // Sort the indexed words (large words -> small words -> excluded words). + if ($searchAPI->supportsMethod('searchSort')) + usort($orParts[$orIndex], 'searchSort'); + + foreach ($orParts[$orIndex] as $word) + { + $is_excluded = in_array($word, $excludedWords); + + $searchWords[$orIndex]['all_words'][] = $word; + + $subjectWords = text2words($word); + if (!$is_excluded || count($subjectWords) === 1) + { + $searchWords[$orIndex]['subject_words'] = array_merge($searchWords[$orIndex]['subject_words'], $subjectWords); + if ($is_excluded) + $excludedSubjectWords = array_merge($excludedSubjectWords, $subjectWords); + } + else + $excludedPhrases[] = $word; + + // Have we got indexes to prepare? + if ($searchAPI->supportsMethod('prepareIndexes')) + $searchAPI->prepareIndexes($word, $searchWords[$orIndex], $excludedIndexWords, $is_excluded); + } + + // Search_force_index requires all AND parts to have at least one fulltext word. + if (!empty($modSettings['search_force_index']) && empty($searchWords[$orIndex]['indexed_words'])) + { + $context['search_errors']['query_not_specific_enough'] = true; + break; + } + elseif ($search_params['subject_only'] && empty($searchWords[$orIndex]['subject_words']) && empty($excludedSubjectWords)) + { + $context['search_errors']['query_not_specific_enough'] = true; + break; + } + + // Make sure we aren't searching for too many indexed words. + else + { + $searchWords[$orIndex]['indexed_words'] = array_slice($searchWords[$orIndex]['indexed_words'], 0, 7); + $searchWords[$orIndex]['subject_words'] = array_slice($searchWords[$orIndex]['subject_words'], 0, 7); + } + } + + // *** Spell checking + $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new'); + if ($context['show_spellchecking']) + { + // Windows fix. + ob_start(); + $old = error_reporting(0); + + pspell_new('en'); + $pspell_link = pspell_new($txt['lang_dictionary'], $txt['lang_spelling'], '', strtr($txt['lang_character_set'], array('iso-' => 'iso', 'ISO-' => 'iso')), PSPELL_FAST | PSPELL_RUN_TOGETHER); + + if (!$pspell_link) + $pspell_link = pspell_new('en', '', '', '', PSPELL_FAST | PSPELL_RUN_TOGETHER); + + error_reporting($old); + ob_end_clean(); + + $did_you_mean = array('search' => array(), 'display' => array()); + $found_misspelling = false; + foreach ($searchArray as $word) + { + if (empty($pspell_link)) + continue; + + $word = $word; + // Don't check phrases. + if (preg_match('~^\w+$~', $word) === 0) + { + $did_you_mean['search'][] = '"' . $word . '"'; + $did_you_mean['display'][] = '"' . $smcFunc['htmlspecialchars']($word) . '"'; + continue; + } + // For some strange reason spell check can crash PHP on decimals. + elseif (preg_match('~\d~', $word) === 1) + { + $did_you_mean['search'][] = $word; + $did_you_mean['display'][] = $smcFunc['htmlspecialchars']($word); + continue; + } + elseif (pspell_check($pspell_link, $word)) + { + $did_you_mean['search'][] = $word; + $did_you_mean['display'][] = $smcFunc['htmlspecialchars']($word); + continue; + } + + $suggestions = pspell_suggest($pspell_link, $word); + foreach ($suggestions as $i => $s) + { + // Search is case insensitive. + if ($smcFunc['strtolower']($s) == $smcFunc['strtolower']($word)) + unset($suggestions[$i]); + // Plus, don't suggest something the user thinks is rude! + elseif ($suggestions[$i] != censorText($s)) + unset($suggestions[$i]); + } + + // Anything found? If so, correct it! + if (!empty($suggestions)) + { + $suggestions = array_values($suggestions); + $did_you_mean['search'][] = $suggestions[0]; + $did_you_mean['display'][] = '' . $smcFunc['htmlspecialchars']($suggestions[0]) . ''; + $found_misspelling = true; + } + else + { + $did_you_mean['search'][] = $word; + $did_you_mean['display'][] = $smcFunc['htmlspecialchars']($word); + } + } + + if ($found_misspelling) + { + // Don't spell check excluded words, but add them still... + $temp_excluded = array('search' => array(), 'display' => array()); + foreach ($excludedWords as $word) + { + $word = $word; + + if (preg_match('~^\w+$~', $word) == 0) + { + $temp_excluded['search'][] = '-"' . $word . '"'; + $temp_excluded['display'][] = '-"' . $smcFunc['htmlspecialchars']($word) . '"'; + } + else + { + $temp_excluded['search'][] = '-' . $word; + $temp_excluded['display'][] = '-' . $smcFunc['htmlspecialchars']($word); + } + } + + $did_you_mean['search'] = array_merge($did_you_mean['search'], $temp_excluded['search']); + $did_you_mean['display'] = array_merge($did_you_mean['display'], $temp_excluded['display']); + + $temp_params = $search_params; + $temp_params['search'] = implode(' ', $did_you_mean['search']); + if (isset($temp_params['brd'])) + $temp_params['brd'] = implode(',', $temp_params['brd']); + $context['params'] = array(); + foreach ($temp_params as $k => $v) + $context['did_you_mean_params'][] = $k . '|\'|' . $v; + $context['did_you_mean_params'] = base64_encode(implode('|"|', $context['did_you_mean_params'])); + $context['did_you_mean'] = implode(' ', $did_you_mean['display']); + } + } + + // Let the user adjust the search query, should they wish? + $context['search_params'] = $search_params; + if (isset($context['search_params']['search'])) + $context['search_params']['search'] = $smcFunc['htmlspecialchars']($context['search_params']['search']); + if (isset($context['search_params']['userspec'])) + $context['search_params']['userspec'] = $smcFunc['htmlspecialchars']($context['search_params']['userspec']); + + // Do we have captcha enabled? + if ($user_info['is_guest'] && !empty($modSettings['search_enable_captcha']) && empty($_SESSION['ss_vv_passed']) && (empty($_SESSION['last_ss']) || $_SESSION['last_ss'] != $search_params['search'])) + { + // If we come from another search box tone down the error... + if (!isset($_REQUEST['search_vv'])) + $context['search_errors']['need_verification_code'] = true; + else + { + require_once($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'search', + ); + $context['require_verification'] = create_control_verification($verificationOptions, true); + + if (is_array($context['require_verification'])) + { + foreach ($context['require_verification'] as $error) + $context['search_errors'][$error] = true; + } + // Don't keep asking for it - they've proven themselves worthy. + else + $_SESSION['ss_vv_passed'] = true; + } + } + + // *** Encode all search params + + // All search params have been checked, let's compile them to a single string... made less simple by PHP 4.3.9 and below. + $temp_params = $search_params; + if (isset($temp_params['brd'])) + $temp_params['brd'] = implode(',', $temp_params['brd']); + $context['params'] = array(); + foreach ($temp_params as $k => $v) + $context['params'][] = $k . '|\'|' . $v; + + if (!empty($context['params'])) + { + // Due to old IE's 2083 character limit, we have to compress long search strings + $params = @gzcompress(implode('|"|', $context['params'])); + // Gzcompress failed, use try non-gz + if (empty($params)) + $params = implode('|"|', $context['params']); + // Base64 encode, then replace +/= with uri safe ones that can be reverted + $context['params'] = str_replace(array('+', '/', '='), array('-', '_', '.'), base64_encode($params)); + } + + // ... and add the links to the link tree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=search;params=' . $context['params'], + 'name' => $txt['search'] + ); + $context['linktree'][] = array( + 'url' => $scripturl . '?action=search2;params=' . $context['params'], + 'name' => $txt['search_results'] + ); + + // *** A last error check + + // One or more search errors? Go back to the first search screen. + if (!empty($context['search_errors'])) + { + $_REQUEST['params'] = $context['params']; + return PlushSearch1(); + } + + // Spam me not, Spam-a-lot? + if (empty($_SESSION['last_ss']) || $_SESSION['last_ss'] != $search_params['search']) + spamProtection('search'); + // Store the last search string to allow pages of results to be browsed. + $_SESSION['last_ss'] = $search_params['search']; + + // *** Reserve an ID for caching the search results. + $query_params = array_merge($search_params, array( + 'min_msg_id' => isset($minMsgID) ? (int) $minMsgID : 0, + 'max_msg_id' => isset($maxMsgID) ? (int) $maxMsgID : 0, + 'memberlist' => !empty($memberlist) ? $memberlist : array(), + )); + + // Can this search rely on the API given the parameters? + if ($searchAPI->supportsMethod('searchQuery', $query_params)) + { + $participants = array(); + $searchArray = array(); + + $num_results = $searchAPI->searchQuery($query_params, $searchWords, $excludedIndexWords, $participants, $searchArray); + } + + // Update the cache if the current search term is not yet cached. + else + { + $update_cache = empty($_SESSION['search_cache']) || ($_SESSION['search_cache']['params'] != $context['params']); + if ($update_cache) + { + // Increase the pointer... + $modSettings['search_pointer'] = empty($modSettings['search_pointer']) ? 0 : (int) $modSettings['search_pointer']; + // ...and store it right off. + updateSettings(array('search_pointer' => $modSettings['search_pointer'] >= 255 ? 0 : $modSettings['search_pointer'] + 1)); + // As long as you don't change the parameters, the cache result is yours. + $_SESSION['search_cache'] = array( + 'id_search' => $modSettings['search_pointer'], + 'num_results' => -1, + 'params' => $context['params'], + ); + + // Clear the previous cache of the final results cache. + $smcFunc['db_search_query']('delete_log_search_results', ' + DELETE FROM {db_prefix}log_search_results + WHERE id_search = {int:search_id}', + array( + 'search_id' => $_SESSION['search_cache']['id_search'], + ) + ); + + if ($search_params['subject_only']) + { + // We do this to try and avoid duplicate keys on databases not supporting INSERT IGNORE. + $inserts = array(); + foreach ($searchWords as $orIndex => $words) + { + $subject_query_params = array(); + $subject_query = array( + 'from' => '{db_prefix}topics AS t', + 'inner_join' => array(), + 'left_join' => array(), + 'where' => array(), + ); + + if ($modSettings['postmod_active']) + $subject_query['where'][] = 't.approved = {int:is_approved}'; + + $numTables = 0; + $prev_join = 0; + $numSubjectResults = 0; + foreach ($words['subject_words'] as $subjectWord) + { + $numTables++; + if (in_array($subjectWord, $excludedSubjectWords)) + { + $subject_query['left_join'][] = '{db_prefix}log_search_subjects AS subj' . $numTables . ' ON (subj' . $numTables . '.word ' . (empty($modSettings['search_match_words']) ? 'LIKE {string:subject_words_' . $numTables . '_wild}' : '= {string:subject_words_' . $numTables . '}') . ' AND subj' . $numTables . '.id_topic = t.id_topic)'; + $subject_query['where'][] = '(subj' . $numTables . '.word IS NULL)'; + } + else + { + $subject_query['inner_join'][] = '{db_prefix}log_search_subjects AS subj' . $numTables . ' ON (subj' . $numTables . '.id_topic = ' . ($prev_join === 0 ? 't' : 'subj' . $prev_join) . '.id_topic)'; + $subject_query['where'][] = 'subj' . $numTables . '.word ' . (empty($modSettings['search_match_words']) ? 'LIKE {string:subject_words_' . $numTables . '_wild}' : '= {string:subject_words_' . $numTables . '}'); + $prev_join = $numTables; + } + $subject_query_params['subject_words_' . $numTables] = $subjectWord; + $subject_query_params['subject_words_' . $numTables . '_wild'] = '%' . $subjectWord . '%'; + } + + if (!empty($userQuery)) + { + if ($subject_query['from'] != '{db_prefix}messages AS m') + { + $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (m.id_topic = t.id_topic)'; + } + $subject_query['where'][] = $userQuery; + } + if (!empty($search_params['topic'])) + $subject_query['where'][] = 't.id_topic = ' . $search_params['topic']; + if (!empty($minMsgID)) + $subject_query['where'][] = 't.id_first_msg >= ' . $minMsgID; + if (!empty($maxMsgID)) + $subject_query['where'][] = 't.id_last_msg <= ' . $maxMsgID; + if (!empty($boardQuery)) + $subject_query['where'][] = 't.id_board ' . $boardQuery; + if (!empty($excludedPhrases)) + { + if ($subject_query['from'] != '{db_prefix}messages AS m') + { + $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)'; + } + $count = 0; + foreach ($excludedPhrases as $phrase) + { + $subject_query['where'][] = 'm.subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:excluded_phrases_' . $count . '}'; + $subject_query_params['excluded_phrases_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]'; + } + } + + $ignoreRequest = $smcFunc['db_search_query']('insert_log_search_results_subject', + ($smcFunc['db_support_ignore'] ? ' + INSERT IGNORE INTO {db_prefix}log_search_results + (id_search, id_topic, relevance, id_msg, num_matches)' : '') . ' + SELECT + {int:id_search}, + t.id_topic, + 1000 * ( + {int:weight_frequency} / (t.num_replies + 1) + + {int:weight_age} * CASE WHEN t.id_first_msg < {int:min_msg} THEN 0 ELSE (t.id_first_msg - {int:min_msg}) / {int:recent_message} END + + {int:weight_length} * CASE WHEN t.num_replies < {int:huge_topic_posts} THEN t.num_replies / {int:huge_topic_posts} ELSE 1 END + + {int:weight_subject} + + {int:weight_sticky} * t.is_sticky + ) / {int:weight_total} AS relevance, + ' . (empty($userQuery) ? 't.id_first_msg' : 'm.id_msg') . ', + 1 + FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : ' + INNER JOIN ' . implode(' + INNER JOIN ', $subject_query['inner_join'])) . (empty($subject_query['left_join']) ? '' : ' + LEFT JOIN ' . implode(' + LEFT JOIN ', $subject_query['left_join'])) . ' + WHERE ' . implode(' + AND ', $subject_query['where']) . (empty($modSettings['search_max_results']) ? '' : ' + LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults)), + array_merge($subject_query_params, array( + 'id_search' => $_SESSION['search_cache']['id_search'], + 'weight_age' => $weight['age'], + 'weight_frequency' => $weight['frequency'], + 'weight_length' => $weight['length'], + 'weight_sticky' => $weight['sticky'], + 'weight_subject' => $weight['subject'], + 'weight_total' => $weight_total, + 'min_msg' => $minMsg, + 'recent_message' => $recentMsg, + 'huge_topic_posts' => $humungousTopicPosts, + 'is_approved' => 1, + )) + ); + + // If the database doesn't support IGNORE to make this fast we need to do some tracking. + if (!$smcFunc['db_support_ignore']) + { + while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) + { + // No duplicates! + if (isset($inserts[$row[1]])) + continue; + + foreach ($row as $key => $value) + $inserts[$row[1]][] = (int) $row[$key]; + } + $smcFunc['db_free_result']($ignoreRequest); + $numSubjectResults = count($inserts); + } + else + $numSubjectResults += $smcFunc['db_affected_rows'](); + + if (!empty($modSettings['search_max_results']) && $numSubjectResults >= $modSettings['search_max_results']) + break; + } + + // If there's data to be inserted for non-IGNORE databases do it here! + if (!empty($inserts)) + { + $smcFunc['db_insert']('', + '{db_prefix}log_search_results', + array('id_search' => 'int', 'id_topic' => 'int', 'relevance' => 'int', 'id_msg' => 'int', 'num_matches' => 'int'), + $inserts, + array('id_search', 'id_topic') + ); + } + + $_SESSION['search_cache']['num_results'] = $numSubjectResults; + } + else + { + $main_query = array( + 'select' => array( + 'id_search' => $_SESSION['search_cache']['id_search'], + 'relevance' => '0', + ), + 'weights' => array(), + 'from' => '{db_prefix}topics AS t', + 'inner_join' => array( + '{db_prefix}messages AS m ON (m.id_topic = t.id_topic)' + ), + 'left_join' => array(), + 'where' => array(), + 'group_by' => array(), + 'parameters' => array( + 'min_msg' => $minMsg, + 'recent_message' => $recentMsg, + 'huge_topic_posts' => $humungousTopicPosts, + 'is_approved' => 1, + ), + ); + + if (empty($search_params['topic']) && empty($search_params['show_complete'])) + { + $main_query['select']['id_topic'] = 't.id_topic'; + $main_query['select']['id_msg'] = 'MAX(m.id_msg) AS id_msg'; + $main_query['select']['num_matches'] = 'COUNT(*) AS num_matches'; + + $main_query['weights'] = array( + 'frequency' => 'COUNT(*) / (MAX(t.num_replies) + 1)', + 'age' => 'CASE WHEN MAX(m.id_msg) < {int:min_msg} THEN 0 ELSE (MAX(m.id_msg) - {int:min_msg}) / {int:recent_message} END', + 'length' => 'CASE WHEN MAX(t.num_replies) < {int:huge_topic_posts} THEN MAX(t.num_replies) / {int:huge_topic_posts} ELSE 1 END', + 'subject' => '0', + 'first_message' => 'CASE WHEN MIN(m.id_msg) = MAX(t.id_first_msg) THEN 1 ELSE 0 END', + 'sticky' => 'MAX(t.is_sticky)', + ); + + $main_query['group_by'][] = 't.id_topic'; + } + else + { + // This is outrageous! + $main_query['select']['id_topic'] = 'm.id_msg AS id_topic'; + $main_query['select']['id_msg'] = 'm.id_msg'; + $main_query['select']['num_matches'] = '1 AS num_matches'; + + $main_query['weights'] = array( + 'age' => '((m.id_msg - t.id_first_msg) / CASE WHEN t.id_last_msg = t.id_first_msg THEN 1 ELSE t.id_last_msg - t.id_first_msg END)', + 'first_message' => 'CASE WHEN m.id_msg = t.id_first_msg THEN 1 ELSE 0 END', + ); + + if (!empty($search_params['topic'])) + { + $main_query['where'][] = 't.id_topic = {int:topic}'; + $main_query['parameters']['topic'] = $search_params['topic']; + } + if (!empty($search_params['show_complete'])) + $main_query['group_by'][] = 'm.id_msg, t.id_first_msg, t.id_last_msg'; + } + + // *** Get the subject results. + $numSubjectResults = 0; + if (empty($search_params['topic'])) + { + $inserts = array(); + // Create a temporary table to store some preliminary results in. + $smcFunc['db_search_query']('drop_tmp_log_search_topics', ' + DROP TABLE IF EXISTS {db_prefix}tmp_log_search_topics', + array( + 'db_error_skip' => true, + ) + ); + $createTemporary = $smcFunc['db_search_query']('create_tmp_log_search_topics', ' + CREATE TEMPORARY TABLE {db_prefix}tmp_log_search_topics ( + id_topic mediumint(8) unsigned NOT NULL default {string:string_zero}, + PRIMARY KEY (id_topic) + ) TYPE=HEAP', + array( + 'string_zero' => '0', + 'db_error_skip' => true, + ) + ) !== false; + + // Clean up some previous cache. + if (!$createTemporary) + $smcFunc['db_search_query']('delete_log_search_topics', ' + DELETE FROM {db_prefix}log_search_topics + WHERE id_search = {int:search_id}', + array( + 'search_id' => $_SESSION['search_cache']['id_search'], + ) + ); + + foreach ($searchWords as $orIndex => $words) + { + $subject_query = array( + 'from' => '{db_prefix}topics AS t', + 'inner_join' => array(), + 'left_join' => array(), + 'where' => array(), + 'params' => array(), + ); + + $numTables = 0; + $prev_join = 0; + $count = 0; + foreach ($words['subject_words'] as $subjectWord) + { + $numTables++; + if (in_array($subjectWord, $excludedSubjectWords)) + { + if ($subject_query['from'] != '{db_prefix}messages AS m') + { + $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)'; + } + $subject_query['left_join'][] = '{db_prefix}log_search_subjects AS subj' . $numTables . ' ON (subj' . $numTables . '.word ' . (empty($modSettings['search_match_words']) ? 'LIKE {string:subject_not_' . $count . '}' : '= {string:subject_not_' . $count . '}') . ' AND subj' . $numTables . '.id_topic = t.id_topic)'; + $subject_query['params']['subject_not_' . $count] = empty($modSettings['search_match_words']) ? '%' . $subjectWord . '%' : $subjectWord; + + $subject_query['where'][] = '(subj' . $numTables . '.word IS NULL)'; + $subject_query['where'][] = 'm.body NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:body_not_' . $count . '}'; + $subject_query['params']['body_not_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($subjectWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $subjectWord), '\\\'') . '[[:>:]]'; + } + else + { + $subject_query['inner_join'][] = '{db_prefix}log_search_subjects AS subj' . $numTables . ' ON (subj' . $numTables . '.id_topic = ' . ($prev_join === 0 ? 't' : 'subj' . $prev_join) . '.id_topic)'; + $subject_query['where'][] = 'subj' . $numTables . '.word LIKE {string:subject_like_' . $count . '}'; + $subject_query['params']['subject_like_' . $count++] = empty($modSettings['search_match_words']) ? '%' . $subjectWord . '%' : $subjectWord; + $prev_join = $numTables; + } + } + + if (!empty($userQuery)) + { + if ($subject_query['from'] != '{db_prefix}messages AS m') + { + $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)'; + } + $subject_query['where'][] = '{raw:user_query}'; + $subject_query['params']['user_query'] = $userQuery; + } + if (!empty($search_params['topic'])) + { + $subject_query['where'][] = 't.id_topic = {int:topic}'; + $subject_query['params']['topic'] = $search_params['topic']; + } + if (!empty($minMsgID)) + { + $subject_query['where'][] = 't.id_first_msg >= {int:min_msg_id}'; + $subject_query['params']['min_msg_id'] = $minMsgID; + } + if (!empty($maxMsgID)) + { + $subject_query['where'][] = 't.id_last_msg <= {int:max_msg_id}'; + $subject_query['params']['max_msg_id'] = $maxMsgID; + } + if (!empty($boardQuery)) + { + $subject_query['where'][] = 't.id_board {raw:board_query}'; + $subject_query['params']['board_query'] = $boardQuery; + } + if (!empty($excludedPhrases)) + { + if ($subject_query['from'] != '{db_prefix}messages AS m') + { + $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)'; + } + $count = 0; + foreach ($excludedPhrases as $phrase) + { + $subject_query['where'][] = 'm.subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:exclude_phrase_' . $count . '}'; + $subject_query['where'][] = 'm.body NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:exclude_phrase_' . $count . '}'; + $subject_query['params']['exclude_phrase_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]'; + } + } + + // Nothing to search for? + if (empty($subject_query['where'])) + continue; + + $ignoreRequest = $smcFunc['db_search_query']('insert_log_search_topics', ($smcFunc['db_support_ignore'] ? ( ' + INSERT IGNORE INTO {db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_topics + (' . ($createTemporary ? '' : 'id_search, ') . 'id_topic)') : '') . ' + SELECT ' . ($createTemporary ? '' : $_SESSION['search_cache']['id_search'] . ', ') . 't.id_topic + FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : ' + INNER JOIN ' . implode(' + INNER JOIN ', $subject_query['inner_join'])) . (empty($subject_query['left_join']) ? '' : ' + LEFT JOIN ' . implode(' + LEFT JOIN ', $subject_query['left_join'])) . ' + WHERE ' . implode(' + AND ', $subject_query['where']) . (empty($modSettings['search_max_results']) ? '' : ' + LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults)), + $subject_query['params'] + ); + // Don't do INSERT IGNORE? Manually fix this up! + if (!$smcFunc['db_support_ignore']) + { + while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) + { + $ind = $createTemporary ? 0 : 1; + // No duplicates! + if (isset($inserts[$row[$ind]])) + continue; + + $inserts[$row[$ind]] = $row; + } + $smcFunc['db_free_result']($ignoreRequest); + $numSubjectResults = count($inserts); + } + else + $numSubjectResults += $smcFunc['db_affected_rows'](); + + if (!empty($modSettings['search_max_results']) && $numSubjectResults >= $modSettings['search_max_results']) + break; + } + + // Got some non-MySQL data to plonk in? + if (!empty($inserts)) + { + $smcFunc['db_insert']('', + ('{db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_topics'), + $createTemporary ? array('id_topic' => 'int') : array('id_search' => 'int', 'id_topic' => 'int'), + $inserts, + $createTemporary ? array('id_topic') : array('id_search', 'id_topic') + ); + } + + if ($numSubjectResults !== 0) + { + $main_query['weights']['subject'] = 'CASE WHEN MAX(lst.id_topic) IS NULL THEN 0 ELSE 1 END'; + $main_query['left_join'][] = '{db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_topics AS lst ON (' . ($createTemporary ? '' : 'lst.id_search = {int:id_search} AND ') . 'lst.id_topic = t.id_topic)'; + if (!$createTemporary) + $main_query['parameters']['id_search'] = $_SESSION['search_cache']['id_search']; + } + } + + $indexedResults = 0; + // We building an index? + if ($searchAPI->supportsMethod('indexedWordQuery', $query_params)) + { + $inserts = array(); + $smcFunc['db_search_query']('drop_tmp_log_search_messages', ' + DROP TABLE IF EXISTS {db_prefix}tmp_log_search_messages', + array( + 'db_error_skip' => true, + ) + ); + + $createTemporary = $smcFunc['db_search_query']('create_tmp_log_search_messages', ' + CREATE TEMPORARY TABLE {db_prefix}tmp_log_search_messages ( + id_msg int(10) unsigned NOT NULL default {string:string_zero}, + PRIMARY KEY (id_msg) + ) TYPE=HEAP', + array( + 'string_zero' => '0', + 'db_error_skip' => true, + ) + ) !== false; + + // Clear, all clear! + if (!$createTemporary) + $smcFunc['db_search_query']('delete_log_search_messages', ' + DELETE FROM {db_prefix}log_search_messages + WHERE id_search = {int:id_search}', + array( + 'id_search' => $_SESSION['search_cache']['id_search'], + ) + ); + + foreach ($searchWords as $orIndex => $words) + { + // Search for this word, assuming we have some words! + if (!empty($words['indexed_words'])) + { + // Variables required for the search. + $search_data = array( + 'insert_into' => ($createTemporary ? 'tmp_' : '') . 'log_search_messages', + 'no_regexp' => $no_regexp, + 'max_results' => $maxMessageResults, + 'indexed_results' => $indexedResults, + 'params' => array( + 'id_search' => !$createTemporary ? $_SESSION['search_cache']['id_search'] : 0, + 'excluded_words' => $excludedWords, + 'user_query' => !empty($userQuery) ? $userQuery : '', + 'board_query' => !empty($boardQuery) ? $boardQuery : '', + 'topic' => !empty($search_params['topic']) ? $search_params['topic'] : 0, + 'min_msg_id' => !empty($minMsgID) ? $minMsgID : 0, + 'max_msg_id' => !empty($maxMsgID) ? $maxMsgID : 0, + 'excluded_phrases' => !empty($excludedPhrases) ? $excludedPhrases : array(), + 'excluded_index_words' => !empty($excludedIndexWords) ? $excludedIndexWords : array(), + 'excluded_subject_words' => !empty($excludedSubjectWords) ? $excludedSubjectWords : array(), + ), + ); + + $ignoreRequest = $searchAPI->indexedWordQuery($words, $search_data); + + if (!$smcFunc['db_support_ignore']) + { + while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) + { + // No duplicates! + if (isset($inserts[$row[0]])) + continue; + + $inserts[$row[0]] = $row; + } + $smcFunc['db_free_result']($ignoreRequest); + $indexedResults = count($inserts); + } + else + $indexedResults += $smcFunc['db_affected_rows'](); + + if (!empty($maxMessageResults) && $indexedResults >= $maxMessageResults) + break; + } + } + + // More non-MySQL stuff needed? + if (!empty($inserts)) + { + $smcFunc['db_insert']('', + '{db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_messages', + $createTemporary ? array('id_msg' => 'int') : array('id_msg' => 'int', 'id_search' => 'int'), + $inserts, + $createTemporary ? array('id_msg') : array('id_msg', 'id_search') + ); + } + + if (empty($indexedResults) && empty($numSubjectResults) && !empty($modSettings['search_force_index'])) + { + $context['search_errors']['query_not_specific_enough'] = true; + $_REQUEST['params'] = $context['params']; + return PlushSearch1(); + } + elseif (!empty($indexedResults)) + { + $main_query['inner_join'][] = '{db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_messages AS lsm ON (lsm.id_msg = m.id_msg)'; + if (!$createTemporary) + { + $main_query['where'][] = 'lsm.id_search = {int:id_search}'; + $main_query['parameters']['id_search'] = $_SESSION['search_cache']['id_search']; + } + } + } + + // Not using an index? All conditions have to be carried over. + else + { + $orWhere = array(); + $count = 0; + foreach ($searchWords as $orIndex => $words) + { + $where = array(); + foreach ($words['all_words'] as $regularWord) + { + $where[] = 'm.body' . (in_array($regularWord, $excludedWords) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:all_word_body_' . $count . '}'; + if (in_array($regularWord, $excludedWords)) + $where[] = 'm.subject NOT' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:all_word_body_' . $count . '}'; + $main_query['parameters']['all_word_body_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . '[[:>:]]'; + } + if (!empty($where)) + $orWhere[] = count($where) > 1 ? '(' . implode(' AND ', $where) . ')' : $where[0]; + } + if (!empty($orWhere)) + $main_query['where'][] = count($orWhere) > 1 ? '(' . implode(' OR ', $orWhere) . ')' : $orWhere[0]; + + if (!empty($userQuery)) + { + $main_query['where'][] = '{raw:user_query}'; + $main_query['parameters']['user_query'] = $userQuery; + } + if (!empty($search_params['topic'])) + { + $main_query['where'][] = 'm.id_topic = {int:topic}'; + $main_query['parameters']['topic'] = $search_params['topic']; + } + if (!empty($minMsgID)) + { + $main_query['where'][] = 'm.id_msg >= {int:min_msg_id}'; + $main_query['parameters']['min_msg_id'] = $minMsgID; + } + if (!empty($maxMsgID)) + { + $main_query['where'][] = 'm.id_msg <= {int:max_msg_id}'; + $main_query['parameters']['max_msg_id'] = $maxMsgID; + } + if (!empty($boardQuery)) + { + $main_query['where'][] = 'm.id_board {raw:board_query}'; + $main_query['parameters']['board_query'] = $boardQuery; + } + } + + // Did we either get some indexed results, or otherwise did not do an indexed query? + if (!empty($indexedResults) || !$searchAPI->supportsMethod('indexedWordQuery', $query_params)) + { + $relevance = '1000 * ('; + $new_weight_total = 0; + foreach ($main_query['weights'] as $type => $value) + { + $relevance .= $weight[$type] . ' * ' . $value . ' + '; + $new_weight_total += $weight[$type]; + } + $main_query['select']['relevance'] = substr($relevance, 0, -3) . ') / ' . $new_weight_total . ' AS relevance'; + + $ignoreRequest = $smcFunc['db_search_query']('insert_log_search_results_no_index', ($smcFunc['db_support_ignore'] ? ( ' + INSERT IGNORE INTO ' . '{db_prefix}log_search_results + (' . implode(', ', array_keys($main_query['select'])) . ')') : '') . ' + SELECT + ' . implode(', + ', $main_query['select']) . ' + FROM ' . $main_query['from'] . (empty($main_query['inner_join']) ? '' : ' + INNER JOIN ' . implode(' + INNER JOIN ', $main_query['inner_join'])) . (empty($main_query['left_join']) ? '' : ' + LEFT JOIN ' . implode(' + LEFT JOIN ', $main_query['left_join'])) . (!empty($main_query['where']) ? ' + WHERE ' : '') . implode(' + AND ', $main_query['where']) . (empty($main_query['group_by']) ? '' : ' + GROUP BY ' . implode(', ', $main_query['group_by'])) . (empty($modSettings['search_max_results']) ? '' : ' + LIMIT ' . $modSettings['search_max_results']), + $main_query['parameters'] + ); + + // We love to handle non-good databases that don't support our ignore! + if (!$smcFunc['db_support_ignore']) + { + $inserts = array(); + while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) + { + // No duplicates! + if (isset($inserts[$row[2]])) + continue; + + foreach ($row as $key => $value) + $inserts[$row[2]][] = (int) $row[$key]; + } + $smcFunc['db_free_result']($ignoreRequest); + + // Now put them in! + if (!empty($inserts)) + { + $query_columns = array(); + foreach ($main_query['select'] as $k => $v) + $query_columns[$k] = 'int'; + + $smcFunc['db_insert']('', + '{db_prefix}log_search_results', + $query_columns, + $inserts, + array('id_search', 'id_topic') + ); + } + $_SESSION['search_cache']['num_results'] += count($inserts); + } + else + $_SESSION['search_cache']['num_results'] = $smcFunc['db_affected_rows'](); + } + + // Insert subject-only matches. + if ($_SESSION['search_cache']['num_results'] < $modSettings['search_max_results'] && $numSubjectResults !== 0) + { + $usedIDs = array_flip(empty($inserts) ? array() : array_keys($inserts)); + $ignoreRequest = $smcFunc['db_search_query']('insert_log_search_results_sub_only', ($smcFunc['db_support_ignore'] ? ( ' + INSERT IGNORE INTO {db_prefix}log_search_results + (id_search, id_topic, relevance, id_msg, num_matches)') : '') . ' + SELECT + {int:id_search}, + t.id_topic, + 1000 * ( + {int:weight_frequency} / (t.num_replies + 1) + + {int:weight_age} * CASE WHEN t.id_first_msg < {int:min_msg} THEN 0 ELSE (t.id_first_msg - {int:min_msg}) / {int:recent_message} END + + {int:weight_length} * CASE WHEN t.num_replies < {int:huge_topic_posts} THEN t.num_replies / {int:huge_topic_posts} ELSE 1 END + + {int:weight_subject} + + {int:weight_sticky} * t.is_sticky + ) / {int:weight_total} AS relevance, + t.id_first_msg, + 1 + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_topics AS lst ON (lst.id_topic = t.id_topic)' + . ($createTemporary ? '' : 'WHERE lst.id_search = {int:id_search}') + . (empty($modSettings['search_max_results']) ? '' : ' + LIMIT ' . ($modSettings['search_max_results'] - $_SESSION['search_cache']['num_results'])), + array( + 'id_search' => $_SESSION['search_cache']['id_search'], + 'weight_age' => $weight['age'], + 'weight_frequency' => $weight['frequency'], + 'weight_length' => $weight['frequency'], + 'weight_sticky' => $weight['frequency'], + 'weight_subject' => $weight['frequency'], + 'weight_total' => $weight_total, + 'min_msg' => $minMsg, + 'recent_message' => $recentMsg, + 'huge_topic_posts' => $humungousTopicPosts, + ) + ); + // Once again need to do the inserts if the database don't support ignore! + if (!$smcFunc['db_support_ignore']) + { + $inserts = array(); + while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) + { + // No duplicates! + if (isset($usedIDs[$row[1]])) + continue; + + $usedIDs[$row[1]] = true; + $inserts[] = $row; + } + $smcFunc['db_free_result']($ignoreRequest); + + // Now put them in! + if (!empty($inserts)) + { + $smcFunc['db_insert']('', + '{db_prefix}log_search_results', + array('id_search' => 'int', 'id_topic' => 'int', 'relevance' => 'float', 'id_msg' => 'int', 'num_matches' => 'int'), + $inserts, + array('id_search', 'id_topic') + ); + } + $_SESSION['search_cache']['num_results'] += count($inserts); + } + else + $_SESSION['search_cache']['num_results'] += $smcFunc['db_affected_rows'](); + } + else + $_SESSION['search_cache']['num_results'] = 0; + } + } + + // *** Retrieve the results to be shown on the page + $participants = array(); + $request = $smcFunc['db_search_query']('', ' + SELECT ' . (empty($search_params['topic']) ? 'lsr.id_topic' : $search_params['topic'] . ' AS id_topic') . ', lsr.id_msg, lsr.relevance, lsr.num_matches + FROM {db_prefix}log_search_results AS lsr' . ($search_params['sort'] == 'num_replies' ? ' + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = lsr.id_topic)' : '') . ' + WHERE lsr.id_search = {int:id_search} + ORDER BY ' . $search_params['sort'] . ' ' . $search_params['sort_dir'] . ' + LIMIT ' . (int) $_REQUEST['start'] . ', ' . $modSettings['search_results_per_page'], + array( + 'id_search' => $_SESSION['search_cache']['id_search'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $context['topics'][$row['id_msg']] = array( + 'relevance' => round($row['relevance'] / 10, 1) . '%', + 'num_matches' => $row['num_matches'], + 'matches' => array(), + ); + // By default they didn't participate in the topic! + $participants[$row['id_topic']] = false; + } + $smcFunc['db_free_result']($request); + + $num_results = $_SESSION['search_cache']['num_results']; + } + + if (!empty($context['topics'])) + { + // Create an array for the permissions. + $boards_can = array( + 'post_reply_own' => boardsAllowedTo('post_reply_own'), + 'post_reply_any' => boardsAllowedTo('post_reply_any'), + 'mark_any_notify' => boardsAllowedTo('mark_any_notify') + ); + + // How's about some quick moderation? + if (!empty($options['display_quick_mod'])) + { + $boards_can['lock_any'] = boardsAllowedTo('lock_any'); + $boards_can['lock_own'] = boardsAllowedTo('lock_own'); + $boards_can['make_sticky'] = boardsAllowedTo('make_sticky'); + $boards_can['move_any'] = boardsAllowedTo('move_any'); + $boards_can['move_own'] = boardsAllowedTo('move_own'); + $boards_can['remove_any'] = boardsAllowedTo('remove_any'); + $boards_can['remove_own'] = boardsAllowedTo('remove_own'); + $boards_can['merge_any'] = boardsAllowedTo('merge_any'); + + $context['can_lock'] = in_array(0, $boards_can['lock_any']); + $context['can_sticky'] = in_array(0, $boards_can['make_sticky']) && !empty($modSettings['enableStickyTopics']); + $context['can_move'] = in_array(0, $boards_can['move_any']); + $context['can_remove'] = in_array(0, $boards_can['remove_any']); + $context['can_merge'] = in_array(0, $boards_can['merge_any']); + } + + // What messages are we using? + $msg_list = array_keys($context['topics']); + + // Load the posters... + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}messages + WHERE id_member != {int:no_member} + AND id_msg IN ({array_int:message_list}) + LIMIT ' . count($context['topics']), + array( + 'message_list' => $msg_list, + 'no_member' => 0, + ) + ); + $posters = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $posters[] = $row['id_member']; + $smcFunc['db_free_result']($request); + + if (!empty($posters)) + loadMemberData(array_unique($posters)); + + // Get the messages out for the callback - select enough that it can be made to look just like Display. + $messages_request = $smcFunc['db_query']('', ' + SELECT + m.id_msg, m.subject, m.poster_name, m.poster_email, m.poster_time, m.id_member, + m.icon, m.poster_ip, m.body, m.smileys_enabled, m.modified_time, m.modified_name, + first_m.id_msg AS first_msg, first_m.subject AS first_subject, first_m.icon AS first_icon, first_m.poster_time AS first_poster_time, + first_mem.id_member AS first_member_id, IFNULL(first_mem.real_name, first_m.poster_name) AS first_member_name, + last_m.id_msg AS last_msg, last_m.poster_time AS last_poster_time, last_mem.id_member AS last_member_id, + IFNULL(last_mem.real_name, last_m.poster_name) AS last_member_name, last_m.icon AS last_icon, last_m.subject AS last_subject, + t.id_topic, t.is_sticky, t.locked, t.id_poll, t.num_replies, t.num_views, + b.id_board, b.name AS board_name, c.id_cat, c.name AS cat_name + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + INNER JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + INNER JOIN {db_prefix}messages AS first_m ON (first_m.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}messages AS last_m ON (last_m.id_msg = t.id_last_msg) + LEFT JOIN {db_prefix}members AS first_mem ON (first_mem.id_member = first_m.id_member) + LEFT JOIN {db_prefix}members AS last_mem ON (last_mem.id_member = first_m.id_member) + WHERE m.id_msg IN ({array_int:message_list})' . ($modSettings['postmod_active'] ? ' + AND m.approved = {int:is_approved}' : '') . ' + ORDER BY FIND_IN_SET(m.id_msg, {string:message_list_in_set}) + LIMIT {int:limit}', + array( + 'message_list' => $msg_list, + 'is_approved' => 1, + 'message_list_in_set' => implode(',', $msg_list), + 'limit' => count($context['topics']), + ) + ); + + // If there are no results that means the things in the cache got deleted, so pretend we have no topics anymore. + if ($smcFunc['db_num_rows']($messages_request) == 0) + $context['topics'] = array(); + + // If we want to know who participated in what then load this now. + if (!empty($modSettings['enableParticipation']) && !$user_info['is_guest']) + { + $result = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topic_list}) + AND id_member = {int:current_member} + GROUP BY id_topic + LIMIT ' . count($participants), + array( + 'current_member' => $user_info['id'], + 'topic_list' => array_keys($participants), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $participants[$row['id_topic']] = true; + $smcFunc['db_free_result']($result); + } + } + + // Now that we know how many results to expect we can start calculating the page numbers. + $context['page_index'] = constructPageIndex($scripturl . '?action=search2;params=' . $context['params'], $_REQUEST['start'], $num_results, $modSettings['search_results_per_page'], false); + + // Consider the search complete! + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2) + cache_put_data('search_start:' . ($user_info['is_guest'] ? $user_info['ip'] : $user_info['id']), null, 90); + + $context['key_words'] = &$searchArray; + + // Setup the default topic icons... for checking they exist and the like! + $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless', 'clip'); + $context['icon_sources'] = array(); + foreach ($stable_icons as $icon) + $context['icon_sources'][$icon] = 'images_url'; + + $context['sub_template'] = 'results'; + $context['page_title'] = $txt['search_results']; + $context['get_topics'] = 'prepareSearchContext'; + $context['can_send_pm'] = allowedTo('pm_send'); + + $context['jump_to'] = array( + 'label' => addslashes(un_htmlspecialchars($txt['jump_to'])), + 'board_name' => addslashes(un_htmlspecialchars($txt['select_destination'])), + ); +} + +// Callback to return messages - saves memory. +// !!! Fix this, update it, whatever... from Display.php mainly. +function prepareSearchContext($reset = false) +{ + global $txt, $modSettings, $scripturl, $user_info, $sourcedir; + global $memberContext, $context, $settings, $options, $messages_request; + global $boards_can, $participants, $smcFunc; + + // Remember which message this is. (ie. reply #83) + static $counter = null; + if ($counter == null || $reset) + $counter = $_REQUEST['start'] + 1; + + // If the query returned false, bail. + if ($messages_request == false) + return false; + + // Start from the beginning... + if ($reset) + return @$smcFunc['db_data_seek']($messages_request, 0); + + // Attempt to get the next message. + $message = $smcFunc['db_fetch_assoc']($messages_request); + if (!$message) + return false; + + // Can't have an empty subject can we? + $message['subject'] = $message['subject'] != '' ? $message['subject'] : $txt['no_subject']; + + $message['first_subject'] = $message['first_subject'] != '' ? $message['first_subject'] : $txt['no_subject']; + $message['last_subject'] = $message['last_subject'] != '' ? $message['last_subject'] : $txt['no_subject']; + + // If it couldn't load, or the user was a guest.... someday may be done with a guest table. + if (!loadMemberContext($message['id_member'])) + { + // Notice this information isn't used anywhere else.... *cough guest table cough*. + $memberContext[$message['id_member']]['name'] = $message['poster_name']; + $memberContext[$message['id_member']]['id'] = 0; + $memberContext[$message['id_member']]['group'] = $txt['guest_title']; + $memberContext[$message['id_member']]['link'] = $message['poster_name']; + $memberContext[$message['id_member']]['email'] = $message['poster_email']; + } + $memberContext[$message['id_member']]['ip'] = $message['poster_ip']; + + // Do the censor thang... + censorText($message['body']); + censorText($message['subject']); + + censorText($message['first_subject']); + censorText($message['last_subject']); + + // Shorten this message if necessary. + if ($context['compact']) + { + // Set the number of characters before and after the searched keyword. + $charLimit = 50; + + $message['body'] = strtr($message['body'], array("\n" => ' ', '
' => "\n")); + $message['body'] = parse_bbc($message['body'], $message['smileys_enabled'], $message['id_msg']); + $message['body'] = strip_tags(strtr($message['body'], array('' => '
', '' => '
')), '
'); + + if ($smcFunc['strlen']($message['body']) > $charLimit) + { + if (empty($context['key_words'])) + $message['body'] = $smcFunc['substr']($message['body'], 0, $charLimit) . '...'; + else + { + $matchString = ''; + $force_partial_word = false; + foreach ($context['key_words'] as $keyword) + { + $keyword = preg_replace('~&#(\d{1,7}|x[0-9a-fA-F]{1,6});~e', '$GLOBALS[\'smcFunc\'][\'entity_fix\'](\'\\1\')', strtr($keyword, array('\\\'' => '\'', '&' => '&'))); + + if (preg_match('~[\'\.,/@%&;:(){}\[\]_\-+\\\\]$~', $keyword) != 0 || preg_match('~^[\'\.,/@%&;:(){}\[\]_\-+\\\\]~', $keyword) != 0) + $force_partial_word = true; + $matchString .= strtr(preg_quote($keyword, '/'), array('\*' => '.+?')) . '|'; + } + $matchString = substr($matchString, 0, -1); + + $message['body'] = un_htmlspecialchars(strtr($message['body'], array(' ' => ' ', '
' => "\n", '[' => '[', ']' => ']', ':' => ':', '@' => '@'))); + + if (empty($modSettings['search_method']) || $force_partial_word) + preg_match_all('/([^\s\W]{' . $charLimit . '}[\s\W]|[\s\W].{0,' . $charLimit . '}?|^)(' . $matchString . ')(.{0,' . $charLimit . '}[\s\W]|[^\s\W]{' . $charLimit . '})/is' . ($context['utf8'] ? 'u' : ''), $message['body'], $matches); + else + preg_match_all('/([^\s\W]{' . $charLimit . '}[\s\W]|[\s\W].{0,' . $charLimit . '}?[\s\W]|^)(' . $matchString . ')([\s\W].{0,' . $charLimit . '}[\s\W]|[\s\W][^\s\W]{' . $charLimit . '})/is' . ($context['utf8'] ? 'u' : ''), $message['body'], $matches); + + $message['body'] = ''; + foreach ($matches[0] as $index => $match) + { + $match = strtr(htmlspecialchars($match, ENT_QUOTES), array("\n" => ' ')); + $message['body'] .= '...... ' . $match . ' ......'; + } + } + + // Re-fix the international characters. + $message['body'] = preg_replace('~&#(\d{1,7}|x[0-9a-fA-F]{1,6});~e', '$GLOBALS[\'smcFunc\'][\'entity_fix\'](\'\\1\')', $message['body']); + } + } + else + { + // Run BBC interpreter on the message. + $message['body'] = parse_bbc($message['body'], $message['smileys_enabled'], $message['id_msg']); + } + + // Make sure we don't end up with a practically empty message body. + $message['body'] = preg_replace('~^(?: )+$~', '', $message['body']); + + // Sadly, we need to check the icon ain't broke. + if (empty($modSettings['messageIconChecks_disable'])) + { + if (!isset($context['icon_sources'][$message['first_icon']])) + $context['icon_sources'][$message['first_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $message['first_icon'] . '.gif') ? 'images_url' : 'default_images_url'; + if (!isset($context['icon_sources'][$message['last_icon']])) + $context['icon_sources'][$message['last_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $message['last_icon'] . '.gif') ? 'images_url' : 'default_images_url'; + if (!isset($context['icon_sources'][$message['icon']])) + $context['icon_sources'][$message['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $message['icon'] . '.gif') ? 'images_url' : 'default_images_url'; + } + else + { + if (!isset($context['icon_sources'][$message['first_icon']])) + $context['icon_sources'][$message['first_icon']] = 'images_url'; + if (!isset($context['icon_sources'][$message['last_icon']])) + $context['icon_sources'][$message['last_icon']] = 'images_url'; + if (!isset($context['icon_sources'][$message['icon']])) + $context['icon_sources'][$message['icon']] = 'images_url'; + } + + // Do we have quote tag enabled? + $quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])); + + $output = array_merge($context['topics'][$message['id_msg']], array( + 'id' => $message['id_topic'], + 'is_sticky' => !empty($modSettings['enableStickyTopics']) && !empty($message['is_sticky']), + 'is_locked' => !empty($message['locked']), + 'is_poll' => $modSettings['pollMode'] == '1' && $message['id_poll'] > 0, + 'is_hot' => $message['num_replies'] >= $modSettings['hotTopicPosts'], + 'is_very_hot' => $message['num_replies'] >= $modSettings['hotTopicVeryPosts'], + 'posted_in' => !empty($participants[$message['id_topic']]), + 'views' => $message['num_views'], + 'replies' => $message['num_replies'], + 'can_reply' => in_array($message['id_board'], $boards_can['post_reply_any']) || in_array(0, $boards_can['post_reply_any']), + 'can_quote' => (in_array($message['id_board'], $boards_can['post_reply_any']) || in_array(0, $boards_can['post_reply_any'])) && $quote_enabled, + 'can_mark_notify' => in_array($message['id_board'], $boards_can['mark_any_notify']) || in_array(0, $boards_can['mark_any_notify']) && !$context['user']['is_guest'], + 'first_post' => array( + 'id' => $message['first_msg'], + 'time' => timeformat($message['first_poster_time']), + 'timestamp' => forum_time(true, $message['first_poster_time']), + 'subject' => $message['first_subject'], + 'href' => $scripturl . '?topic=' . $message['id_topic'] . '.0', + 'link' => '' . $message['first_subject'] . '', + 'icon' => $message['first_icon'], + 'icon_url' => $settings[$context['icon_sources'][$message['first_icon']]] . '/post/' . $message['first_icon'] . '.gif', + 'member' => array( + 'id' => $message['first_member_id'], + 'name' => $message['first_member_name'], + 'href' => !empty($message['first_member_id']) ? $scripturl . '?action=profile;u=' . $message['first_member_id'] : '', + 'link' => !empty($message['first_member_id']) ? '' . $message['first_member_name'] . '' : $message['first_member_name'] + ) + ), + 'last_post' => array( + 'id' => $message['last_msg'], + 'time' => timeformat($message['last_poster_time']), + 'timestamp' => forum_time(true, $message['last_poster_time']), + 'subject' => $message['last_subject'], + 'href' => $scripturl . '?topic=' . $message['id_topic'] . ($message['num_replies'] == 0 ? '.0' : '.msg' . $message['last_msg']) . '#msg' . $message['last_msg'], + 'link' => '' . $message['last_subject'] . '', + 'icon' => $message['last_icon'], + 'icon_url' => $settings[$context['icon_sources'][$message['last_icon']]] . '/post/' . $message['last_icon'] . '.gif', + 'member' => array( + 'id' => $message['last_member_id'], + 'name' => $message['last_member_name'], + 'href' => !empty($message['last_member_id']) ? $scripturl . '?action=profile;u=' . $message['last_member_id'] : '', + 'link' => !empty($message['last_member_id']) ? '' . $message['last_member_name'] . '' : $message['last_member_name'] + ) + ), + 'board' => array( + 'id' => $message['id_board'], + 'name' => $message['board_name'], + 'href' => $scripturl . '?board=' . $message['id_board'] . '.0', + 'link' => '' . $message['board_name'] . '' + ), + 'category' => array( + 'id' => $message['id_cat'], + 'name' => $message['cat_name'], + 'href' => $scripturl . '#c' . $message['id_cat'], + 'link' => '' . $message['cat_name'] . '' + ) + )); + determineTopicClass($output); + + if ($output['posted_in']) + $output['class'] = 'my_' . $output['class']; + + $body_highlighted = $message['body']; + $subject_highlighted = $message['subject']; + + if (!empty($options['display_quick_mod'])) + { + $started = $output['first_post']['member']['id'] == $user_info['id']; + + $output['quick_mod'] = array( + 'lock' => in_array(0, $boards_can['lock_any']) || in_array($output['board']['id'], $boards_can['lock_any']) || ($started && (in_array(0, $boards_can['lock_own']) || in_array($output['board']['id'], $boards_can['lock_own']))), + 'sticky' => (in_array(0, $boards_can['make_sticky']) || in_array($output['board']['id'], $boards_can['make_sticky'])) && !empty($modSettings['enableStickyTopics']), + 'move' => in_array(0, $boards_can['move_any']) || in_array($output['board']['id'], $boards_can['move_any']) || ($started && (in_array(0, $boards_can['move_own']) || in_array($output['board']['id'], $boards_can['move_own']))), + 'remove' => in_array(0, $boards_can['remove_any']) || in_array($output['board']['id'], $boards_can['remove_any']) || ($started && (in_array(0, $boards_can['remove_own']) || in_array($output['board']['id'], $boards_can['remove_own']))), + ); + + $context['can_lock'] |= $output['quick_mod']['lock']; + $context['can_sticky'] |= $output['quick_mod']['sticky']; + $context['can_move'] |= $output['quick_mod']['move']; + $context['can_remove'] |= $output['quick_mod']['remove']; + $context['can_merge'] |= in_array($output['board']['id'], $boards_can['merge_any']); + + // If we've found a message we can move, and we don't already have it, load the destinations. + if ($options['display_quick_mod'] == 1 && !isset($context['move_to_boards']) && $context['can_move']) + { + require_once($sourcedir . '/Subs-MessageIndex.php'); + $boardListOptions = array( + 'use_permissions' => true, + 'not_redirection' => true, + 'selected_board' => empty($_SESSION['move_to_topic']) ? null : $_SESSION['move_to_topic'], + ); + $context['move_to_boards'] = getBoardList($boardListOptions); + } + } + + foreach ($context['key_words'] as $query) + { + // Fix the international characters in the keyword too. + $query = strtr($smcFunc['htmlspecialchars']($query), array('\\\'' => '\'')); + + $body_highlighted = preg_replace('/((<[^>]*)|' . preg_quote(strtr($query, array('\'' => ''')), '/') . ')/ie' . ($context['utf8'] ? 'u' : ''), "'\$2' == '\$1' ? stripslashes('\$1') : '\$1'", $body_highlighted); + $subject_highlighted = preg_replace('/(' . preg_quote($query, '/') . ')/i' . ($context['utf8'] ? 'u' : ''), '$1', $subject_highlighted); + } + + $output['matches'][] = array( + 'id' => $message['id_msg'], + 'attachment' => loadAttachmentContext($message['id_msg']), + 'alternate' => $counter % 2, + 'member' => &$memberContext[$message['id_member']], + 'icon' => $message['icon'], + 'icon_url' => $settings[$context['icon_sources'][$message['icon']]] . '/post/' . $message['icon'] . '.gif', + 'subject' => $message['subject'], + 'subject_highlighted' => $subject_highlighted, + 'time' => timeformat($message['poster_time']), + 'timestamp' => forum_time(true, $message['poster_time']), + 'counter' => $counter, + 'modified' => array( + 'time' => timeformat($message['modified_time']), + 'timestamp' => forum_time(true, $message['modified_time']), + 'name' => $message['modified_name'] + ), + 'body' => $message['body'], + 'body_highlighted' => $body_highlighted, + 'start' => 'msg' . $message['id_msg'] + ); + $counter++; + + return $output; +} + +// This function compares the length of two strings plus a little. +function searchSort($a, $b) +{ + global $searchAPI; + + return $searchAPI->searchSort($a, $b); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/SearchAPI-Custom.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/SearchAPI-Custom.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,214 @@ +supported_databases)) + { + $this->is_supported = false; + return; + } + + if (empty($modSettings['search_custom_index_config'])) + return; + + $this->indexSettings = unserialize($modSettings['search_custom_index_config']); + + $this->bannedWords = empty($modSettings['search_stopwords']) ? array() : explode(',', $modSettings['search_stopwords']); + $this->min_word_length = $this->indexSettings['bytes_per_word']; + } + + // Check whether the search can be performed by this API. + public function supportsMethod($methodName, $query_params = null) + { + switch ($methodName) + { + case 'isValid': + case 'searchSort': + case 'prepareIndexes': + case 'indexedWordQuery': + return true; + break; + + default: + + // All other methods, too bad dunno you. + return false; + return; + } + } + + // If the settings don't exist we can't continue. + public function isValid() + { + global $modSettings; + + return !empty($modSettings['search_custom_index_config']); + } + + // This function compares the length of two strings plus a little. + public function searchSort($a, $b) + { + global $modSettings, $excludedWords; + + $x = strlen($a) - (in_array($a, $excludedWords) ? 1000 : 0); + $y = strlen($b) - (in_array($b, $excludedWords) ? 1000 : 0); + + return $y < $x ? 1 : ($y > $x ? -1 : 0); + } + + // Do we have to do some work with the words we are searching for to prepare them? + public function prepareIndexes($word, &$wordsSearch, &$wordsExclude, $isExcluded) + { + global $modSettings, $smcFunc; + + $subwords = text2words($word, $this->min_word_length, true); + + if (empty($modSettings['search_force_index'])) + $wordsSearch['words'][] = $word; + + // Excluded phrases don't benefit from being split into subwords. + if (count($subwords) > 1 && $isExcluded) + continue; + else + { + foreach ($subwords as $subword) + { + if ($smcFunc['strlen']($subword) >= $this->min_word_length && !in_array($subword, $this->bannedWords)) + { + $wordsSearch['indexed_words'][] = $subword; + if ($isExcluded) + $wordsExclude[] = $subword; + } + } + } + } + + // Search for indexed words. + public function indexedWordQuery($words, $search_data) + { + global $modSettings, $smcFunc; + + $query_select = array( + 'id_msg' => 'm.id_msg', + ); + $query_inner_join = array(); + $query_left_join = array(); + $query_where = array(); + $query_params = $search_data['params']; + + if ($query_params['id_search']) + $query_select['id_search'] = '{int:id_search}'; + + $count = 0; + foreach ($words['words'] as $regularWord) + { + $query_where[] = 'm.body' . (in_array($regularWord, $query_params['excluded_words']) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? ' LIKE ' : ' RLIKE ') . '{string:complex_body_' . $count . '}'; + $query_params['complex_body_' . $count++] = empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? '%' . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . '[[:>:]]'; + } + + if ($query_params['user_query']) + $query_where[] = '{raw:user_query}'; + if ($query_params['board_query']) + $query_where[] = 'm.id_board {raw:board_query}'; + + if ($query_params['topic']) + $query_where[] = 'm.id_topic = {int:topic}'; + if ($query_params['min_msg_id']) + $query_where[] = 'm.id_msg >= {int:min_msg_id}'; + if ($query_params['max_msg_id']) + $query_where[] = 'm.id_msg <= {int:max_msg_id}'; + + $count = 0; + if (!empty($query_params['excluded_phrases']) && empty($modSettings['search_force_index'])) + foreach ($query_params['excluded_phrases'] as $phrase) + { + $query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? ' LIKE ' : ' RLIKE ') . '{string:exclude_subject_phrase_' . $count . '}'; + $query_params['exclude_subject_phrase_' . $count++] = empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]'; + } + $count = 0; + if (!empty($query_params['excluded_subject_words']) && empty($modSettings['search_force_index'])) + foreach ($query_params['excluded_subject_words'] as $excludedWord) + { + $query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? ' LIKE ' : ' RLIKE ') . '{string:exclude_subject_words_' . $count . '}'; + $query_params['exclude_subject_words_' . $count++] = empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? '%' . strtr($excludedWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $excludedWord), '\\\'') . '[[:>:]]'; + } + + $numTables = 0; + $prev_join = 0; + foreach ($words['indexed_words'] as $indexedWord) + { + $numTables++; + if (in_array($indexedWord, $query_params['excluded_index_words'])) + { + $query_left_join[] = '{db_prefix}log_search_words AS lsw' . $numTables . ' ON (lsw' . $numTables . '.id_word = ' . $indexedWord . ' AND lsw' . $numTables . '.id_msg = m.id_msg)'; + $query_where[] = '(lsw' . $numTables . '.id_word IS NULL)'; + } + else + { + $query_inner_join[] = '{db_prefix}log_search_words AS lsw' . $numTables . ' ON (lsw' . $numTables . '.id_msg = ' . ($prev_join === 0 ? 'm' : 'lsw' . $prev_join) . '.id_msg)'; + $query_where[] = 'lsw' . $numTables . '.id_word = ' . $indexedWord; + $prev_join = $numTables; + } + } + + $ignoreRequest = $smcFunc['db_search_query']('insert_into_log_messages_fulltext', ($smcFunc['db_support_ignore'] ? ( ' + INSERT IGNORE INTO {db_prefix}' . $search_data['insert_into'] . ' + (' . implode(', ', array_keys($query_select)) . ')') : '') . ' + SELECT ' . implode(', ', $query_select) . ' + FROM {db_prefix}messages AS m' . (empty($query_inner_join) ? '' : ' + INNER JOIN ' . implode(' + INNER JOIN ', $query_inner_join)) . (empty($query_left_join) ? '' : ' + LEFT JOIN ' . implode(' + LEFT JOIN ', $query_left_join)) . ' + WHERE ' . implode(' + AND ', $query_where) . (empty($search_data['max_results']) ? '' : ' + LIMIT ' . ($search_data['max_results'] - $search_data['indexed_results'])), + $query_params + ); + + return $ignoreRequest; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/SearchAPI-Fulltext.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/SearchAPI-Fulltext.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,241 @@ +supported_databases)) + { + $this->is_supported = false; + return; + } + + // Some MySQL versions are superior to others :P. + $this->canDoBooleanSearch = version_compare($smcFunc['db_server_info']($db_connection), '4.0.1', '>='); + + $this->bannedWords = empty($modSettings['search_banned_words']) ? array() : explode(',', $modSettings['search_banned_words']); + $this->min_word_length = $this->_getMinWordLength(); + } + + // Check whether the method can be performed by this API. + public function supportsMethod($methodName, $query_params = null) + { + switch ($methodName) + { + case 'searchSort': + case 'prepareIndexes': + case 'indexedWordQuery': + return true; + break; + + default: + return false; + break; + } + } + + // What is the minimum word length full text supports? + protected function _getMinWordLength() + { + global $smcFunc; + + // Try to determine the minimum number of letters for a fulltext search. + $request = $smcFunc['db_search_query']('max_fulltext_length', ' + SHOW VARIABLES + LIKE {string:fulltext_minimum_word_length}', + array( + 'fulltext_minimum_word_length' => 'ft_min_word_len', + ) + ); + if ($request !== false && $smcFunc['db_num_rows']($request) == 1) + { + list (, $min_word_length) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + // 4 is the MySQL default... + else + $min_word_length = 4; + + return $min_word_length; + } + + // This function compares the length of two strings plus a little. + public function searchSort($a, $b) + { + global $modSettings, $excludedWords; + + $x = strlen($a) - (in_array($a, $excludedWords) ? 1000 : 0); + $y = strlen($b) - (in_array($b, $excludedWords) ? 1000 : 0); + + return $x < $y ? 1 : ($x > $y ? -1 : 0); + } + + // Do we have to do some work with the words we are searching for to prepare them? + public function prepareIndexes($word, &$wordsSearch, &$wordsExclude, $isExcluded) + { + global $modSettings; + + $subwords = text2words($word, null, false); + + if (!$this->canDoBooleanSearch && count($subwords) > 1 && empty($modSettings['search_force_index'])) + $wordsSearch['words'][] = $word; + + if ($this->canDoBooleanSearch) + { + $fulltextWord = count($subwords) === 1 ? $word : '"' . $word . '"'; + $wordsSearch['indexed_words'][] = $fulltextWord; + if ($isExcluded) + $wordsExclude[] = $fulltextWord; + } + // Excluded phrases don't benefit from being split into subwords. + elseif (count($subwords) > 1 && $isExcluded) + return; + else + { + $relyOnIndex = true; + foreach ($subwords as $subword) + { + if (($smcFunc['strlen']($subword) >= $this->min_word_length) && !in_array($subword, $this->bannedWords)) + { + $wordsSearch['indexed_words'][] = $subword; + if ($isExcluded) + $wordsExclude[] = $subword; + } + elseif (!in_array($subword, $this->bannedWords)) + $relyOnIndex = false; + } + + if ($this->canDoBooleanSearch && !$relyOnIndex && empty($modSettings['search_force_index'])) + $wordsSearch['words'][] = $word; + } + } + + // Search for indexed words. + public function indexedWordQuery($words, $search_data) + { + global $modSettings, $smcFunc; + + $query_select = array( + 'id_msg' => 'm.id_msg', + ); + $query_where = array(); + $query_params = $search_data['params']; + + if ($query_params['id_search']) + $query_select['id_search'] = '{int:id_search}'; + + $count = 0; + if (empty($modSettings['search_simple_fulltext'])) + foreach ($words['words'] as $regularWord) + { + $query_where[] = 'm.body' . (in_array($regularWord, $query_params['excluded_words']) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : 'RLIKE') . '{string:complex_body_' . $count . '}'; + $query_params['complex_body_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . '[[:>:]]'; + } + + if ($query_params['user_query']) + $query_where[] = '{raw:user_query}'; + if ($query_params['board_query']) + $query_where[] = 'm.id_board {raw:board_query}'; + + if ($query_params['topic']) + $query_where[] = 'm.id_topic = {int:topic}'; + if ($query_params['min_msg_id']) + $query_where[] = 'm.id_msg >= {int:min_msg_id}'; + if ($query_params['max_msg_id']) + $query_where[] = 'm.id_msg <= {int:max_msg_id}'; + + $count = 0; + if (!empty($query_params['excluded_phrases']) && empty($modSettings['search_force_index'])) + foreach ($query_params['excluded_phrases'] as $phrase) + { + $query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_phrase_' . $count . '}'; + $query_params['exclude_subject_phrase_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]'; + } + $count = 0; + if (!empty($query_params['excluded_subject_words']) && empty($modSettings['search_force_index'])) + foreach ($query_params['excluded_subject_words'] as $excludedWord) + { + $query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_words_' . $count . '}'; + $query_params['exclude_subject_words_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($excludedWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $excludedWord), '\\\'') . '[[:>:]]'; + } + + if (!empty($modSettings['search_simple_fulltext'])) + { + $query_where[] = 'MATCH (body) AGAINST ({string:body_match})'; + $query_params['body_match'] = implode(' ', array_diff($words['indexed_words'], $query_params['excluded_index_words'])); + } + elseif ($this->canDoBooleanSearch) + { + $query_params['boolean_match'] = ''; + foreach ($words['indexed_words'] as $fulltextWord) + $query_params['boolean_match'] .= (in_array($fulltextWord, $query_params['excluded_index_words']) ? '-' : '+') . $fulltextWord . ' '; + $query_params['boolean_match'] = substr($query_params['boolean_match'], 0, -1); + + $query_where[] = 'MATCH (body) AGAINST ({string:boolean_match} IN BOOLEAN MODE)'; + } + else + { + $count = 0; + foreach ($words['indexed_words'] as $fulltextWord) + { + $query_where[] = (in_array($fulltextWord, $query_params['excluded_index_words']) ? 'NOT ' : '') . 'MATCH (body) AGAINST ({string:fulltext_match_' . $count . '})'; + $query_params['fulltext_match_' . $count++] = $fulltextWord; + } + } + + $ignoreRequest = $smcFunc['db_search_query']('insert_into_log_messages_fulltext', ($smcFunc['db_support_ignore'] ? ( ' + INSERT IGNORE INTO {db_prefix}' . $search_data['insert_into'] . ' + (' . implode(', ', array_keys($query_select)) . ')') : '') . ' + SELECT ' . implode(', ', $query_select) . ' + FROM {db_prefix}messages AS m + WHERE ' . implode(' + AND ', $query_where) . (empty($search_data['max_results']) ? '' : ' + LIMIT ' . ($search_data['max_results'] - $search_data['indexed_results'])), + $query_params + ); + + return $ignoreRequest; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/SearchAPI-Standard.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/SearchAPI-Standard.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,36 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Security.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Security.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,990 @@ += time())) + return; + + require_once($sourcedir . '/Subs-Auth.php'); + + // Hashed password, ahoy! + if (isset($_POST['admin_hash_pass']) && strlen($_POST['admin_hash_pass']) == 40) + { + checkSession(); + + $good_password = in_array(true, call_integration_hook('integrate_verify_password', array($user_info['username'], $_POST['admin_hash_pass'], true)), true); + + if ($good_password || $_POST['admin_hash_pass'] == sha1($user_info['passwd'] . $sc)) + { + $_SESSION['admin_time'] = time(); + unset($_SESSION['request_referer']); + return; + } + } + // Posting the password... check it. + if (isset($_POST['admin_pass'])) + { + checkSession(); + + $good_password = in_array(true, call_integration_hook('integrate_verify_password', array($user_info['username'], $_POST['admin_pass'], false)), true); + + // Password correct? + if ($good_password || sha1(strtolower($user_info['username']) . $_POST['admin_pass']) == $user_info['passwd']) + { + $_SESSION['admin_time'] = time(); + unset($_SESSION['request_referer']); + return; + } + } + // OpenID? + if (!empty($user_settings['openid_uri'])) + { + require_once($sourcedir . '/Subs-OpenID.php'); + smf_openID_revalidate(); + + $_SESSION['admin_time'] = time(); + unset($_SESSION['request_referer']); + return; + } + + // Better be sure to remember the real referer + if (empty($_SESSION['request_referer'])) + $_SESSION['request_referer'] = isset($_SERVER['HTTP_REFERER']) ? @parse_url($_SERVER['HTTP_REFERER']) : array(); + elseif (empty($_POST)) + unset($_SESSION['request_referer']); + // Need to type in a password for that, man. + adminLogin(); +} + +// Require a user who is logged in. (not a guest.) +function is_not_guest($message = '') +{ + global $user_info, $txt, $context, $scripturl; + + // Luckily, this person isn't a guest. + if (!$user_info['is_guest']) + return; + + // People always worry when they see people doing things they aren't actually doing... + $_GET['action'] = ''; + $_GET['board'] = ''; + $_GET['topic'] = ''; + writeLog(true); + + // Just die. + if (isset($_REQUEST['xml'])) + obExit(false); + + // Attempt to detect if they came from dlattach. + if (!WIRELESS && SMF != 'SSI' && empty($context['theme_loaded'])) + loadTheme(); + + // Never redirect to an attachment + if (strpos($_SERVER['REQUEST_URL'], 'dlattach') === false) + $_SESSION['login_url'] = $_SERVER['REQUEST_URL']; + + // Load the Login template and language file. + loadLanguage('Login'); + + // Are we in wireless mode? + if (WIRELESS) + { + $context['login_error'] = $message ? $message : $txt['only_members_can_access']; + $context['sub_template'] = WIRELESS_PROTOCOL . '_login'; + } + // Apparently we're not in a position to handle this now. Let's go to a safer location for now. + elseif (empty($context['template_layers'])) + { + $_SESSION['login_url'] = $scripturl . '?' . $_SERVER['QUERY_STRING']; + redirectexit('action=login'); + } + else + { + loadTemplate('Login'); + $context['sub_template'] = 'kick_guest'; + $context['robot_no_index'] = true; + } + + // Use the kick_guest sub template... + $context['kick_message'] = $message; + $context['page_title'] = $txt['login']; + + obExit(); + + // We should never get to this point, but if we did we wouldn't know the user isn't a guest. + trigger_error('Hacking attempt...', E_USER_ERROR); +} + +// Do banning related stuff. (ie. disallow access....) +function is_not_banned($forceCheck = false) +{ + global $txt, $modSettings, $context, $user_info; + global $sourcedir, $cookiename, $user_settings, $smcFunc; + + // You cannot be banned if you are an admin - doesn't help if you log out. + if ($user_info['is_admin']) + return; + + // Only check the ban every so often. (to reduce load.) + if ($forceCheck || !isset($_SESSION['ban']) || empty($modSettings['banLastUpdated']) || ($_SESSION['ban']['last_checked'] < $modSettings['banLastUpdated']) || $_SESSION['ban']['id_member'] != $user_info['id'] || $_SESSION['ban']['ip'] != $user_info['ip'] || $_SESSION['ban']['ip2'] != $user_info['ip2'] || (isset($user_info['email'], $_SESSION['ban']['email']) && $_SESSION['ban']['email'] != $user_info['email'])) + { + // Innocent until proven guilty. (but we know you are! :P) + $_SESSION['ban'] = array( + 'last_checked' => time(), + 'id_member' => $user_info['id'], + 'ip' => $user_info['ip'], + 'ip2' => $user_info['ip2'], + 'email' => $user_info['email'], + ); + + $ban_query = array(); + $ban_query_vars = array('current_time' => time()); + $flag_is_activated = false; + + // Check both IP addresses. + foreach (array('ip', 'ip2') as $ip_number) + { + // Check if we have a valid IP address. + if (preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $user_info[$ip_number], $ip_parts) == 1) + { + $ban_query[] = '((' . $ip_parts[1] . ' BETWEEN bi.ip_low1 AND bi.ip_high1) + AND (' . $ip_parts[2] . ' BETWEEN bi.ip_low2 AND bi.ip_high2) + AND (' . $ip_parts[3] . ' BETWEEN bi.ip_low3 AND bi.ip_high3) + AND (' . $ip_parts[4] . ' BETWEEN bi.ip_low4 AND bi.ip_high4))'; + + // IP was valid, maybe there's also a hostname... + if (empty($modSettings['disableHostnameLookup'])) + { + $hostname = host_from_ip($user_info[$ip_number]); + if (strlen($hostname) > 0) + { + $ban_query[] = '({string:hostname} LIKE bi.hostname)'; + $ban_query_vars['hostname'] = $hostname; + } + } + } + // We use '255.255.255.255' for 'unknown' since it's not valid anyway. + elseif ($user_info['ip'] == 'unknown') + $ban_query[] = '(bi.ip_low1 = 255 AND bi.ip_high1 = 255 + AND bi.ip_low2 = 255 AND bi.ip_high2 = 255 + AND bi.ip_low3 = 255 AND bi.ip_high3 = 255 + AND bi.ip_low4 = 255 AND bi.ip_high4 = 255)'; + } + + // Is their email address banned? + if (strlen($user_info['email']) != 0) + { + $ban_query[] = '({string:email} LIKE bi.email_address)'; + $ban_query_vars['email'] = $user_info['email']; + } + + // How about this user? + if (!$user_info['is_guest'] && !empty($user_info['id'])) + { + $ban_query[] = 'bi.id_member = {int:id_member}'; + $ban_query_vars['id_member'] = $user_info['id']; + } + + // Check the ban, if there's information. + if (!empty($ban_query)) + { + $restrictions = array( + 'cannot_access', + 'cannot_login', + 'cannot_post', + 'cannot_register', + ); + $request = $smcFunc['db_query']('', ' + SELECT bi.id_ban, bi.email_address, bi.id_member, bg.cannot_access, bg.cannot_register, + bg.cannot_post, bg.cannot_login, bg.reason, IFNULL(bg.expire_time, 0) AS expire_time + FROM {db_prefix}ban_items AS bi + INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})) + WHERE + (' . implode(' OR ', $ban_query) . ')', + $ban_query_vars + ); + // Store every type of ban that applies to you in your session. + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + foreach ($restrictions as $restriction) + if (!empty($row[$restriction])) + { + $_SESSION['ban'][$restriction]['reason'] = $row['reason']; + $_SESSION['ban'][$restriction]['ids'][] = $row['id_ban']; + if (!isset($_SESSION['ban']['expire_time']) || ($_SESSION['ban']['expire_time'] != 0 && ($row['expire_time'] == 0 || $row['expire_time'] > $_SESSION['ban']['expire_time']))) + $_SESSION['ban']['expire_time'] = $row['expire_time']; + + if (!$user_info['is_guest'] && $restriction == 'cannot_access' && ($row['id_member'] == $user_info['id'] || $row['email_address'] == $user_info['email'])) + $flag_is_activated = true; + } + } + $smcFunc['db_free_result']($request); + } + + // Mark the cannot_access and cannot_post bans as being 'hit'. + if (isset($_SESSION['ban']['cannot_access']) || isset($_SESSION['ban']['cannot_post']) || isset($_SESSION['ban']['cannot_login'])) + log_ban(array_merge(isset($_SESSION['ban']['cannot_access']) ? $_SESSION['ban']['cannot_access']['ids'] : array(), isset($_SESSION['ban']['cannot_post']) ? $_SESSION['ban']['cannot_post']['ids'] : array(), isset($_SESSION['ban']['cannot_login']) ? $_SESSION['ban']['cannot_login']['ids'] : array())); + + // If for whatever reason the is_activated flag seems wrong, do a little work to clear it up. + if ($user_info['id'] && (($user_settings['is_activated'] >= 10 && !$flag_is_activated) + || ($user_settings['is_activated'] < 10 && $flag_is_activated))) + { + require_once($sourcedir . '/ManageBans.php'); + updateBanMembers(); + } + } + + // Hey, I know you! You're ehm... + if (!isset($_SESSION['ban']['cannot_access']) && !empty($_COOKIE[$cookiename . '_'])) + { + $bans = explode(',', $_COOKIE[$cookiename . '_']); + foreach ($bans as $key => $value) + $bans[$key] = (int) $value; + $request = $smcFunc['db_query']('', ' + SELECT bi.id_ban, bg.reason + FROM {db_prefix}ban_items AS bi + INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group) + WHERE bi.id_ban IN ({array_int:ban_list}) + AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time}) + AND bg.cannot_access = {int:cannot_access} + LIMIT ' . count($bans), + array( + 'cannot_access' => 1, + 'ban_list' => $bans, + 'current_time' => time(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $_SESSION['ban']['cannot_access']['ids'][] = $row['id_ban']; + $_SESSION['ban']['cannot_access']['reason'] = $row['reason']; + } + $smcFunc['db_free_result']($request); + + // My mistake. Next time better. + if (!isset($_SESSION['ban']['cannot_access'])) + { + require_once($sourcedir . '/Subs-Auth.php'); + $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); + setcookie($cookiename . '_', '', time() - 3600, $cookie_url[1], $cookie_url[0], 0); + } + } + + // If you're fully banned, it's end of the story for you. + if (isset($_SESSION['ban']['cannot_access'])) + { + // We don't wanna see you! + if (!$user_info['is_guest']) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_online + WHERE id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + ) + ); + + // 'Log' the user out. Can't have any funny business... (save the name!) + $old_name = isset($user_info['name']) && $user_info['name'] != '' ? $user_info['name'] : $txt['guest_title']; + $user_info['name'] = ''; + $user_info['username'] = ''; + $user_info['is_guest'] = true; + $user_info['is_admin'] = false; + $user_info['permissions'] = array(); + $user_info['id'] = 0; + $context['user'] = array( + 'id' => 0, + 'username' => '', + 'name' => $txt['guest_title'], + 'is_guest' => true, + 'is_logged' => false, + 'is_admin' => false, + 'is_mod' => false, + 'can_mod' => false, + 'language' => $user_info['language'], + ); + + // A goodbye present. + require_once($sourcedir . '/Subs-Auth.php'); + $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); + setcookie($cookiename . '_', implode(',', $_SESSION['ban']['cannot_access']['ids']), time() + 3153600, $cookie_url[1], $cookie_url[0], 0); + + // Don't scare anyone, now. + $_GET['action'] = ''; + $_GET['board'] = ''; + $_GET['topic'] = ''; + writeLog(true); + + // You banned, sucka! + fatal_error(sprintf($txt['your_ban'], $old_name) . (empty($_SESSION['ban']['cannot_access']['reason']) ? '' : '
' . $_SESSION['ban']['cannot_access']['reason']) . '
' . (!empty($_SESSION['ban']['expire_time']) ? sprintf($txt['your_ban_expires'], timeformat($_SESSION['ban']['expire_time'], false)) : $txt['your_ban_expires_never']), 'user'); + + // If we get here, something's gone wrong.... but let's try anyway. + trigger_error('Hacking attempt...', E_USER_ERROR); + } + // You're not allowed to log in but yet you are. Let's fix that. + elseif (isset($_SESSION['ban']['cannot_login']) && !$user_info['is_guest']) + { + // We don't wanna see you! + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_online + WHERE id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + ) + ); + + // 'Log' the user out. Can't have any funny business... (save the name!) + $old_name = isset($user_info['name']) && $user_info['name'] != '' ? $user_info['name'] : $txt['guest_title']; + $user_info['name'] = ''; + $user_info['username'] = ''; + $user_info['is_guest'] = true; + $user_info['is_admin'] = false; + $user_info['permissions'] = array(); + $user_info['id'] = 0; + $context['user'] = array( + 'id' => 0, + 'username' => '', + 'name' => $txt['guest_title'], + 'is_guest' => true, + 'is_logged' => false, + 'is_admin' => false, + 'is_mod' => false, + 'can_mod' => false, + 'language' => $user_info['language'], + ); + + // SMF's Wipe 'n Clean(r) erases all traces. + $_GET['action'] = ''; + $_GET['board'] = ''; + $_GET['topic'] = ''; + writeLog(true); + + require_once($sourcedir . '/LogInOut.php'); + Logout(true, false); + + fatal_error(sprintf($txt['your_ban'], $old_name) . (empty($_SESSION['ban']['cannot_login']['reason']) ? '' : '
' . $_SESSION['ban']['cannot_login']['reason']) . '
' . (!empty($_SESSION['ban']['expire_time']) ? sprintf($txt['your_ban_expires'], timeformat($_SESSION['ban']['expire_time'], false)) : $txt['your_ban_expires_never']) . '
' . $txt['ban_continue_browse'], 'user'); + } + + // Fix up the banning permissions. + if (isset($user_info['permissions'])) + banPermissions(); +} + +// Fix permissions according to ban status. +function banPermissions() +{ + global $user_info, $sourcedir, $modSettings, $context; + + // Somehow they got here, at least take away all permissions... + if (isset($_SESSION['ban']['cannot_access'])) + $user_info['permissions'] = array(); + // Okay, well, you can watch, but don't touch a thing. + elseif (isset($_SESSION['ban']['cannot_post']) || (!empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $user_info['warning'])) + { + $denied_permissions = array( + 'pm_send', + 'calendar_post', 'calendar_edit_own', 'calendar_edit_any', + 'poll_post', + 'poll_add_own', 'poll_add_any', + 'poll_edit_own', 'poll_edit_any', + 'poll_lock_own', 'poll_lock_any', + 'poll_remove_own', 'poll_remove_any', + 'manage_attachments', 'manage_smileys', 'manage_boards', 'admin_forum', 'manage_permissions', + 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', + 'profile_identity_any', 'profile_extra_any', 'profile_title_any', + 'post_new', 'post_reply_own', 'post_reply_any', + 'delete_own', 'delete_any', 'delete_replies', + 'make_sticky', + 'merge_any', 'split_any', + 'modify_own', 'modify_any', 'modify_replies', + 'move_any', + 'send_topic', + 'lock_own', 'lock_any', + 'remove_own', 'remove_any', + 'post_unapproved_topics', 'post_unapproved_replies_own', 'post_unapproved_replies_any', + ); + $user_info['permissions'] = array_diff($user_info['permissions'], $denied_permissions); + } + // Are they absolutely under moderation? + elseif (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $user_info['warning']) + { + // Work out what permissions should change... + $permission_change = array( + 'post_new' => 'post_unapproved_topics', + 'post_reply_own' => 'post_unapproved_replies_own', + 'post_reply_any' => 'post_unapproved_replies_any', + 'post_attachment' => 'post_unapproved_attachments', + ); + foreach ($permission_change as $old => $new) + { + if (!in_array($old, $user_info['permissions'])) + unset($permission_change[$old]); + else + $user_info['permissions'][] = $new; + } + $user_info['permissions'] = array_diff($user_info['permissions'], array_keys($permission_change)); + } + + //!!! Find a better place to call this? Needs to be after permissions loaded! + // Finally, some bits we cache in the session because it saves queries. + if (isset($_SESSION['mc']) && $_SESSION['mc']['time'] > $modSettings['settings_updated'] && $_SESSION['mc']['id'] == $user_info['id']) + $user_info['mod_cache'] = $_SESSION['mc']; + else + { + require_once($sourcedir . '/Subs-Auth.php'); + rebuildModCache(); + } + + // Now that we have the mod cache taken care of lets setup a cache for the number of mod reports still open + if (isset($_SESSION['rc']) && $_SESSION['rc']['time'] > $modSettings['last_mod_report_action'] && $_SESSION['rc']['id'] == $user_info['id']) + $context['open_mod_reports'] = $_SESSION['rc']['reports']; + elseif ($_SESSION['mc']['bq'] != '0=1') + { + require_once($sourcedir . '/ModerationCenter.php'); + recountOpenReports(); + } + else + $context['open_mod_reports'] = 0; +} + +// Log a ban in the database. +function log_ban($ban_ids = array(), $email = null) +{ + global $user_info, $smcFunc; + + // Don't log web accelerators, it's very confusing... + if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') + return; + + $smcFunc['db_insert']('', + '{db_prefix}log_banned', + array('id_member' => 'int', 'ip' => 'string-16', 'email' => 'string', 'log_time' => 'int'), + array($user_info['id'], $user_info['ip'], ($email === null ? ($user_info['is_guest'] ? '' : $user_info['email']) : $email), time()), + array('id_ban_log') + ); + + // One extra point for these bans. + if (!empty($ban_ids)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}ban_items + SET hits = hits + 1 + WHERE id_ban IN ({array_int:ban_ids})', + array( + 'ban_ids' => $ban_ids, + ) + ); +} + +// Checks if a given email address might be banned. +function isBannedEmail($email, $restriction, $error) +{ + global $txt, $smcFunc; + + // Can't ban an empty email + if (empty($email) || trim($email) == '') + return; + + // Let's start with the bans based on your IP/hostname/memberID... + $ban_ids = isset($_SESSION['ban'][$restriction]) ? $_SESSION['ban'][$restriction]['ids'] : array(); + $ban_reason = isset($_SESSION['ban'][$restriction]) ? $_SESSION['ban'][$restriction]['reason'] : ''; + + // ...and add to that the email address you're trying to register. + $request = $smcFunc['db_query']('', ' + SELECT bi.id_ban, bg.' . $restriction . ', bg.cannot_access, bg.reason + FROM {db_prefix}ban_items AS bi + INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group) + WHERE {string:email} LIKE bi.email_address + AND (bg.' . $restriction . ' = {int:cannot_access} OR bg.cannot_access = {int:cannot_access}) + AND (bg.expire_time IS NULL OR bg.expire_time >= {int:now})', + array( + 'email' => $email, + 'cannot_access' => 1, + 'now' => time(), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!empty($row['cannot_access'])) + { + $_SESSION['ban']['cannot_access']['ids'][] = $row['id_ban']; + $_SESSION['ban']['cannot_access']['reason'] = $row['reason']; + } + if (!empty($row[$restriction])) + { + $ban_ids[] = $row['id_ban']; + $ban_reason = $row['reason']; + } + } + $smcFunc['db_free_result']($request); + + // You're in biiig trouble. Banned for the rest of this session! + if (isset($_SESSION['ban']['cannot_access'])) + { + log_ban($_SESSION['ban']['cannot_access']['ids']); + $_SESSION['ban']['last_checked'] = time(); + + fatal_error(sprintf($txt['your_ban'], $txt['guest_title']) . $_SESSION['ban']['cannot_access']['reason'], false); + } + + if (!empty($ban_ids)) + { + // Log this ban for future reference. + log_ban($ban_ids, $email); + fatal_error($error . $ban_reason, false); + } +} + +// Make sure the user's correct session was passed, and they came from here. (type can be post, get, or request.) +function checkSession($type = 'post', $from_action = '', $is_fatal = true) +{ + global $sc, $modSettings, $boardurl; + + // Is it in as $_POST['sc']? + if ($type == 'post') + { + $check = isset($_POST[$_SESSION['session_var']]) ? $_POST[$_SESSION['session_var']] : (empty($modSettings['strictSessionCheck']) && isset($_POST['sc']) ? $_POST['sc'] : null); + if ($check !== $sc) + $error = 'session_timeout'; + } + + // How about $_GET['sesc']? + elseif ($type == 'get') + { + $check = isset($_GET[$_SESSION['session_var']]) ? $_GET[$_SESSION['session_var']] : (empty($modSettings['strictSessionCheck']) && isset($_GET['sesc']) ? $_GET['sesc'] : null); + if ($check !== $sc) + $error = 'session_verify_fail'; + } + + // Or can it be in either? + elseif ($type == 'request') + { + $check = isset($_GET[$_SESSION['session_var']]) ? $_GET[$_SESSION['session_var']] : (empty($modSettings['strictSessionCheck']) && isset($_GET['sesc']) ? $_GET['sesc'] : (isset($_POST[$_SESSION['session_var']]) ? $_POST[$_SESSION['session_var']] : (empty($modSettings['strictSessionCheck']) && isset($_POST['sc']) ? $_POST['sc'] : null))); + + if ($check !== $sc) + $error = 'session_verify_fail'; + } + + // Verify that they aren't changing user agents on us - that could be bad. + if ((!isset($_SESSION['USER_AGENT']) || $_SESSION['USER_AGENT'] != $_SERVER['HTTP_USER_AGENT']) && empty($modSettings['disableCheckUA'])) + $error = 'session_verify_fail'; + + // Make sure a page with session check requirement is not being prefetched. + if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') + { + ob_end_clean(); + header('HTTP/1.1 403 Forbidden'); + die; + } + + // Check the referring site - it should be the same server at least! + if (isset($_SESSION['request_referer'])) + $referrer = $_SESSION['request_referer']; + else + $referrer = isset($_SERVER['HTTP_REFERER']) ? @parse_url($_SERVER['HTTP_REFERER']) : array(); + if (!empty($referrer['host'])) + { + if (strpos($_SERVER['HTTP_HOST'], ':') !== false) + $real_host = substr($_SERVER['HTTP_HOST'], 0, strpos($_SERVER['HTTP_HOST'], ':')); + else + $real_host = $_SERVER['HTTP_HOST']; + + $parsed_url = parse_url($boardurl); + + // Are global cookies on? If so, let's check them ;). + if (!empty($modSettings['globalCookies'])) + { + if (preg_match('~(?:[^\.]+\.)?([^\.]{3,}\..+)\z~i', $parsed_url['host'], $parts) == 1) + $parsed_url['host'] = $parts[1]; + + if (preg_match('~(?:[^\.]+\.)?([^\.]{3,}\..+)\z~i', $referrer['host'], $parts) == 1) + $referrer['host'] = $parts[1]; + + if (preg_match('~(?:[^\.]+\.)?([^\.]{3,}\..+)\z~i', $real_host, $parts) == 1) + $real_host = $parts[1]; + } + + // Okay: referrer must either match parsed_url or real_host. + if (isset($parsed_url['host']) && strtolower($referrer['host']) != strtolower($parsed_url['host']) && strtolower($referrer['host']) != strtolower($real_host)) + { + $error = 'verify_url_fail'; + $log_error = true; + } + } + + // Well, first of all, if a from_action is specified you'd better have an old_url. + if (!empty($from_action) && (!isset($_SESSION['old_url']) || preg_match('~[?;&]action=' . $from_action . '([;&]|$)~', $_SESSION['old_url']) == 0)) + { + $error = 'verify_url_fail'; + $log_error = true; + } + + if (strtolower($_SERVER['HTTP_USER_AGENT']) == 'hacker') + fatal_error('Sound the alarm! It\'s a hacker! Close the castle gates!!', false); + + // Everything is ok, return an empty string. + if (!isset($error)) + return ''; + // A session error occurred, show the error. + elseif ($is_fatal) + { + if (isset($_GET['xml'])) + { + ob_end_clean(); + header('HTTP/1.1 403 Forbidden - Session timeout'); + die; + } + else + fatal_lang_error($error, isset($log_error) ? 'user' : false); + } + // A session error occurred, return the error to the calling function. + else + return $error; + + // We really should never fall through here, for very important reasons. Let's make sure. + trigger_error('Hacking attempt...', E_USER_ERROR); +} + +// Check if a specific confirm parameter was given. +function checkConfirm($action) +{ + global $modSettings; + + if (isset($_GET['confirm']) && isset($_SESSION['confirm_' . $action]) && md5($_GET['confirm'] . $_SERVER['HTTP_USER_AGENT']) == $_SESSION['confirm_' . $action]) + return true; + + else + { + $token = md5(mt_rand() . session_id() . (string) microtime() . $modSettings['rand_seed']); + $_SESSION['confirm_' . $action] = md5($token . $_SERVER['HTTP_USER_AGENT']); + + return $token; + } +} + +// Check whether a form has been submitted twice. +function checkSubmitOnce($action, $is_fatal = true) +{ + global $context; + + if (!isset($_SESSION['forms'])) + $_SESSION['forms'] = array(); + + // Register a form number and store it in the session stack. (use this on the page that has the form.) + if ($action == 'register') + { + $context['form_sequence_number'] = 0; + while (empty($context['form_sequence_number']) || in_array($context['form_sequence_number'], $_SESSION['forms'])) + $context['form_sequence_number'] = mt_rand(1, 16000000); + } + // Check whether the submitted number can be found in the session. + elseif ($action == 'check') + { + if (!isset($_REQUEST['seqnum'])) + return true; + elseif (!in_array($_REQUEST['seqnum'], $_SESSION['forms'])) + { + $_SESSION['forms'][] = (int) $_REQUEST['seqnum']; + return true; + } + elseif ($is_fatal) + fatal_lang_error('error_form_already_submitted', false); + else + return false; + } + // Don't check, just free the stack number. + elseif ($action == 'free' && isset($_REQUEST['seqnum']) && in_array($_REQUEST['seqnum'], $_SESSION['forms'])) + $_SESSION['forms'] = array_diff($_SESSION['forms'], array($_REQUEST['seqnum'])); + elseif ($action != 'free') + trigger_error('checkSubmitOnce(): Invalid action \'' . $action . '\'', E_USER_WARNING); +} + +// Check the user's permissions. +function allowedTo($permission, $boards = null) +{ + global $user_info, $modSettings, $smcFunc; + + // You're always allowed to do nothing. (unless you're a working man, MR. LAZY :P!) + if (empty($permission)) + return true; + + // You're never allowed to do something if your data hasn't been loaded yet! + if (empty($user_info)) + return false; + + // Administrators are supermen :P. + if ($user_info['is_admin']) + return true; + + // Are we checking the _current_ board, or some other boards? + if ($boards === null) + { + // Check if they can do it. + if (!is_array($permission) && in_array($permission, $user_info['permissions'])) + return true; + // Search for any of a list of permissions. + elseif (is_array($permission) && count(array_intersect($permission, $user_info['permissions'])) != 0) + return true; + // You aren't allowed, by default. + else + return false; + } + elseif (!is_array($boards)) + $boards = array($boards); + + $request = $smcFunc['db_query']('', ' + SELECT MIN(bp.add_deny) AS add_deny + FROM {db_prefix}boards AS b + INNER JOIN {db_prefix}board_permissions AS bp ON (bp.id_profile = b.id_profile) + LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member}) + WHERE b.id_board IN ({array_int:board_list}) + AND bp.id_group IN ({array_int:group_list}, {int:moderator_group}) + AND bp.permission {raw:permission_list} + AND (mods.id_member IS NOT NULL OR bp.id_group != {int:moderator_group}) + GROUP BY b.id_board', + array( + 'current_member' => $user_info['id'], + 'board_list' => $boards, + 'group_list' => $user_info['groups'], + 'moderator_group' => 3, + 'permission_list' => (is_array($permission) ? 'IN (\'' . implode('\', \'', $permission) . '\')' : ' = \'' . $permission . '\''), + ) + ); + + // Make sure they can do it on all of the boards. + if ($smcFunc['db_num_rows']($request) != count($boards)) + return false; + + $result = true; + while ($row = $smcFunc['db_fetch_assoc']($request)) + $result &= !empty($row['add_deny']); + $smcFunc['db_free_result']($request); + + // If the query returned 1, they can do it... otherwise, they can't. + return $result; +} + +// Fatal error if they cannot... +function isAllowedTo($permission, $boards = null) +{ + global $user_info, $txt; + + static $heavy_permissions = array( + 'admin_forum', + 'manage_attachments', + 'manage_smileys', + 'manage_boards', + 'edit_news', + 'moderate_forum', + 'manage_bans', + 'manage_membergroups', + 'manage_permissions', + ); + + // Make it an array, even if a string was passed. + $permission = is_array($permission) ? $permission : array($permission); + + // Check the permission and return an error... + if (!allowedTo($permission, $boards)) + { + // Pick the last array entry as the permission shown as the error. + $error_permission = array_shift($permission); + + // If they are a guest, show a login. (because the error might be gone if they do!) + if ($user_info['is_guest']) + { + loadLanguage('Errors'); + is_not_guest($txt['cannot_' . $error_permission]); + } + + // Clear the action because they aren't really doing that! + $_GET['action'] = ''; + $_GET['board'] = ''; + $_GET['topic'] = ''; + writeLog(true); + + fatal_lang_error('cannot_' . $error_permission, false); + + // Getting this far is a really big problem, but let's try our best to prevent any cases... + trigger_error('Hacking attempt...', E_USER_ERROR); + } + + // If you're doing something on behalf of some "heavy" permissions, validate your session. + // (take out the heavy permissions, and if you can't do anything but those, you need a validated session.) + if (!allowedTo(array_diff($permission, $heavy_permissions), $boards)) + validateSession(); +} + +// Return the boards a user has a certain (board) permission on. (array(0) if all.) +function boardsAllowedTo($permissions, $check_access = true) +{ + global $user_info, $modSettings, $smcFunc; + + // Administrators are all powerful, sorry. + if ($user_info['is_admin']) + return array(0); + + // Arrays are nice, most of the time. + if (!is_array($permissions)) + $permissions = array($permissions); + + // All groups the user is in except 'moderator'. + $groups = array_diff($user_info['groups'], array(3)); + + $request = $smcFunc['db_query']('', ' + SELECT b.id_board, bp.add_deny + FROM {db_prefix}board_permissions AS bp + INNER JOIN {db_prefix}boards AS b ON (b.id_profile = bp.id_profile) + LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member}) + WHERE bp.id_group IN ({array_int:group_list}, {int:moderator_group}) + AND bp.permission IN ({array_string:permissions}) + AND (mods.id_member IS NOT NULL OR bp.id_group != {int:moderator_group})' . + ($check_access ? ' AND {query_see_board}' : ''), + array( + 'current_member' => $user_info['id'], + 'group_list' => $groups, + 'moderator_group' => 3, + 'permissions' => $permissions, + ) + ); + $boards = array(); + $deny_boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($row['add_deny'])) + $deny_boards[] = $row['id_board']; + else + $boards[] = $row['id_board']; + } + $smcFunc['db_free_result']($request); + + $boards = array_unique(array_values(array_diff($boards, $deny_boards))); + + return $boards; +} + +function showEmailAddress($userProfile_hideEmail, $userProfile_id) +{ + global $modSettings, $user_info; + + // Should this users email address be shown? + // If you're guest and the forum is set to hide email for guests: no. + // If the user is post-banned: no. + // If it's your own profile and you've set your address hidden: yes_permission_override. + // If you're a moderator with sufficient permissions: yes_permission_override. + // If the user has set their email address to be hidden: no. + // If the forum is set to show full email addresses: yes. + // Otherwise: no_through_forum. + + return (!empty($modSettings['guest_hideContacts']) && $user_info['is_guest']) || isset($_SESSION['ban']['cannot_post']) ? 'no' : ((!$user_info['is_guest'] && $user_info['id'] == $userProfile_id && !$userProfile_hideEmail) || allowedTo('moderate_forum') ? 'yes_permission_override' : ($userProfile_hideEmail ? 'no' : (!empty($modSettings['make_email_viewable']) ? 'yes' : 'no_through_forum'))); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/SendTopic.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/SendTopic.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,586 @@ + 'CustomEmail', + 'sendtopic' => 'SendTopic', + ); + + if (!isset($_GET['sa']) || !isset($sub_actions[$_GET['sa']])) + $_GET['sa'] = 'sendtopic'; + + $sub_actions[$_GET['sa']](); +} + +// Send a topic to a friend. +function SendTopic() +{ + global $topic, $txt, $context, $scripturl, $sourcedir, $smcFunc, $modSettings; + + // Check permissions... + isAllowedTo('send_topic'); + + // We need at least a topic... go away if you don't have one. + if (empty($topic)) + fatal_lang_error('not_a_topic', false); + + // Get the topic's subject. + $request = $smcFunc['db_query']('', ' + SELECT m.subject, t.approved + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE t.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('not_a_topic', false); + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Can't send topic if its unapproved and using post moderation. + if ($modSettings['postmod_active'] && !$row['approved']) + fatal_lang_error('not_approved_topic', false); + + // Censor the subject.... + censorText($row['subject']); + + // Sending yet, or just getting prepped? + if (empty($_POST['send'])) + { + $context['page_title'] = sprintf($txt['sendtopic_title'], $row['subject']); + $context['start'] = $_REQUEST['start']; + + return; + } + + // Actually send the message... + checkSession(); + spamProtection('sendtopc'); + + // This is needed for sendmail(). + require_once($sourcedir . '/Subs-Post.php'); + + // Trim the names.. + $_POST['y_name'] = trim($_POST['y_name']); + $_POST['r_name'] = trim($_POST['r_name']); + + // Make sure they aren't playing "let's use a fake email". + if ($_POST['y_name'] == '_' || !isset($_POST['y_name']) || $_POST['y_name'] == '') + fatal_lang_error('no_name', false); + if (!isset($_POST['y_email']) || $_POST['y_email'] == '') + fatal_lang_error('no_email', false); + if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['y_email']) == 0) + fatal_lang_error('email_invalid_character', false); + + // The receiver should be valid to. + if ($_POST['r_name'] == '_' || !isset($_POST['r_name']) || $_POST['r_name'] == '') + fatal_lang_error('no_name', false); + if (!isset($_POST['r_email']) || $_POST['r_email'] == '') + fatal_lang_error('no_email', false); + if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['r_email']) == 0) + fatal_lang_error('email_invalid_character', false); + + // Emails don't like entities... + $row['subject'] = un_htmlspecialchars($row['subject']); + + $replacements = array( + 'TOPICSUBJECT' => $row['subject'], + 'SENDERNAME' => $_POST['y_name'], + 'RECPNAME' => $_POST['r_name'], + 'TOPICLINK' => $scripturl . '?topic=' . $topic . '.0', + ); + + $emailtemplate = 'send_topic'; + + if (!empty($_POST['comment'])) + { + $emailtemplate .= '_comment'; + $replacements['COMMENT'] = $_POST['comment']; + } + + $emaildata = loadEmailTemplate($emailtemplate, $replacements); + // And off we go! + sendmail($_POST['r_email'], $emaildata['subject'], $emaildata['body'], $_POST['y_email']); + + // Back to the topic! + redirectexit('topic=' . $topic . '.0'); +} + +// Allow a user to send an email. +function CustomEmail() +{ + global $context, $modSettings, $user_info, $smcFunc, $txt, $scripturl, $sourcedir; + + // Can the user even see this information? + if ($user_info['is_guest'] && !empty($modSettings['guest_hideContacts'])) + fatal_lang_error('no_access', false); + + // Are we sending to a user? + $context['form_hidden_vars'] = array(); + if (isset($_REQUEST['uid'])) + { + $request = $smcFunc['db_query']('', ' + SELECT email_address AS email, real_name AS name, id_member, hide_email + FROM {db_prefix}members + WHERE id_member = {int:id_member}', + array( + 'id_member' => (int) $_REQUEST['uid'], + ) + ); + + $context['form_hidden_vars']['uid'] = (int) $_REQUEST['uid']; + } + elseif (isset($_REQUEST['msg'])) + { + $request = $smcFunc['db_query']('', ' + SELECT IFNULL(mem.email_address, m.poster_email) AS email, IFNULL(mem.real_name, m.poster_name) AS name, IFNULL(mem.id_member, 0) AS id_member, hide_email + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_msg = {int:id_msg}', + array( + 'id_msg' => (int) $_REQUEST['msg'], + ) + ); + + $context['form_hidden_vars']['msg'] = (int) $_REQUEST['msg']; + } + + if (empty($request) || $smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('cant_find_user_email'); + + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // Are you sure you got the address? + if (empty($row['email'])) + fatal_lang_error('cant_find_user_email'); + + // Can they actually do this? + $context['show_email_address'] = showEmailAddress(!empty($row['hide_email']), $row['id_member']); + if ($context['show_email_address'] === 'no') + fatal_lang_error('no_access', false); + + // Setup the context! + $context['recipient'] = array( + 'id' => $row['id_member'], + 'name' => $row['name'], + 'email' => $row['email'], + 'email_link' => ($context['show_email_address'] == 'yes_permission_override' ? '' : '') . '' . $row['email'] . '' . ($context['show_email_address'] == 'yes_permission_override' ? '' : ''), + 'link' => $row['id_member'] ? '' . $row['name'] . '' : $row['name'], + ); + + // Can we see this person's email address? + $context['can_view_receipient_email'] = $context['show_email_address'] == 'yes' || $context['show_email_address'] == 'yes_permission_override'; + + // Are we actually sending it? + if (isset($_POST['send']) && isset($_POST['email_body'])) + { + require_once($sourcedir . '/Subs-Post.php'); + + checkSession(); + + // If it's a guest sort out their names. + if ($user_info['is_guest']) + { + if (empty($_POST['y_name']) || $_POST['y_name'] == '_' || trim($_POST['y_name']) == '') + fatal_lang_error('no_name', false); + if (empty($_POST['y_email'])) + fatal_lang_error('no_email', false); + if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['y_email']) == 0) + fatal_lang_error('email_invalid_character', false); + + $from_name = trim($_POST['y_name']); + $from_email = trim($_POST['y_email']); + } + else + { + $from_name = $user_info['name']; + $from_email = $user_info['email']; + } + + // Check we have a body (etc). + if (trim($_POST['email_body']) == '' || trim($_POST['email_subject']) == '') + fatal_lang_error('email_missing_data'); + + // We use a template in case they want to customise! + $replacements = array( + 'EMAILSUBJECT' => $_POST['email_subject'], + 'EMAILBODY' => $_POST['email_body'], + 'SENDERNAME' => $from_name, + 'RECPNAME' => $context['recipient']['name'], + ); + + // Don't let them send too many! + spamProtection('sendmail'); + + // Get the template and get out! + $emaildata = loadEmailTemplate('send_email', $replacements); + sendmail($context['recipient']['email'], $emaildata['subject'], $emaildata['body'], $from_email, null, false, 1, null, true); + + // Now work out where to go! + if (isset($_REQUEST['uid'])) + redirectexit('action=profile;u=' . (int) $_REQUEST['uid']); + elseif (isset($_REQUEST['msg'])) + redirectexit('msg=' . (int) $_REQUEST['msg']); + else + redirectexit(); + } + + $context['sub_template'] = 'custom_email'; + $context['page_title'] = $txt['send_email']; +} + +// Report a post to the moderator... ask for a comment. +function ReportToModerator() +{ + global $txt, $topic, $sourcedir, $modSettings, $user_info, $context, $smcFunc; + + $context['robot_no_index'] = true; + + // You can't use this if it's off or you are not allowed to do it. + isAllowedTo('report_any'); + + // If they're posting, it should be processed by ReportToModerator2. + if ((isset($_POST[$context['session_var']]) || isset($_POST['submit'])) && empty($context['post_errors'])) + ReportToModerator2(); + + // We need a message ID to check! + if (empty($_REQUEST['msg']) && empty($_REQUEST['mid'])) + fatal_lang_error('no_access', false); + + // For compatibility, accept mid, but we should be using msg. (not the flavor kind!) + $_REQUEST['msg'] = empty($_REQUEST['msg']) ? (int) $_REQUEST['mid'] : (int) $_REQUEST['msg']; + + // Check the message's ID - don't want anyone reporting a post they can't even see! + $result = $smcFunc['db_query']('', ' + SELECT m.id_msg, m.id_member, t.id_member_started + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic}) + WHERE m.id_msg = {int:id_msg} + AND m.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + 'id_msg' => $_REQUEST['msg'], + ) + ); + if ($smcFunc['db_num_rows']($result) == 0) + fatal_lang_error('no_board', false); + list ($_REQUEST['msg'], $member, $starter) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Do we need to show the visual verification image? + $context['require_verification'] = $user_info['is_guest'] && !empty($modSettings['guests_report_require_captcha']); + if ($context['require_verification']) + { + require_once($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'report', + ); + $context['require_verification'] = create_control_verification($verificationOptions); + $context['visual_verification_id'] = $verificationOptions['id']; + } + + // Show the inputs for the comment, etc. + loadLanguage('Post'); + loadTemplate('SendTopic'); + + $context['comment_body'] = !isset($_POST['comment']) ? '' : trim($_POST['comment']); + $context['email_address'] = !isset($_POST['email']) ? '' : trim($_POST['email']); + + // This is here so that the user could, in theory, be redirected back to the topic. + $context['start'] = $_REQUEST['start']; + $context['message_id'] = $_REQUEST['msg']; + + $context['page_title'] = $txt['report_to_mod']; + $context['sub_template'] = 'report'; +} + +// Send the emails. +function ReportToModerator2() +{ + global $txt, $scripturl, $topic, $board, $user_info, $modSettings, $sourcedir, $language, $context, $smcFunc; + + // You must have the proper permissions! + isAllowedTo('report_any'); + + // Make sure they aren't spamming. + spamProtection('reporttm'); + + require_once($sourcedir . '/Subs-Post.php'); + + // No errors, yet. + $post_errors = array(); + + // Check their session. + if (checkSession('post', '', false) != '') + $post_errors[] = 'session_timeout'; + + // Make sure we have a comment and it's clean. + if (!isset($_POST['comment']) || $smcFunc['htmltrim']($_POST['comment']) === '') + $post_errors[] = 'no_comment'; + $poster_comment = strtr($smcFunc['htmlspecialchars']($_POST['comment']), array("\r" => '', "\n" => '', "\t" => '')); + + // Guests need to provide their address! + if ($user_info['is_guest']) + { + $_POST['email'] = !isset($_POST['email']) ? '' : trim($_POST['email']); + if ($_POST['email'] === '') + $post_errors[] = 'no_email'; + elseif (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['email']) == 0) + $post_errors[] = 'bad_email'; + + isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title'])); + + $user_info['email'] = htmlspecialchars($_POST['email']); + } + + // Could they get the right verification code? + if ($user_info['is_guest'] && !empty($modSettings['guests_report_require_captcha'])) + { + require_once($sourcedir . '/Subs-Editor.php'); + $verificationOptions = array( + 'id' => 'report', + ); + $context['require_verification'] = create_control_verification($verificationOptions, true); + if (is_array($context['require_verification'])) + $post_errors = array_merge($post_errors, $context['require_verification']); + } + + // Any errors? + if (!empty($post_errors)) + { + loadLanguage('Errors'); + + $context['post_errors'] = array(); + foreach ($post_errors as $post_error) + $context['post_errors'][] = $txt['error_' . $post_error]; + + return ReportToModerator(); + } + + // Get the basic topic information, and make sure they can see it. + $_POST['msg'] = (int) $_POST['msg']; + + $request = $smcFunc['db_query']('', ' + SELECT m.id_topic, m.id_board, m.subject, m.body, m.id_member AS id_poster, m.poster_name, mem.real_name + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}members AS mem ON (m.id_member = mem.id_member) + WHERE m.id_msg = {int:id_msg} + AND m.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + 'id_msg' => $_POST['msg'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_board', false); + $message = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $poster_name = un_htmlspecialchars($message['real_name']) . ($message['real_name'] != $message['poster_name'] ? ' (' . $message['poster_name'] . ')' : ''); + $reporterName = un_htmlspecialchars($user_info['name']) . ($user_info['name'] != $user_info['username'] && $user_info['username'] != '' ? ' (' . $user_info['username'] . ')' : ''); + $subject = un_htmlspecialchars($message['subject']); + + // Get a list of members with the moderate_board permission. + require_once($sourcedir . '/Subs-Members.php'); + $moderators = membersAllowedTo('moderate_board', $board); + + $request = $smcFunc['db_query']('', ' + SELECT id_member, email_address, lngfile, mod_prefs + FROM {db_prefix}members + WHERE id_member IN ({array_int:moderator_list}) + AND notify_types != {int:notify_types} + ORDER BY lngfile', + array( + 'moderator_list' => $moderators, + 'notify_types' => 4, + ) + ); + + // Check that moderators do exist! + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_mods', false); + + // If we get here, I believe we should make a record of this, for historical significance, yabber. + if (empty($modSettings['disable_log_report'])) + { + $request2 = $smcFunc['db_query']('', ' + SELECT id_report, ignore_all + FROM {db_prefix}log_reported + WHERE id_msg = {int:id_msg} + AND (closed = {int:not_closed} OR ignore_all = {int:ignored}) + ORDER BY ignore_all DESC', + array( + 'id_msg' => $_POST['msg'], + 'not_closed' => 0, + 'ignored' => 1, + ) + ); + if ($smcFunc['db_num_rows']($request2) != 0) + list ($id_report, $ignore) = $smcFunc['db_fetch_row']($request2); + $smcFunc['db_free_result']($request2); + + // If we're just going to ignore these, then who gives a monkeys... + if (!empty($ignore)) + redirectexit('topic=' . $topic . '.msg' . $_POST['msg'] . '#msg' . $_POST['msg']); + + // Already reported? My god, we could be dealing with a real rogue here... + if (!empty($id_report)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_reported + SET num_reports = num_reports + 1, time_updated = {int:current_time} + WHERE id_report = {int:id_report}', + array( + 'current_time' => time(), + 'id_report' => $id_report, + ) + ); + // Otherwise, we shall make one! + else + { + if (empty($message['real_name'])) + $message['real_name'] = $message['poster_name']; + + $smcFunc['db_insert']('', + '{db_prefix}log_reported', + array( + 'id_msg' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'id_member' => 'int', 'membername' => 'string', + 'subject' => 'string', 'body' => 'string', 'time_started' => 'int', 'time_updated' => 'int', + 'num_reports' => 'int', 'closed' => 'int', + ), + array( + $_POST['msg'], $message['id_topic'], $message['id_board'], $message['id_poster'], $message['real_name'], + $message['subject'], $message['body'] , time(), time(), 1, 0, + ), + array('id_report') + ); + $id_report = $smcFunc['db_insert_id']('{db_prefix}log_reported', 'id_report'); + } + + // Now just add our report... + if ($id_report) + { + $smcFunc['db_insert']('', + '{db_prefix}log_reported_comments', + array( + 'id_report' => 'int', 'id_member' => 'int', 'membername' => 'string', 'email_address' => 'string', + 'member_ip' => 'string', 'comment' => 'string', 'time_sent' => 'int', + ), + array( + $id_report, $user_info['id'], $user_info['name'], $user_info['email'], + $user_info['ip'], $poster_comment, time(), + ), + array('id_comment') + ); + } + } + + // Find out who the real moderators are - for mod preferences. + $request2 = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}moderators + WHERE id_board = {int:current_board}', + array( + 'current_board' => $board, + ) + ); + $real_mods = array(); + while ($row = $smcFunc['db_fetch_assoc']($request2)) + $real_mods[] = $row['id_member']; + $smcFunc['db_free_result']($request2); + + // Send every moderator an email. + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Maybe they don't want to know?! + if (!empty($row['mod_prefs'])) + { + list(,, $pref_binary) = explode('|', $row['mod_prefs']); + if (!($pref_binary & 1) && (!($pref_binary & 2) || !in_array($row['id_member'], $real_mods))) + continue; + } + + $replacements = array( + 'TOPICSUBJECT' => $subject, + 'POSTERNAME' => $poster_name, + 'REPORTERNAME' => $reporterName, + 'TOPICLINK' => $scripturl . '?topic=' . $topic . '.msg' . $_POST['msg'] . '#msg' . $_POST['msg'], + 'REPORTLINK' => !empty($id_report) ? $scripturl . '?action=moderate;area=reports;report=' . $id_report : '', + 'COMMENT' => $_POST['comment'], + ); + + $emaildata = loadEmailTemplate('report_to_moderator', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); + + // Send it to the moderator. + sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], $user_info['email'], null, false, 2); + } + $smcFunc['db_free_result']($request); + + // Keep track of when the mod reports get updated, that way we know when we need to look again. + updateSettings(array('last_mod_report_action' => time())); + + // Back to the post we reported! + redirectexit('reportsent;topic=' . $topic . '.msg' . $_POST['msg'] . '#msg' . $_POST['msg']); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/SplitTopics.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/SplitTopics.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1576 @@ + 'SplitSelectTopics', + 'execute' => 'SplitExecute', + 'index' => 'SplitIndex', + 'splitSelection' => 'SplitSelectionExecute', + ); + + // ?action=splittopics;sa=LETSBREAKIT won't work, sorry. + if (empty($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']])) + SplitIndex(); + else + $subActions[$_REQUEST['sa']](); +} + +// Part 1: General stuff. +function SplitIndex() +{ + global $txt, $topic, $context, $smcFunc, $modSettings; + + // Validate "at". + if (empty($_GET['at'])) + fatal_lang_error('numbers_one_to_nine', false); + $_GET['at'] = (int) $_GET['at']; + + // Retrieve the subject and stuff of the specific topic/message. + $request = $smcFunc['db_query']('', ' + SELECT m.subject, t.num_replies, t.unapproved_posts, t.id_first_msg, t.approved + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic}) + WHERE m.id_msg = {int:split_at}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND m.approved = 1') . ' + AND m.id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + 'split_at' => $_GET['at'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('cant_find_messages'); + list ($_REQUEST['subname'], $num_replies, $unapproved_posts, $id_first_msg, $approved) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // If not approved validate they can see it. + if ($modSettings['postmod_active'] && !$approved) + isAllowedTo('approve_posts'); + + // If this topic has unapproved posts, we need to count them too... + if ($modSettings['postmod_active'] && allowedTo('approve_posts')) + $num_replies += $unapproved_posts - ($approved ? 0 : 1); + + // Check if there is more than one message in the topic. (there should be.) + if ($num_replies < 1) + fatal_lang_error('topic_one_post', false); + + // Check if this is the first message in the topic (if so, the first and second option won't be available) + if ($id_first_msg == $_GET['at']) + return SplitSelectTopics(); + + // Basic template information.... + $context['message'] = array( + 'id' => $_GET['at'], + 'subject' => $_REQUEST['subname'] + ); + $context['sub_template'] = 'ask'; + $context['page_title'] = $txt['split']; +} + +// Alright, you've decided what you want to do with it.... now to do it. +function SplitExecute() +{ + global $txt, $board, $topic, $context, $user_info, $smcFunc, $modSettings; + + // Check the session to make sure they meant to do this. + checkSession(); + + // Clean up the subject. + if (!isset($_POST['subname']) || $_POST['subname'] == '') + $_POST['subname'] = $txt['new_topic']; + + // Redirect to the selector if they chose selective. + if ($_POST['step2'] == 'selective') + { + $_REQUEST['subname'] = $_POST['subname']; + return SplitSelectTopics(); + } + + $_POST['at'] = (int) $_POST['at']; + $messagesToBeSplit = array(); + + if ($_POST['step2'] == 'afterthis') + { + // Fetch the message IDs of the topic that are at or after the message. + $request = $smcFunc['db_query']('', ' + SELECT id_msg + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic} + AND id_msg >= {int:split_at}', + array( + 'current_topic' => $topic, + 'split_at' => $_POST['at'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $messagesToBeSplit[] = $row['id_msg']; + $smcFunc['db_free_result']($request); + } + // Only the selected message has to be split. That should be easy. + elseif ($_POST['step2'] == 'onlythis') + $messagesToBeSplit[] = $_POST['at']; + // There's another action?! + else + fatal_lang_error('no_access', false); + + $context['old_topic'] = $topic; + $context['new_topic'] = splitTopic($topic, $messagesToBeSplit, $_POST['subname']); + $context['page_title'] = $txt['split']; +} + +// Get a selective list of topics... +function SplitSelectTopics() +{ + global $txt, $scripturl, $topic, $context, $modSettings, $original_msgs, $smcFunc, $options; + + $context['page_title'] = $txt['split'] . ' - ' . $txt['select_split_posts']; + + // Haven't selected anything have we? + $_SESSION['split_selection'][$topic] = empty($_SESSION['split_selection'][$topic]) ? array() : $_SESSION['split_selection'][$topic]; + + $context['not_selected'] = array( + 'num_messages' => 0, + 'start' => empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start'], + 'messages' => array(), + ); + + $context['selected'] = array( + 'num_messages' => 0, + 'start' => empty($_REQUEST['start2']) ? 0 : (int) $_REQUEST['start2'], + 'messages' => array(), + ); + + $context['topic'] = array( + 'id' => $topic, + 'subject' => urlencode($_REQUEST['subname']), + ); + + // Some stuff for our favorite template. + $context['new_subject'] = $_REQUEST['subname']; + + // Using the "select" sub template. + $context['sub_template'] = isset($_REQUEST['xml']) ? 'split' : 'select'; + + // Are we using a custom messages per page? + $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages']; + + // Get the message ID's from before the move. + if (isset($_REQUEST['xml'])) + { + $original_msgs = array( + 'not_selected' => array(), + 'selected' => array(), + ); + $request = $smcFunc['db_query']('', ' + SELECT id_msg + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic}' . (empty($_SESSION['split_selection'][$topic]) ? '' : ' + AND id_msg NOT IN ({array_int:no_split_msgs})') . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND approved = {int:is_approved}') . ' + ORDER BY id_msg DESC + LIMIT {int:start}, {int:messages_per_page}', + array( + 'current_topic' => $topic, + 'no_split_msgs' => empty($_SESSION['split_selection'][$topic]) ? array() : $_SESSION['split_selection'][$topic], + 'is_approved' => 1, + 'start' => $context['not_selected']['start'], + 'messages_per_page' => $context['messages_per_page'], + ) + ); + // You can't split the last message off. + if (empty($context['not_selected']['start']) && $smcFunc['db_num_rows']($request) <= 1 && $_REQUEST['move'] == 'down') + $_REQUEST['move'] = ''; + while ($row = $smcFunc['db_fetch_assoc']($request)) + $original_msgs['not_selected'][] = $row['id_msg']; + $smcFunc['db_free_result']($request); + if (!empty($_SESSION['split_selection'][$topic])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_msg + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic} + AND id_msg IN ({array_int:split_msgs})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND approved = {int:is_approved}') . ' + ORDER BY id_msg DESC + LIMIT {int:start}, {int:messages_per_page}', + array( + 'current_topic' => $topic, + 'split_msgs' => $_SESSION['split_selection'][$topic], + 'is_approved' => 1, + 'start' => $context['selected']['start'], + 'messages_per_page' => $context['messages_per_page'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $original_msgs['selected'][] = $row['id_msg']; + $smcFunc['db_free_result']($request); + } + } + + // (De)select a message.. + if (!empty($_REQUEST['move'])) + { + $_REQUEST['msg'] = (int) $_REQUEST['msg']; + + if ($_REQUEST['move'] == 'reset') + $_SESSION['split_selection'][$topic] = array(); + elseif ($_REQUEST['move'] == 'up') + $_SESSION['split_selection'][$topic] = array_diff($_SESSION['split_selection'][$topic], array($_REQUEST['msg'])); + else + $_SESSION['split_selection'][$topic][] = $_REQUEST['msg']; + } + + // Make sure the selection is still accurate. + if (!empty($_SESSION['split_selection'][$topic])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_msg + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic} + AND id_msg IN ({array_int:split_msgs})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND approved = {int:is_approved}'), + array( + 'current_topic' => $topic, + 'split_msgs' => $_SESSION['split_selection'][$topic], + 'is_approved' => 1, + ) + ); + $_SESSION['split_selection'][$topic] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $_SESSION['split_selection'][$topic][] = $row['id_msg']; + $smcFunc['db_free_result']($request); + } + + // Get the number of messages (not) selected to be split. + $request = $smcFunc['db_query']('', ' + SELECT ' . (empty($_SESSION['split_selection'][$topic]) ? '0' : 'm.id_msg IN ({array_int:split_msgs})') . ' AS is_selected, COUNT(*) AS num_messages + FROM {db_prefix}messages AS m + WHERE m.id_topic = {int:current_topic}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND approved = {int:is_approved}') . (empty($_SESSION['split_selection'][$topic]) ? '' : ' + GROUP BY is_selected'), + array( + 'current_topic' => $topic, + 'split_msgs' => !empty($_SESSION['split_selection'][$topic]) ? $_SESSION['split_selection'][$topic] : array(), + 'is_approved' => 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context[empty($row['is_selected']) ? 'not_selected' : 'selected']['num_messages'] = $row['num_messages']; + $smcFunc['db_free_result']($request); + + // Fix an oversized starting page (to make sure both pageindexes are properly set). + if ($context['selected']['start'] >= $context['selected']['num_messages']) + $context['selected']['start'] = $context['selected']['num_messages'] <= $context['messages_per_page'] ? 0 : ($context['selected']['num_messages'] - (($context['selected']['num_messages'] % $context['messages_per_page']) == 0 ? $context['messages_per_page'] : ($context['selected']['num_messages'] % $context['messages_per_page']))); + + // Build a page list of the not-selected topics... + $context['not_selected']['page_index'] = constructPageIndex($scripturl . '?action=splittopics;sa=selectTopics;subname=' . strtr(urlencode($_REQUEST['subname']), array('%' => '%%')) . ';topic=' . $topic . '.%1$d;start2=' . $context['selected']['start'], $context['not_selected']['start'], $context['not_selected']['num_messages'], $context['messages_per_page'], true); + // ...and one of the selected topics. + $context['selected']['page_index'] = constructPageIndex($scripturl . '?action=splittopics;sa=selectTopics;subname=' . strtr(urlencode($_REQUEST['subname']), array('%' => '%%')) . ';topic=' . $topic . '.' . $context['not_selected']['start'] . ';start2=%1$d', $context['selected']['start'], $context['selected']['num_messages'], $context['messages_per_page'], true); + + // Get the messages and stick them into an array. + $request = $smcFunc['db_query']('', ' + SELECT m.subject, IFNULL(mem.real_name, m.poster_name) AS real_name, m.poster_time, m.body, m.id_msg, m.smileys_enabled + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_topic = {int:current_topic}' . (empty($_SESSION['split_selection'][$topic]) ? '' : ' + AND id_msg NOT IN ({array_int:no_split_msgs})') . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND approved = {int:is_approved}') . ' + ORDER BY m.id_msg DESC + LIMIT {int:start}, {int:messages_per_page}', + array( + 'current_topic' => $topic, + 'no_split_msgs' => !empty($_SESSION['split_selection'][$topic]) ? $_SESSION['split_selection'][$topic] : array(), + 'is_approved' => 1, + 'start' => $context['not_selected']['start'], + 'messages_per_page' => $context['messages_per_page'], + ) + ); + $context['messages'] = array(); + for ($counter = 0; $row = $smcFunc['db_fetch_assoc']($request); $counter ++) + { + censorText($row['subject']); + censorText($row['body']); + + $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); + + $context['not_selected']['messages'][$row['id_msg']] = array( + 'id' => $row['id_msg'], + 'alternate' => $counter % 2, + 'subject' => $row['subject'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'body' => $row['body'], + 'poster' => $row['real_name'], + ); + } + $smcFunc['db_free_result']($request); + + // Now get the selected messages. + if (!empty($_SESSION['split_selection'][$topic])) + { + // Get the messages and stick them into an array. + $request = $smcFunc['db_query']('', ' + SELECT m.subject, IFNULL(mem.real_name, m.poster_name) AS real_name, m.poster_time, m.body, m.id_msg, m.smileys_enabled + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_topic = {int:current_topic} + AND m.id_msg IN ({array_int:split_msgs})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : ' + AND approved = {int:is_approved}') . ' + ORDER BY m.id_msg DESC + LIMIT {int:start}, {int:messages_per_page}', + array( + 'current_topic' => $topic, + 'split_msgs' => $_SESSION['split_selection'][$topic], + 'is_approved' => 1, + 'start' => $context['selected']['start'], + 'messages_per_page' => $context['messages_per_page'], + ) + ); + $context['messages'] = array(); + for ($counter = 0; $row = $smcFunc['db_fetch_assoc']($request); $counter ++) + { + censorText($row['subject']); + censorText($row['body']); + + $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); + + $context['selected']['messages'][$row['id_msg']] = array( + 'id' => $row['id_msg'], + 'alternate' => $counter % 2, + 'subject' => $row['subject'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'body' => $row['body'], + 'poster' => $row['real_name'] + ); + } + $smcFunc['db_free_result']($request); + } + + // The XMLhttp method only needs the stuff that changed, so let's compare. + if (isset($_REQUEST['xml'])) + { + $changes = array( + 'remove' => array( + 'not_selected' => array_diff($original_msgs['not_selected'], array_keys($context['not_selected']['messages'])), + 'selected' => array_diff($original_msgs['selected'], array_keys($context['selected']['messages'])), + ), + 'insert' => array( + 'not_selected' => array_diff(array_keys($context['not_selected']['messages']), $original_msgs['not_selected']), + 'selected' => array_diff(array_keys($context['selected']['messages']), $original_msgs['selected']), + ), + ); + + $context['changes'] = array(); + foreach ($changes as $change_type => $change_array) + foreach ($change_array as $section => $msg_array) + { + if (empty($msg_array)) + continue; + + foreach ($msg_array as $id_msg) + { + $context['changes'][$change_type . $id_msg] = array( + 'id' => $id_msg, + 'type' => $change_type, + 'section' => $section, + ); + if ($change_type == 'insert') + $context['changes']['insert' . $id_msg]['insert_value'] = $context[$section]['messages'][$id_msg]; + } + } + } +} + +// Actually and selectively split the topics out. +function SplitSelectionExecute() +{ + global $txt, $board, $topic, $context, $user_info; + + // Make sure the session id was passed with post. + checkSession(); + + // Default the subject in case it's blank. + if (!isset($_POST['subname']) || $_POST['subname'] == '') + $_POST['subname'] = $txt['new_topic']; + + // You must've selected some messages! Can't split out none! + if (empty($_SESSION['split_selection'][$topic])) + fatal_lang_error('no_posts_selected', false); + + $context['old_topic'] = $topic; + $context['new_topic'] = splitTopic($topic, $_SESSION['split_selection'][$topic], $_POST['subname']); + $context['page_title'] = $txt['split']; +} + +// Split a topic in two topics. +function splitTopic($split1_ID_TOPIC, $splitMessages, $new_subject) +{ + global $user_info, $topic, $board, $modSettings, $smcFunc, $txt; + + // Nothing to split? + if (empty($splitMessages)) + fatal_lang_error('no_posts_selected', false); + + // Get some board info. + $request = $smcFunc['db_query']('', ' + SELECT id_board, approved + FROM {db_prefix}topics + WHERE id_topic = {int:id_topic} + LIMIT 1', + array( + 'id_topic' => $split1_ID_TOPIC, + ) + ); + list ($id_board, $split1_approved) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Find the new first and last not in the list. (old topic) + $request = $smcFunc['db_query']('', ' + SELECT + MIN(m.id_msg) AS myid_first_msg, MAX(m.id_msg) AS myid_last_msg, COUNT(*) AS message_count, m.approved + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:id_topic}) + WHERE m.id_msg NOT IN ({array_int:no_msg_list}) + AND m.id_topic = {int:id_topic} + GROUP BY m.approved + ORDER BY m.approved DESC + LIMIT 2', + array( + 'id_topic' => $split1_ID_TOPIC, + 'no_msg_list' => $splitMessages, + ) + ); + // You can't select ALL the messages! + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('selected_all_posts', false); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Get the right first and last message dependant on approved state... + if (empty($split1_first_msg) || $row['myid_first_msg'] < $split1_first_msg) + $split1_first_msg = $row['myid_first_msg']; + if (empty($split1_last_msg) || $row['approved']) + $split1_last_msg = $row['myid_last_msg']; + + // Get the counts correct... + if ($row['approved']) + { + $split1_replies = $row['message_count'] - 1; + $split1_unapprovedposts = 0; + } + else + { + if (!isset($split1_replies)) + $split1_replies = 0; + // If the topic isn't approved then num replies must go up by one... as first post wouldn't be counted. + elseif (!$split1_approved) + $split1_replies++; + + $split1_unapprovedposts = $row['message_count']; + } + } + $smcFunc['db_free_result']($request); + $split1_firstMem = getMsgMemberID($split1_first_msg); + $split1_lastMem = getMsgMemberID($split1_last_msg); + + // Find the first and last in the list. (new topic) + $request = $smcFunc['db_query']('', ' + SELECT MIN(id_msg) AS myid_first_msg, MAX(id_msg) AS myid_last_msg, COUNT(*) AS message_count, approved + FROM {db_prefix}messages + WHERE id_msg IN ({array_int:msg_list}) + AND id_topic = {int:id_topic} + GROUP BY id_topic, approved + ORDER BY approved DESC + LIMIT 2', + array( + 'msg_list' => $splitMessages, + 'id_topic' => $split1_ID_TOPIC, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // As before get the right first and last message dependant on approved state... + if (empty($split2_first_msg) || $row['myid_first_msg'] < $split2_first_msg) + $split2_first_msg = $row['myid_first_msg']; + if (empty($split2_last_msg) || $row['approved']) + $split2_last_msg = $row['myid_last_msg']; + + // Then do the counts again... + if ($row['approved']) + { + $split2_approved = true; + $split2_replies = $row['message_count'] - 1; + $split2_unapprovedposts = 0; + } + else + { + // Should this one be approved?? + if ($split2_first_msg == $row['myid_first_msg']) + $split2_approved = false; + + if (!isset($split2_replies)) + $split2_replies = 0; + // As before, fix number of replies. + elseif (!$split2_approved) + $split2_replies++; + + $split2_unapprovedposts = $row['message_count']; + } + } + $smcFunc['db_free_result']($request); + $split2_firstMem = getMsgMemberID($split2_first_msg); + $split2_lastMem = getMsgMemberID($split2_last_msg); + + // No database changes yet, so let's double check to see if everything makes at least a little sense. + if ($split1_first_msg <= 0 || $split1_last_msg <= 0 || $split2_first_msg <= 0 || $split2_last_msg <= 0 || $split1_replies < 0 || $split2_replies < 0 || $split1_unapprovedposts < 0 || $split2_unapprovedposts < 0 || !isset($split1_approved) || !isset($split2_approved)) + fatal_lang_error('cant_find_messages'); + + // You cannot split off the first message of a topic. + if ($split1_first_msg > $split2_first_msg) + fatal_lang_error('split_first_post', false); + + // We're off to insert the new topic! Use 0 for now to avoid UNIQUE errors. + $smcFunc['db_insert']('', + '{db_prefix}topics', + array( + 'id_board' => 'int', + 'id_member_started' => 'int', + 'id_member_updated' => 'int', + 'id_first_msg' => 'int', + 'id_last_msg' => 'int', + 'num_replies' => 'int', + 'unapproved_posts' => 'int', + 'approved' => 'int', + 'is_sticky' => 'int', + ), + array( + (int) $id_board, $split2_firstMem, $split2_lastMem, 0, + 0, $split2_replies, $split2_unapprovedposts, (int) $split2_approved, 0, + ), + array('id_topic') + ); + $split2_ID_TOPIC = $smcFunc['db_insert_id']('{db_prefix}topics', 'id_topic'); + if ($split2_ID_TOPIC <= 0) + fatal_lang_error('cant_insert_topic'); + + // Move the messages over to the other topic. + $new_subject = strtr($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($new_subject)), array("\r" => '', "\n" => '', "\t" => '')); + // Check the subject length. + if ($smcFunc['strlen']($new_subject) > 100) + $new_subject = $smcFunc['substr']($new_subject, 0, 100); + // Valid subject? + if ($new_subject != '') + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET + id_topic = {int:id_topic}, + subject = CASE WHEN id_msg = {int:split_first_msg} THEN {string:new_subject} ELSE {string:new_subject_replies} END + WHERE id_msg IN ({array_int:split_msgs})', + array( + 'split_msgs' => $splitMessages, + 'id_topic' => $split2_ID_TOPIC, + 'new_subject' => $new_subject, + 'split_first_msg' => $split2_first_msg, + 'new_subject_replies' => $txt['response_prefix'] . $new_subject, + ) + ); + + // Cache the new topics subject... we can do it now as all the subjects are the same! + updateStats('subject', $split2_ID_TOPIC, $new_subject); + } + + // Any associated reported posts better follow... + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_reported + SET id_topic = {int:id_topic} + WHERE id_msg IN ({array_int:split_msgs})', + array( + 'split_msgs' => $splitMessages, + 'id_topic' => $split2_ID_TOPIC, + ) + ); + + // Mess with the old topic's first, last, and number of messages. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET + num_replies = {int:num_replies}, + id_first_msg = {int:id_first_msg}, + id_last_msg = {int:id_last_msg}, + id_member_started = {int:id_member_started}, + id_member_updated = {int:id_member_updated}, + unapproved_posts = {int:unapproved_posts} + WHERE id_topic = {int:id_topic}', + array( + 'num_replies' => $split1_replies, + 'id_first_msg' => $split1_first_msg, + 'id_last_msg' => $split1_last_msg, + 'id_member_started' => $split1_firstMem, + 'id_member_updated' => $split1_lastMem, + 'unapproved_posts' => $split1_unapprovedposts, + 'id_topic' => $split1_ID_TOPIC, + ) + ); + + // Now, put the first/last message back to what they should be. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET + id_first_msg = {int:id_first_msg}, + id_last_msg = {int:id_last_msg} + WHERE id_topic = {int:id_topic}', + array( + 'id_first_msg' => $split2_first_msg, + 'id_last_msg' => $split2_last_msg, + 'id_topic' => $split2_ID_TOPIC, + ) + ); + + // If the new topic isn't approved ensure the first message flags this just in case. + if (!$split2_approved) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET approved = {int:approved} + WHERE id_msg = {int:id_msg} + AND id_topic = {int:id_topic}', + array( + 'approved' => 0, + 'id_msg' => $split2_first_msg, + 'id_topic' => $split2_ID_TOPIC, + ) + ); + + // The board has more topics now (Or more unapproved ones!). + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET ' . ($split2_approved ? ' + num_topics = num_topics + 1' : ' + unapproved_topics = unapproved_topics + 1') . ' + WHERE id_board = {int:id_board}', + array( + 'id_board' => $id_board, + ) + ); + + // Copy log topic entries. + // !!! This should really be chunked. + $request = $smcFunc['db_query']('', ' + SELECT id_member, id_msg + FROM {db_prefix}log_topics + WHERE id_topic = {int:id_topic}', + array( + 'id_topic' => (int) $split1_ID_TOPIC, + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + { + $replaceEntries = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $replaceEntries[] = array($row['id_member'], $split2_ID_TOPIC, $row['id_msg']); + + $smcFunc['db_insert']('ignore', + '{db_prefix}log_topics', + array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int'), + $replaceEntries, + array('id_member', 'id_topic') + ); + unset($replaceEntries); + } + $smcFunc['db_free_result']($request); + + // Housekeeping. + updateStats('topic'); + updateLastMessages($id_board); + + logAction('split', array('topic' => $split1_ID_TOPIC, 'new_topic' => $split2_ID_TOPIC, 'board' => $id_board)); + + // Notify people that this topic has been split? + sendNotifications($split1_ID_TOPIC, 'split'); + + // Return the ID of the newly created topic. + return $split2_ID_TOPIC; +} + +// Merge two topics into one topic... useful if they have the same basic subject. +function MergeTopics() +{ + // Load the template.... + loadTemplate('SplitTopics'); + + $subActions = array( + 'done' => 'MergeDone', + 'execute' => 'MergeExecute', + 'index' => 'MergeIndex', + 'options' => 'MergeExecute', + ); + + // ?action=mergetopics;sa=LETSBREAKIT won't work, sorry. + if (empty($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']])) + MergeIndex(); + else + $subActions[$_REQUEST['sa']](); +} + +// Merge two topics together. +function MergeIndex() +{ + global $txt, $board, $context, $smcFunc; + global $scripturl, $topic, $user_info, $modSettings; + + if (!isset($_GET['from'])) + fatal_lang_error('no_access', false); + $_GET['from'] = (int) $_GET['from']; + + $_REQUEST['targetboard'] = isset($_REQUEST['targetboard']) ? (int) $_REQUEST['targetboard'] : $board; + $context['target_board'] = $_REQUEST['targetboard']; + + // Prepare a handy query bit for approval... + if ($modSettings['postmod_active']) + { + $can_approve_boards = boardsAllowedTo('approve_posts'); + $onlyApproved = $can_approve_boards !== array(0) && !in_array($_REQUEST['targetboard'], $can_approve_boards); + } + else + $onlyApproved = false; + + // How many topics are on this board? (used for paging.) + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}topics AS t + WHERE t.id_board = {int:id_board}' . ($onlyApproved ? ' + AND t.approved = {int:is_approved}' : ''), + array( + 'id_board' => $_REQUEST['targetboard'], + 'is_approved' => 1, + ) + ); + list ($topiccount) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Make the page list. + $context['page_index'] = constructPageIndex($scripturl . '?action=mergetopics;from=' . $_GET['from'] . ';targetboard=' . $_REQUEST['targetboard'] . ';board=' . $board . '.%1$d', $_REQUEST['start'], $topiccount, $modSettings['defaultMaxTopics'], true); + + // Get the topic's subject. + $request = $smcFunc['db_query']('', ' + SELECT m.subject + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE t.id_topic = {int:id_topic} + AND t.id_board = {int:current_board}' . ($onlyApproved ? ' + AND t.approved = {int:is_approved}' : '') . ' + LIMIT 1', + array( + 'current_board' => $board, + 'id_topic' => $_GET['from'], + 'is_approved' => 1, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_board'); + list ($subject) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Tell the template a few things.. + $context['origin_topic'] = $_GET['from']; + $context['origin_subject'] = $subject; + $context['origin_js_subject'] = addcslashes(addslashes($subject), '/'); + $context['page_title'] = $txt['merge']; + + // Check which boards you have merge permissions on. + $merge_boards = boardsAllowedTo('merge_any'); + + if (empty($merge_boards)) + fatal_lang_error('cannot_merge_any', 'user'); + + // Get a list of boards they can navigate to to merge. + $request = $smcFunc['db_query']('order_by_board_order', ' + SELECT b.id_board, b.name AS board_name, c.name AS cat_name + FROM {db_prefix}boards AS b + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) + WHERE {query_see_board}' . (!in_array(0, $merge_boards) ? ' + AND b.id_board IN ({array_int:merge_boards})' : ''), + array( + 'merge_boards' => $merge_boards, + ) + ); + $context['boards'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['boards'][] = array( + 'id' => $row['id_board'], + 'name' => $row['board_name'], + 'category' => $row['cat_name'] + ); + $smcFunc['db_free_result']($request); + + // Get some topics to merge it with. + $request = $smcFunc['db_query']('', ' + SELECT t.id_topic, m.subject, m.id_member, IFNULL(mem.real_name, m.poster_name) AS poster_name + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE t.id_board = {int:id_board} + AND t.id_topic != {int:id_topic}' . ($onlyApproved ? ' + AND t.approved = {int:is_approved}' : '') . ' + ORDER BY {raw:sort} + LIMIT {int:offset}, {int:limit}', + array( + 'id_board' => $_REQUEST['targetboard'], + 'id_topic' => $_GET['from'], + 'sort' => (!empty($modSettings['enableStickyTopics']) ? 't.is_sticky DESC, ' : '') . 't.id_last_msg DESC', + 'offset' => $_REQUEST['start'], + 'limit' => $modSettings['defaultMaxTopics'], + 'is_approved' => 1, + ) + ); + $context['topics'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + censorText($row['subject']); + + $context['topics'][] = array( + 'id' => $row['id_topic'], + 'poster' => array( + 'id' => $row['id_member'], + 'name' => $row['poster_name'], + 'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => empty($row['id_member']) ? $row['poster_name'] : '' . $row['poster_name'] . '' + ), + 'subject' => $row['subject'], + 'js_subject' => addcslashes(addslashes($row['subject']), '/') + ); + } + $smcFunc['db_free_result']($request); + + if (empty($context['topics']) && count($context['boards']) <= 1) + fatal_lang_error('merge_need_more_topics'); + + $context['sub_template'] = 'merge'; +} + +// Now that the topic IDs are known, do the proper merging. +function MergeExecute($topics = array()) +{ + global $user_info, $txt, $context, $scripturl, $sourcedir; + global $smcFunc, $language, $modSettings; + + // Check the session. + checkSession('request'); + + // Handle URLs from MergeIndex. + if (!empty($_GET['from']) && !empty($_GET['to'])) + $topics = array((int) $_GET['from'], (int) $_GET['to']); + + // If we came from a form, the topic IDs came by post. + if (!empty($_POST['topics']) && is_array($_POST['topics'])) + $topics = $_POST['topics']; + + // There's nothing to merge with just one topic... + if (empty($topics) || !is_array($topics) || count($topics) == 1) + fatal_lang_error('merge_need_more_topics'); + + // Make sure every topic is numeric, or some nasty things could be done with the DB. + foreach ($topics as $id => $topic) + $topics[$id] = (int) $topic; + + // Joy of all joys, make sure they're not pi**ing about with unapproved topics they can't see :P + if ($modSettings['postmod_active']) + $can_approve_boards = boardsAllowedTo('approve_posts'); + + // Get info about the topics and polls that will be merged. + $request = $smcFunc['db_query']('', ' + SELECT + t.id_topic, t.id_board, t.id_poll, t.num_views, t.is_sticky, t.approved, t.num_replies, t.unapproved_posts, + m1.subject, m1.poster_time AS time_started, IFNULL(mem1.id_member, 0) AS id_member_started, IFNULL(mem1.real_name, m1.poster_name) AS name_started, + m2.poster_time AS time_updated, IFNULL(mem2.id_member, 0) AS id_member_updated, IFNULL(mem2.real_name, m2.poster_name) AS name_updated + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m1 ON (m1.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}messages AS m2 ON (m2.id_msg = t.id_last_msg) + LEFT JOIN {db_prefix}members AS mem1 ON (mem1.id_member = m1.id_member) + LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member) + WHERE t.id_topic IN ({array_int:topic_list}) + ORDER BY t.id_first_msg + LIMIT ' . count($topics), + array( + 'topic_list' => $topics, + ) + ); + if ($smcFunc['db_num_rows']($request) < 2) + fatal_lang_error('no_topic_id'); + $num_views = 0; + $is_sticky = 0; + $boardTotals = array(); + $boards = array(); + $polls = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Make a note for the board counts... + if (!isset($boardTotals[$row['id_board']])) + $boardTotals[$row['id_board']] = array( + 'posts' => 0, + 'topics' => 0, + 'unapproved_posts' => 0, + 'unapproved_topics' => 0 + ); + + // We can't see unapproved topics here? + if ($modSettings['postmod_active'] && !$row['approved'] && $can_approve_boards != array(0) && in_array($row['id_board'], $can_approve_boards)) + continue; + elseif (!$row['approved']) + $boardTotals[$row['id_board']]['unapproved_topics']++; + else + $boardTotals[$row['id_board']]['topics']++; + + $boardTotals[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts']; + $boardTotals[$row['id_board']]['posts'] += $row['num_replies'] + ($row['approved'] ? 1 : 0); + + $topic_data[$row['id_topic']] = array( + 'id' => $row['id_topic'], + 'board' => $row['id_board'], + 'poll' => $row['id_poll'], + 'num_views' => $row['num_views'], + 'subject' => $row['subject'], + 'started' => array( + 'time' => timeformat($row['time_started']), + 'timestamp' => forum_time(true, $row['time_started']), + 'href' => empty($row['id_member_started']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_started'], + 'link' => empty($row['id_member_started']) ? $row['name_started'] : '' . $row['name_started'] . '' + ), + 'updated' => array( + 'time' => timeformat($row['time_updated']), + 'timestamp' => forum_time(true, $row['time_updated']), + 'href' => empty($row['id_member_updated']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_updated'], + 'link' => empty($row['id_member_updated']) ? $row['name_updated'] : '' . $row['name_updated'] . '' + ) + ); + $num_views += $row['num_views']; + $boards[] = $row['id_board']; + + // If there's no poll, id_poll == 0... + if ($row['id_poll'] > 0) + $polls[] = $row['id_poll']; + // Store the id_topic with the lowest id_first_msg. + if (empty($firstTopic)) + $firstTopic = $row['id_topic']; + + $is_sticky = max($is_sticky, $row['is_sticky']); + } + $smcFunc['db_free_result']($request); + + // If we didn't get any topics then they've been messing with unapproved stuff. + if (empty($topic_data)) + fatal_lang_error('no_topic_id'); + + $boards = array_values(array_unique($boards)); + + // The parameters of MergeExecute were set, so this must've been an internal call. + if (!empty($topics)) + { + isAllowedTo('merge_any', $boards); + loadTemplate('SplitTopics'); + } + + // Get the boards a user is allowed to merge in. + $merge_boards = boardsAllowedTo('merge_any'); + if (empty($merge_boards)) + fatal_lang_error('cannot_merge_any', 'user'); + + // Make sure they can see all boards.... + $request = $smcFunc['db_query']('', ' + SELECT b.id_board + FROM {db_prefix}boards AS b + WHERE b.id_board IN ({array_int:boards}) + AND {query_see_board}' . (!in_array(0, $merge_boards) ? ' + AND b.id_board IN ({array_int:merge_boards})' : '') . ' + LIMIT ' . count($boards), + array( + 'boards' => $boards, + 'merge_boards' => $merge_boards, + ) + ); + // If the number of boards that's in the output isn't exactly the same as we've put in there, you're in trouble. + if ($smcFunc['db_num_rows']($request) != count($boards)) + fatal_lang_error('no_board'); + $smcFunc['db_free_result']($request); + + if (empty($_REQUEST['sa']) || $_REQUEST['sa'] == 'options') + { + if (count($polls) > 1) + { + $request = $smcFunc['db_query']('', ' + SELECT t.id_topic, t.id_poll, m.subject, p.question + FROM {db_prefix}polls AS p + INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll) + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE p.id_poll IN ({array_int:polls}) + LIMIT ' . count($polls), + array( + 'polls' => $polls, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['polls'][] = array( + 'id' => $row['id_poll'], + 'topic' => array( + 'id' => $row['id_topic'], + 'subject' => $row['subject'] + ), + 'question' => $row['question'], + 'selected' => $row['id_topic'] == $firstTopic + ); + $smcFunc['db_free_result']($request); + } + if (count($boards) > 1) + { + $request = $smcFunc['db_query']('', ' + SELECT id_board, name + FROM {db_prefix}boards + WHERE id_board IN ({array_int:boards}) + ORDER BY name + LIMIT ' . count($boards), + array( + 'boards' => $boards, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['boards'][] = array( + 'id' => $row['id_board'], + 'name' => $row['name'], + 'selected' => $row['id_board'] == $topic_data[$firstTopic]['board'] + ); + $smcFunc['db_free_result']($request); + } + + $context['topics'] = $topic_data; + foreach ($topic_data as $id => $topic) + $context['topics'][$id]['selected'] = $topic['id'] == $firstTopic; + + $context['page_title'] = $txt['merge']; + $context['sub_template'] = 'merge_extra_options'; + return; + } + + // Determine target board. + $target_board = count($boards) > 1 ? (int) $_REQUEST['board'] : $boards[0]; + if (!in_array($target_board, $boards)) + fatal_lang_error('no_board'); + + // Determine which poll will survive and which polls won't. + $target_poll = count($polls) > 1 ? (int) $_POST['poll'] : (count($polls) == 1 ? $polls[0] : 0); + if ($target_poll > 0 && !in_array($target_poll, $polls)) + fatal_lang_error('no_access', false); + $deleted_polls = empty($target_poll) ? $polls : array_diff($polls, array($target_poll)); + + // Determine the subject of the newly merged topic - was a custom subject specified? + if (empty($_POST['subject']) && isset($_POST['custom_subject']) && $_POST['custom_subject'] != '') + { + $target_subject = strtr($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => '')); + // Keep checking the length. + if ($smcFunc['strlen']($target_subject) > 100) + $target_subject = $smcFunc['substr']($target_subject, 0, 100); + + // Nothing left - odd but pick the first topics subject. + if ($target_subject == '') + $target_subject = $topic_data[$firstTopic]['subject']; + } + // A subject was selected from the list. + elseif (!empty($topic_data[(int) $_POST['subject']]['subject'])) + $target_subject = $topic_data[(int) $_POST['subject']]['subject']; + // Nothing worked? Just take the subject of the first message. + else + $target_subject = $topic_data[$firstTopic]['subject']; + + // Get the first and last message and the number of messages.... + $request = $smcFunc['db_query']('', ' + SELECT approved, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg, COUNT(*) AS message_count + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topics}) + GROUP BY approved + ORDER BY approved DESC', + array( + 'topics' => $topics, + ) + ); + $topic_approved = 1; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // If this is approved, or is fully unapproved. + if ($row['approved'] || !isset($first_msg)) + { + $first_msg = $row['first_msg']; + $last_msg = $row['last_msg']; + if ($row['approved']) + { + $num_replies = $row['message_count'] - 1; + $num_unapproved = 0; + } + else + { + $topic_approved = 0; + $num_replies = 0; + $num_unapproved = $row['message_count']; + } + } + else + { + // If this has a lower first_msg then the first post is not approved and hence the number of replies was wrong! + if ($first_msg > $row['first_msg']) + { + $first_msg = $row['first_msg']; + $num_replies++; + $topic_approved = 0; + } + $num_unapproved = $row['message_count']; + } + } + $smcFunc['db_free_result']($request); + + // Ensure we have a board stat for the target board. + if (!isset($boardTotals[$target_board])) + { + $boardTotals[$target_board] = array( + 'posts' => 0, + 'topics' => 0, + 'unapproved_posts' => 0, + 'unapproved_topics' => 0 + ); + } + + // Fix the topic count stuff depending on what the new one counts as. + if ($topic_approved) + $boardTotals[$target_board]['topics']--; + else + $boardTotals[$target_board]['unapproved_topics']--; + + $boardTotals[$target_board]['unapproved_posts'] -= $num_unapproved; + $boardTotals[$target_board]['posts'] -= $topic_approved ? $num_replies + 1 : $num_replies; + + // Get the member ID of the first and last message. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}messages + WHERE id_msg IN ({int:first_msg}, {int:last_msg}) + ORDER BY id_msg + LIMIT 2', + array( + 'first_msg' => $first_msg, + 'last_msg' => $last_msg, + ) + ); + list ($member_started) = $smcFunc['db_fetch_row']($request); + list ($member_updated) = $smcFunc['db_fetch_row']($request); + // First and last message are the same, so only row was returned. + if ($member_updated === NULL) + $member_updated = $member_started; + + $smcFunc['db_free_result']($request); + + // Assign the first topic ID to be the merged topic. + $id_topic = min($topics); + + // Delete the remaining topics. + $deleted_topics = array_diff($topics, array($id_topic)); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}topics + WHERE id_topic IN ({array_int:deleted_topics})', + array( + 'deleted_topics' => $deleted_topics, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_search_subjects + WHERE id_topic IN ({array_int:deleted_topics})', + array( + 'deleted_topics' => $deleted_topics, + ) + ); + + // Asssign the properties of the newly merged topic. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET + id_board = {int:id_board}, + id_member_started = {int:id_member_started}, + id_member_updated = {int:id_member_updated}, + id_first_msg = {int:id_first_msg}, + id_last_msg = {int:id_last_msg}, + id_poll = {int:id_poll}, + num_replies = {int:num_replies}, + unapproved_posts = {int:unapproved_posts}, + num_views = {int:num_views}, + is_sticky = {int:is_sticky}, + approved = {int:approved} + WHERE id_topic = {int:id_topic}', + array( + 'id_board' => $target_board, + 'is_sticky' => $is_sticky, + 'approved' => $topic_approved, + 'id_topic' => $id_topic, + 'id_member_started' => $member_started, + 'id_member_updated' => $member_updated, + 'id_first_msg' => $first_msg, + 'id_last_msg' => $last_msg, + 'id_poll' => $target_poll, + 'num_replies' => $num_replies, + 'unapproved_posts' => $num_unapproved, + 'num_views' => $num_views, + ) + ); + + // Grab the response prefix (like 'Re: ') in the default forum language. + if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix'))) + { + if ($language === $user_info['language']) + $context['response_prefix'] = $txt['response_prefix']; + else + { + loadLanguage('index', $language, false); + $context['response_prefix'] = $txt['response_prefix']; + loadLanguage('index'); + } + cache_put_data('response_prefix', $context['response_prefix'], 600); + } + + // Change the topic IDs of all messages that will be merged. Also adjust subjects if 'enforce subject' was checked. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET + id_topic = {int:id_topic}, + id_board = {int:target_board}' . (empty($_POST['enforce_subject']) ? '' : ', + subject = {string:subject}') . ' + WHERE id_topic IN ({array_int:topic_list})', + array( + 'topic_list' => $topics, + 'id_topic' => $id_topic, + 'target_board' => $target_board, + 'subject' => $context['response_prefix'] . $target_subject, + ) + ); + + // Any reported posts should reflect the new board. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_reported + SET + id_topic = {int:id_topic}, + id_board = {int:target_board} + WHERE id_topic IN ({array_int:topics_list})', + array( + 'topics_list' => $topics, + 'id_topic' => $id_topic, + 'target_board' => $target_board, + ) + ); + + // Change the subject of the first message... + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET subject = {string:target_subject} + WHERE id_msg = {int:first_msg}', + array( + 'first_msg' => $first_msg, + 'target_subject' => $target_subject, + ) + ); + + // Adjust all calendar events to point to the new topic. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}calendar + SET + id_topic = {int:id_topic}, + id_board = {int:target_board} + WHERE id_topic IN ({array_int:deleted_topics})', + array( + 'deleted_topics' => $deleted_topics, + 'id_topic' => $id_topic, + 'target_board' => $target_board, + ) + ); + + // Merge log topic entries. + $request = $smcFunc['db_query']('', ' + SELECT id_member, MIN(id_msg) AS new_id_msg + FROM {db_prefix}log_topics + WHERE id_topic IN ({array_int:topics}) + GROUP BY id_member', + array( + 'topics' => $topics, + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + { + $replaceEntries = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $replaceEntries[] = array($row['id_member'], $id_topic, $row['new_id_msg']); + + $smcFunc['db_insert']('replace', + '{db_prefix}log_topics', + array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int'), + $replaceEntries, + array('id_member', 'id_topic') + ); + unset($replaceEntries); + + // Get rid of the old log entries. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_topics + WHERE id_topic IN ({array_int:deleted_topics})', + array( + 'deleted_topics' => $deleted_topics, + ) + ); + } + $smcFunc['db_free_result']($request); + + // Merge topic notifications. + $notifications = isset($_POST['notifications']) && is_array($_POST['notifications']) ? array_intersect($topics, $_POST['notifications']) : array(); + if (!empty($notifications)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member, MAX(sent) AS sent + FROM {db_prefix}log_notify + WHERE id_topic IN ({array_int:topics_list}) + GROUP BY id_member', + array( + 'topics_list' => $notifications, + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + { + $replaceEntries = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $replaceEntries[] = array($row['id_member'], $id_topic, 0, $row['sent']); + + $smcFunc['db_insert']('replace', + '{db_prefix}log_notify', + array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'sent' => 'int'), + $replaceEntries, + array('id_member', 'id_topic', 'id_board') + ); + unset($replaceEntries); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_topics + WHERE id_topic IN ({array_int:deleted_topics})', + array( + 'deleted_topics' => $deleted_topics, + ) + ); + } + $smcFunc['db_free_result']($request); + } + + // Get rid of the redundant polls. + if (!empty($deleted_polls)) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}polls + WHERE id_poll IN ({array_int:deleted_polls})', + array( + 'deleted_polls' => $deleted_polls, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}poll_choices + WHERE id_poll IN ({array_int:deleted_polls})', + array( + 'deleted_polls' => $deleted_polls, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_polls + WHERE id_poll IN ({array_int:deleted_polls})', + array( + 'deleted_polls' => $deleted_polls, + ) + ); + } + + // Cycle through each board... + foreach ($boardTotals as $id_board => $stats) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET + num_topics = CASE WHEN {int:topics} > num_topics THEN 0 ELSE num_topics - {int:topics} END, + unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END, + num_posts = CASE WHEN {int:posts} > num_posts THEN 0 ELSE num_posts - {int:posts} END, + unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END + WHERE id_board = {int:id_board}', + array( + 'id_board' => $id_board, + 'topics' => $stats['topics'], + 'unapproved_topics' => $stats['unapproved_topics'], + 'posts' => $stats['posts'], + 'unapproved_posts' => $stats['unapproved_posts'], + ) + ); + } + + // Determine the board the final topic resides in + $request = $smcFunc['db_query']('', ' + SELECT id_board + FROM {db_prefix}topics + WHERE id_topic = {int:id_topic} + LIMIT 1', + array( + 'id_topic' => $id_topic, + ) + ); + list($id_board) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + require_once($sourcedir . '/Subs-Post.php'); + + // Update all the statistics. + updateStats('topic'); + updateStats('subject', $id_topic, $target_subject); + updateLastMessages($boards); + + logAction('merge', array('topic' => $id_topic, 'board' => $id_board)); + + // Notify people that these topics have been merged? + sendNotifications($id_topic, 'merge'); + + // Send them to the all done page. + redirectexit('action=mergetopics;sa=done;to=' . $id_topic . ';targetboard=' . $target_board); +} + +// Tell the user the move was done properly. +function MergeDone() +{ + global $txt, $context; + + // Make sure the template knows everything... + $context['target_board'] = (int) $_GET['targetboard']; + $context['target_topic'] = (int) $_GET['to']; + + $context['page_title'] = $txt['merge']; + $context['sub_template'] = 'merge_done'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Stats.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Stats.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,741 @@ + 1900 && $year < 2200 && $month >= 1 && $month <= 12) + $_SESSION['expanded_stats'][$year][] = $month; + } + elseif (!empty($_REQUEST['collapse'])) + { + $context['robot_no_index'] = true; + + $month = (int) substr($_REQUEST['collapse'], 4); + $year = (int) substr($_REQUEST['collapse'], 0, 4); + if (!empty($_SESSION['expanded_stats'][$year])) + $_SESSION['expanded_stats'][$year] = array_diff($_SESSION['expanded_stats'][$year], array($month)); + } + + // Handle the XMLHttpRequest. + if (isset($_REQUEST['xml'])) + { + // Collapsing stats only needs adjustments of the session variables. + if (!empty($_REQUEST['collapse'])) + obExit(false); + + $context['sub_template'] = 'stats'; + getDailyStats('YEAR(date) = {int:year} AND MONTH(date) = {int:month}', array('year' => $year, 'month' => $month)); + $context['yearly'][$year]['months'][$month]['date'] = array( + 'month' => sprintf('%02d', $month), + 'year' => $year, + ); + return; + } + + loadLanguage('Stats'); + loadTemplate('Stats'); + + // Build the link tree...... + $context['linktree'][] = array( + 'url' => $scripturl . '?action=stats', + 'name' => $txt['stats_center'] + ); + $context['page_title'] = $context['forum_name'] . ' - ' . $txt['stats_center']; + + $context['show_member_list'] = allowedTo('view_mlist'); + + // Get averages... + $result = $smcFunc['db_query']('', ' + SELECT + SUM(posts) AS posts, SUM(topics) AS topics, SUM(registers) AS registers, + SUM(most_on) AS most_on, MIN(date) AS date, SUM(hits) AS hits + FROM {db_prefix}log_activity', + array( + ) + ); + $row = $smcFunc['db_fetch_assoc']($result); + $smcFunc['db_free_result']($result); + + // This would be the amount of time the forum has been up... in days... + $total_days_up = ceil((time() - strtotime($row['date'])) / (60 * 60 * 24)); + + $context['average_posts'] = comma_format(round($row['posts'] / $total_days_up, 2)); + $context['average_topics'] = comma_format(round($row['topics'] / $total_days_up, 2)); + $context['average_members'] = comma_format(round($row['registers'] / $total_days_up, 2)); + $context['average_online'] = comma_format(round($row['most_on'] / $total_days_up, 2)); + $context['average_hits'] = comma_format(round($row['hits'] / $total_days_up, 2)); + + $context['num_hits'] = comma_format($row['hits'], 0); + + // How many users are online now. + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_online', + array( + ) + ); + list ($context['users_online']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Statistics such as number of boards, categories, etc. + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}boards AS b + WHERE b.redirect = {string:blank_redirect}', + array( + 'blank_redirect' => '', + ) + ); + list ($context['num_boards']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}categories AS c', + array( + ) + ); + list ($context['num_categories']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Format the numbers nicely. + $context['users_online'] = comma_format($context['users_online']); + $context['num_boards'] = comma_format($context['num_boards']); + $context['num_categories'] = comma_format($context['num_categories']); + + $context['num_members'] = comma_format($modSettings['totalMembers']); + $context['num_posts'] = comma_format($modSettings['totalMessages']); + $context['num_topics'] = comma_format($modSettings['totalTopics']); + $context['most_members_online'] = array( + 'number' => comma_format($modSettings['mostOnline']), + 'date' => timeformat($modSettings['mostDate']) + ); + $context['latest_member'] = &$context['common_stats']['latest_member']; + + // Male vs. female ratio - let's calculate this only every four minutes. + if (($context['gender'] = cache_get_data('stats_gender', 240)) == null) + { + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) AS total_members, gender + FROM {db_prefix}members + GROUP BY gender', + array( + ) + ); + $context['gender'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Assuming we're telling... male or female? + if (!empty($row['gender'])) + $context['gender'][$row['gender'] == 2 ? 'females' : 'males'] = $row['total_members']; + } + $smcFunc['db_free_result']($result); + + // Set these two zero if the didn't get set at all. + if (empty($context['gender']['males'])) + $context['gender']['males'] = 0; + if (empty($context['gender']['females'])) + $context['gender']['females'] = 0; + + // Try and come up with some "sensible" default states in case of a non-mixed board. + if ($context['gender']['males'] == $context['gender']['females']) + $context['gender']['ratio'] = '1:1'; + elseif ($context['gender']['males'] == 0) + $context['gender']['ratio'] = '0:1'; + elseif ($context['gender']['females'] == 0) + $context['gender']['ratio'] = '1:0'; + elseif ($context['gender']['males'] > $context['gender']['females']) + $context['gender']['ratio'] = round($context['gender']['males'] / $context['gender']['females'], 1) . ':1'; + elseif ($context['gender']['females'] > $context['gender']['males']) + $context['gender']['ratio'] = '1:' . round($context['gender']['females'] / $context['gender']['males'], 1); + + cache_put_data('stats_gender', $context['gender'], 240); + } + + $date = strftime('%Y-%m-%d', forum_time(false)); + + // Members online so far today. + $result = $smcFunc['db_query']('', ' + SELECT most_on + FROM {db_prefix}log_activity + WHERE date = {date:today_date} + LIMIT 1', + array( + 'today_date' => $date, + ) + ); + list ($context['online_today']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + $context['online_today'] = comma_format((int) $context['online_today']); + + // Poster top 10. + $members_result = $smcFunc['db_query']('', ' + SELECT id_member, real_name, posts + FROM {db_prefix}members + WHERE posts > {int:no_posts} + ORDER BY posts DESC + LIMIT 10', + array( + 'no_posts' => 0, + ) + ); + $context['top_posters'] = array(); + $max_num_posts = 1; + while ($row_members = $smcFunc['db_fetch_assoc']($members_result)) + { + $context['top_posters'][] = array( + 'name' => $row_members['real_name'], + 'id' => $row_members['id_member'], + 'num_posts' => $row_members['posts'], + 'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'], + 'link' => '' . $row_members['real_name'] . '' + ); + + if ($max_num_posts < $row_members['posts']) + $max_num_posts = $row_members['posts']; + } + $smcFunc['db_free_result']($members_result); + + foreach ($context['top_posters'] as $i => $poster) + { + $context['top_posters'][$i]['post_percent'] = round(($poster['num_posts'] * 100) / $max_num_posts); + $context['top_posters'][$i]['num_posts'] = comma_format($context['top_posters'][$i]['num_posts']); + } + + // Board top 10. + $boards_result = $smcFunc['db_query']('', ' + SELECT id_board, name, num_posts + FROM {db_prefix}boards AS b + WHERE {query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_board}' : '') . ' + AND b.redirect = {string:blank_redirect} + ORDER BY num_posts DESC + LIMIT 10', + array( + 'recycle_board' => $modSettings['recycle_board'], + 'blank_redirect' => '', + ) + ); + $context['top_boards'] = array(); + $max_num_posts = 1; + while ($row_board = $smcFunc['db_fetch_assoc']($boards_result)) + { + $context['top_boards'][] = array( + 'id' => $row_board['id_board'], + 'name' => $row_board['name'], + 'num_posts' => $row_board['num_posts'], + 'href' => $scripturl . '?board=' . $row_board['id_board'] . '.0', + 'link' => '' . $row_board['name'] . '' + ); + + if ($max_num_posts < $row_board['num_posts']) + $max_num_posts = $row_board['num_posts']; + } + $smcFunc['db_free_result']($boards_result); + + foreach ($context['top_boards'] as $i => $board) + { + $context['top_boards'][$i]['post_percent'] = round(($board['num_posts'] * 100) / $max_num_posts); + $context['top_boards'][$i]['num_posts'] = comma_format($context['top_boards'][$i]['num_posts']); + } + + // Are you on a larger forum? If so, let's try to limit the number of topics we search through. + if ($modSettings['totalMessages'] > 100000) + { + $request = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}topics + WHERE num_replies != {int:no_replies}' . ($modSettings['postmod_active'] ? ' + AND approved = {int:is_approved}' : '') . ' + ORDER BY num_replies DESC + LIMIT 100', + array( + 'no_replies' => 0, + 'is_approved' => 1, + ) + ); + $topic_ids = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topic_ids[] = $row['id_topic']; + $smcFunc['db_free_result']($request); + } + else + $topic_ids = array(); + + // Topic replies top 10. + $topic_reply_result = $smcFunc['db_query']('', ' + SELECT m.subject, t.num_replies, t.id_board, t.id_topic, b.name + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_board}' : '') . ') + WHERE {query_see_board}' . (!empty($topic_ids) ? ' + AND t.id_topic IN ({array_int:topic_list})' : ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : '')) . ' + ORDER BY t.num_replies DESC + LIMIT 10', + array( + 'topic_list' => $topic_ids, + 'recycle_board' => $modSettings['recycle_board'], + 'is_approved' => 1, + ) + ); + $context['top_topics_replies'] = array(); + $max_num_replies = 1; + while ($row_topic_reply = $smcFunc['db_fetch_assoc']($topic_reply_result)) + { + censorText($row_topic_reply['subject']); + + $context['top_topics_replies'][] = array( + 'id' => $row_topic_reply['id_topic'], + 'board' => array( + 'id' => $row_topic_reply['id_board'], + 'name' => $row_topic_reply['name'], + 'href' => $scripturl . '?board=' . $row_topic_reply['id_board'] . '.0', + 'link' => '' . $row_topic_reply['name'] . '' + ), + 'subject' => $row_topic_reply['subject'], + 'num_replies' => $row_topic_reply['num_replies'], + 'href' => $scripturl . '?topic=' . $row_topic_reply['id_topic'] . '.0', + 'link' => '' . $row_topic_reply['subject'] . '' + ); + + if ($max_num_replies < $row_topic_reply['num_replies']) + $max_num_replies = $row_topic_reply['num_replies']; + } + $smcFunc['db_free_result']($topic_reply_result); + + foreach ($context['top_topics_replies'] as $i => $topic) + { + $context['top_topics_replies'][$i]['post_percent'] = round(($topic['num_replies'] * 100) / $max_num_replies); + $context['top_topics_replies'][$i]['num_replies'] = comma_format($context['top_topics_replies'][$i]['num_replies']); + } + + // Large forums may need a bit more prodding... + if ($modSettings['totalMessages'] > 100000) + { + $request = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}topics + WHERE num_views != {int:no_views} + ORDER BY num_views DESC + LIMIT 100', + array( + 'no_views' => 0, + ) + ); + $topic_ids = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topic_ids[] = $row['id_topic']; + $smcFunc['db_free_result']($request); + } + else + $topic_ids = array(); + + // Topic views top 10. + $topic_view_result = $smcFunc['db_query']('', ' + SELECT m.subject, t.num_views, t.id_board, t.id_topic, b.name + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_board}' : '') . ') + WHERE {query_see_board}' . (!empty($topic_ids) ? ' + AND t.id_topic IN ({array_int:topic_list})' : ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : '')) . ' + ORDER BY t.num_views DESC + LIMIT 10', + array( + 'topic_list' => $topic_ids, + 'recycle_board' => $modSettings['recycle_board'], + 'is_approved' => 1, + ) + ); + $context['top_topics_views'] = array(); + $max_num_views = 1; + while ($row_topic_views = $smcFunc['db_fetch_assoc']($topic_view_result)) + { + censorText($row_topic_views['subject']); + + $context['top_topics_views'][] = array( + 'id' => $row_topic_views['id_topic'], + 'board' => array( + 'id' => $row_topic_views['id_board'], + 'name' => $row_topic_views['name'], + 'href' => $scripturl . '?board=' . $row_topic_views['id_board'] . '.0', + 'link' => '' . $row_topic_views['name'] . '' + ), + 'subject' => $row_topic_views['subject'], + 'num_views' => $row_topic_views['num_views'], + 'href' => $scripturl . '?topic=' . $row_topic_views['id_topic'] . '.0', + 'link' => '' . $row_topic_views['subject'] . '' + ); + + if ($max_num_views < $row_topic_views['num_views']) + $max_num_views = $row_topic_views['num_views']; + } + $smcFunc['db_free_result']($topic_view_result); + + foreach ($context['top_topics_views'] as $i => $topic) + { + $context['top_topics_views'][$i]['post_percent'] = round(($topic['num_views'] * 100) / $max_num_views); + $context['top_topics_views'][$i]['num_views'] = comma_format($context['top_topics_views'][$i]['num_views']); + } + + // Try to cache this when possible, because it's a little unavoidably slow. + if (($members = cache_get_data('stats_top_starters', 360)) == null) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member_started, COUNT(*) AS hits + FROM {db_prefix}topics' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + WHERE id_board != {int:recycle_board}' : '') . ' + GROUP BY id_member_started + ORDER BY hits DESC + LIMIT 20', + array( + 'recycle_board' => $modSettings['recycle_board'], + ) + ); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[$row['id_member_started']] = $row['hits']; + $smcFunc['db_free_result']($request); + + cache_put_data('stats_top_starters', $members, 360); + } + + if (empty($members)) + $members = array(0 => 0); + + // Topic poster top 10. + $members_result = $smcFunc['db_query']('top_topic_starters', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE id_member IN ({array_int:member_list}) + ORDER BY FIND_IN_SET(id_member, {string:top_topic_posters}) + LIMIT 10', + array( + 'member_list' => array_keys($members), + 'top_topic_posters' => implode(',', array_keys($members)), + ) + ); + $context['top_starters'] = array(); + $max_num_topics = 1; + while ($row_members = $smcFunc['db_fetch_assoc']($members_result)) + { + $context['top_starters'][] = array( + 'name' => $row_members['real_name'], + 'id' => $row_members['id_member'], + 'num_topics' => $members[$row_members['id_member']], + 'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'], + 'link' => '' . $row_members['real_name'] . '' + ); + + if ($max_num_topics < $members[$row_members['id_member']]) + $max_num_topics = $members[$row_members['id_member']]; + } + $smcFunc['db_free_result']($members_result); + + foreach ($context['top_starters'] as $i => $topic) + { + $context['top_starters'][$i]['post_percent'] = round(($topic['num_topics'] * 100) / $max_num_topics); + $context['top_starters'][$i]['num_topics'] = comma_format($context['top_starters'][$i]['num_topics']); + } + + // Time online top 10. + $temp = cache_get_data('stats_total_time_members', 600); + $members_result = $smcFunc['db_query']('', ' + SELECT id_member, real_name, total_time_logged_in + FROM {db_prefix}members' . (!empty($temp) ? ' + WHERE id_member IN ({array_int:member_list_cached})' : '') . ' + ORDER BY total_time_logged_in DESC + LIMIT 20', + array( + 'member_list_cached' => $temp, + ) + ); + $context['top_time_online'] = array(); + $temp2 = array(); + $max_time_online = 1; + while ($row_members = $smcFunc['db_fetch_assoc']($members_result)) + { + $temp2[] = (int) $row_members['id_member']; + if (count($context['top_time_online']) >= 10) + continue; + + // Figure out the days, hours and minutes. + $timeDays = floor($row_members['total_time_logged_in'] / 86400); + $timeHours = floor(($row_members['total_time_logged_in'] % 86400) / 3600); + + // Figure out which things to show... (days, hours, minutes, etc.) + $timelogged = ''; + if ($timeDays > 0) + $timelogged .= $timeDays . $txt['totalTimeLogged5']; + if ($timeHours > 0) + $timelogged .= $timeHours . $txt['totalTimeLogged6']; + $timelogged .= floor(($row_members['total_time_logged_in'] % 3600) / 60) . $txt['totalTimeLogged7']; + + $context['top_time_online'][] = array( + 'id' => $row_members['id_member'], + 'name' => $row_members['real_name'], + 'time_online' => $timelogged, + 'seconds_online' => $row_members['total_time_logged_in'], + 'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'], + 'link' => '' . $row_members['real_name'] . '' + ); + + if ($max_time_online < $row_members['total_time_logged_in']) + $max_time_online = $row_members['total_time_logged_in']; + } + $smcFunc['db_free_result']($members_result); + + foreach ($context['top_time_online'] as $i => $member) + $context['top_time_online'][$i]['time_percent'] = round(($member['seconds_online'] * 100) / $max_time_online); + + // Cache the ones we found for a bit, just so we don't have to look again. + if ($temp !== $temp2) + cache_put_data('stats_total_time_members', $temp2, 480); + + // Activity by month. + $months_result = $smcFunc['db_query']('', ' + SELECT + YEAR(date) AS stats_year, MONTH(date) AS stats_month, SUM(hits) AS hits, SUM(registers) AS registers, SUM(topics) AS topics, SUM(posts) AS posts, MAX(most_on) AS most_on, COUNT(*) AS num_days + FROM {db_prefix}log_activity + GROUP BY stats_year, stats_month', + array() + ); + + $context['yearly'] = array(); + while ($row_months = $smcFunc['db_fetch_assoc']($months_result)) + { + $ID_MONTH = $row_months['stats_year'] . sprintf('%02d', $row_months['stats_month']); + $expanded = !empty($_SESSION['expanded_stats'][$row_months['stats_year']]) && in_array($row_months['stats_month'], $_SESSION['expanded_stats'][$row_months['stats_year']]); + + if (!isset($context['yearly'][$row_months['stats_year']])) + $context['yearly'][$row_months['stats_year']] = array( + 'year' => $row_months['stats_year'], + 'new_topics' => 0, + 'new_posts' => 0, + 'new_members' => 0, + 'most_members_online' => 0, + 'hits' => 0, + 'num_months' => 0, + 'months' => array(), + 'expanded' => false, + 'current_year' => $row_months['stats_year'] == date('Y'), + ); + + $context['yearly'][$row_months['stats_year']]['months'][(int) $row_months['stats_month']] = array( + 'id' => $ID_MONTH, + 'date' => array( + 'month' => sprintf('%02d', $row_months['stats_month']), + 'year' => $row_months['stats_year'] + ), + 'href' => $scripturl . '?action=stats;' . ($expanded ? 'collapse' : 'expand') . '=' . $ID_MONTH . '#m' . $ID_MONTH, + 'link' => '' . $txt['months'][(int) $row_months['stats_month']] . ' ' . $row_months['stats_year'] . '', + 'month' => $txt['months'][(int) $row_months['stats_month']], + 'year' => $row_months['stats_year'], + 'new_topics' => comma_format($row_months['topics']), + 'new_posts' => comma_format($row_months['posts']), + 'new_members' => comma_format($row_months['registers']), + 'most_members_online' => comma_format($row_months['most_on']), + 'hits' => comma_format($row_months['hits']), + 'num_days' => $row_months['num_days'], + 'days' => array(), + 'expanded' => $expanded + ); + + $context['yearly'][$row_months['stats_year']]['new_topics'] += $row_months['topics']; + $context['yearly'][$row_months['stats_year']]['new_posts'] += $row_months['posts']; + $context['yearly'][$row_months['stats_year']]['new_members'] += $row_months['registers']; + $context['yearly'][$row_months['stats_year']]['hits'] += $row_months['hits']; + $context['yearly'][$row_months['stats_year']]['num_months']++; + $context['yearly'][$row_months['stats_year']]['expanded'] |= $expanded; + $context['yearly'][$row_months['stats_year']]['most_members_online'] = max($context['yearly'][$row_months['stats_year']]['most_members_online'], $row_months['most_on']); + } + + krsort($context['yearly']); + + $context['collapsed_years'] = array(); + foreach ($context['yearly'] as $year => $data) + { + // This gets rid of the filesort on the query ;). + krsort($context['yearly'][$year]['months']); + + $context['yearly'][$year]['new_topics'] = comma_format($data['new_topics']); + $context['yearly'][$year]['new_posts'] = comma_format($data['new_posts']); + $context['yearly'][$year]['new_members'] = comma_format($data['new_members']); + $context['yearly'][$year]['most_members_online'] = comma_format($data['most_members_online']); + $context['yearly'][$year]['hits'] = comma_format($data['hits']); + + // Keep a list of collapsed years. + if (!$data['expanded'] && !$data['current_year']) + $context['collapsed_years'][] = $year; + } + + if (empty($_SESSION['expanded_stats'])) + return; + + $condition_text = array(); + $condition_params = array(); + foreach ($_SESSION['expanded_stats'] as $year => $months) + if (!empty($months)) + { + $condition_text[] = 'YEAR(date) = {int:year_' . $year . '} AND MONTH(date) IN ({array_int:months_' . $year . '})'; + $condition_params['year_' . $year] = $year; + $condition_params['months_' . $year] = $months; + } + + // No daily stats to even look at? + if (empty($condition_text)) + return; + + getDailyStats(implode(' OR ', $condition_text), $condition_params); +} + +function getDailyStats($condition_string, $condition_parameters = array()) +{ + global $context, $smcFunc; + + // Activity by day. + $days_result = $smcFunc['db_query']('', ' + SELECT YEAR(date) AS stats_year, MONTH(date) AS stats_month, DAYOFMONTH(date) AS stats_day, topics, posts, registers, most_on, hits + FROM {db_prefix}log_activity + WHERE ' . $condition_string . ' + ORDER BY stats_day ASC', + $condition_parameters + ); + while ($row_days = $smcFunc['db_fetch_assoc']($days_result)) + $context['yearly'][$row_days['stats_year']]['months'][(int) $row_days['stats_month']]['days'][] = array( + 'day' => sprintf('%02d', $row_days['stats_day']), + 'month' => sprintf('%02d', $row_days['stats_month']), + 'year' => $row_days['stats_year'], + 'new_topics' => comma_format($row_days['topics']), + 'new_posts' => comma_format($row_days['posts']), + 'new_members' => comma_format($row_days['registers']), + 'most_members_online' => comma_format($row_days['most_on']), + 'hits' => comma_format($row_days['hits']) + ); + $smcFunc['db_free_result']($days_result); +} + +// This is the function which returns stats to simplemachines.org IF enabled! +// See http://www.simplemachines.org/about/stats.php for more info. +function SMStats() +{ + global $modSettings, $user_info, $forum_version, $sourcedir; + + // First, is it disabled? + if (empty($modSettings['allow_sm_stats'])) + die(); + + // Are we saying who we are, and are we right? (OR an admin) + if (!$user_info['is_admin'] && (!isset($_GET['sid']) || $_GET['sid'] != $modSettings['allow_sm_stats'])) + die(); + + // Verify the referer... + if (!$user_info['is_admin'] && (!isset($_SERVER['HTTP_REFERER']) || md5($_SERVER['HTTP_REFERER']) != '746cb59a1a0d5cf4bd240e5a67c73085')) + die(); + + // Get some server versions. + require_once($sourcedir . '/Subs-Admin.php'); + $checkFor = array( + 'php', + 'db_server', + ); + $serverVersions = getServerVersions($checkFor); + + // Get the actual stats. + $stats_to_send = array( + 'UID' => $modSettings['allow_sm_stats'], + 'time_added' => time(), + 'members' => $modSettings['totalMembers'], + 'messages' => $modSettings['totalMessages'], + 'topics' => $modSettings['totalTopics'], + 'boards' => 0, + 'php_version' => $serverVersions['php']['version'], + 'database_type' => strtolower($serverVersions['db_server']['title']), + 'database_version' => $serverVersions['db_server']['version'], + 'smf_version' => $forum_version, + 'smfd_version' => $modSettings['smfVersion'], + ); + + // Encode all the data, for security. + foreach ($stats_to_send as $k => $v) + $stats_to_send[$k] = urlencode($k) . '=' . urlencode($v); + + // Turn this into the query string! + $stats_to_send = implode('&', $stats_to_send); + + // If we're an admin, just plonk them out. + if ($user_info['is_admin']) + echo $stats_to_send; + else + { + // Connect to the collection script. + $fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr); + if ($fp) + { + $length = strlen($stats_to_send); + + $out = 'POST /smf/stats/collect_stats.php HTTP/1.1' . "\r\n"; + $out .= 'Host: www.simplemachines.org' . "\r\n"; + $out .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n"; + $out .= 'Content-Length: ' . $length . "\r\n\r\n"; + $out .= $stats_to_send . "\r\n"; + $out .= 'Connection: Close' . "\r\n\r\n"; + fwrite($fp, $out); + fclose($fp); + } + } + + // Die. + die('OK'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Admin.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Admin.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,550 @@ + $txt['support_versions_gd'], 'version' => $temp['GD Version']); + } + + // Now lets check for the Database. + if (in_array('db_server', $checkFor)) + { + db_extend(); + if (!isset($db_connection) || $db_connection === false) + trigger_error('getServerVersions(): you need to be connected to the database in order to get its server version', E_USER_NOTICE); + else + { + $versions['db_server'] = array('title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), 'version' => ''); + $versions['db_server']['version'] = $smcFunc['db_get_version'](); + } + } + + // If we're using memcache we need the server info. + if (empty($memcached) && function_exists('memcache_get') && isset($modSettings['cache_memcached']) && trim($modSettings['cache_memcached']) != '') + get_memcached_server(); + + // Check to see if we have any accelerators installed... + if (in_array('mmcache', $checkFor) && defined('MMCACHE_VERSION')) + $versions['mmcache'] = array('title' => 'Turck MMCache', 'version' => MMCACHE_VERSION); + if (in_array('eaccelerator', $checkFor) && defined('EACCELERATOR_VERSION')) + $versions['eaccelerator'] = array('title' => 'eAccelerator', 'version' => EACCELERATOR_VERSION); + if (in_array('phpa', $checkFor) && isset($_PHPA)) + $versions['phpa'] = array('title' => 'ionCube PHP-Accelerator', 'version' => $_PHPA['VERSION']); + if (in_array('apc', $checkFor) && extension_loaded('apc')) + $versions['apc'] = array('title' => 'Alternative PHP Cache', 'version' => phpversion('apc')); + if (in_array('memcache', $checkFor) && function_exists('memcache_set')) + $versions['memcache'] = array('title' => 'Memcached', 'version' => empty($memcached) ? '???' : memcache_get_version($memcached)); + if (in_array('xcache', $checkFor) && function_exists('xcache_set')) + $versions['xcache'] = array('title' => 'XCache', 'version' => XCACHE_VERSION); + if (in_array('php', $checkFor)) + $versions['php'] = array('title' => 'PHP', 'version' => PHP_VERSION); + + if (in_array('server', $checkFor)) + $versions['server'] = array('title' => $txt['support_versions_server'], 'version' => $_SERVER['SERVER_SOFTWARE']); + + return $versions; +} + +// Search through source, theme and language files to determine their version. +function getFileVersions(&$versionOptions) +{ + global $boarddir, $sourcedir, $settings; + + // Default place to find the languages would be the default theme dir. + $lang_dir = $settings['default_theme_dir'] . '/languages'; + + $version_info = array( + 'file_versions' => array(), + 'default_template_versions' => array(), + 'template_versions' => array(), + 'default_language_versions' => array(), + ); + + // Find the version in SSI.php's file header. + if (!empty($versionOptions['include_ssi']) && file_exists($boarddir . '/SSI.php')) + { + $fp = fopen($boarddir . '/SSI.php', 'rb'); + $header = fread($fp, 4096); + fclose($fp); + + // The comment looks rougly like... that. + if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) + $version_info['file_versions']['SSI.php'] = $match[1]; + // Not found! This is bad. + else + $version_info['file_versions']['SSI.php'] = '??'; + } + + // Do the paid subscriptions handler? + if (!empty($versionOptions['include_subscriptions']) && file_exists($boarddir . '/subscriptions.php')) + { + $fp = fopen($boarddir . '/subscriptions.php', 'rb'); + $header = fread($fp, 4096); + fclose($fp); + + // Found it? + if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) + $version_info['file_versions']['subscriptions.php'] = $match[1]; + // If we haven't how do we all get paid? + else + $version_info['file_versions']['subscriptions.php'] = '??'; + } + + // Load all the files in the Sources directory, except this file and the redirect. + $sources_dir = dir($sourcedir); + while ($entry = $sources_dir->read()) + { + if (substr($entry, -4) === '.php' && !is_dir($sourcedir . '/' . $entry) && $entry !== 'index.php') + { + // Read the first 4k from the file.... enough for the header. + $fp = fopen($sourcedir . '/' . $entry, 'rb'); + $header = fread($fp, 4096); + fclose($fp); + + // Look for the version comment in the file header. + if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) + $version_info['file_versions'][$entry] = $match[1]; + // It wasn't found, but the file was... show a '??'. + else + $version_info['file_versions'][$entry] = '??'; + } + } + $sources_dir->close(); + + // Load all the files in the default template directory - and the current theme if applicable. + $directories = array('default_template_versions' => $settings['default_theme_dir']); + if ($settings['theme_id'] != 1) + $directories += array('template_versions' => $settings['theme_dir']); + + foreach ($directories as $type => $dirname) + { + $this_dir = dir($dirname); + while ($entry = $this_dir->read()) + { + if (substr($entry, -12) == 'template.php' && !is_dir($dirname . '/' . $entry)) + { + // Read the first 768 bytes from the file.... enough for the header. + $fp = fopen($dirname . '/' . $entry, 'rb'); + $header = fread($fp, 768); + fclose($fp); + + // Look for the version comment in the file header. + if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) + $version_info[$type][$entry] = $match[1]; + // It wasn't found, but the file was... show a '??'. + else + $version_info[$type][$entry] = '??'; + } + } + $this_dir->close(); + } + + // Load up all the files in the default language directory and sort by language. + $this_dir = dir($lang_dir); + while ($entry = $this_dir->read()) + { + if (substr($entry, -4) == '.php' && $entry != 'index.php' && !is_dir($lang_dir . '/' . $entry)) + { + // Read the first 768 bytes from the file.... enough for the header. + $fp = fopen($lang_dir . '/' . $entry, 'rb'); + $header = fread($fp, 768); + fclose($fp); + + // Split the file name off into useful bits. + list ($name, $language) = explode('.', $entry); + + // Look for the version comment in the file header. + if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1) + $version_info['default_language_versions'][$language][$name] = $match[1]; + // It wasn't found, but the file was... show a '??'. + else + $version_info['default_language_versions'][$language][$name] = '??'; + } + } + $this_dir->close(); + + // Sort the file versions by filename. + if (!empty($versionOptions['sort_results'])) + { + ksort($version_info['file_versions']); + ksort($version_info['default_template_versions']); + ksort($version_info['template_versions']); + ksort($version_info['default_language_versions']); + + // For languages sort each language too. + foreach ($version_info['default_language_versions'] as $language => $dummy) + ksort($version_info['default_language_versions'][$language]); + } + return $version_info; +} + +// Update the Settings.php file. +function updateSettingsFile($config_vars) +{ + global $boarddir, $cachedir; + + // When is Settings.php last changed? + $last_settings_change = filemtime($boarddir . '/Settings.php'); + + // Load the file. Break it up based on \r or \n, and then clean out extra characters. + $settingsArray = trim(file_get_contents($boarddir . '/Settings.php')); + if (strpos($settingsArray, "\n") !== false) + $settingsArray = explode("\n", $settingsArray); + elseif (strpos($settingsArray, "\r") !== false) + $settingsArray = explode("\r", $settingsArray); + else + return; + + // Make sure we got a good file. + if (count($config_vars) == 1 && isset($config_vars['db_last_error'])) + { + $temp = trim(implode("\n", $settingsArray)); + if (substr($temp, 0, 5) != '') + return; + if (strpos($temp, 'sourcedir') === false || strpos($temp, 'boarddir') === false || strpos($temp, 'cookiename') === false) + return; + } + + // Presumably, the file has to have stuff in it for this function to be called :P. + if (count($settingsArray) < 10) + return; + + foreach ($settingsArray as $k => $dummy) + $settingsArray[$k] = strtr($dummy, array("\r" => '')) . "\n"; + + for ($i = 0, $n = count($settingsArray); $i < $n; $i++) + { + // Don't trim or bother with it if it's not a variable. + if (substr($settingsArray[$i], 0, 1) != '$') + continue; + + $settingsArray[$i] = trim($settingsArray[$i]) . "\n"; + + // Look through the variables to set.... + foreach ($config_vars as $var => $val) + { + if (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0) + { + $comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#'); + $settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment == '' ? '' : "\t\t" . rtrim($comment)) . "\n"; + + // This one's been 'used', so to speak. + unset($config_vars[$var]); + } + } + + if (substr(trim($settingsArray[$i]), 0, 2) == '?' . '>') + $end = $i; + } + + // This should never happen, but apparently it is happening. + if (empty($end) || $end < 10) + $end = count($settingsArray) - 1; + + // Still more? Add them at the end. + if (!empty($config_vars)) + { + if (trim($settingsArray[$end]) == '?' . '>') + $settingsArray[$end++] = ''; + else + $end++; + + foreach ($config_vars as $var => $val) + $settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n"; + $settingsArray[$end] = '?' . '>'; + } + else + $settingsArray[$end] = trim($settingsArray[$end]); + + // Sanity error checking: the file needs to be at least 12 lines. + if (count($settingsArray) < 12) + return; + + // Try to avoid a few pitfalls: + // like a possible race condition, + // or a failure to write at low diskspace + + // Check before you act: if cache is enabled, we can do a simple test + // Can we even write things on this filesystem? + if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache')) + $cachedir = $boarddir . '/cache'; + $test_fp = @fopen($cachedir . '/settings_update.tmp', "w+"); + if ($test_fp) + { + fclose($test_fp); + + $test_fp = @fopen($cachedir . '/settings_update.tmp', 'r+'); + $written_bytes = fwrite($test_fp, "test"); + fclose($test_fp); + @unlink($cachedir . '/settings_update.tmp'); + + if ($written_bytes !== strlen("test")) + { + // Oops. Low disk space, perhaps. Don't mess with Settings.php then. + // No means no. :P + return; + } + } + + // Protect me from what I want! :P + clearstatcache(); + if (filemtime($boarddir . '/Settings.php') === $last_settings_change) + { + // You asked for it... + // Blank out the file - done to fix a oddity with some servers. + $fp = @fopen($boarddir . '/Settings.php', 'w'); + + // Is it even writable, though? + if ($fp) + { + fclose($fp); + + $fp = fopen($boarddir . '/Settings.php', 'r+'); + foreach ($settingsArray as $line) + fwrite($fp, strtr($line, "\r", '')); + fclose($fp); + } + } +} + +function updateAdminPreferences() +{ + global $options, $context, $smcFunc, $settings, $user_info; + + // This must exist! + if (!isset($context['admin_preferences'])) + return false; + + // This is what we'll be saving. + $options['admin_preferences'] = serialize($context['admin_preferences']); + + // Just check we haven't ended up with something theme exclusive somehow. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE id_theme != {int:default_theme} + AND variable = {string:admin_preferences}', + array( + 'default_theme' => 1, + 'admin_preferences' => 'admin_preferences', + ) + ); + + // Update the themes table. + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + array($user_info['id'], 1, 'admin_preferences', $options['admin_preferences']), + array('id_member', 'id_theme', 'variable') + ); + + // Make sure we invalidate any cache. + cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 0); +} + +// Send all the administrators a lovely email. +function emailAdmins($template, $replacements = array(), $additional_recipients = array()) +{ + global $smcFunc, $sourcedir, $language, $modSettings; + + // We certainly want this. + require_once($sourcedir . '/Subs-Post.php'); + + // Load all groups which are effectively admins. + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}permissions + WHERE permission = {string:admin_forum} + AND add_deny = {int:add_deny} + AND id_group != {int:id_group}', + array( + 'add_deny' => 1, + 'id_group' => 0, + 'admin_forum' => 'admin_forum', + ) + ); + $groups = array(1); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $groups[] = $row['id_group']; + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT id_member, member_name, real_name, lngfile, email_address + FROM {db_prefix}members + WHERE (id_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:group_array_implode}, additional_groups) != 0) + AND notify_types != {int:notify_types} + ORDER BY lngfile', + array( + 'group_list' => $groups, + 'notify_types' => 4, + 'group_array_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $groups), + ) + ); + $emails_sent = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Stick their particulars in the replacement data. + $replacements['IDMEMBER'] = $row['id_member']; + $replacements['REALNAME'] = $row['member_name']; + $replacements['USERNAME'] = $row['real_name']; + + // Load the data from the template. + $emaildata = loadEmailTemplate($template, $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); + + // Then send the actual email. + sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); + + // Track who we emailed so we don't do it twice. + $emails_sent[] = $row['email_address']; + } + $smcFunc['db_free_result']($request); + + // Any additional users we must email this to? + if (!empty($additional_recipients)) + foreach ($additional_recipients as $recipient) + { + if (in_array($recipient['email'], $emails_sent)) + continue; + + $replacements['IDMEMBER'] = $recipient['id']; + $replacements['REALNAME'] = $recipient['name']; + $replacements['USERNAME'] = $recipient['name']; + + // Load the template again. + $emaildata = loadEmailTemplate($template, $replacements, empty($recipient['lang']) || empty($modSettings['userLanguage']) ? $language : $recipient['lang']); + + // Send off the email. + sendmail($recipient['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); + } +} + +function updateLastDatabaseError() +{ + global $boarddir; + + // Find out this way if we can even write things on this filesystem. + // In addition, store things first in the backup file + + $last_settings_change = @filemtime($boarddir . '/Settings.php'); + + // Make sure the backup file is there... + $file = $boarddir . '/Settings_bak.php'; + if ((!file_exists($file) || filesize($file) == 0) && !copy($boarddir . '/Settings.php', $file)) + return false; + + // ...and writable! + if (!is_writable($file)) + { + chmod($file, 0755); + if (!is_writable($file)) + { + chmod($file, 0775); + if (!is_writable($file)) + { + chmod($file, 0777); + if (!is_writable($file)) + return false; + } + } + } + + // Put the new timestamp. + $data = file_get_contents($file); + $data = preg_replace('~\$db_last_error = \d+;~', '$db_last_error = ' . time() . ';', $data); + + // Open the backup file for writing + if ($fp = @fopen($file, 'w')) + { + // Reset the file buffer. + set_file_buffer($fp, 0); + + // Update the file. + $t = flock($fp, LOCK_EX); + $bytes = fwrite($fp, $data); + flock($fp, LOCK_UN); + fclose($fp); + + // Was it a success? + // ...only relevant if we're still dealing with the same good ole' settings file. + clearstatcache(); + if (($bytes == strlen($data)) && (filemtime($boarddir . '/Settings.php') === $last_settings_change)) + { + // This is our new Settings file... + // At least this one is an atomic operation + @copy($file, $boarddir . '/Settings.php'); + return true; + } + else + { + // Oops. Someone might have been faster + // or we have no more disk space left, troubles, troubles... + // Copy the file back and run for your life! + @copy($boarddir . '/Settings.php', $file); + } + } + + return false; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Auth.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Auth.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,740 @@ + 0, $array[3] & 2 > 0); + setcookie($cookiename, serialize(array(0, '', 0)), time() - 3600, $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies'])); + } + } + + // Get the data and path to set it on. + $data = serialize(empty($id) ? array(0, '', 0) : array($id, $password, time() + $cookie_length, $cookie_state)); + $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); + + // Set the cookie, $_COOKIE, and session variable. + setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies'])); + + // If subdomain-independent cookies are on, unset the subdomain-dependent cookie too. + if (empty($id) && !empty($modSettings['globalCookies'])) + setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1], '', !empty($modSettings['secureCookies'])); + + // Any alias URLs? This is mainly for use with frames, etc. + if (!empty($modSettings['forum_alias_urls'])) + { + $aliases = explode(',', $modSettings['forum_alias_urls']); + + $temp = $boardurl; + foreach ($aliases as $alias) + { + // Fake the $boardurl so we can set a different cookie. + $alias = strtr(trim($alias), array('http://' => '', 'https://' => '')); + $boardurl = 'http://' . $alias; + + $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); + + if ($cookie_url[0] == '') + $cookie_url[0] = strtok($alias, '/'); + + setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies'])); + } + + $boardurl = $temp; + } + + $_COOKIE[$cookiename] = $data; + + // Make sure the user logs in with a new session ID. + if (!isset($_SESSION['login_' . $cookiename]) || $_SESSION['login_' . $cookiename] !== $data) + { + // Backup and remove the old session. + $oldSessionData = $_SESSION; + $_SESSION = array(); + session_destroy(); + + // Recreate and restore the new session. + loadSession(); + session_regenerate_id(); + $_SESSION = $oldSessionData; + + // Version 4.3.2 didn't store the cookie of the new session. + if (version_compare(PHP_VERSION, '4.3.2') === 0) + { + $sessionCookieLifetime = @ini_get('session.cookie_lifetime'); + setcookie(session_name(), session_id(), time() + (empty($sessionCookieLifetime) ? $cookie_length : $sessionCookieLifetime), $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies'])); + } + + $_SESSION['login_' . $cookiename] = $data; + } +} + +// PHP < 4.3.2 doesn't have this function +if (!function_exists('session_regenerate_id')) +{ + function session_regenerate_id() + { + // Too late to change the session now. + if (headers_sent()) + return false; + + session_id(strtolower(md5(uniqid(mt_rand(), true)))); + return true; + } + +} + +// Get the domain and path for the cookie... +function url_parts($local, $global) +{ + global $boardurl; + + // Parse the URL with PHP to make life easier. + $parsed_url = parse_url($boardurl); + + // Is local cookies off? + if (empty($parsed_url['path']) || !$local) + $parsed_url['path'] = ''; + + // Globalize cookies across domains (filter out IP-addresses)? + if ($global && preg_match('~^\d{1,3}(\.\d{1,3}){3}$~', $parsed_url['host']) == 0 && preg_match('~(?:[^\.]+\.)?([^\.]{2,}\..+)\z~i', $parsed_url['host'], $parts) == 1) + $parsed_url['host'] = '.' . $parts[1]; + + // We shouldn't use a host at all if both options are off. + elseif (!$local && !$global) + $parsed_url['host'] = ''; + + // The host also shouldn't be set if there aren't any dots in it. + elseif (!isset($parsed_url['host']) || strpos($parsed_url['host'], '.') === false) + $parsed_url['host'] = ''; + + return array($parsed_url['host'], $parsed_url['path'] . '/'); +} + +// Kick out a guest when guest access is off... +function KickGuest() +{ + global $txt, $context; + + loadLanguage('Login'); + loadTemplate('Login'); + + // Never redirect to an attachment + if (strpos($_SERVER['REQUEST_URL'], 'dlattach') === false) + $_SESSION['login_url'] = $_SERVER['REQUEST_URL']; + + $context['sub_template'] = 'kick_guest'; + $context['page_title'] = $txt['login']; +} + +// Display a message about the forum being in maintenance mode, etc. +function InMaintenance() +{ + global $txt, $mtitle, $mmessage, $context; + + loadLanguage('Login'); + loadTemplate('Login'); + + // Send a 503 header, so search engines don't bother indexing while we're in maintenance mode. + header('HTTP/1.1 503 Service Temporarily Unavailable'); + + // Basic template stuff.. + $context['sub_template'] = 'maintenance'; + $context['title'] = &$mtitle; + $context['description'] = &$mmessage; + $context['page_title'] = $txt['maintain_mode']; +} + +function adminLogin() +{ + global $context, $scripturl, $txt, $user_info, $user_settings; + + loadLanguage('Admin'); + loadTemplate('Login'); + + // They used a wrong password, log it and unset that. + if (isset($_POST['admin_hash_pass']) || isset($_POST['admin_pass'])) + { + $txt['security_wrong'] = sprintf($txt['security_wrong'], isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : $txt['unknown'], $_SERVER['HTTP_USER_AGENT'], $user_info['ip']); + log_error($txt['security_wrong'], 'critical'); + + if (isset($_POST['admin_hash_pass'])) + unset($_POST['admin_hash_pass']); + if (isset($_POST['admin_pass'])) + unset($_POST['admin_pass']); + + $context['incorrect_password'] = true; + } + + // Figure out the get data and post data. + $context['get_data'] = '?' . construct_query_string($_GET); + $context['post_data'] = ''; + + // Now go through $_POST. Make sure the session hash is sent. + $_POST[$context['session_var']] = $context['session_id']; + foreach ($_POST as $k => $v) + $context['post_data'] .= adminLogin_outputPostVars($k, $v); + + // Now we'll use the admin_login sub template of the Login template. + $context['sub_template'] = 'admin_login'; + + // And title the page something like "Login". + if (!isset($context['page_title'])) + $context['page_title'] = $txt['login']; + + obExit(); + + // We MUST exit at this point, because otherwise we CANNOT KNOW that the user is privileged. + trigger_error('Hacking attempt...', E_USER_ERROR); +} + +function adminLogin_outputPostVars($k, $v) +{ + global $smcFunc; + + if (!is_array($v)) + return ' + '"', '<' => '<', '>' => '>')) . '" />'; + else + { + $ret = ''; + foreach ($v as $k2 => $v2) + $ret .= adminLogin_outputPostVars($k . '[' . $k2 . ']', $v2); + + return $ret; + } +} + +function construct_query_string($get) +{ + global $scripturl; + + $query_string = ''; + + // Awww, darn. The $scripturl contains GET stuff! + $q = strpos($scripturl, '?'); + if ($q !== false) + { + parse_str(preg_replace('/&(\w+)(?=&|$)/', '&$1=', strtr(substr($scripturl, $q + 1), ';', '&')), $temp); + + foreach ($get as $k => $v) + { + // Only if it's not already in the $scripturl! + if (!isset($temp[$k])) + $query_string .= urlencode($k) . '=' . urlencode($v) . ';'; + // If it changed, put it out there, but with an ampersand. + elseif ($temp[$k] != $get[$k]) + $query_string .= urlencode($k) . '=' . urlencode($v) . '&'; + } + } + else + { + // Add up all the data from $_GET into get_data. + foreach ($get as $k => $v) + $query_string .= urlencode($k) . '=' . urlencode($v) . ';'; + } + + $query_string = substr($query_string, 0, -1); + return $query_string; +} + +// Find members by email address, username, or real name. +function findMembers($names, $use_wildcards = false, $buddies_only = false, $max = 500) +{ + global $scripturl, $user_info, $modSettings, $smcFunc; + + // If it's not already an array, make it one. + if (!is_array($names)) + $names = explode(',', $names); + + $maybe_email = false; + foreach ($names as $i => $name) + { + // Trim, and fix wildcards for each name. + $names[$i] = trim($smcFunc['strtolower']($name)); + + $maybe_email |= strpos($name, '@') !== false; + + // Make it so standard wildcards will work. (* and ?) + if ($use_wildcards) + $names[$i] = strtr($names[$i], array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_', '\'' => ''')); + else + $names[$i] = strtr($names[$i], array('\'' => ''')); + } + + // What are we using to compare? + $comparison = $use_wildcards ? 'LIKE' : '='; + + // Nothing found yet. + $results = array(); + + // This ensures you can't search someones email address if you can't see it. + $email_condition = allowedTo('moderate_forum') ? '' : 'hide_email = 0 AND '; + + if ($use_wildcards || $maybe_email) + $email_condition = ' + OR (' . $email_condition . 'email_address ' . $comparison . ' \'' . implode( '\') OR (' . $email_condition . ' email_address ' . $comparison . ' \'', $names) . '\')'; + else + $email_condition = ''; + + // Get the case of the columns right - but only if we need to as things like MySQL will go slow needlessly otherwise. + $member_name = $smcFunc['db_case_sensitive'] ? 'LOWER(member_name)' : 'member_name'; + $real_name = $smcFunc['db_case_sensitive'] ? 'LOWER(real_name)' : 'real_name'; + + // Search by username, display name, and email address. + $request = $smcFunc['db_query']('', ' + SELECT id_member, member_name, real_name, email_address, hide_email + FROM {db_prefix}members + WHERE ({raw:member_name_search} + OR {raw:real_name_search} {raw:email_condition}) + ' . ($buddies_only ? 'AND id_member IN ({array_int:buddy_list})' : '') . ' + AND is_activated IN (1, 11) + LIMIT {int:limit}', + array( + 'buddy_list' => $user_info['buddies'], + 'member_name_search' => $member_name . ' ' . $comparison . ' \'' . implode( '\' OR ' . $member_name . ' ' . $comparison . ' \'', $names) . '\'', + 'real_name_search' => $real_name . ' ' . $comparison . ' \'' . implode( '\' OR ' . $real_name . ' ' . $comparison . ' \'', $names) . '\'', + 'email_condition' => $email_condition, + 'limit' => $max, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $results[$row['id_member']] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + 'username' => $row['member_name'], + 'email' => in_array(showEmailAddress(!empty($row['hide_email']), $row['id_member']), array('yes', 'yes_permission_override')) ? $row['email_address'] : '', + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => '' . $row['real_name'] . '' + ); + } + $smcFunc['db_free_result']($request); + + // Return all the results. + return $results; +} + +function JSMembers() +{ + global $context, $scripturl, $user_info, $smcFunc; + + checkSession('get'); + + if (WIRELESS) + $context['sub_template'] = WIRELESS_PROTOCOL . '_pm'; + else + { + // Why is this in the Help template, you ask? Well, erm... it helps you. Does that work? + loadTemplate('Help'); + + $context['template_layers'] = array(); + $context['sub_template'] = 'find_members'; + } + + if (isset($_REQUEST['search'])) + $context['last_search'] = $smcFunc['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'])) + { + $_REQUEST['search'] = $smcFunc['htmlspecialchars']($_REQUEST['search'], ENT_QUOTES); + + $context['results'] = findMembers(array($_REQUEST['search']), true, $context['buddy_search']); + $total_results = count($context['results']); + + $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 (especially useful for the wireless template). + $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( + 'first' => $_REQUEST['start'] >= 7 ? $base_url . ';start=0' : '', + 'prev' => $_REQUEST['start'] >= 7 ? $base_url . ';start=' . ($_REQUEST['start'] - 7) : '', + 'next' => $_REQUEST['start'] + 7 < $total_results ? $base_url . ';start=' . ($_REQUEST['start'] + 7) : '', + 'last' => $_REQUEST['start'] + 7 < $total_results ? $base_url . ';start=' . (floor(($total_results - 1) / 7) * 7) : '', + 'up' => $scripturl . '?action=pm;sa=send' . (empty($_REQUEST['u']) ? '' : ';u=' . $_REQUEST['u']), + ); + $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); + } + else + $context['links']['up'] = $scripturl . '?action=pm;sa=send' . (empty($_REQUEST['u']) ? '' : ';u=' . $_REQUEST['u']); +} + +function RequestMembers() +{ + global $user_info, $txt, $smcFunc; + + checkSession('get'); + + $_REQUEST['search'] = $smcFunc['htmlspecialchars']($_REQUEST['search']) . '*'; + $_REQUEST['search'] = trim($smcFunc['strtolower']($_REQUEST['search'])); + $_REQUEST['search'] = strtr($_REQUEST['search'], array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_', '&' => '&')); + + if (function_exists('iconv')) + header('Content-Type: text/plain; charset=UTF-8'); + + $request = $smcFunc['db_query']('', ' + SELECT real_name + FROM {db_prefix}members + WHERE real_name LIKE {string:search}' . (isset($_REQUEST['buddies']) ? ' + AND id_member IN ({array_int:buddy_list})' : '') . ' + AND is_activated IN (1, 11) + LIMIT ' . ($smcFunc['strlen']($_REQUEST['search']) <= 2 ? '100' : '800'), + array( + 'buddy_list' => $user_info['buddies'], + 'search' => $_REQUEST['search'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (function_exists('iconv')) + { + $utf8 = iconv($txt['lang_character_set'], 'UTF-8', $row['real_name']); + if ($utf8) + $row['real_name'] = $utf8; + } + + $row['real_name'] = strtr($row['real_name'], array('&' => '&', '<' => '<', '>' => '>', '"' => '"')); + + if (preg_match('~&#\d+;~', $row['real_name']) != 0) + { + $fixchar = create_function('$n', ' + if ($n < 128) + return chr($n); + elseif ($n < 2048) + return chr(192 | $n >> 6) . chr(128 | $n & 63); + elseif ($n < 65536) + return chr(224 | $n >> 12) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63); + else + return chr(240 | $n >> 18) . chr(128 | $n >> 12 & 63) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63);'); + + $row['real_name'] = preg_replace('~&#(\d+);~e', '$fixchar(\'$1\')', $row['real_name']); + } + + echo $row['real_name'], "\n"; + } + $smcFunc['db_free_result']($request); + + obExit(false); +} + +// This function generates a random password for a user and emails it to them. +function resetPassword($memID, $username = null) +{ + global $scripturl, $context, $txt, $sourcedir, $modSettings, $smcFunc, $language; + + // Language... and a required file. + loadLanguage('Login'); + require_once($sourcedir . '/Subs-Post.php'); + + // Get some important details. + $request = $smcFunc['db_query']('', ' + SELECT member_name, email_address, lngfile + FROM {db_prefix}members + WHERE id_member = {int:id_member}', + array( + 'id_member' => $memID, + ) + ); + list ($user, $email, $lngfile) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if ($username !== null) + { + $old_user = $user; + $user = trim($username); + } + + // Generate a random password. + $newPassword = substr(preg_replace('/\W/', '', md5(mt_rand())), 0, 10); + $newPassword_sha1 = sha1(strtolower($user) . $newPassword); + + // Do some checks on the username if needed. + if ($username !== null) + { + validateUsername($memID, $user); + + // Update the database... + updateMemberData($memID, array('member_name' => $user, 'passwd' => $newPassword_sha1)); + } + else + updateMemberData($memID, array('passwd' => $newPassword_sha1)); + + call_integration_hook('integrate_reset_pass', array($old_user, $user, $newPassword)); + + $replacements = array( + 'USERNAME' => $user, + 'PASSWORD' => $newPassword, + ); + + $emaildata = loadEmailTemplate('change_password', $replacements, empty($lngfile) || empty($modSettings['userLanguage']) ? $language : $lngfile); + + // Send them the email informing them of the change - then we're done! + sendmail($email, $emaildata['subject'], $emaildata['body'], null, null, false, 0); +} + +// Is this a valid username? +function validateUsername($memID, $username) +{ + global $sourcedir, $txt; + + // No name?! How can you register with no name? + if ($username == '') + fatal_lang_error('need_username', false); + + // Only these characters are permitted. + if (in_array($username, array('_', '|')) || preg_match('~[<>&"\'=\\\\]~', preg_replace('~&#(?:\\d{1,7}|x[0-9a-fA-F]{1,6});~', '', $username)) != 0 || strpos($username, '[code') !== false || strpos($username, '[/code') !== false) + fatal_lang_error('error_invalid_characters_username', false); + + if (stristr($username, $txt['guest_title']) !== false) + fatal_lang_error('username_reserved', true, array($txt['guest_title'])); + + require_once($sourcedir . '/Subs-Members.php'); + if (isReservedName($username, $memID, false)) + fatal_error('(' . htmlspecialchars($username) . ') ' . $txt['name_in_use'], false); + + return null; +} + +// This function simply checks whether a password meets the current forum rules. +function validatePassword($password, $username, $restrict_in = array()) +{ + global $modSettings, $smcFunc; + + // Perform basic requirements first. + if ($smcFunc['strlen']($password) < (empty($modSettings['password_strength']) ? 4 : 8)) + return 'short'; + + // Is this enough? + if (empty($modSettings['password_strength'])) + return null; + + // Otherwise, perform the medium strength test - checking if password appears in the restricted string. + if (preg_match('~\b' . preg_quote($password, '~') . '\b~', implode(' ', $restrict_in)) != 0) + return 'restricted_words'; + elseif ($smcFunc['strpos']($password, $username) !== false) + return 'restricted_words'; + + // !!! If pspell is available, use it on the word, and return restricted_words if it doesn't give "bad spelling"? + + // If just medium, we're done. + if ($modSettings['password_strength'] == 1) + return null; + + // Otherwise, hard test next, check for numbers and letters, uppercase too. + $good = preg_match('~(\D\d|\d\D)~', $password) != 0; + $good &= $smcFunc['strtolower']($password) != $password; + + return $good ? null : 'chars'; +} + +// Quickly find out what this user can and cannot do. +function rebuildModCache() +{ + global $user_info, $smcFunc; + + // What groups can they moderate? + $group_query = allowedTo('manage_membergroups') ? '1=1' : '0=1'; + + if ($group_query == '0=1') + { + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}group_moderators + WHERE id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + ) + ); + $groups = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $groups[] = $row['id_group']; + $smcFunc['db_free_result']($request); + + if (empty($groups)) + $group_query = '0=1'; + else + $group_query = 'id_group IN (' . implode(',', $groups) . ')'; + } + + // Then, same again, just the boards this time! + $board_query = allowedTo('moderate_forum') ? '1=1' : '0=1'; + + if ($board_query == '0=1') + { + $boards = boardsAllowedTo('moderate_board', true); + + if (empty($boards)) + $board_query = '0=1'; + else + $board_query = 'id_board IN (' . implode(',', $boards) . ')'; + } + + // What boards are they the moderator of? + $boards_mod = array(); + if (!$user_info['is_guest']) + { + $request = $smcFunc['db_query']('', ' + SELECT id_board + FROM {db_prefix}moderators + WHERE id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards_mod[] = $row['id_board']; + $smcFunc['db_free_result']($request); + } + + $mod_query = empty($boards_mod) ? '0=1' : 'b.id_board IN (' . implode(',', $boards_mod) . ')'; + + $_SESSION['mc'] = array( + 'time' => time(), + // This looks a bit funny but protects against the login redirect. + 'id' => $user_info['id'] && $user_info['name'] ? $user_info['id'] : 0, + // If you change the format of 'gq' and/or 'bq' make sure to adjust 'can_mod' in Load.php. + 'gq' => $group_query, + 'bq' => $board_query, + 'ap' => boardsAllowedTo('approve_posts'), + 'mb' => $boards_mod, + 'mq' => $mod_query, + ); + + $user_info['mod_cache'] = $_SESSION['mc']; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-BoardIndex.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-BoardIndex.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,291 @@ +boards->child_boards or an associative array + with boards->child_boards. +*/ + +function getBoardIndex($boardIndexOptions) +{ + global $smcFunc, $scripturl, $user_info, $modSettings, $txt; + global $settings, $context; + + // For performance, track the latest post while going through the boards. + if (!empty($boardIndexOptions['set_latest_post'])) + $latest_post = array( + 'timestamp' => 0, + 'ref' => 0, + ); + + // Find all boards and categories, as well as related information. This will be sorted by the natural order of boards and categories, which we control. + $result_boards = $smcFunc['db_query']('boardindex_fetch_boards', ' + SELECT' . ($boardIndexOptions['include_categories'] ? ' + c.id_cat, c.name AS cat_name,' : '') . ' + b.id_board, b.name AS board_name, b.description, + CASE WHEN b.redirect != {string:blank_string} THEN 1 ELSE 0 END AS is_redirect, + b.num_posts, b.num_topics, b.unapproved_posts, b.unapproved_topics, b.id_parent, + IFNULL(m.poster_time, 0) AS poster_time, IFNULL(mem.member_name, m.poster_name) AS poster_name, + m.subject, m.id_topic, IFNULL(mem.real_name, m.poster_name) AS real_name, + ' . ($user_info['is_guest'] ? ' 1 AS is_read, 0 AS new_from,' : ' + (IFNULL(lb.id_msg, 0) >= b.id_msg_updated) AS is_read, IFNULL(lb.id_msg, -1) + 1 AS new_from,' . ($boardIndexOptions['include_categories'] ? ' + c.can_collapse, IFNULL(cc.id_member, 0) AS is_collapsed,' : '')) . ' + IFNULL(mem.id_member, 0) AS id_member, m.id_msg, + IFNULL(mods_mem.id_member, 0) AS id_moderator, mods_mem.real_name AS mod_real_name + FROM {db_prefix}boards AS b' . ($boardIndexOptions['include_categories'] ? ' + LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)' : '') . ' + LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = b.id_last_msg) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . ($user_info['is_guest'] ? '' : ' + LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})' . ($boardIndexOptions['include_categories'] ? ' + LEFT JOIN {db_prefix}collapsed_categories AS cc ON (cc.id_cat = c.id_cat AND cc.id_member = {int:current_member})' : '')) . ' + LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board) + LEFT JOIN {db_prefix}members AS mods_mem ON (mods_mem.id_member = mods.id_member) + WHERE {query_see_board}' . (empty($boardIndexOptions['countChildPosts']) ? (empty($boardIndexOptions['base_level']) ? '' : ' + AND b.child_level >= {int:child_level}') : ' + AND b.child_level BETWEEN ' . $boardIndexOptions['base_level'] . ' AND ' . ($boardIndexOptions['base_level'] + 1)), + array( + 'current_member' => $user_info['id'], + 'child_level' => $boardIndexOptions['base_level'], + 'blank_string' => '', + ) + ); + + // Start with an empty array. + if ($boardIndexOptions['include_categories']) + $categories = array(); + else + $this_category = array(); + + // Run through the categories and boards (or only boards).... + while ($row_board = $smcFunc['db_fetch_assoc']($result_boards)) + { + // Perhaps we are ignoring this board? + $ignoreThisBoard = in_array($row_board['id_board'], $user_info['ignoreboards']); + $row_board['is_read'] = !empty($row_board['is_read']) || $ignoreThisBoard ? '1' : '0'; + + if ($boardIndexOptions['include_categories']) + { + // Haven't set this category yet. + if (empty($categories[$row_board['id_cat']])) + { + $categories[$row_board['id_cat']] = array( + 'id' => $row_board['id_cat'], + 'name' => $row_board['cat_name'], + 'is_collapsed' => isset($row_board['can_collapse']) && $row_board['can_collapse'] == 1 && $row_board['is_collapsed'] > 0, + 'can_collapse' => isset($row_board['can_collapse']) && $row_board['can_collapse'] == 1, + 'collapse_href' => isset($row_board['can_collapse']) ? $scripturl . '?action=collapse;c=' . $row_board['id_cat'] . ';sa=' . ($row_board['is_collapsed'] > 0 ? 'expand;' : 'collapse;') . $context['session_var'] . '=' . $context['session_id'] . '#c' . $row_board['id_cat'] : '', + 'collapse_image' => isset($row_board['can_collapse']) ? '+' : '', + 'href' => $scripturl . '#c' . $row_board['id_cat'], + 'boards' => array(), + 'new' => false + ); + $categories[$row_board['id_cat']]['link'] = '' . ($categories[$row_board['id_cat']]['can_collapse'] ? '' . $row_board['cat_name'] . '' : $row_board['cat_name']); + } + + // If this board has new posts in it (and isn't the recycle bin!) then the category is new. + if (empty($modSettings['recycle_enable']) || $modSettings['recycle_board'] != $row_board['id_board']) + $categories[$row_board['id_cat']]['new'] |= empty($row_board['is_read']) && $row_board['poster_name'] != ''; + + // Avoid showing category unread link where it only has redirection boards. + $categories[$row_board['id_cat']]['show_unread'] = !empty($categories[$row_board['id_cat']]['show_unread']) ? 1 : !$row_board['is_redirect']; + + // Collapsed category - don't do any of this. + if ($categories[$row_board['id_cat']]['is_collapsed']) + continue; + + // Let's save some typing. Climbing the array might be slower, anyhow. + $this_category = &$categories[$row_board['id_cat']]['boards']; + } + + // This is a parent board. + if ($row_board['id_parent'] == $boardIndexOptions['parent_id']) + { + // Is this a new board, or just another moderator? + if (!isset($this_category[$row_board['id_board']])) + { + // Not a child. + $isChild = false; + + $this_category[$row_board['id_board']] = array( + 'new' => empty($row_board['is_read']), + 'id' => $row_board['id_board'], + 'name' => $row_board['board_name'], + 'description' => $row_board['description'], + 'moderators' => array(), + 'link_moderators' => array(), + 'children' => array(), + 'link_children' => array(), + 'children_new' => false, + 'topics' => $row_board['num_topics'], + 'posts' => $row_board['num_posts'], + 'is_redirect' => $row_board['is_redirect'], + 'unapproved_topics' => $row_board['unapproved_topics'], + 'unapproved_posts' => $row_board['unapproved_posts'] - $row_board['unapproved_topics'], + 'can_approve_posts' => !empty($user_info['mod_cache']['ap']) && ($user_info['mod_cache']['ap'] == array(0) || in_array($row_board['id_board'], $user_info['mod_cache']['ap'])), + 'href' => $scripturl . '?board=' . $row_board['id_board'] . '.0', + 'link' => '' . $row_board['board_name'] . '' + ); + } + if (!empty($row_board['id_moderator'])) + { + $this_category[$row_board['id_board']]['moderators'][$row_board['id_moderator']] = array( + 'id' => $row_board['id_moderator'], + 'name' => $row_board['mod_real_name'], + 'href' => $scripturl . '?action=profile;u=' . $row_board['id_moderator'], + 'link' => '' . $row_board['mod_real_name'] . '' + ); + $this_category[$row_board['id_board']]['link_moderators'][] = '' . $row_board['mod_real_name'] . ''; + } + } + // Found a child board.... make sure we've found its parent and the child hasn't been set already. + elseif (isset($this_category[$row_board['id_parent']]['children']) && !isset($this_category[$row_board['id_parent']]['children'][$row_board['id_board']])) + { + // A valid child! + $isChild = true; + + $this_category[$row_board['id_parent']]['children'][$row_board['id_board']] = array( + 'id' => $row_board['id_board'], + 'name' => $row_board['board_name'], + 'description' => $row_board['description'], + 'new' => empty($row_board['is_read']) && $row_board['poster_name'] != '', + 'topics' => $row_board['num_topics'], + 'posts' => $row_board['num_posts'], + 'is_redirect' => $row_board['is_redirect'], + 'unapproved_topics' => $row_board['unapproved_topics'], + 'unapproved_posts' => $row_board['unapproved_posts'] - $row_board['unapproved_topics'], + 'can_approve_posts' => !empty($user_info['mod_cache']['ap']) && ($user_info['mod_cache']['ap'] == array(0) || in_array($row_board['id_board'], $user_info['mod_cache']['ap'])), + 'href' => $scripturl . '?board=' . $row_board['id_board'] . '.0', + 'link' => '' . $row_board['board_name'] . '' + ); + + // Counting child board posts is... slow :/. + if (!empty($boardIndexOptions['countChildPosts']) && !$row_board['is_redirect']) + { + $this_category[$row_board['id_parent']]['posts'] += $row_board['num_posts']; + $this_category[$row_board['id_parent']]['topics'] += $row_board['num_topics']; + } + + // Does this board contain new boards? + $this_category[$row_board['id_parent']]['children_new'] |= empty($row_board['is_read']); + + // This is easier to use in many cases for the theme.... + $this_category[$row_board['id_parent']]['link_children'][] = &$this_category[$row_board['id_parent']]['children'][$row_board['id_board']]['link']; + } + // Child of a child... just add it on... + elseif (!empty($boardIndexOptions['countChildPosts'])) + { + if (!isset($parent_map)) + $parent_map = array(); + + if (!isset($parent_map[$row_board['id_parent']])) + foreach ($this_category as $id => $board) + { + if (!isset($board['children'][$row_board['id_parent']])) + continue; + + $parent_map[$row_board['id_parent']] = array(&$this_category[$id], &$this_category[$id]['children'][$row_board['id_parent']]); + $parent_map[$row_board['id_board']] = array(&$this_category[$id], &$this_category[$id]['children'][$row_board['id_parent']]); + + break; + } + + if (isset($parent_map[$row_board['id_parent']]) && !$row_board['is_redirect']) + { + $parent_map[$row_board['id_parent']][0]['posts'] += $row_board['num_posts']; + $parent_map[$row_board['id_parent']][0]['topics'] += $row_board['num_topics']; + $parent_map[$row_board['id_parent']][1]['posts'] += $row_board['num_posts']; + $parent_map[$row_board['id_parent']][1]['topics'] += $row_board['num_topics']; + + continue; + } + + continue; + } + // Found a child of a child - skip. + else + continue; + + // Prepare the subject, and make sure it's not too long. + censorText($row_board['subject']); + $row_board['short_subject'] = shorten_subject($row_board['subject'], 24); + $this_last_post = array( + 'id' => $row_board['id_msg'], + 'time' => $row_board['poster_time'] > 0 ? timeformat($row_board['poster_time']) : $txt['not_applicable'], + 'timestamp' => forum_time(true, $row_board['poster_time']), + 'subject' => $row_board['short_subject'], + 'member' => array( + 'id' => $row_board['id_member'], + 'username' => $row_board['poster_name'] != '' ? $row_board['poster_name'] : $txt['not_applicable'], + 'name' => $row_board['real_name'], + 'href' => $row_board['poster_name'] != '' && !empty($row_board['id_member']) ? $scripturl . '?action=profile;u=' . $row_board['id_member'] : '', + 'link' => $row_board['poster_name'] != '' ? (!empty($row_board['id_member']) ? '' . $row_board['real_name'] . '' : $row_board['real_name']) : $txt['not_applicable'], + ), + 'start' => 'msg' . $row_board['new_from'], + 'topic' => $row_board['id_topic'] + ); + + // Provide the href and link. + if ($row_board['subject'] != '') + { + $this_last_post['href'] = $scripturl . '?topic=' . $row_board['id_topic'] . '.msg' . ($user_info['is_guest'] ? $row_board['id_msg'] : $row_board['new_from']) . (empty($row_board['is_read']) ? ';boardseen' : '') . '#new'; + $this_last_post['link'] = '' . $row_board['short_subject'] . ''; + } + else + { + $this_last_post['href'] = ''; + $this_last_post['link'] = $txt['not_applicable']; + } + + // Set the last post in the parent board. + if ($row_board['id_parent'] == $boardIndexOptions['parent_id'] || ($isChild && !empty($row_board['poster_time']) && $this_category[$row_board['id_parent']]['last_post']['timestamp'] < forum_time(true, $row_board['poster_time']))) + $this_category[$isChild ? $row_board['id_parent'] : $row_board['id_board']]['last_post'] = $this_last_post; + // Just in the child...? + if ($isChild) + { + $this_category[$row_board['id_parent']]['children'][$row_board['id_board']]['last_post'] = $this_last_post; + + // If there are no posts in this board, it really can't be new... + $this_category[$row_board['id_parent']]['children'][$row_board['id_board']]['new'] &= $row_board['poster_name'] != ''; + } + // No last post for this board? It's not new then, is it..? + elseif ($row_board['poster_name'] == '') + $this_category[$row_board['id_board']]['new'] = false; + + // Determine a global most recent topic. + if (!empty($boardIndexOptions['set_latest_post']) && !empty($row_board['poster_time']) && $row_board['poster_time'] > $latest_post['timestamp'] && !$ignoreThisBoard) + $latest_post = array( + 'timestamp' => $row_board['poster_time'], + 'ref' => &$this_category[$isChild ? $row_board['id_parent'] : $row_board['id_board']]['last_post'], + ); + } + $smcFunc['db_free_result']($result_boards); + + // By now we should know the most recent post...if we wanna know it that is. + if (!empty($boardIndexOptions['set_latest_post']) && !empty($latest_post['ref'])) + $context['latest_post'] = $latest_post['ref']; + + return $boardIndexOptions['include_categories'] ? $categories : $this_category; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Boards.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Boards.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1170 @@ + $user_info['id'], + 'board_list' => $boards, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_boards + WHERE id_board IN ({array_int:board_list}) + AND id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'board_list' => $boards, + ) + ); + } + // Otherwise mark the board as read. + else + { + $markRead = array(); + foreach ($boards as $board) + $markRead[] = array($modSettings['maxMsgID'], $user_info['id'], $board); + + // Update log_mark_read and log_boards. + $smcFunc['db_insert']('replace', + '{db_prefix}log_mark_read', + array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'), + $markRead, + array('id_board', 'id_member') + ); + + $smcFunc['db_insert']('replace', + '{db_prefix}log_boards', + array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'), + $markRead, + array('id_board', 'id_member') + ); + } + + // Get rid of useless log_topics data, because log_mark_read is better for it - even if marking unread - I think so... + $result = $smcFunc['db_query']('', ' + SELECT MIN(id_topic) + FROM {db_prefix}log_topics + WHERE id_member = {int:current_member}', + array( + 'current_member' => $user_info['id'], + ) + ); + list ($lowest_topic) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + if (empty($lowest_topic)) + return; + + // !!!SLOW This query seems to eat it sometimes. + $result = $smcFunc['db_query']('', ' + SELECT lt.id_topic + FROM {db_prefix}log_topics AS lt + INNER JOIN {db_prefix}topics AS t /*!40000 USE INDEX (PRIMARY) */ ON (t.id_topic = lt.id_topic + AND t.id_board IN ({array_int:board_list})) + WHERE lt.id_member = {int:current_member} + AND lt.id_topic >= {int:lowest_topic}', + array( + 'current_member' => $user_info['id'], + 'board_list' => $boards, + 'lowest_topic' => $lowest_topic, + ) + ); + $topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $topics[] = $row['id_topic']; + $smcFunc['db_free_result']($result); + + if (!empty($topics)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_topics + WHERE id_member = {int:current_member} + AND id_topic IN ({array_int:topic_list})', + array( + 'current_member' => $user_info['id'], + 'topic_list' => $topics, + ) + ); +} + +// Mark one or more boards as read. +function MarkRead() +{ + global $board, $topic, $user_info, $board_info, $modSettings, $smcFunc; + + // No Guests allowed! + is_not_guest(); + + checkSession('get'); + + if (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'all') + { + // Find all the boards this user can see. + $result = $smcFunc['db_query']('', ' + SELECT b.id_board + FROM {db_prefix}boards AS b + WHERE {query_see_board}', + array( + ) + ); + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $boards[] = $row['id_board']; + $smcFunc['db_free_result']($result); + + if (!empty($boards)) + markBoardsRead($boards, isset($_REQUEST['unread'])); + + $_SESSION['id_msg_last_visit'] = $modSettings['maxMsgID']; + if (!empty($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'action=unread') !== false) + redirectexit('action=unread'); + + if (isset($_SESSION['topicseen_cache'])) + $_SESSION['topicseen_cache'] = array(); + + redirectexit(); + } + elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'unreadreplies') + { + // Make sure all the boards are integers! + $topics = explode('-', $_REQUEST['topics']); + + $markRead = array(); + foreach ($topics as $id_topic) + $markRead[] = array($modSettings['maxMsgID'], $user_info['id'], (int) $id_topic); + + $smcFunc['db_insert']('replace', + '{db_prefix}log_topics', + array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int'), + $markRead, + array('id_member', 'id_topic') + ); + + if (isset($_SESSION['topicseen_cache'])) + $_SESSION['topicseen_cache'] = array(); + + redirectexit('action=unreadreplies'); + } + + // Special case: mark a topic unread! + elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'topic') + { + // First, let's figure out what the latest message is. + $result = $smcFunc['db_query']('', ' + SELECT id_first_msg, id_last_msg + FROM {db_prefix}topics + WHERE id_topic = {int:current_topic}', + array( + 'current_topic' => $topic, + ) + ); + $topicinfo = $smcFunc['db_fetch_assoc']($result); + $smcFunc['db_free_result']($result); + + if (!empty($_GET['t'])) + { + // If they read the whole topic, go back to the beginning. + if ($_GET['t'] >= $topicinfo['id_last_msg']) + $earlyMsg = 0; + // If they want to mark the whole thing read, same. + elseif ($_GET['t'] <= $topicinfo['id_first_msg']) + $earlyMsg = 0; + // Otherwise, get the latest message before the named one. + else + { + $result = $smcFunc['db_query']('', ' + SELECT MAX(id_msg) + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic} + AND id_msg >= {int:id_first_msg} + AND id_msg < {int:topic_msg_id}', + array( + 'current_topic' => $topic, + 'topic_msg_id' => (int) $_GET['t'], + 'id_first_msg' => $topicinfo['id_first_msg'], + ) + ); + list ($earlyMsg) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + } + } + // Marking read from first page? That's the whole topic. + elseif ($_REQUEST['start'] == 0) + $earlyMsg = 0; + else + { + $result = $smcFunc['db_query']('', ' + SELECT id_msg + FROM {db_prefix}messages + WHERE id_topic = {int:current_topic} + ORDER BY id_msg + LIMIT ' . (int) $_REQUEST['start'] . ', 1', + array( + 'current_topic' => $topic, + ) + ); + list ($earlyMsg) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + $earlyMsg--; + } + + // Blam, unread! + $smcFunc['db_insert']('replace', + '{db_prefix}log_topics', + array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int'), + array($earlyMsg, $user_info['id'], $topic), + array('id_member', 'id_topic') + ); + + redirectexit('board=' . $board . '.0'); + } + else + { + $categories = array(); + $boards = array(); + + if (isset($_REQUEST['c'])) + { + $_REQUEST['c'] = explode(',', $_REQUEST['c']); + foreach ($_REQUEST['c'] as $c) + $categories[] = (int) $c; + } + if (isset($_REQUEST['boards'])) + { + $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); + foreach ($_REQUEST['boards'] as $b) + $boards[] = (int) $b; + } + if (!empty($board)) + $boards[] = (int) $board; + + if (isset($_REQUEST['children']) && !empty($boards)) + { + // They want to mark the entire tree starting with the boards specified + // The easist thing is to just get all the boards they can see, but since we've specified the top of tree we ignore some of them + + $request = $smcFunc['db_query']('', ' + SELECT b.id_board, b.id_parent + FROM {db_prefix}boards AS b + WHERE {query_see_board} + AND b.child_level > {int:no_parents} + AND b.id_board NOT IN ({array_int:board_list}) + ORDER BY child_level ASC + ', + array( + 'no_parents' => 0, + 'board_list' => $boards, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + if (in_array($row['id_parent'], $boards)) + $boards[] = $row['id_board']; + $smcFunc['db_free_result']($request); + } + + $clauses = array(); + $clauseParameters = array(); + if (!empty($categories)) + { + $clauses[] = 'id_cat IN ({array_int:category_list})'; + $clauseParameters['category_list'] = $categories; + } + if (!empty($boards)) + { + $clauses[] = 'id_board IN ({array_int:board_list})'; + $clauseParameters['board_list'] = $boards; + } + + if (empty($clauses)) + redirectexit(); + + $request = $smcFunc['db_query']('', ' + SELECT b.id_board + FROM {db_prefix}boards AS b + WHERE {query_see_board} + AND b.' . implode(' OR b.', $clauses), + array_merge($clauseParameters, array( + )) + ); + $boards = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards[] = $row['id_board']; + $smcFunc['db_free_result']($request); + + if (empty($boards)) + redirectexit(); + + markBoardsRead($boards, isset($_REQUEST['unread'])); + + foreach ($boards as $b) + { + if (isset($_SESSION['topicseen_cache'][$b])) + $_SESSION['topicseen_cache'][$b] = array(); + } + + if (!isset($_REQUEST['unread'])) + { + // Find all the boards this user can see. + $result = $smcFunc['db_query']('', ' + SELECT b.id_board + FROM {db_prefix}boards AS b + WHERE b.id_parent IN ({array_int:parent_list}) + AND {query_see_board}', + array( + 'parent_list' => $boards, + ) + ); + if ($smcFunc['db_num_rows']($result) > 0) + { + $logBoardInserts = ''; + while ($row = $smcFunc['db_fetch_assoc']($result)) + $logBoardInserts[] = array($modSettings['maxMsgID'], $user_info['id'], $row['id_board']); + + $smcFunc['db_insert']('replace', + '{db_prefix}log_boards', + array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'), + $logBoardInserts, + array('id_member', 'id_board') + ); + } + $smcFunc['db_free_result']($result); + + if (empty($board)) + redirectexit(); + else + redirectexit('board=' . $board . '.0'); + } + else + { + if (empty($board_info['parent'])) + redirectexit(); + else + redirectexit('board=' . $board_info['parent'] . '.0'); + } + } +} + +// Get the id_member associated with the specified message. +function getMsgMemberID($messageID) +{ + global $smcFunc; + + // Find the topic and make sure the member still exists. + $result = $smcFunc['db_query']('', ' + SELECT IFNULL(mem.id_member, 0) + FROM {db_prefix}messages AS m + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_msg = {int:selected_message} + LIMIT 1', + array( + 'selected_message' => (int) $messageID, + ) + ); + if ($smcFunc['db_num_rows']($result) > 0) + list ($memberID) = $smcFunc['db_fetch_row']($result); + // The message doesn't even exist. + else + $memberID = 0; + $smcFunc['db_free_result']($result); + + return (int) $memberID; +} + +// Modify the settings and position of a board. +function modifyBoard($board_id, &$boardOptions) +{ + global $sourcedir, $cat_tree, $boards, $boardList, $modSettings, $smcFunc; + + // Get some basic information about all boards and categories. + getBoardTree(); + + // Make sure given boards and categories exist. + if (!isset($boards[$board_id]) || (isset($boardOptions['target_board']) && !isset($boards[$boardOptions['target_board']])) || (isset($boardOptions['target_category']) && !isset($cat_tree[$boardOptions['target_category']]))) + fatal_lang_error('no_board'); + + // All things that will be updated in the database will be in $boardUpdates. + $boardUpdates = array(); + $boardUpdateParameters = array(); + + // In case the board has to be moved + if (isset($boardOptions['move_to'])) + { + // Move the board to the top of a given category. + if ($boardOptions['move_to'] == 'top') + { + $id_cat = $boardOptions['target_category']; + $child_level = 0; + $id_parent = 0; + $after = $cat_tree[$id_cat]['last_board_order']; + } + + // Move the board to the bottom of a given category. + elseif ($boardOptions['move_to'] == 'bottom') + { + $id_cat = $boardOptions['target_category']; + $child_level = 0; + $id_parent = 0; + $after = 0; + foreach ($cat_tree[$id_cat]['children'] as $id_board => $dummy) + $after = max($after, $boards[$id_board]['order']); + } + + // Make the board a child of a given board. + elseif ($boardOptions['move_to'] == 'child') + { + $id_cat = $boards[$boardOptions['target_board']]['category']; + $child_level = $boards[$boardOptions['target_board']]['level'] + 1; + $id_parent = $boardOptions['target_board']; + + // People can be creative, in many ways... + if (isChildOf($id_parent, $board_id)) + fatal_lang_error('mboards_parent_own_child_error', false); + elseif ($id_parent == $board_id) + fatal_lang_error('mboards_board_own_child_error', false); + + $after = $boards[$boardOptions['target_board']]['order']; + + // Check if there are already children and (if so) get the max board order. + if (!empty($boards[$id_parent]['tree']['children']) && empty($boardOptions['move_first_child'])) + foreach ($boards[$id_parent]['tree']['children'] as $childBoard_id => $dummy) + $after = max($after, $boards[$childBoard_id]['order']); + } + + // Place a board before or after another board, on the same child level. + elseif (in_array($boardOptions['move_to'], array('before', 'after'))) + { + $id_cat = $boards[$boardOptions['target_board']]['category']; + $child_level = $boards[$boardOptions['target_board']]['level']; + $id_parent = $boards[$boardOptions['target_board']]['parent']; + $after = $boards[$boardOptions['target_board']]['order'] - ($boardOptions['move_to'] == 'before' ? 1 : 0); + } + + // Oops...? + else + trigger_error('modifyBoard(): The move_to value \'' . $boardOptions['move_to'] . '\' is incorrect', E_USER_ERROR); + + // Get a list of children of this board. + $childList = array(); + recursiveBoards($childList, $boards[$board_id]['tree']); + + // See if there are changes that affect children. + $childUpdates = array(); + $levelDiff = $child_level - $boards[$board_id]['level']; + if ($levelDiff != 0) + $childUpdates[] = 'child_level = child_level ' . ($levelDiff > 0 ? '+ ' : '') . '{int:level_diff}'; + if ($id_cat != $boards[$board_id]['category']) + $childUpdates[] = 'id_cat = {int:category}'; + + // Fix the children of this board. + if (!empty($childList) && !empty($childUpdates)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET ' . implode(', + ', $childUpdates) . ' + WHERE id_board IN ({array_int:board_list})', + array( + 'board_list' => $childList, + 'category' => $id_cat, + 'level_diff' => $levelDiff, + ) + ); + + // Make some room for this spot. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET board_order = board_order + {int:new_order} + WHERE board_order > {int:insert_after} + AND id_board != {int:selected_board}', + array( + 'insert_after' => $after, + 'selected_board' => $board_id, + 'new_order' => 1 + count($childList), + ) + ); + + $boardUpdates[] = 'id_cat = {int:id_cat}'; + $boardUpdates[] = 'id_parent = {int:id_parent}'; + $boardUpdates[] = 'child_level = {int:child_level}'; + $boardUpdates[] = 'board_order = {int:board_order}'; + $boardUpdateParameters += array( + 'id_cat' => $id_cat, + 'id_parent' => $id_parent, + 'child_level' => $child_level, + 'board_order' => $after + 1, + ); + } + + // This setting is a little twisted in the database... + if (isset($boardOptions['posts_count'])) + { + $boardUpdates[] = 'count_posts = {int:count_posts}'; + $boardUpdateParameters['count_posts'] = $boardOptions['posts_count'] ? 0 : 1; + } + + // Set the theme for this board. + if (isset($boardOptions['board_theme'])) + { + $boardUpdates[] = 'id_theme = {int:id_theme}'; + $boardUpdateParameters['id_theme'] = (int) $boardOptions['board_theme']; + } + + // Should the board theme override the user preferred theme? + if (isset($boardOptions['override_theme'])) + { + $boardUpdates[] = 'override_theme = {int:override_theme}'; + $boardUpdateParameters['override_theme'] = $boardOptions['override_theme'] ? 1 : 0; + } + + // Who's allowed to access this board. + if (isset($boardOptions['access_groups'])) + { + $boardUpdates[] = 'member_groups = {string:member_groups}'; + $boardUpdateParameters['member_groups'] = implode(',', $boardOptions['access_groups']); + } + + if (isset($boardOptions['board_name'])) + { + $boardUpdates[] = 'name = {string:board_name}'; + $boardUpdateParameters['board_name'] = $boardOptions['board_name']; + } + + if (isset($boardOptions['board_description'])) + { + $boardUpdates[] = 'description = {string:board_description}'; + $boardUpdateParameters['board_description'] = $boardOptions['board_description']; + } + + if (isset($boardOptions['profile'])) + { + $boardUpdates[] = 'id_profile = {int:profile}'; + $boardUpdateParameters['profile'] = (int) $boardOptions['profile']; + } + + if (isset($boardOptions['redirect'])) + { + $boardUpdates[] = 'redirect = {string:redirect}'; + $boardUpdateParameters['redirect'] = $boardOptions['redirect']; + } + + if (isset($boardOptions['num_posts'])) + { + $boardUpdates[] = 'num_posts = {int:num_posts}'; + $boardUpdateParameters['num_posts'] = (int) $boardOptions['num_posts']; + } + + // Do the updates (if any). + if (!empty($boardUpdates)) + $request = $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET + ' . implode(', + ', $boardUpdates) . ' + WHERE id_board = {int:selected_board}', + array_merge($boardUpdateParameters, array( + 'selected_board' => $board_id, + )) + ); + + // Set moderators of this board. + if (isset($boardOptions['moderators']) || isset($boardOptions['moderator_string'])) + { + // Reset current moderators for this board - if there are any! + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}moderators + WHERE id_board = {int:board_list}', + array( + 'board_list' => $board_id, + ) + ); + + // Validate and get the IDs of the new moderators. + if (isset($boardOptions['moderator_string']) && trim($boardOptions['moderator_string']) != '') + { + // Divvy out the usernames, remove extra space. + $moderator_string = strtr($smcFunc['htmlspecialchars']($boardOptions['moderator_string'], ENT_QUOTES), array('"' => '"')); + preg_match_all('~"([^"]+)"~', $moderator_string, $matches); + $moderators = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $moderator_string))); + for ($k = 0, $n = count($moderators); $k < $n; $k++) + { + $moderators[$k] = trim($moderators[$k]); + + if (strlen($moderators[$k]) == 0) + unset($moderators[$k]); + } + + // Find all the id_member's for the member_name's in the list. + if (empty($boardOptions['moderators'])) + $boardOptions['moderators'] = array(); + if (!empty($moderators)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE member_name IN ({array_string:moderator_list}) OR real_name IN ({array_string:moderator_list}) + LIMIT ' . count($moderators), + array( + 'moderator_list' => $moderators, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boardOptions['moderators'][] = $row['id_member']; + $smcFunc['db_free_result']($request); + } + } + + // Add the moderators to the board. + if (!empty($boardOptions['moderators'])) + { + $inserts = array(); + foreach ($boardOptions['moderators'] as $moderator) + $inserts[] = array($board_id, $moderator); + + $smcFunc['db_insert']('insert', + '{db_prefix}moderators', + array('id_board' => 'int', 'id_member' => 'int'), + $inserts, + array('id_board', 'id_member') + ); + } + + // Note that caches can now be wrong! + updateSettings(array('settings_updated' => time())); + } + + if (isset($boardOptions['move_to'])) + reorderBoards(); + + clean_cache('data'); + + if (empty($boardOptions['dont_log'])) + logAction('edit_board', array('board' => $board_id), 'admin'); +} + +// Create a new board and set its properties and position. +function createBoard($boardOptions) +{ + global $boards, $modSettings, $smcFunc; + + // Trigger an error if one of the required values is not set. + if (!isset($boardOptions['board_name']) || trim($boardOptions['board_name']) == '' || !isset($boardOptions['move_to']) || !isset($boardOptions['target_category'])) + trigger_error('createBoard(): One or more of the required options is not set', E_USER_ERROR); + + if (in_array($boardOptions['move_to'], array('child', 'before', 'after')) && !isset($boardOptions['target_board'])) + trigger_error('createBoard(): Target board is not set', E_USER_ERROR); + + // Set every optional value to its default value. + $boardOptions += array( + 'posts_count' => true, + 'override_theme' => false, + 'board_theme' => 0, + 'access_groups' => array(), + 'board_description' => '', + 'profile' => 1, + 'moderators' => '', + 'inherit_permissions' => true, + 'dont_log' => true, + ); + + // Insert a board, the settings are dealt with later. + $smcFunc['db_insert']('', + '{db_prefix}boards', + array( + 'id_cat' => 'int', 'name' => 'string-255', 'description' => 'string', 'board_order' => 'int', + 'member_groups' => 'string', 'redirect' => 'string', + ), + array( + $boardOptions['target_category'], $boardOptions['board_name'] , '', 0, + '-1,0', '', + ), + array('id_board') + ); + $board_id = $smcFunc['db_insert_id']('{db_prefix}boards', 'id_board'); + + if (empty($board_id)) + return 0; + + // Change the board according to the given specifications. + modifyBoard($board_id, $boardOptions); + + // Do we want the parent permissions to be inherited? + if ($boardOptions['inherit_permissions']) + { + getBoardTree(); + + if (!empty($boards[$board_id]['parent'])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_profile + FROM {db_prefix}boards + WHERE id_board = {int:board_parent} + LIMIT 1', + array( + 'board_parent' => (int) $boards[$board_id]['parent'], + ) + ); + list ($boardOptions['profile']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET id_profile = {int:new_profile} + WHERE id_board = {int:current_board}', + array( + 'new_profile' => $boardOptions['profile'], + 'current_board' => $board_id, + ) + ); + } + } + + // Clean the data cache. + clean_cache('data'); + + // Created it. + logAction('add_board', array('board' => $board_id), 'admin'); + + // Here you are, a new board, ready to be spammed. + return $board_id; +} + +// Remove one or more boards. +function deleteBoards($boards_to_remove, $moveChildrenTo = null) +{ + global $sourcedir, $boards, $smcFunc; + + // No boards to delete? Return! + if (empty($boards_to_remove)) + return; + + getBoardTree(); + + // If $moveChildrenTo is set to null, include the children in the removal. + if ($moveChildrenTo === null) + { + // Get a list of the child boards that will also be removed. + $child_boards_to_remove = array(); + foreach ($boards_to_remove as $board_to_remove) + recursiveBoards($child_boards_to_remove, $boards[$board_to_remove]['tree']); + + // Merge the children with their parents. + if (!empty($child_boards_to_remove)) + $boards_to_remove = array_unique(array_merge($boards_to_remove, $child_boards_to_remove)); + } + // Move the children to a safe home. + else + { + foreach ($boards_to_remove as $id_board) + { + // !!! Separate category? + if ($moveChildrenTo === 0) + fixChildren($id_board, 0, 0); + else + fixChildren($id_board, $boards[$moveChildrenTo]['level'] + 1, $moveChildrenTo); + } + } + + // Delete ALL topics in the selected boards (done first so topics can't be marooned.) + $request = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}topics + WHERE id_board IN ({array_int:boards_to_remove})', + array( + 'boards_to_remove' => $boards_to_remove, + ) + ); + $topics = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topics[] = $row['id_topic']; + $smcFunc['db_free_result']($request); + + require_once($sourcedir . '/RemoveTopic.php'); + removeTopics($topics, false); + + // Delete the board's logs. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_mark_read + WHERE id_board IN ({array_int:boards_to_remove})', + array( + 'boards_to_remove' => $boards_to_remove, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_boards + WHERE id_board IN ({array_int:boards_to_remove})', + array( + 'boards_to_remove' => $boards_to_remove, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_notify + WHERE id_board IN ({array_int:boards_to_remove})', + array( + 'boards_to_remove' => $boards_to_remove, + ) + ); + + // Delete this board's moderators. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}moderators + WHERE id_board IN ({array_int:boards_to_remove})', + array( + 'boards_to_remove' => $boards_to_remove, + ) + ); + + // Delete any extra events in the calendar. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}calendar + WHERE id_board IN ({array_int:boards_to_remove})', + array( + 'boards_to_remove' => $boards_to_remove, + ) + ); + + // Delete any message icons that only appear on these boards. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}message_icons + WHERE id_board IN ({array_int:boards_to_remove})', + array( + 'boards_to_remove' => $boards_to_remove, + ) + ); + + // Delete the boards. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}boards + WHERE id_board IN ({array_int:boards_to_remove})', + array( + 'boards_to_remove' => $boards_to_remove, + ) + ); + + // Latest message/topic might not be there anymore. + updateStats('message'); + updateStats('topic'); + updateSettings(array( + 'calendar_updated' => time(), + )); + + // Plus reset the cache to stop people getting odd results. + updateSettings(array('settings_updated' => time())); + + // Clean the cache as well. + clean_cache('data'); + + // Let's do some serious logging. + foreach ($boards_to_remove as $id_board) + logAction('delete_board', array('boardname' => $boards[$id_board]['name']), 'admin'); + + reorderBoards(); +} + +// Put all boards in the right order. +function reorderBoards() +{ + global $cat_tree, $boardList, $boards, $smcFunc; + + getBoardTree(); + + // Set the board order for each category. + $board_order = 0; + foreach ($cat_tree as $catID => $dummy) + { + foreach ($boardList[$catID] as $boardID) + if ($boards[$boardID]['order'] != ++$board_order) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET board_order = {int:new_order} + WHERE id_board = {int:selected_board}', + array( + 'new_order' => $board_order, + 'selected_board' => $boardID, + ) + ); + } + + // Sort the records of the boards table on the board_order value. + $smcFunc['db_query']('alter_table_boards', ' + ALTER TABLE {db_prefix}boards + ORDER BY board_order', + array( + 'db_error_skip' => true, + ) + ); +} + +// Fixes the children of a board by setting their child_levels to new values. +function fixChildren($parent, $newLevel, $newParent) +{ + global $smcFunc; + + // Grab all children of $parent... + $result = $smcFunc['db_query']('', ' + SELECT id_board + FROM {db_prefix}boards + WHERE id_parent = {int:parent_board}', + array( + 'parent_board' => $parent, + ) + ); + $children = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + $children[] = $row['id_board']; + $smcFunc['db_free_result']($result); + + // ...and set it to a new parent and child_level. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET id_parent = {int:new_parent}, child_level = {int:new_child_level} + WHERE id_parent = {int:parent_board}', + array( + 'new_parent' => $newParent, + 'new_child_level' => $newLevel, + 'parent_board' => $parent, + ) + ); + + // Recursively fix the children of the children. + foreach ($children as $child) + fixChildren($child, $newLevel + 1, $child); +} + +// Load a lot of useful information regarding the boards and categories. +function getBoardTree() +{ + global $cat_tree, $boards, $boardList, $txt, $modSettings, $smcFunc; + + // Getting all the board and category information you'd ever wanted. + $request = $smcFunc['db_query']('', ' + SELECT + IFNULL(b.id_board, 0) AS id_board, b.id_parent, b.name AS board_name, b.description, b.child_level, + b.board_order, b.count_posts, b.member_groups, b.id_theme, b.override_theme, b.id_profile, b.redirect, + b.num_posts, b.num_topics, c.id_cat, c.name AS cat_name, c.cat_order, c.can_collapse + FROM {db_prefix}categories AS c + LEFT JOIN {db_prefix}boards AS b ON (b.id_cat = c.id_cat) + ORDER BY c.cat_order, b.child_level, b.board_order', + array( + ) + ); + $cat_tree = array(); + $boards = array(); + $last_board_order = 0; + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($cat_tree[$row['id_cat']])) + { + $cat_tree[$row['id_cat']] = array( + 'node' => array( + 'id' => $row['id_cat'], + 'name' => $row['cat_name'], + 'order' => $row['cat_order'], + 'can_collapse' => $row['can_collapse'] + ), + 'is_first' => empty($cat_tree), + 'last_board_order' => $last_board_order, + 'children' => array() + ); + $prevBoard = 0; + $curLevel = 0; + } + + if (!empty($row['id_board'])) + { + if ($row['child_level'] != $curLevel) + $prevBoard = 0; + + $boards[$row['id_board']] = array( + 'id' => $row['id_board'], + 'category' => $row['id_cat'], + 'parent' => $row['id_parent'], + 'level' => $row['child_level'], + 'order' => $row['board_order'], + 'name' => $row['board_name'], + 'member_groups' => explode(',', $row['member_groups']), + 'description' => $row['description'], + 'count_posts' => empty($row['count_posts']), + 'posts' => $row['num_posts'], + 'topics' => $row['num_topics'], + 'theme' => $row['id_theme'], + 'override_theme' => $row['override_theme'], + 'profile' => $row['id_profile'], + 'redirect' => $row['redirect'], + 'prev_board' => $prevBoard + ); + $prevBoard = $row['id_board']; + $last_board_order = $row['board_order']; + + if (empty($row['child_level'])) + { + $cat_tree[$row['id_cat']]['children'][$row['id_board']] = array( + 'node' => &$boards[$row['id_board']], + 'is_first' => empty($cat_tree[$row['id_cat']]['children']), + 'children' => array() + ); + $boards[$row['id_board']]['tree'] = &$cat_tree[$row['id_cat']]['children'][$row['id_board']]; + } + else + { + // Parent doesn't exist! + if (!isset($boards[$row['id_parent']]['tree'])) + fatal_lang_error('no_valid_parent', false, array($row['board_name'])); + + // Wrong childlevel...we can silently fix this... + if ($boards[$row['id_parent']]['tree']['node']['level'] != $row['child_level'] - 1) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET child_level = {int:new_child_level} + WHERE id_board = {int:selected_board}', + array( + 'new_child_level' => $boards[$row['id_parent']]['tree']['node']['level'] + 1, + 'selected_board' => $row['id_board'], + ) + ); + + $boards[$row['id_parent']]['tree']['children'][$row['id_board']] = array( + 'node' => &$boards[$row['id_board']], + 'is_first' => empty($boards[$row['id_parent']]['tree']['children']), + 'children' => array() + ); + $boards[$row['id_board']]['tree'] = &$boards[$row['id_parent']]['tree']['children'][$row['id_board']]; + } + } + } + $smcFunc['db_free_result']($request); + + // Get a list of all the boards in each category (using recursion). + $boardList = array(); + foreach ($cat_tree as $catID => $node) + { + $boardList[$catID] = array(); + recursiveBoards($boardList[$catID], $node); + } +} + +// Recursively get a list of boards. +function recursiveBoards(&$_boardList, &$_tree) +{ + if (empty($_tree['children'])) + return; + + foreach ($_tree['children'] as $id => $node) + { + $_boardList[] = $id; + recursiveBoards($_boardList, $node); + } +} + +// Returns whether the child board id is actually a child of the parent (recursive). +function isChildOf($child, $parent) +{ + global $boards; + + if (empty($boards[$child]['parent'])) + return false; + + if ($boards[$child]['parent'] == $parent) + return true; + + return isChildOf($boards[$child]['parent'], $parent); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Calendar.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Calendar.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1046 @@ + 1, + 'no_month' => 0, + 'no_day' => 0, + 'year_one' => '0001', + 'year_low' => $year_low . '-%m-%d', + 'year_high' => $year_high . '-%m-%d', + 'low_date' => $low_date, + 'high_date' => $high_date, + 'max_year' => $year_high, + ) + ); + $bday = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if ($year_low != $year_high) + $age_year = substr($row['birthdate'], 5) < substr($high_date, 5) ? $year_high : $year_low; + else + $age_year = $year_low; + + $bday[$age_year . substr($row['birthdate'], 4)][] = array( + 'id' => $row['id_member'], + 'name' => $row['real_name'], + 'age' => $row['birth_year'] > 4 && $row['birth_year'] <= $age_year ? $age_year - $row['birth_year'] : null, + 'is_last' => false + ); + } + $smcFunc['db_free_result']($result); + + // Set is_last, so the themes know when to stop placing separators. + foreach ($bday as $mday => $array) + $bday[$mday][count($array) - 1]['is_last'] = true; + + return $bday; +} + +// Get all events within the given time range. +function getEventRange($low_date, $high_date, $use_permissions = true) +{ + global $scripturl, $modSettings, $user_info, $smcFunc, $context; + + $low_date_time = sscanf($low_date, '%04d-%02d-%02d'); + $low_date_time = mktime(0, 0, 0, $low_date_time[1], $low_date_time[2], $low_date_time[0]); + $high_date_time = sscanf($high_date, '%04d-%02d-%02d'); + $high_date_time = mktime(0, 0, 0, $high_date_time[1], $high_date_time[2], $high_date_time[0]); + + // Find all the calendar info... + $result = $smcFunc['db_query']('', ' + SELECT + cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, cal.id_topic, + cal.id_board, b.member_groups, t.id_first_msg, t.approved, b.id_board + FROM {db_prefix}calendar AS cal + LEFT JOIN {db_prefix}boards AS b ON (b.id_board = cal.id_board) + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic) + WHERE cal.start_date <= {date:high_date} + AND cal.end_date >= {date:low_date}' . ($use_permissions ? ' + AND (cal.id_board = {int:no_board_link} OR {query_wanna_see_board})' : ''), + array( + 'high_date' => $high_date, + 'low_date' => $low_date, + 'no_board_link' => 0, + ) + ); + $events = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // If the attached topic is not approved then for the moment pretend it doesn't exist + //!!! This should be fixed to show them all and then sort by approval state later? + if (!empty($row['id_first_msg']) && $modSettings['postmod_active'] && !$row['approved']) + continue; + + // Force a censor of the title - as often these are used by others. + censorText($row['title'], $use_permissions ? false : true); + + $start_date = sscanf($row['start_date'], '%04d-%02d-%02d'); + $start_date = max(mktime(0, 0, 0, $start_date[1], $start_date[2], $start_date[0]), $low_date_time); + $end_date = sscanf($row['end_date'], '%04d-%02d-%02d'); + $end_date = min(mktime(0, 0, 0, $end_date[1], $end_date[2], $end_date[0]), $high_date_time); + + $lastDate = ''; + for ($date = $start_date; $date <= $end_date; $date += 86400) + { + // Attempt to avoid DST problems. + //!!! Resolve this properly at some point. + if (strftime('%Y-%m-%d', $date) == $lastDate) + $date += 3601; + $lastDate = strftime('%Y-%m-%d', $date); + + // If we're using permissions (calendar pages?) then just ouput normal contextual style information. + if ($use_permissions) + $events[strftime('%Y-%m-%d', $date)][] = array( + 'id' => $row['id_event'], + 'title' => $row['title'], + 'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')), + 'modify_href' => $scripturl . '?action=' . ($row['id_board'] == 0 ? 'calendar;sa=post;' : 'post;msg=' . $row['id_first_msg'] . ';topic=' . $row['id_topic'] . '.0;calendar;') . 'eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], + 'href' => $row['id_board'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'link' => $row['id_board'] == 0 ? $row['title'] : '' . $row['title'] . '', + 'start_date' => $row['start_date'], + 'end_date' => $row['end_date'], + 'is_last' => false, + 'id_board' => $row['id_board'], + ); + // Otherwise, this is going to be cached and the VIEWER'S permissions should apply... just put together some info. + else + $events[strftime('%Y-%m-%d', $date)][] = array( + 'id' => $row['id_event'], + 'title' => $row['title'], + 'topic' => $row['id_topic'], + 'msg' => $row['id_first_msg'], + 'poster' => $row['id_member'], + 'start_date' => $row['start_date'], + 'end_date' => $row['end_date'], + 'is_last' => false, + 'allowed_groups' => explode(',', $row['member_groups']), + 'id_board' => $row['id_board'], + 'href' => $row['id_topic'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0', + 'link' => $row['id_topic'] == 0 ? $row['title'] : '' . $row['title'] . '', + 'can_edit' => false, + ); + } + } + $smcFunc['db_free_result']($result); + + // If we're doing normal contextual data, go through and make things clear to the templates ;). + if ($use_permissions) + { + foreach ($events as $mday => $array) + $events[$mday][count($array) - 1]['is_last'] = true; + } + + return $events; +} + +// Get all holidays within the given time range. +function getHolidayRange($low_date, $high_date) +{ + global $smcFunc; + + // Get the lowest and highest dates for "all years". + if (substr($low_date, 0, 4) != substr($high_date, 0, 4)) + $allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_dec} + OR event_date BETWEEN {date:all_year_jan} AND {date:all_year_high}'; + else + $allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_high}'; + + // Find some holidays... ;). + $result = $smcFunc['db_query']('', ' + SELECT event_date, YEAR(event_date) AS year, title + FROM {db_prefix}calendar_holidays + WHERE event_date BETWEEN {date:low_date} AND {date:high_date} + OR ' . $allyear_part, + array( + 'low_date' => $low_date, + 'high_date' => $high_date, + 'all_year_low' => '0004' . substr($low_date, 4), + 'all_year_high' => '0004' . substr($high_date, 4), + 'all_year_jan' => '0004-01-01', + 'all_year_dec' => '0004-12-31', + ) + ); + $holidays = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + if (substr($low_date, 0, 4) != substr($high_date, 0, 4)) + $event_year = substr($row['event_date'], 5) < substr($high_date, 5) ? substr($high_date, 0, 4) : substr($low_date, 0, 4); + else + $event_year = substr($low_date, 0, 4); + + $holidays[$event_year . substr($row['event_date'], 4)][] = $row['title']; + } + $smcFunc['db_free_result']($result); + + return $holidays; +} + +// Does permission checks to see if an event can be linked to a board/topic. +function canLinkEvent() +{ + global $user_info, $topic, $board, $smcFunc; + + // If you can't post, you can't link. + isAllowedTo('calendar_post'); + + // No board? No topic?!? + if (empty($board)) + fatal_lang_error('missing_board_id', false); + if (empty($topic)) + fatal_lang_error('missing_topic_id', false); + + // Administrator, Moderator, or owner. Period. + if (!allowedTo('admin_forum') && !allowedTo('moderate_board')) + { + // Not admin or a moderator of this board. You better be the owner - or else. + $result = $smcFunc['db_query']('', ' + SELECT id_member_started + FROM {db_prefix}topics + WHERE id_topic = {int:current_topic} + LIMIT 1', + array( + 'current_topic' => $topic, + ) + ); + if ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Not the owner of the topic. + if ($row['id_member_started'] != $user_info['id']) + fatal_lang_error('not_your_topic', 'user'); + } + // Topic/Board doesn't exist..... + else + fatal_lang_error('calendar_no_topic', 'general'); + $smcFunc['db_free_result']($result); + } +} + +// Returns date information about 'today' relative to the users time offset. +function getTodayInfo() +{ + return array( + 'day' => (int) strftime('%d', forum_time()), + 'month' => (int) strftime('%m', forum_time()), + 'year' => (int) strftime('%Y', forum_time()), + 'date' => strftime('%Y-%m-%d', forum_time()), + ); +} + +// Returns the information needed to show a calendar grid for the given month. +function getCalendarGrid($month, $year, $calendarOptions) +{ + global $scripturl, $modSettings; + + // Eventually this is what we'll be returning. + $calendarGrid = array( + 'week_days' => array(), + 'weeks' => array(), + 'short_day_titles' => !empty($calendarOptions['short_day_titles']), + 'current_month' => $month, + 'current_year' => $year, + 'show_next_prev' => !empty($calendarOptions['show_next_prev']), + 'show_week_links' => !empty($calendarOptions['show_week_links']), + 'previous_calendar' => array( + 'year' => $month == 1 ? $year - 1 : $year, + 'month' => $month == 1 ? 12 : $month - 1, + 'disabled' => $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year), + ), + 'next_calendar' => array( + 'year' => $month == 12 ? $year + 1 : $year, + 'month' => $month == 12 ? 1 : $month + 1, + 'disabled' => $modSettings['cal_maxyear'] < ($month == 12 ? $year + 1 : $year), + ), + //!!! Better tweaks? + 'size' => isset($calendarOptions['size']) ? $calendarOptions['size'] : 'large', + ); + + // Get todays date. + $today = getTodayInfo(); + + // Get information about this month. + $month_info = array( + 'first_day' => array( + 'day_of_week' => (int) strftime('%w', mktime(0, 0, 0, $month, 1, $year)), + 'week_num' => (int) strftime('%U', mktime(0, 0, 0, $month, 1, $year)), + 'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month, 1, $year)), + ), + 'last_day' => array( + 'day_of_month' => (int) strftime('%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)), + 'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)), + ), + 'first_day_of_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year)), + 'first_day_of_next_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year + 1)), + ); + + // The number of days the first row is shifted to the right for the starting day. + $nShift = $month_info['first_day']['day_of_week']; + + $calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day']; + + // Starting any day other than Sunday means a shift... + if (!empty($calendarOptions['start_day'])) + { + $nShift -= $calendarOptions['start_day']; + if ($nShift < 0) + $nShift = 7 + $nShift; + } + + // Number of rows required to fit the month. + $nRows = floor(($month_info['last_day']['day_of_month'] + $nShift) / 7); + if (($month_info['last_day']['day_of_month'] + $nShift) % 7) + $nRows++; + + // Fetch the arrays for birthdays, posted events, and holidays. + $bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array(); + $events = $calendarOptions['show_events'] ? getEventRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array(); + $holidays = $calendarOptions['show_holidays'] ? getHolidayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array(); + + // Days of the week taking into consideration that they may want it to start on any day. + $count = $calendarOptions['start_day']; + for ($i = 0; $i < 7; $i++) + { + $calendarGrid['week_days'][] = $count; + $count++; + if ($count == 7) + $count = 0; + } + + // An adjustment value to apply to all calculated week numbers. + if (!empty($calendarOptions['show_week_num'])) + { + // If the first day of the year is a Sunday, then there is no + // adjustment to be made. However, if the first day of the year is not + // a Sunday, then there is a partial week at the start of the year + // that needs to be accounted for. + if ($calendarOptions['start_day'] === 0) + $nWeekAdjust = $month_info['first_day_of_year'] === 0 ? 0 : 1; + // If we are viewing the weeks, with a starting date other than Sunday, + // then things get complicated! Basically, as PHP is calculating the + // weeks with a Sunday starting date, we need to take this into account + // and offset the whole year dependant on whether the first day in the + // year is above or below our starting date. Note that we offset by + // two, as some of this will get undone quite quickly by the statement + // below. + else + $nWeekAdjust = $calendarOptions['start_day'] > $month_info['first_day_of_year'] && $month_info['first_day_of_year'] !== 0 ? 2 : 1; + + // If our week starts on a day greater than the day the month starts + // on, then our week numbers will be one too high. So we need to + // reduce it by one - all these thoughts of offsets makes my head + // hurt... + if ($month_info['first_day']['day_of_week'] < $calendarOptions['start_day'] || $month_info['first_day_of_year'] > 4) + $nWeekAdjust--; + } + else + $nWeekAdjust = 0; + + // Iterate through each week. + $calendarGrid['weeks'] = array(); + for ($nRow = 0; $nRow < $nRows; $nRow++) + { + // Start off the week - and don't let it go above 52, since that's the number of weeks in a year. + $calendarGrid['weeks'][$nRow] = array( + 'days' => array(), + 'number' => $month_info['first_day']['week_num'] + $nRow + $nWeekAdjust + ); + // Handle the dreaded "week 53", it can happen, but only once in a blue moon ;) + if ($calendarGrid['weeks'][$nRow]['number'] == 53 && $nShift != 4 && $month_info['first_day_of_next_year'] < 4) + $calendarGrid['weeks'][$nRow]['number'] = 1; + + // And figure out all the days. + for ($nCol = 0; $nCol < 7; $nCol++) + { + $nDay = ($nRow * 7) + $nCol - $nShift + 1; + + if ($nDay < 1 || $nDay > $month_info['last_day']['day_of_month']) + $nDay = 0; + + $date = sprintf('%04d-%02d-%02d', $year, $month, $nDay); + + $calendarGrid['weeks'][$nRow]['days'][$nCol] = array( + 'day' => $nDay, + 'date' => $date, + 'is_today' => $date == $today['date'], + 'is_first_day' => !empty($calendarOptions['show_week_num']) && (($month_info['first_day']['day_of_week'] + $nDay - 1) % 7 == $calendarOptions['start_day']), + 'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(), + 'events' => !empty($events[$date]) ? $events[$date] : array(), + 'birthdays' => !empty($bday[$date]) ? $bday[$date] : array() + ); + } + } + + // Set the previous and the next month's links. + $calendarGrid['previous_calendar']['href'] = $scripturl . '?action=calendar;year=' . $calendarGrid['previous_calendar']['year'] . ';month=' . $calendarGrid['previous_calendar']['month']; + $calendarGrid['next_calendar']['href'] = $scripturl . '?action=calendar;year=' . $calendarGrid['next_calendar']['year'] . ';month=' . $calendarGrid['next_calendar']['month']; + + return $calendarGrid; +} + +// Returns the information needed to show a calendar for the given week. +function getCalendarWeek($month, $year, $day, $calendarOptions) +{ + global $scripturl, $modSettings; + + // Get todays date. + $today = getTodayInfo(); + + // What is the actual "start date" for the passed day. + $calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day']; + $day_of_week = (int) strftime('%w', mktime(0, 0, 0, $month, $day, $year)); + if ($day_of_week != $calendarOptions['start_day']) + { + // Here we offset accordingly to get things to the real start of a week. + $date_diff = $day_of_week - $calendarOptions['start_day']; + if ($date_diff < 0) + $date_diff += 7; + $new_timestamp = mktime(0, 0, 0, $month, $day, $year) - $date_diff * 86400; + $day = (int) strftime('%d', $new_timestamp); + $month = (int) strftime('%m', $new_timestamp); + $year = (int) strftime('%Y', $new_timestamp); + } + + // Now start filling in the calendar grid. + $calendarGrid = array( + 'show_next_prev' => !empty($calendarOptions['show_next_prev']), + // Previous week is easy - just step back one day. + 'previous_week' => array( + 'year' => $day == 1 ? ($month == 1 ? $year - 1 : $year) : $year, + 'month' => $day == 1 ? ($month == 1 ? 12 : $month - 1) : $month, + 'day' => $day == 1 ? 28 : $day - 1, + 'disabled' => $day < 7 && $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year), + ), + 'next_week' => array( + 'disabled' => $day > 25 && $modSettings['cal_maxyear'] < ($month == 12 ? $year + 1 : $year), + ), + ); + + // The next week calculation requires a bit more work. + $curTimestamp = mktime(0, 0, 0, $month, $day, $year); + $nextWeekTimestamp = $curTimestamp + 604800; + $calendarGrid['next_week']['day'] = (int) strftime('%d', $nextWeekTimestamp); + $calendarGrid['next_week']['month'] = (int) strftime('%m', $nextWeekTimestamp); + $calendarGrid['next_week']['year'] = (int) strftime('%Y', $nextWeekTimestamp); + + // Fetch the arrays for birthdays, posted events, and holidays. + $startDate = strftime('%Y-%m-%d', $curTimestamp); + $endDate = strftime('%Y-%m-%d', $nextWeekTimestamp); + $bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($startDate, $endDate) : array(); + $events = $calendarOptions['show_events'] ? getEventRange($startDate, $endDate) : array(); + $holidays = $calendarOptions['show_holidays'] ? getHolidayRange($startDate, $endDate) : array(); + + // An adjustment value to apply to all calculated week numbers. + if (!empty($calendarOptions['show_week_num'])) + { + $first_day_of_year = (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year)); + $first_day_of_next_year = (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year + 1)); + $last_day_of_last_year = (int) strftime('%w', mktime(0, 0, 0, 12, 31, $year - 1)); + + // All this is as getCalendarGrid. + if ($calendarOptions['start_day'] === 0) + $nWeekAdjust = $first_day_of_year === 0 && $first_day_of_year > 3 ? 0 : 1; + else + $nWeekAdjust = $calendarOptions['start_day'] > $first_day_of_year && $first_day_of_year !== 0 ? 2 : 1; + + $calendarGrid['week_number'] = (int) strftime('%U', mktime(0, 0, 0, $month, $day, $year)) + $nWeekAdjust; + + // If this crosses a year boundry and includes january it should be week one. + if ((int) strftime('%Y', $curTimestamp + 518400) != $year && $calendarGrid['week_number'] > 53 && $first_day_of_next_year < 5) + $calendarGrid['week_number'] = 1; + } + + // This holds all the main data - there is at least one month! + $calendarGrid['months'] = array(); + $lastDay = 99; + $curDay = $day; + $curDayOfWeek = $calendarOptions['start_day']; + for ($i = 0; $i < 7; $i++) + { + // Have we gone into a new month (Always happens first cycle too) + if ($lastDay > $curDay) + { + $curMonth = $lastDay == 99 ? $month : ($month == 12 ? 1 : $month + 1); + $curYear = $lastDay == 99 ? $year : ($curMonth == 1 && $month == 12 ? $year + 1 : $year); + $calendarGrid['months'][$curMonth] = array( + 'current_month' => $curMonth, + 'current_year' => $curYear, + 'days' => array(), + ); + } + + // Add todays information to the pile! + $date = sprintf('%04d-%02d-%02d', $curYear, $curMonth, $curDay); + + $calendarGrid['months'][$curMonth]['days'][$curDay] = array( + 'day' => $curDay, + 'day_of_week' => $curDayOfWeek, + 'date' => $date, + 'is_today' => $date == $today['date'], + 'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(), + 'events' => !empty($events[$date]) ? $events[$date] : array(), + 'birthdays' => !empty($bday[$date]) ? $bday[$date] : array() + ); + + // Make the last day what the current day is and work out what the next day is. + $lastDay = $curDay; + $curTimestamp += 86400; + $curDay = (int) strftime('%d', $curTimestamp); + + // Also increment the current day of the week. + $curDayOfWeek = $curDayOfWeek >= 6 ? 0 : ++$curDayOfWeek; + } + + // Set the previous and the next week's links. + $calendarGrid['previous_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['previous_week']['year'] . ';month=' . $calendarGrid['previous_week']['month'] . ';day=' . $calendarGrid['previous_week']['day']; + $calendarGrid['next_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['next_week']['year'] . ';month=' . $calendarGrid['next_week']['month'] . ';day=' . $calendarGrid['next_week']['day']; + + return $calendarGrid; +} + +// Retrieve all events for the given days, independently of the users offset. +function cache_getOffsetIndependentEvents($days_to_index) +{ + global $sourcedir; + + $low_date = strftime('%Y-%m-%d', forum_time(false) - 24 * 3600); + $high_date = strftime('%Y-%m-%d', forum_time(false) + $days_to_index * 24 * 3600); + + return array( + 'data' => array( + 'holidays' => getHolidayRange($low_date, $high_date), + 'birthdays' => getBirthdayRange($low_date, $high_date), + 'events' => getEventRange($low_date, $high_date, false), + ), + 'refresh_eval' => 'return \'' . strftime('%Y%m%d', forum_time(false)) . '\' != strftime(\'%Y%m%d\', forum_time(false)) || (!empty($modSettings[\'calendar_updated\']) && ' . time() . ' < $modSettings[\'calendar_updated\']);', + 'expires' => time() + 3600, + ); +} + +// Called from the BoardIndex to display the current day's events on the board index. +function cache_getRecentEvents($eventOptions) +{ + global $modSettings, $user_info, $scripturl; + + // With the 'static' cached data we can calculate the user-specific data. + $cached_data = cache_quick_get('calendar_index', 'Subs-Calendar.php', 'cache_getOffsetIndependentEvents', array($eventOptions['num_days_shown'])); + + // Get the information about today (from user perspective). + $today = getTodayInfo(); + + $return_data = array( + 'calendar_holidays' => array(), + 'calendar_birthdays' => array(), + 'calendar_events' => array(), + ); + + // Set the event span to be shown in seconds. + $days_for_index = $eventOptions['num_days_shown'] * 86400; + + // Get the current member time/date. + $now = forum_time(); + + // Holidays between now and now + days. + for ($i = $now; $i < $now + $days_for_index; $i += 86400) + { + if (isset($cached_data['holidays'][strftime('%Y-%m-%d', $i)])) + $return_data['calendar_holidays'] = array_merge($return_data['calendar_holidays'], $cached_data['holidays'][strftime('%Y-%m-%d', $i)]); + } + + // Happy Birthday, guys and gals! + for ($i = $now; $i < $now + $days_for_index; $i += 86400) + { + $loop_date = strftime('%Y-%m-%d', $i); + if (isset($cached_data['birthdays'][$loop_date])) + { + foreach ($cached_data['birthdays'][$loop_date] as $index => $dummy) + $cached_data['birthdays'][strftime('%Y-%m-%d', $i)][$index]['is_today'] = $loop_date === $today['date']; + $return_data['calendar_birthdays'] = array_merge($return_data['calendar_birthdays'], $cached_data['birthdays'][$loop_date]); + } + } + + $duplicates = array(); + for ($i = $now; $i < $now + $days_for_index; $i += 86400) + { + // Determine the date of the current loop step. + $loop_date = strftime('%Y-%m-%d', $i); + + // No events today? Check the next day. + if (empty($cached_data['events'][$loop_date])) + continue; + + // Loop through all events to add a few last-minute values. + foreach ($cached_data['events'][$loop_date] as $ev => $event) + { + // Create a shortcut variable for easier access. + $this_event = &$cached_data['events'][$loop_date][$ev]; + + // Skip duplicates. + if (isset($duplicates[$this_event['topic'] . $this_event['title']])) + { + unset($cached_data['events'][$loop_date][$ev]); + continue; + } + else + $duplicates[$this_event['topic'] . $this_event['title']] = true; + + // Might be set to true afterwards, depending on the permissions. + $this_event['can_edit'] = false; + $this_event['is_today'] = $loop_date === $today['date']; + $this_event['date'] = $loop_date; + } + + if (!empty($cached_data['events'][$loop_date])) + $return_data['calendar_events'] = array_merge($return_data['calendar_events'], $cached_data['events'][$loop_date]); + } + + // Mark the last item so that a list separator can be used in the template. + for ($i = 0, $n = count($return_data['calendar_birthdays']); $i < $n; $i++) + $return_data['calendar_birthdays'][$i]['is_last'] = !isset($return_data['calendar_birthdays'][$i + 1]); + for ($i = 0, $n = count($return_data['calendar_events']); $i < $n; $i++) + $return_data['calendar_events'][$i]['is_last'] = !isset($return_data['calendar_events'][$i + 1]); + + return array( + 'data' => $return_data, + 'expires' => time() + 3600, + 'refresh_eval' => 'return \'' . strftime('%Y%m%d', forum_time(false)) . '\' != strftime(\'%Y%m%d\', forum_time(false)) || (!empty($modSettings[\'calendar_updated\']) && ' . time() . ' < $modSettings[\'calendar_updated\']);', + 'post_retri_eval' => ' + global $context, $scripturl, $user_info; + + foreach ($cache_block[\'data\'][\'calendar_events\'] as $k => $event) + { + // Remove events that the user may not see or wants to ignore. + if ((count(array_intersect($user_info[\'groups\'], $event[\'allowed_groups\'])) === 0 && !allowedTo(\'admin_forum\') && !empty($event[\'id_board\'])) || in_array($event[\'id_board\'], $user_info[\'ignoreboards\'])) + unset($cache_block[\'data\'][\'calendar_events\'][$k]); + else + { + // Whether the event can be edited depends on the permissions. + $cache_block[\'data\'][\'calendar_events\'][$k][\'can_edit\'] = allowedTo(\'calendar_edit_any\') || ($event[\'poster\'] == $user_info[\'id\'] && allowedTo(\'calendar_edit_own\')); + + // The added session code makes this URL not cachable. + $cache_block[\'data\'][\'calendar_events\'][$k][\'modify_href\'] = $scripturl . \'?action=\' . ($event[\'topic\'] == 0 ? \'calendar;sa=post;\' : \'post;msg=\' . $event[\'msg\'] . \';topic=\' . $event[\'topic\'] . \'.0;calendar;\') . \'eventid=\' . $event[\'id\'] . \';\' . $context[\'session_var\'] . \'=\' . $context[\'session_id\']; + } + } + + if (empty($params[0][\'include_holidays\'])) + $cache_block[\'data\'][\'calendar_holidays\'] = array(); + if (empty($params[0][\'include_birthdays\'])) + $cache_block[\'data\'][\'calendar_birthdays\'] = array(); + if (empty($params[0][\'include_events\'])) + $cache_block[\'data\'][\'calendar_events\'] = array(); + + $cache_block[\'data\'][\'show_calendar\'] = !empty($cache_block[\'data\'][\'calendar_holidays\']) || !empty($cache_block[\'data\'][\'calendar_birthdays\']) || !empty($cache_block[\'data\'][\'calendar_events\']);', + ); +} + +// Makes sure the calendar post is valid. +function validateEventPost() +{ + global $modSettings, $txt, $sourcedir, $smcFunc; + + if (!isset($_POST['deleteevent'])) + { + // No month? No year? + if (!isset($_POST['month'])) + fatal_lang_error('event_month_missing', false); + if (!isset($_POST['year'])) + fatal_lang_error('event_year_missing', false); + + // Check the month and year... + if ($_POST['month'] < 1 || $_POST['month'] > 12) + fatal_lang_error('invalid_month', false); + if ($_POST['year'] < $modSettings['cal_minyear'] || $_POST['year'] > $modSettings['cal_maxyear']) + fatal_lang_error('invalid_year', false); + } + + // Make sure they're allowed to post... + isAllowedTo('calendar_post'); + + if (isset($_POST['span'])) + { + // Make sure it's turned on and not some fool trying to trick it. + if (empty($modSettings['cal_allowspan'])) + fatal_lang_error('no_span', false); + if ($_POST['span'] < 1 || $_POST['span'] > $modSettings['cal_maxspan']) + fatal_lang_error('invalid_days_numb', false); + } + + // There is no need to validate the following values if we are just deleting the event. + if (!isset($_POST['deleteevent'])) + { + // No day? + if (!isset($_POST['day'])) + fatal_lang_error('event_day_missing', false); + if (!isset($_POST['evtitle']) && !isset($_POST['subject'])) + fatal_lang_error('event_title_missing', false); + elseif (!isset($_POST['evtitle'])) + $_POST['evtitle'] = $_POST['subject']; + + // Bad day? + if (!checkdate($_POST['month'], $_POST['day'], $_POST['year'])) + fatal_lang_error('invalid_date', false); + + // No title? + if ($smcFunc['htmltrim']($_POST['evtitle']) === '') + fatal_lang_error('no_event_title', false); + if ($smcFunc['strlen']($_POST['evtitle']) > 30) + $_POST['evtitle'] = $smcFunc['substr']($_POST['evtitle'], 0, 30); + $_POST['evtitle'] = str_replace(';', '', $_POST['evtitle']); + } +} + +// Get the event's poster. +function getEventPoster($event_id) +{ + global $smcFunc; + + // A simple database query, how hard can that be? + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}calendar + WHERE id_event = {int:id_event} + LIMIT 1', + array( + 'id_event' => $event_id, + ) + ); + + // No results, return false. + if ($smcFunc['db_num_rows'] === 0) + return false; + + // Grab the results and return. + list ($poster) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + return $poster; +} + +// Consolidating the various INSERT statements into this function. +function insertEvent(&$eventOptions) +{ + global $modSettings, $smcFunc; + + // Add special chars to the title. + $eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES); + + // Add some sanity checking to the span. + $eventOptions['span'] = isset($eventOptions['span']) && $eventOptions['span'] > 0 ? (int) $eventOptions['span'] : 0; + + // Make sure the start date is in ISO order. + if (($num_results = sscanf($eventOptions['start_date'], '%d-%d-%d', $year, $month, $day)) !== 3) + trigger_error('modifyEvent(): invalid start date format given', E_USER_ERROR); + + // Set the end date (if not yet given) + if (!isset($eventOptions['end_date'])) + $eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, $month, $day, $year) + $eventOptions['span'] * 86400); + + // If no topic and board are given, they are not linked to a topic. + $eventOptions['board'] = isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0; + $eventOptions['topic'] = isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0; + + // Insert the event! + $smcFunc['db_insert']('', + '{db_prefix}calendar', + array( + 'id_board' => 'int', 'id_topic' => 'int', 'title' => 'string-60', 'id_member' => 'int', + 'start_date' => 'date', 'end_date' => 'date', + ), + array( + $eventOptions['board'], $eventOptions['topic'], $eventOptions['title'], $eventOptions['member'], + $eventOptions['start_date'], $eventOptions['end_date'], + ), + array('id_event') + ); + + // Store the just inserted id_event for future reference. + $eventOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}calendar', 'id_event'); + + // Update the settings to show something calendarish was updated. + updateSettings(array( + 'calendar_updated' => time(), + )); +} + +function modifyEvent($event_id, &$eventOptions) +{ + global $smcFunc; + + // Properly sanitize the title. + $eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES); + + // Scan the start date for validity and get its components. + if (($num_results = sscanf($eventOptions['start_date'], '%d-%d-%d', $year, $month, $day)) !== 3) + trigger_error('modifyEvent(): invalid start date format given', E_USER_ERROR); + + // Default span to 0 days. + $eventOptions['span'] = isset($eventOptions['span']) ? (int) $eventOptions['span'] : 0; + + // Set the end date to the start date + span (if the end date wasn't already given). + if (!isset($eventOptions['end_date'])) + $eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, $month, $day, $year) + $eventOptions['span'] * 86400); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}calendar + SET + start_date = {date:start_date}, + end_date = {date:end_date}, + title = SUBSTRING({string:title}, 1, 60), + id_board = {int:id_board}, + id_topic = {int:id_topic} + WHERE id_event = {int:id_event}', + array( + 'start_date' => $eventOptions['start_date'], + 'end_date' => $eventOptions['end_date'], + 'title' => $eventOptions['title'], + 'id_board' => isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0, + 'id_topic' => isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0, + 'id_event' => $event_id, + ) + ); + + updateSettings(array( + 'calendar_updated' => time(), + )); +} + +function removeEvent($event_id) +{ + global $smcFunc; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}calendar + WHERE id_event = {int:id_event}', + array( + 'id_event' => $event_id, + ) + ); + + updateSettings(array( + 'calendar_updated' => time(), + )); +} + +function getEventProperties($event_id) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT + c.id_event, c.id_board, c.id_topic, MONTH(c.start_date) AS month, + DAYOFMONTH(c.start_date) AS day, YEAR(c.start_date) AS year, + (TO_DAYS(c.end_date) - TO_DAYS(c.start_date)) AS span, c.id_member, c.title, + t.id_first_msg, t.id_member_started + FROM {db_prefix}calendar AS c + LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = c.id_topic) + WHERE c.id_event = {int:id_event}', + array( + 'id_event' => $event_id, + ) + ); + + // If nothing returned, we are in poo, poo. + if ($smcFunc['db_num_rows']($request) === 0) + return false; + + $row = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + $return_value = array( + 'boards' => array(), + 'board' => $row['id_board'], + 'new' => 0, + 'eventid' => $event_id, + 'year' => $row['year'], + 'month' => $row['month'], + 'day' => $row['day'], + 'title' => $row['title'], + 'span' => 1 + $row['span'], + 'member' => $row['id_member'], + 'topic' => array( + 'id' => $row['id_topic'], + 'member_started' => $row['id_member_started'], + 'first_msg' => $row['id_first_msg'], + ), + ); + + $return_value['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $return_value['month'] == 12 ? 1 : $return_value['month'] + 1, 0, $return_value['month'] == 12 ? $return_value['year'] + 1 : $return_value['year'])); + + return $return_value; +} + +function list_getHolidays($start, $items_per_page, $sort) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT id_holiday, YEAR(event_date) AS year, MONTH(event_date) AS month, DAYOFMONTH(event_date) AS day, title + FROM {db_prefix}calendar_holidays + ORDER BY {raw:sort} + LIMIT ' . $start . ', ' . $items_per_page, + array( + 'sort' => $sort, + ) + ); + $holidays = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $holidays[] = $row; + $smcFunc['db_free_result']($request); + + return $holidays; +} + +function list_getNumHolidays() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}calendar_holidays', + array( + ) + ); + list($num_items) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $num_items; +} + +function removeHolidays($holiday_ids) +{ + global $smcFunc; + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}calendar_holidays + WHERE id_holiday IN ({array_int:id_holiday})', + array( + 'id_holiday' => $holiday_ids, + ) + ); + + updateSettings(array( + 'calendar_updated' => time(), + )); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Categories.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Categories.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,332 @@ + $cat) + if ($index != $cat_order[$cat]) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}categories + SET cat_order = {int:new_order} + WHERE id_cat = {int:current_category}', + array( + 'new_order' => $index, + 'current_category' => $cat, + ) + ); + + // If the category order changed, so did the board order. + require_once($sourcedir . '/Subs-Boards.php'); + reorderBoards(); + } + + if (isset($catOptions['cat_name'])) + { + $catUpdates[] = 'name = {string:cat_name}'; + $catParameters['cat_name'] = $catOptions['cat_name']; + } + + // Can a user collapse this category or is it too important? + if (isset($catOptions['is_collapsible'])) + { + $catUpdates[] = 'can_collapse = {int:is_collapsible}'; + $catParameters['is_collapsible'] = $catOptions['is_collapsible'] ? 1 : 0; + } + + // Do the updates (if any). + if (!empty($catUpdates)) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}categories + SET + ' . implode(', + ', $catUpdates) . ' + WHERE id_cat = {int:current_category}', + array_merge($catParameters, array( + 'current_category' => $category_id, + )) + ); + + if (empty($catOptions['dont_log'])) + logAction('edit_cat', array('catname' => isset($catOptions['cat_name']) ? $catOptions['cat_name'] : $category_id), 'admin'); + } +} + +// Create a new category. +function createCategory($catOptions) +{ + global $smcFunc; + + // Check required values. + if (!isset($catOptions['cat_name']) || trim($catOptions['cat_name']) == '') + trigger_error('createCategory(): A category name is required', E_USER_ERROR); + + // Set default values. + if (!isset($catOptions['move_after'])) + $catOptions['move_after'] = 0; + if (!isset($catOptions['is_collapsible'])) + $catOptions['is_collapsible'] = true; + // Don't log an edit right after. + $catOptions['dont_log'] = true; + + // Add the category to the database. + $smcFunc['db_insert']('', + '{db_prefix}categories', + array( + 'name' => 'string-48', + ), + array( + $catOptions['cat_name'], + ), + array('id_cat') + ); + + // Grab the new category ID. + $category_id = $smcFunc['db_insert_id']('{db_prefix}categories', 'id_cat'); + + // Set the given properties to the newly created category. + modifyCategory($category_id, $catOptions); + + logAction('add_cat', array('catname' => $catOptions['cat_name']), 'admin'); + + // Return the database ID of the category. + return $category_id; +} + +// Remove one or more categories. +function deleteCategories($categories, $moveBoardsTo = null) +{ + global $sourcedir, $smcFunc, $cat_tree; + + require_once($sourcedir . '/Subs-Boards.php'); + + getBoardTree(); + + // With no category set to move the boards to, delete them all. + if ($moveBoardsTo === null) + { + $request = $smcFunc['db_query']('', ' + SELECT id_board + FROM {db_prefix}boards + WHERE id_cat IN ({array_int:category_list})', + array( + 'category_list' => $categories, + ) + ); + $boards_inside = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $boards_inside[] = $row['id_board']; + $smcFunc['db_free_result']($request); + + if (!empty($boards_inside)) + deleteBoards($boards_inside, null); + } + + // Make sure the safe category is really safe. + elseif (in_array($moveBoardsTo, $categories)) + trigger_error('deleteCategories(): You cannot move the boards to a category that\'s being deleted', E_USER_ERROR); + + // Move the boards inside the categories to a safe category. + else + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET id_cat = {int:new_parent_cat} + WHERE id_cat IN ({array_int:category_list})', + array( + 'category_list' => $categories, + 'new_parent_cat' => $moveBoardsTo, + ) + ); + + // Noone will ever be able to collapse these categories anymore. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}collapsed_categories + WHERE id_cat IN ({array_int:category_list})', + array( + 'category_list' => $categories, + ) + ); + + // Do the deletion of the category itself + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}categories + WHERE id_cat IN ({array_int:category_list})', + array( + 'category_list' => $categories, + ) + ); + + // Log what we've done. + foreach ($categories as $category) + logAction('delete_cat', array('catname' => $cat_tree[$category]['node']['name']), 'admin'); + + // Get all boards back into the right order. + reorderBoards(); +} + +// Collapse, expand or toggle one or more categories for one or more members. +function collapseCategories($categories, $new_status, $members = null, $check_collapsable = true) +{ + global $smcFunc; + + // Collapse or expand the categories. + if ($new_status === 'collapse' || $new_status === 'expand') + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}collapsed_categories + WHERE id_cat IN ({array_int:category_list})' . ($members === null ? '' : ' + AND id_member IN ({array_int:member_list})'), + array( + 'category_list' => $categories, + 'member_list' => $members, + ) + ); + + if ($new_status === 'collapse') + $smcFunc['db_query']('', ' + INSERT INTO {db_prefix}collapsed_categories + (id_cat, id_member) + SELECT c.id_cat, mem.id_member + FROM {db_prefix}categories AS c + INNER JOIN {db_prefix}members AS mem ON (' . ($members === null ? '1=1' : ' + mem.id_member IN ({array_int:member_list})') . ') + WHERE c.id_cat IN ({array_int:category_list})' . ($check_collapsable ? ' + AND c.can_collapse = {int:is_collapsible}' : ''), + array( + 'member_list' => $members, + 'category_list' => $categories, + 'is_collapsible' => 1, + ) + ); + } + + // Toggle the categories: collapsed get expanded and expanded get collapsed. + elseif ($new_status === 'toggle') + { + // Get the current state of the categories. + $updates = array( + 'insert' => array(), + 'remove' => array(), + ); + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member, c.id_cat, IFNULL(cc.id_cat, 0) AS is_collapsed, c.can_collapse + FROM {db_prefix}members AS mem + INNER JOIN {db_prefix}categories AS c ON (c.id_cat IN ({array_int:category_list})) + LEFT JOIN {db_prefix}collapsed_categories AS cc ON (cc.id_cat = c.id_cat AND cc.id_member = mem.id_member) + ' . ($members === null ? '' : ' + WHERE mem.id_member IN ({array_int:member_list})'), + array( + 'category_list' => $categories, + 'member_list' => $members, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($row['is_collapsed']) && (!empty($row['can_collapse']) || !$check_collapsable)) + $updates['insert'][] = array($row['id_member'], $row['id_cat']); + elseif (!empty($row['is_collapsed'])) + $updates['remove'][] = '(id_member = ' . $row['id_member'] . ' AND id_cat = ' . $row['id_cat'] . ')'; + } + $smcFunc['db_free_result']($request); + + // Collapse the ones that were originally expanded... + if (!empty($updates['insert'])) + $smcFunc['db_insert']('replace', + '{db_prefix}collapsed_categories', + array( + 'id_cat' => 'int', 'id_member' => 'int', + ), + $updates['insert'], + array('id_cat', 'id_member') + ); + + // And expand the ones that were originally collapsed. + if (!empty($updates['remove'])) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}collapsed_categories + WHERE ' . implode(' OR ', $updates['remove']), + array( + ) + ); + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Charset.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Charset.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,593 @@ + 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', + 'E' => 'e', 'F' => 'f', 'G' => 'g', 'H' => 'h', + 'I' => 'i', 'J' => 'j', 'K' => 'k', 'L' => 'l', + 'M' => 'm', 'N' => 'n', 'O' => 'o', 'P' => 'p', + 'Q' => 'q', 'R' => 'r', 'S' => 's', 'T' => 't', + 'U' => 'u', 'V' => 'v', 'W' => 'w', 'X' => 'x', + 'Y' => 'y', 'Z' => 'z', 'µ' => 'μ', 'À' => 'à', + 'Ã' => 'á', 'Â' => 'â', 'Ã' => 'ã', 'Ä' => 'ä', + 'Ã…' => 'Ã¥', 'Æ' => 'æ', 'Ç' => 'ç', 'È' => 'è', + 'É' => 'é', 'Ê' => 'ê', 'Ë' => 'ë', 'ÃŒ' => 'ì', + 'Ã' => 'í', 'ÃŽ' => 'î', 'Ã' => 'ï', 'Ã' => 'ð', + 'Ñ' => 'ñ', 'Ã’' => 'ò', 'Ó' => 'ó', 'Ô' => 'ô', + 'Õ' => 'õ', 'Ö' => 'ö', 'Ø' => 'ø', 'Ù' => 'ù', + 'Ú' => 'ú', 'Û' => 'û', 'Ü' => 'ü', 'Ã' => 'ý', + 'Þ' => 'þ', 'ß' => 'ss', 'Ä€' => 'Ä', 'Ä‚' => 'ă', + 'Ä„' => 'Ä…', 'Ć' => 'ć', 'Ĉ' => 'ĉ', 'ÄŠ' => 'Ä‹', + 'ÄŒ' => 'Ä', 'ÄŽ' => 'Ä', 'Ä' => 'Ä‘', 'Ä’' => 'Ä“', + 'Ä”' => 'Ä•', 'Ä–' => 'Ä—', 'Ę' => 'Ä™', 'Äš' => 'Ä›', + 'Äœ' => 'Ä', 'Äž' => 'ÄŸ', 'Ä ' => 'Ä¡', 'Ä¢' => 'Ä£', + 'Ĥ' => 'Ä¥', 'Ħ' => 'ħ', 'Ĩ' => 'Ä©', 'Ī' => 'Ä«', + 'Ĭ' => 'Ä­', 'Ä®' => 'į', 'İ' => 'i̇', 'IJ' => 'ij', + 'Ä´' => 'ĵ', 'Ķ' => 'Ä·', 'Ĺ' => 'ĺ', 'Ä»' => 'ļ', + 'Ľ' => 'ľ', 'Ä¿' => 'Å€', 'Å' => 'Å‚', 'Ń' => 'Å„', + 'Å…' => 'ņ', 'Ň' => 'ň', 'ʼn' => 'ʼn', 'ÅŠ' => 'Å‹', + 'ÅŒ' => 'Å', 'ÅŽ' => 'Å', 'Å' => 'Å‘', 'Å’' => 'Å“', + 'Å”' => 'Å•', 'Å–' => 'Å—', 'Ř' => 'Å™', 'Åš' => 'Å›', + 'Åœ' => 'Å', 'Åž' => 'ÅŸ', 'Å ' => 'Å¡', 'Å¢' => 'Å£', + 'Ť' => 'Å¥', 'Ŧ' => 'ŧ', 'Ũ' => 'Å©', 'Ū' => 'Å«', + 'Ŭ' => 'Å­', 'Å®' => 'ů', 'Ű' => 'ű', 'Ų' => 'ų', + 'Å´' => 'ŵ', 'Ŷ' => 'Å·', 'Ÿ' => 'ÿ', 'Ź' => 'ź', + 'Å»' => 'ż', 'Ž' => 'ž', 'Å¿' => 's', 'Æ' => 'É“', + 'Æ‚' => 'ƃ', 'Æ„' => 'Æ…', 'Ɔ' => 'É”', 'Ƈ' => 'ƈ', + 'Ɖ' => 'É–', 'ÆŠ' => 'É—', 'Æ‹' => 'ÆŒ', 'ÆŽ' => 'Ç', + 'Æ' => 'É™', 'Æ' => 'É›', 'Æ‘' => 'Æ’', 'Æ“' => 'É ', + 'Æ”' => 'É£', 'Æ–' => 'É©', 'Æ—' => 'ɨ', 'Ƙ' => 'Æ™', + 'Æœ' => 'ɯ', 'Æ' => 'ɲ', 'ÆŸ' => 'ɵ', 'Æ ' => 'Æ¡', + 'Æ¢' => 'Æ£', 'Ƥ' => 'Æ¥', 'Ʀ' => 'Ê€', 'Ƨ' => 'ƨ', + 'Æ©' => 'ʃ', 'Ƭ' => 'Æ­', 'Æ®' => 'ʈ', 'Ư' => 'ư', + 'Ʊ' => 'ÊŠ', 'Ʋ' => 'Ê‹', 'Ƴ' => 'Æ´', 'Ƶ' => 'ƶ', + 'Æ·' => 'Ê’', 'Ƹ' => 'ƹ', 'Ƽ' => 'ƽ', 'Ç„' => 'dž', + 'Ç…' => 'dž', 'LJ' => 'lj', 'Lj' => 'lj', 'ÇŠ' => 'ÇŒ', + 'Ç‹' => 'ÇŒ', 'Ç' => 'ÇŽ', 'Ç' => 'Ç', 'Ç‘' => 'Ç’', + 'Ç“' => 'Ç”', 'Ç•' => 'Ç–', 'Ç—' => 'ǘ', 'Ç™' => 'Çš', + 'Ç›' => 'Çœ', 'Çž' => 'ÇŸ', 'Ç ' => 'Ç¡', 'Ç¢' => 'Ç£', + 'Ǥ' => 'Ç¥', 'Ǧ' => 'ǧ', 'Ǩ' => 'Ç©', 'Ǫ' => 'Ç«', + 'Ǭ' => 'Ç­', 'Ç®' => 'ǯ', 'ǰ' => 'jÌŒ', 'DZ' => 'dz', + 'Dz' => 'dz', 'Ç´' => 'ǵ', 'Ƕ' => 'Æ•', 'Ç·' => 'Æ¿', + 'Ǹ' => 'ǹ', 'Ǻ' => 'Ç»', 'Ǽ' => 'ǽ', 'Ǿ' => 'Ç¿', + 'È€' => 'È', 'È‚' => 'ȃ', 'È„' => 'È…', 'Ȇ' => 'ȇ', + 'Ȉ' => 'ȉ', 'ÈŠ' => 'È‹', 'ÈŒ' => 'È', 'ÈŽ' => 'È', + 'È' => 'È‘', 'È’' => 'È“', 'È”' => 'È•', 'È–' => 'È—', + 'Ș' => 'È™', 'Èš' => 'È›', 'Èœ' => 'È', 'Èž' => 'ÈŸ', + 'È ' => 'Æž', 'È¢' => 'È£', 'Ȥ' => 'È¥', 'Ȧ' => 'ȧ', + 'Ȩ' => 'È©', 'Ȫ' => 'È«', 'Ȭ' => 'È­', 'È®' => 'ȯ', + 'Ȱ' => 'ȱ', 'Ȳ' => 'ȳ', 'Ⱥ' => 'â±¥', 'È»' => 'ȼ', + 'Ƚ' => 'Æš', 'Ⱦ' => 'ⱦ', 'É' => 'É‚', 'Ƀ' => 'Æ€', + 'É„' => 'ʉ', 'É…' => 'ÊŒ', 'Ɇ' => 'ɇ', 'Ɉ' => 'ɉ', + 'ÉŠ' => 'É‹', 'ÉŒ' => 'É', 'ÉŽ' => 'É', 'Í…' => 'ι', + 'Ά' => 'ά', 'Έ' => 'έ', 'Ή' => 'ή', 'Ί' => 'ί', + 'ÎŒ' => 'ÏŒ', 'ÎŽ' => 'Ï', 'Î' => 'ÏŽ', 'Î' => 'ϊÌ', + 'Α' => 'α', 'Î’' => 'β', 'Γ' => 'γ', 'Δ' => 'δ', + 'Ε' => 'ε', 'Ζ' => 'ζ', 'Η' => 'η', 'Θ' => 'θ', + 'Ι' => 'ι', 'Κ' => 'κ', 'Λ' => 'λ', 'Μ' => 'μ', + 'Î' => 'ν', 'Ξ' => 'ξ', 'Ο' => 'ο', 'Π' => 'Ï€', + 'Ρ' => 'Ï', 'Σ' => 'σ', 'Τ' => 'Ï„', 'Î¥' => 'Ï…', + 'Φ' => 'φ', 'Χ' => 'χ', 'Ψ' => 'ψ', 'Ω' => 'ω', + 'Ϊ' => 'ÏŠ', 'Ϋ' => 'Ï‹', 'ΰ' => 'ϋÌ', 'Ï‚' => 'σ', + 'Ï' => 'β', 'Ï‘' => 'θ', 'Ï•' => 'φ', 'Ï–' => 'Ï€', + 'Ϙ' => 'Ï™', 'Ïš' => 'Ï›', 'Ïœ' => 'Ï', 'Ïž' => 'ÏŸ', + 'Ï ' => 'Ï¡', 'Ï¢' => 'Ï£', 'Ϥ' => 'Ï¥', 'Ϧ' => 'ϧ', + 'Ϩ' => 'Ï©', 'Ϫ' => 'Ï«', 'Ϭ' => 'Ï­', 'Ï®' => 'ϯ', + 'ϰ' => 'κ', 'ϱ' => 'Ï', 'Ï´' => 'θ', 'ϵ' => 'ε', + 'Ï·' => 'ϸ', 'Ϲ' => 'ϲ', 'Ϻ' => 'Ï»', 'Ͻ' => 'Í»', + 'Ͼ' => 'ͼ', 'Ï¿' => 'ͽ', 'Ѐ' => 'Ñ', 'Ð' => 'Ñ‘', + 'Ђ' => 'Ñ’', 'Ѓ' => 'Ñ“', 'Є' => 'Ñ”', 'Ð…' => 'Ñ•', + 'І' => 'Ñ–', 'Ї' => 'Ñ—', 'Ј' => 'ј', 'Љ' => 'Ñ™', + 'Њ' => 'Ñš', 'Ћ' => 'Ñ›', 'ÐŒ' => 'Ñœ', 'Ð' => 'Ñ', + 'ÐŽ' => 'Ñž', 'Ð' => 'ÑŸ', 'Ð' => 'а', 'Б' => 'б', + 'Ð’' => 'в', 'Г' => 'г', 'Д' => 'д', 'Е' => 'е', + 'Ж' => 'ж', 'З' => 'з', 'И' => 'и', 'Й' => 'й', + 'К' => 'к', 'Л' => 'л', 'М' => 'м', 'Ð' => 'н', + 'О' => 'о', 'П' => 'п', 'Р' => 'Ñ€', 'С' => 'Ñ', + 'Т' => 'Ñ‚', 'У' => 'у', 'Ф' => 'Ñ„', 'Ð¥' => 'Ñ…', + 'Ц' => 'ц', 'Ч' => 'ч', 'Ш' => 'ш', 'Щ' => 'щ', + 'Ъ' => 'ÑŠ', 'Ы' => 'Ñ‹', 'Ь' => 'ÑŒ', 'Э' => 'Ñ', + 'Ю' => 'ÑŽ', 'Я' => 'Ñ', 'Ñ ' => 'Ñ¡', 'Ñ¢' => 'Ñ£', + 'Ѥ' => 'Ñ¥', 'Ѧ' => 'ѧ', 'Ѩ' => 'Ñ©', 'Ѫ' => 'Ñ«', + 'Ѭ' => 'Ñ­', 'Ñ®' => 'ѯ', 'Ѱ' => 'ѱ', 'Ѳ' => 'ѳ', + 'Ñ´' => 'ѵ', 'Ѷ' => 'Ñ·', 'Ѹ' => 'ѹ', 'Ѻ' => 'Ñ»', + 'Ѽ' => 'ѽ', 'Ѿ' => 'Ñ¿', 'Ò€' => 'Ò', 'ÒŠ' => 'Ò‹', + 'ÒŒ' => 'Ò', 'ÒŽ' => 'Ò', 'Ò' => 'Ò‘', 'Ò’' => 'Ò“', + 'Ò”' => 'Ò•', 'Ò–' => 'Ò—', 'Ò˜' => 'Ò™', 'Òš' => 'Ò›', + 'Òœ' => 'Ò', 'Òž' => 'ÒŸ', 'Ò ' => 'Ò¡', 'Ò¢' => 'Ò£', + 'Ò¤' => 'Ò¥', 'Ò¦' => 'Ò§', 'Ò¨' => 'Ò©', 'Òª' => 'Ò«', + 'Ò¬' => 'Ò­', 'Ò®' => 'Ò¯', 'Ò°' => 'Ò±', 'Ò²' => 'Ò³', + 'Ò´' => 'Òµ', 'Ò¶' => 'Ò·', 'Ò¸' => 'Ò¹', 'Òº' => 'Ò»', + 'Ò¼' => 'Ò½', 'Ò¾' => 'Ò¿', 'Ó€' => 'Ó', 'Ó' => 'Ó‚', + 'Óƒ' => 'Ó„', 'Ó…' => 'Ó†', 'Ó‡' => 'Óˆ', 'Ó‰' => 'ÓŠ', + 'Ó‹' => 'ÓŒ', 'Ó' => 'ÓŽ', 'Ó' => 'Ó‘', 'Ó’' => 'Ó“', + 'Ó”' => 'Ó•', 'Ó–' => 'Ó—', 'Ó˜' => 'Ó™', 'Óš' => 'Ó›', + 'Óœ' => 'Ó', 'Óž' => 'ÓŸ', 'Ó ' => 'Ó¡', 'Ó¢' => 'Ó£', + 'Ó¤' => 'Ó¥', 'Ó¦' => 'Ó§', 'Ó¨' => 'Ó©', 'Óª' => 'Ó«', + 'Ó¬' => 'Ó­', 'Ó®' => 'Ó¯', 'Ó°' => 'Ó±', 'Ó²' => 'Ó³', + 'Ó´' => 'Óµ', 'Ó¶' => 'Ó·', 'Ó¸' => 'Ó¹', 'Óº' => 'Ó»', + 'Ó¼' => 'Ó½', 'Ó¾' => 'Ó¿', 'Ô€' => 'Ô', 'Ô‚' => 'Ôƒ', + 'Ô„' => 'Ô…', 'Ô†' => 'Ô‡', 'Ôˆ' => 'Ô‰', 'ÔŠ' => 'Ô‹', + 'ÔŒ' => 'Ô', 'ÔŽ' => 'Ô', 'Ô' => 'Ô‘', 'Ô’' => 'Ô“', + 'Ô±' => 'Õ¡', 'Ô²' => 'Õ¢', 'Ô³' => 'Õ£', 'Ô´' => 'Õ¤', + 'Ôµ' => 'Õ¥', 'Ô¶' => 'Õ¦', 'Ô·' => 'Õ§', 'Ô¸' => 'Õ¨', + 'Ô¹' => 'Õ©', 'Ôº' => 'Õª', 'Ô»' => 'Õ«', 'Ô¼' => 'Õ¬', + 'Ô½' => 'Õ­', 'Ô¾' => 'Õ®', 'Ô¿' => 'Õ¯', 'Õ€' => 'Õ°', + 'Õ' => 'Õ±', 'Õ‚' => 'Õ²', 'Õƒ' => 'Õ³', 'Õ„' => 'Õ´', + 'Õ…' => 'Õµ', 'Õ†' => 'Õ¶', 'Õ‡' => 'Õ·', 'Õˆ' => 'Õ¸', + 'Õ‰' => 'Õ¹', 'ÕŠ' => 'Õº', 'Õ‹' => 'Õ»', 'ÕŒ' => 'Õ¼', + 'Õ' => 'Õ½', 'ÕŽ' => 'Õ¾', 'Õ' => 'Õ¿', 'Õ' => 'Ö€', + 'Õ‘' => 'Ö', 'Õ’' => 'Ö‚', 'Õ“' => 'Öƒ', 'Õ”' => 'Ö„', + 'Õ•' => 'Ö…', 'Õ–' => 'Ö†', 'Ö‡' => 'Õ¥Ö‚', 'á‚ ' => 'â´€', + 'á‚¡' => 'â´', 'á‚¢' => 'â´‚', 'á‚£' => 'â´ƒ', 'Ⴄ' => 'â´„', + 'á‚¥' => 'â´…', 'Ⴆ' => 'â´†', 'á‚§' => 'â´‡', 'Ⴈ' => 'â´ˆ', + 'á‚©' => 'â´‰', 'Ⴊ' => 'â´Š', 'á‚«' => 'â´‹', 'Ⴌ' => 'â´Œ', + 'á‚­' => 'â´', 'á‚®' => 'â´Ž', 'Ⴏ' => 'â´', 'á‚°' => 'â´', + 'Ⴑ' => 'â´‘', 'Ⴒ' => 'â´’', 'Ⴓ' => 'â´“', 'á‚´' => 'â´”', + 'Ⴕ' => 'â´•', 'á‚¶' => 'â´–', 'á‚·' => 'â´—', 'Ⴘ' => 'â´˜', + 'Ⴙ' => 'â´™', 'Ⴚ' => 'â´š', 'á‚»' => 'â´›', 'Ⴜ' => 'â´œ', + 'Ⴝ' => 'â´', 'Ⴞ' => 'â´ž', 'á‚¿' => 'â´Ÿ', 'Ⴠ' => 'â´ ', + 'áƒ' => 'â´¡', 'Ⴢ' => 'â´¢', 'Ⴣ' => 'â´£', 'Ⴤ' => 'â´¤', + 'Ⴥ' => 'â´¥', 'Ḁ' => 'á¸', 'Ḃ' => 'ḃ', 'Ḅ' => 'ḅ', + 'Ḇ' => 'ḇ', 'Ḉ' => 'ḉ', 'Ḋ' => 'ḋ', 'Ḍ' => 'á¸', + 'Ḏ' => 'á¸', 'á¸' => 'ḑ', 'Ḓ' => 'ḓ', 'Ḕ' => 'ḕ', + 'Ḗ' => 'ḗ', 'Ḙ' => 'ḙ', 'Ḛ' => 'ḛ', 'Ḝ' => 'á¸', + 'Ḟ' => 'ḟ', 'Ḡ' => 'ḡ', 'Ḣ' => 'ḣ', 'Ḥ' => 'ḥ', + 'Ḧ' => 'ḧ', 'Ḩ' => 'ḩ', 'Ḫ' => 'ḫ', 'Ḭ' => 'ḭ', + 'Ḯ' => 'ḯ', 'Ḱ' => 'ḱ', 'Ḳ' => 'ḳ', 'Ḵ' => 'ḵ', + 'Ḷ' => 'ḷ', 'Ḹ' => 'ḹ', 'Ḻ' => 'ḻ', 'Ḽ' => 'ḽ', + 'Ḿ' => 'ḿ', 'á¹€' => 'á¹', 'Ṃ' => 'ṃ', 'Ṅ' => 'á¹…', + 'Ṇ' => 'ṇ', 'Ṉ' => 'ṉ', 'Ṋ' => 'ṋ', 'Ṍ' => 'á¹', + 'Ṏ' => 'á¹', 'á¹' => 'ṑ', 'á¹’' => 'ṓ', 'á¹”' => 'ṕ', + 'á¹–' => 'á¹—', 'Ṙ' => 'á¹™', 'Ṛ' => 'á¹›', 'Ṝ' => 'á¹', + 'Ṟ' => 'ṟ', 'á¹ ' => 'ṡ', 'á¹¢' => 'á¹£', 'Ṥ' => 'á¹¥', + 'Ṧ' => 'á¹§', 'Ṩ' => 'ṩ', 'Ṫ' => 'ṫ', 'Ṭ' => 'á¹­', + 'á¹®' => 'ṯ', 'á¹°' => 'á¹±', 'á¹²' => 'á¹³', 'á¹´' => 'á¹µ', + 'á¹¶' => 'á¹·', 'Ṹ' => 'á¹¹', 'Ṻ' => 'á¹»', 'á¹¼' => 'á¹½', + 'á¹¾' => 'ṿ', 'Ẁ' => 'áº', 'Ẃ' => 'ẃ', 'Ẅ' => 'ẅ', + 'Ẇ' => 'ẇ', 'Ẉ' => 'ẉ', 'Ẋ' => 'ẋ', 'Ẍ' => 'áº', + 'Ẏ' => 'áº', 'áº' => 'ẑ', 'Ẓ' => 'ẓ', 'Ẕ' => 'ẕ', + 'ẖ' => 'ẖ', 'ẗ' => 'ẗ', 'ẘ' => 'wÌŠ', 'ẙ' => 'yÌŠ', + 'ẚ' => 'aʾ', 'ẛ' => 'ṡ', 'Ạ' => 'ạ', 'Ả' => 'ả', + 'Ấ' => 'ấ', 'Ầ' => 'ầ', 'Ẩ' => 'ẩ', 'Ẫ' => 'ẫ', + 'Ậ' => 'ậ', 'Ắ' => 'ắ', 'Ằ' => 'ằ', 'Ẳ' => 'ẳ', + 'Ẵ' => 'ẵ', 'Ặ' => 'ặ', 'Ẹ' => 'ẹ', 'Ẻ' => 'ẻ', + 'Ẽ' => 'ẽ', 'Ế' => 'ế', 'Ề' => 'á»', 'Ể' => 'ể', + 'Ễ' => 'á»…', 'Ệ' => 'ệ', 'Ỉ' => 'ỉ', 'Ị' => 'ị', + 'Ọ' => 'á»', 'Ỏ' => 'á»', 'á»' => 'ố', 'á»’' => 'ồ', + 'á»”' => 'ổ', 'á»–' => 'á»—', 'Ộ' => 'á»™', 'Ớ' => 'á»›', + 'Ờ' => 'á»', 'Ở' => 'ở', 'á» ' => 'ỡ', 'Ợ' => 'ợ', + 'Ụ' => 'ụ', 'Ủ' => 'á»§', 'Ứ' => 'ứ', 'Ừ' => 'ừ', + 'Ử' => 'á»­', 'á»®' => 'ữ', 'á»°' => 'á»±', 'Ỳ' => 'ỳ', + 'á»´' => 'ỵ', 'á»¶' => 'á»·', 'Ỹ' => 'ỹ', 'Ἀ' => 'á¼€', + 'Ἁ' => 'á¼', 'Ἂ' => 'ἂ', 'Ἃ' => 'ἃ', 'Ἄ' => 'ἄ', + 'á¼' => 'á¼…', 'Ἆ' => 'ἆ', 'á¼' => 'ἇ', 'Ἐ' => 'á¼', + 'á¼™' => 'ἑ', 'Ἒ' => 'á¼’', 'á¼›' => 'ἓ', 'Ἔ' => 'á¼”', + 'á¼' => 'ἕ', 'Ἠ' => 'á¼ ', 'Ἡ' => 'ἡ', 'Ἢ' => 'á¼¢', + 'Ἣ' => 'á¼£', 'Ἤ' => 'ἤ', 'á¼­' => 'á¼¥', 'á¼®' => 'ἦ', + 'Ἧ' => 'á¼§', 'Ἰ' => 'á¼°', 'á¼¹' => 'á¼±', 'Ἲ' => 'á¼²', + 'á¼»' => 'á¼³', 'á¼¼' => 'á¼´', 'á¼½' => 'á¼µ', 'á¼¾' => 'á¼¶', + 'Ἷ' => 'á¼·', 'Ὀ' => 'á½€', 'Ὁ' => 'á½', 'Ὂ' => 'ὂ', + 'Ὃ' => 'ὃ', 'Ὄ' => 'ὄ', 'á½' => 'á½…', 'á½' => 'Ï…Ì“', + 'á½’' => 'ὒ', 'á½”' => 'Ï…Ì“Ì', 'á½–' => 'ὖ', 'á½™' => 'ὑ', + 'á½›' => 'ὓ', 'á½' => 'ὕ', 'Ὗ' => 'á½—', 'Ὠ' => 'á½ ', + 'Ὡ' => 'ὡ', 'Ὢ' => 'á½¢', 'Ὣ' => 'á½£', 'Ὤ' => 'ὤ', + 'á½­' => 'á½¥', 'á½®' => 'ὦ', 'Ὧ' => 'á½§', 'á¾€' => 'ἀι', + 'á¾' => 'á¼Î¹', 'ᾂ' => 'ἂι', 'ᾃ' => 'ἃι', 'ᾄ' => 'ἄι', + 'á¾…' => 'ἅι', 'ᾆ' => 'ἆι', 'ᾇ' => 'ἇι', 'ᾈ' => 'á¾€', + 'ᾉ' => 'á¾', 'ᾊ' => 'ᾂ', 'ᾋ' => 'ᾃ', 'ᾌ' => 'ᾄ', + 'á¾' => 'á¾…', 'ᾎ' => 'ᾆ', 'á¾' => 'ᾇ', 'á¾' => 'ἠι', + 'ᾑ' => 'ἡι', 'á¾’' => 'ἢι', 'ᾓ' => 'ἣι', 'á¾”' => 'ἤι', + 'ᾕ' => 'ἥι', 'á¾–' => 'ἦι', 'á¾—' => 'ἧι', 'ᾘ' => 'á¾', + 'á¾™' => 'ᾑ', 'ᾚ' => 'á¾’', 'á¾›' => 'ᾓ', 'ᾜ' => 'á¾”', + 'á¾' => 'ᾕ', 'ᾞ' => 'á¾–', 'ᾟ' => 'á¾—', 'á¾ ' => 'ὠι', + 'ᾡ' => 'ὡι', 'á¾¢' => 'ὢι', 'á¾£' => 'ὣι', 'ᾤ' => 'ὤι', + 'á¾¥' => 'ὥι', 'ᾦ' => 'ὦι', 'á¾§' => 'ὧι', 'ᾨ' => 'á¾ ', + 'ᾩ' => 'ᾡ', 'ᾪ' => 'á¾¢', 'ᾫ' => 'á¾£', 'ᾬ' => 'ᾤ', + 'á¾­' => 'á¾¥', 'á¾®' => 'ᾦ', 'ᾯ' => 'á¾§', 'á¾²' => 'ὰι', + 'á¾³' => 'αι', 'á¾´' => 'άι', 'á¾¶' => 'ᾶ', 'á¾·' => 'ᾶι', + 'Ᾰ' => 'á¾°', 'á¾¹' => 'á¾±', 'Ὰ' => 'á½°', 'á¾»' => 'á½±', + 'á¾¼' => 'á¾³', 'á¾¾' => 'ι', 'á¿‚' => 'ὴι', 'ῃ' => 'ηι', + 'á¿„' => 'ήι', 'ῆ' => 'ῆ', 'ῇ' => 'ῆι', 'Ὲ' => 'á½²', + 'Έ' => 'á½³', 'Ὴ' => 'á½´', 'á¿‹' => 'á½µ', 'ῌ' => 'ῃ', + 'á¿’' => 'ῒ', 'á¿“' => 'ϊÌ', 'á¿–' => 'ῖ', 'á¿—' => 'ῗ', + 'Ῐ' => 'á¿', 'á¿™' => 'á¿‘', 'Ὶ' => 'á½¶', 'á¿›' => 'á½·', + 'á¿¢' => 'ῢ', 'á¿£' => 'ϋÌ', 'ῤ' => 'ÏÌ“', 'ῦ' => 'Ï…Í‚', + 'á¿§' => 'ῧ', 'Ῠ' => 'á¿ ', 'á¿©' => 'á¿¡', 'Ὺ' => 'ὺ', + 'á¿«' => 'á½»', 'Ῥ' => 'á¿¥', 'ῲ' => 'ὼι', 'ῳ' => 'ωι', + 'á¿´' => 'ώι', 'á¿¶' => 'ῶ', 'á¿·' => 'ῶι', 'Ὸ' => 'ὸ', + 'Ό' => 'á½¹', 'Ὼ' => 'á½¼', 'á¿»' => 'á½½', 'ῼ' => 'ῳ', + 'Ω' => 'ω', 'K' => 'k', 'â„«' => 'Ã¥', 'Ⅎ' => 'â…Ž', + 'â… ' => 'â…°', 'â…¡' => 'â…±', 'â…¢' => 'â…²', 'â…£' => 'â…³', + 'â…¤' => 'â…´', 'â…¥' => 'â…µ', 'â…¦' => 'â…¶', 'â…§' => 'â…·', + 'â…¨' => 'â…¸', 'â…©' => 'â…¹', 'â…ª' => 'â…º', 'â…«' => 'â…»', + 'â…¬' => 'â…¼', 'â…­' => 'â…½', 'â…®' => 'â…¾', 'â…¯' => 'â…¿', + 'Ↄ' => 'ↄ', 'â’¶' => 'â“', 'â’·' => 'â“‘', 'â’¸' => 'â“’', + 'â’¹' => 'â““', 'â’º' => 'â“”', 'â’»' => 'â“•', 'â’¼' => 'â“–', + 'â’½' => 'â“—', 'â’¾' => 'ⓘ', 'â’¿' => 'â“™', 'â“€' => 'ⓚ', + 'â“' => 'â“›', 'â“‚' => 'ⓜ', 'Ⓝ' => 'â“', 'â“„' => 'ⓞ', + 'â“…' => 'ⓟ', 'Ⓠ' => 'â“ ', 'Ⓡ' => 'â“¡', 'Ⓢ' => 'â“¢', + 'Ⓣ' => 'â“£', 'Ⓤ' => 'ⓤ', 'â“‹' => 'â“¥', 'Ⓦ' => 'ⓦ', + 'â“' => 'â“§', 'Ⓨ' => 'ⓨ', 'â“' => 'â“©', 'â°€' => 'â°°', + 'â°' => 'â°±', 'â°‚' => 'â°²', 'â°ƒ' => 'â°³', 'â°„' => 'â°´', + 'â°…' => 'â°µ', 'â°†' => 'â°¶', 'â°‡' => 'â°·', 'â°ˆ' => 'â°¸', + 'â°‰' => 'â°¹', 'â°Š' => 'â°º', 'â°‹' => 'â°»', 'â°Œ' => 'â°¼', + 'â°' => 'â°½', 'â°Ž' => 'â°¾', 'â°' => 'â°¿', 'â°' => 'â±€', + 'â°‘' => 'â±', 'â°’' => 'ⱂ', 'â°“' => 'ⱃ', 'â°”' => 'ⱄ', + 'â°•' => 'â±…', 'â°–' => 'ⱆ', 'â°—' => 'ⱇ', 'â°˜' => 'ⱈ', + 'â°™' => 'ⱉ', 'â°š' => 'ⱊ', 'â°›' => 'ⱋ', 'â°œ' => 'ⱌ', + 'â°' => 'â±', 'â°ž' => 'ⱎ', 'â°Ÿ' => 'â±', 'â° ' => 'â±', + 'â°¡' => 'ⱑ', 'â°¢' => 'â±’', 'â°£' => 'ⱓ', 'â°¤' => 'â±”', + 'â°¥' => 'ⱕ', 'â°¦' => 'â±–', 'â°§' => 'â±—', 'â°¨' => 'ⱘ', + 'â°©' => 'â±™', 'â°ª' => 'ⱚ', 'â°«' => 'â±›', 'â°¬' => 'ⱜ', + 'â°­' => 'â±', 'â°®' => 'ⱞ', 'â± ' => 'ⱡ', 'â±¢' => 'É«', + 'â±£' => 'áµ½', 'Ɽ' => 'ɽ', 'â±§' => 'ⱨ', 'Ⱪ' => 'ⱪ', + 'Ⱬ' => 'ⱬ', 'â±µ' => 'â±¶', 'â²€' => 'â²', 'Ⲃ' => 'ⲃ', + 'Ⲅ' => 'â²…', 'Ⲇ' => 'ⲇ', 'Ⲉ' => 'ⲉ', 'Ⲋ' => 'ⲋ', + 'Ⲍ' => 'â²', 'Ⲏ' => 'â²', 'â²' => 'ⲑ', 'â²’' => 'ⲓ', + 'â²”' => 'ⲕ', 'â²–' => 'â²—', 'Ⲙ' => 'â²™', 'Ⲛ' => 'â²›', + 'Ⲝ' => 'â²', 'Ⲟ' => 'ⲟ', 'â² ' => 'ⲡ', 'â²¢' => 'â²£', + 'Ⲥ' => 'â²¥', 'Ⲧ' => 'â²§', 'Ⲩ' => 'ⲩ', 'Ⲫ' => 'ⲫ', + 'Ⲭ' => 'â²­', 'â²®' => 'ⲯ', 'â²°' => 'â²±', 'â²²' => 'â²³', + 'â²´' => 'â²µ', 'â²¶' => 'â²·', 'Ⲹ' => 'â²¹', 'Ⲻ' => 'â²»', + 'â²¼' => 'â²½', 'â²¾' => 'ⲿ', 'â³€' => 'â³', 'Ⳃ' => 'ⳃ', + 'Ⳅ' => 'â³…', 'Ⳇ' => 'ⳇ', 'Ⳉ' => 'ⳉ', 'Ⳋ' => 'ⳋ', + 'Ⳍ' => 'â³', 'Ⳏ' => 'â³', 'â³' => 'ⳑ', 'â³’' => 'ⳓ', + 'â³”' => 'ⳕ', 'â³–' => 'â³—', 'Ⳙ' => 'â³™', 'Ⳛ' => 'â³›', + 'Ⳝ' => 'â³', 'Ⳟ' => 'ⳟ', 'â³ ' => 'ⳡ', 'â³¢' => 'â³£', + 'ff' => 'ff', 'ï¬' => 'fi', 'fl' => 'fl', 'ffi' => 'ffi', + 'ffl' => 'ffl', 'ſt' => 'st', 'st' => 'st', 'ﬓ' => 'Õ´Õ¶', + 'ﬔ' => 'Õ´Õ¥', 'ﬕ' => 'Õ´Õ«', 'ﬖ' => 'Õ¾Õ¶', 'ﬗ' => 'Õ´Õ­', + 'A' => 'ï½', 'ï¼¢' => 'b', 'ï¼£' => 'c', 'D' => 'd', + 'ï¼¥' => 'ï½…', 'F' => 'f', 'ï¼§' => 'g', 'H' => 'h', + 'I' => 'i', 'J' => 'j', 'K' => 'k', 'L' => 'l', + 'ï¼­' => 'ï½', 'ï¼®' => 'n', 'O' => 'ï½', 'ï¼°' => 'ï½', + 'ï¼±' => 'q', 'ï¼²' => 'ï½’', 'ï¼³' => 's', 'ï¼´' => 'ï½”', + 'ï¼µ' => 'u', 'ï¼¶' => 'ï½–', 'ï¼·' => 'ï½—', 'X' => 'x', + 'ï¼¹' => 'ï½™', 'Z' => 'z', 'ð€' => 'ð¨', 'ð' => 'ð©', + 'ð‚' => 'ðª', 'ðƒ' => 'ð«', 'ð„' => 'ð¬', 'ð…' => 'ð­', + 'ð†' => 'ð®', 'ð‡' => 'ð¯', 'ðˆ' => 'ð°', 'ð‰' => 'ð±', + 'ðŠ' => 'ð²', 'ð‹' => 'ð³', 'ðŒ' => 'ð´', 'ð' => 'ðµ', + 'ðŽ' => 'ð¶', 'ð' => 'ð·', 'ð' => 'ð¸', 'ð‘' => 'ð¹', + 'ð’' => 'ðº', 'ð“' => 'ð»', 'ð”' => 'ð¼', 'ð•' => 'ð½', + 'ð–' => 'ð¾', 'ð—' => 'ð¿', 'ð˜' => 'ð‘€', 'ð™' => 'ð‘', + 'ðš' => 'ð‘‚', 'ð›' => 'ð‘ƒ', 'ðœ' => 'ð‘„', 'ð' => 'ð‘…', + 'ðž' => 'ð‘†', 'ðŸ' => 'ð‘‡', 'ð ' => 'ð‘ˆ', 'ð¡' => 'ð‘‰', + 'ð¢' => 'ð‘Š', 'ð£' => 'ð‘‹', 'ð¤' => 'ð‘Œ', 'ð¥' => 'ð‘', + 'ð‘Ž' => 'ð¦', 'ð‘' => 'ð§', + ); + + return strtr($string, $case_folding); +} + +// Convert the given UTF-8 string to uppercase. +function utf8_strtoupper($string) +{ + static $case_folding = array( + 'a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', + 'e' => 'E', 'f' => 'F', 'g' => 'G', 'h' => 'H', + 'i' => 'I', 'j' => 'J', 'k' => 'K', 'l' => 'L', + 'm' => 'M', 'n' => 'N', 'o' => 'O', 'p' => 'P', + 'q' => 'Q', 'r' => 'R', 's' => 'S', 't' => 'T', + 'u' => 'U', 'v' => 'V', 'w' => 'W', 'x' => 'X', + 'y' => 'Y', 'z' => 'Z', 'μ' => 'µ', 'à' => 'À', + 'á' => 'Ã', 'â' => 'Â', 'ã' => 'Ã', 'ä' => 'Ä', + 'Ã¥' => 'Ã…', 'æ' => 'Æ', 'ç' => 'Ç', 'è' => 'È', + 'é' => 'É', 'ê' => 'Ê', 'ë' => 'Ë', 'ì' => 'ÃŒ', + 'í' => 'Ã', 'î' => 'ÃŽ', 'ï' => 'Ã', 'ð' => 'Ã', + 'ñ' => 'Ñ', 'ò' => 'Ã’', 'ó' => 'Ó', 'ô' => 'Ô', + 'õ' => 'Õ', 'ö' => 'Ö', 'ø' => 'Ø', 'ù' => 'Ù', + 'ú' => 'Ú', 'û' => 'Û', 'ü' => 'Ü', 'ý' => 'Ã', + 'þ' => 'Þ', 'ss' => 'ß', 'Ä' => 'Ä€', 'ă' => 'Ä‚', + 'Ä…' => 'Ä„', 'ć' => 'Ć', 'ĉ' => 'Ĉ', 'Ä‹' => 'ÄŠ', + 'Ä' => 'ÄŒ', 'Ä' => 'ÄŽ', 'Ä‘' => 'Ä', 'Ä“' => 'Ä’', + 'Ä•' => 'Ä”', 'Ä—' => 'Ä–', 'Ä™' => 'Ę', 'Ä›' => 'Äš', + 'Ä' => 'Äœ', 'ÄŸ' => 'Äž', 'Ä¡' => 'Ä ', 'Ä£' => 'Ä¢', + 'Ä¥' => 'Ĥ', 'ħ' => 'Ħ', 'Ä©' => 'Ĩ', 'Ä«' => 'Ī', + 'Ä­' => 'Ĭ', 'į' => 'Ä®', 'i̇' => 'İ', 'ij' => 'IJ', + 'ĵ' => 'Ä´', 'Ä·' => 'Ķ', 'ĺ' => 'Ĺ', 'ļ' => 'Ä»', + 'ľ' => 'Ľ', 'Å€' => 'Ä¿', 'Å‚' => 'Å', 'Å„' => 'Ń', + 'ņ' => 'Å…', 'ň' => 'Ň', 'ʼn' => 'ʼn', 'Å‹' => 'ÅŠ', + 'Å' => 'ÅŒ', 'Å' => 'ÅŽ', 'Å‘' => 'Å', 'Å“' => 'Å’', + 'Å•' => 'Å”', 'Å—' => 'Å–', 'Å™' => 'Ř', 'Å›' => 'Åš', + 'Å' => 'Åœ', 'ÅŸ' => 'Åž', 'Å¡' => 'Å ', 'Å£' => 'Å¢', + 'Å¥' => 'Ť', 'ŧ' => 'Ŧ', 'Å©' => 'Ũ', 'Å«' => 'Ū', + 'Å­' => 'Ŭ', 'ů' => 'Å®', 'ű' => 'Ű', 'ų' => 'Ų', + 'ŵ' => 'Å´', 'Å·' => 'Ŷ', 'ÿ' => 'Ÿ', 'ź' => 'Ź', + 'ż' => 'Å»', 'ž' => 'Ž', 's' => 'Å¿', 'É“' => 'Æ', + 'ƃ' => 'Æ‚', 'Æ…' => 'Æ„', 'É”' => 'Ɔ', 'ƈ' => 'Ƈ', + 'É–' => 'Ɖ', 'É—' => 'ÆŠ', 'ÆŒ' => 'Æ‹', 'Ç' => 'ÆŽ', + 'É™' => 'Æ', 'É›' => 'Æ', 'Æ’' => 'Æ‘', 'É ' => 'Æ“', + 'É£' => 'Æ”', 'É©' => 'Æ–', 'ɨ' => 'Æ—', 'Æ™' => 'Ƙ', + 'ɯ' => 'Æœ', 'ɲ' => 'Æ', 'ɵ' => 'ÆŸ', 'Æ¡' => 'Æ ', + 'Æ£' => 'Æ¢', 'Æ¥' => 'Ƥ', 'Ê€' => 'Ʀ', 'ƨ' => 'Ƨ', + 'ʃ' => 'Æ©', 'Æ­' => 'Ƭ', 'ʈ' => 'Æ®', 'ư' => 'Ư', + 'ÊŠ' => 'Ʊ', 'Ê‹' => 'Ʋ', 'Æ´' => 'Ƴ', 'ƶ' => 'Ƶ', + 'Ê’' => 'Æ·', 'ƹ' => 'Ƹ', 'ƽ' => 'Ƽ', 'dž' => 'Ç„', + 'dž' => 'Ç…', 'lj' => 'LJ', 'lj' => 'Lj', 'ÇŒ' => 'ÇŠ', + 'ÇŒ' => 'Ç‹', 'ÇŽ' => 'Ç', 'Ç' => 'Ç', 'Ç’' => 'Ç‘', + 'Ç”' => 'Ç“', 'Ç–' => 'Ç•', 'ǘ' => 'Ç—', 'Çš' => 'Ç™', + 'Çœ' => 'Ç›', 'ÇŸ' => 'Çž', 'Ç¡' => 'Ç ', 'Ç£' => 'Ç¢', + 'Ç¥' => 'Ǥ', 'ǧ' => 'Ǧ', 'Ç©' => 'Ǩ', 'Ç«' => 'Ǫ', + 'Ç­' => 'Ǭ', 'ǯ' => 'Ç®', 'jÌŒ' => 'ǰ', 'dz' => 'DZ', + 'dz' => 'Dz', 'ǵ' => 'Ç´', 'Æ•' => 'Ƕ', 'Æ¿' => 'Ç·', + 'ǹ' => 'Ǹ', 'Ç»' => 'Ǻ', 'ǽ' => 'Ǽ', 'Ç¿' => 'Ǿ', + 'È' => 'È€', 'ȃ' => 'È‚', 'È…' => 'È„', 'ȇ' => 'Ȇ', + 'ȉ' => 'Ȉ', 'È‹' => 'ÈŠ', 'È' => 'ÈŒ', 'È' => 'ÈŽ', + 'È‘' => 'È', 'È“' => 'È’', 'È•' => 'È”', 'È—' => 'È–', + 'È™' => 'Ș', 'È›' => 'Èš', 'È' => 'Èœ', 'ÈŸ' => 'Èž', + 'Æž' => 'È ', 'È£' => 'È¢', 'È¥' => 'Ȥ', 'ȧ' => 'Ȧ', + 'È©' => 'Ȩ', 'È«' => 'Ȫ', 'È­' => 'Ȭ', 'ȯ' => 'È®', + 'ȱ' => 'Ȱ', 'ȳ' => 'Ȳ', 'â±¥' => 'Ⱥ', 'ȼ' => 'È»', + 'Æš' => 'Ƚ', 'ⱦ' => 'Ⱦ', 'É‚' => 'É', 'Æ€' => 'Ƀ', + 'ʉ' => 'É„', 'ÊŒ' => 'É…', 'ɇ' => 'Ɇ', 'ɉ' => 'Ɉ', + 'É‹' => 'ÉŠ', 'É' => 'ÉŒ', 'É' => 'ÉŽ', 'ι' => 'Í…', + 'ά' => 'Ά', 'έ' => 'Έ', 'ή' => 'Ή', 'ί' => 'Ί', + 'ÏŒ' => 'ÎŒ', 'Ï' => 'ÎŽ', 'ÏŽ' => 'Î', 'ϊÌ' => 'Î', + 'α' => 'Α', 'β' => 'Î’', 'γ' => 'Γ', 'δ' => 'Δ', + 'ε' => 'Ε', 'ζ' => 'Ζ', 'η' => 'Η', 'θ' => 'Θ', + 'ι' => 'Ι', 'κ' => 'Κ', 'λ' => 'Λ', 'μ' => 'Μ', + 'ν' => 'Î', 'ξ' => 'Ξ', 'ο' => 'Ο', 'Ï€' => 'Π', + 'Ï' => 'Ρ', 'σ' => 'Σ', 'Ï„' => 'Τ', 'Ï…' => 'Î¥', + 'φ' => 'Φ', 'χ' => 'Χ', 'ψ' => 'Ψ', 'ω' => 'Ω', + 'ÏŠ' => 'Ϊ', 'Ï‹' => 'Ϋ', 'ϋÌ' => 'ΰ', 'σ' => 'Ï‚', + 'β' => 'Ï', 'θ' => 'Ï‘', 'φ' => 'Ï•', 'Ï€' => 'Ï–', + 'Ï™' => 'Ϙ', 'Ï›' => 'Ïš', 'Ï' => 'Ïœ', 'ÏŸ' => 'Ïž', + 'Ï¡' => 'Ï ', 'Ï£' => 'Ï¢', 'Ï¥' => 'Ϥ', 'ϧ' => 'Ϧ', + 'Ï©' => 'Ϩ', 'Ï«' => 'Ϫ', 'Ï­' => 'Ϭ', 'ϯ' => 'Ï®', + 'κ' => 'ϰ', 'Ï' => 'ϱ', 'θ' => 'Ï´', 'ε' => 'ϵ', + 'ϸ' => 'Ï·', 'ϲ' => 'Ϲ', 'Ï»' => 'Ϻ', 'Í»' => 'Ͻ', + 'ͼ' => 'Ͼ', 'ͽ' => 'Ï¿', 'Ñ' => 'Ѐ', 'Ñ‘' => 'Ð', + 'Ñ’' => 'Ђ', 'Ñ“' => 'Ѓ', 'Ñ”' => 'Є', 'Ñ•' => 'Ð…', + 'Ñ–' => 'І', 'Ñ—' => 'Ї', 'ј' => 'Ј', 'Ñ™' => 'Љ', + 'Ñš' => 'Њ', 'Ñ›' => 'Ћ', 'Ñœ' => 'ÐŒ', 'Ñ' => 'Ð', + 'Ñž' => 'ÐŽ', 'ÑŸ' => 'Ð', 'а' => 'Ð', 'б' => 'Б', + 'в' => 'Ð’', 'г' => 'Г', 'д' => 'Д', 'е' => 'Е', + 'ж' => 'Ж', 'з' => 'З', 'и' => 'И', 'й' => 'Й', + 'к' => 'К', 'л' => 'Л', 'м' => 'М', 'н' => 'Ð', + 'о' => 'О', 'п' => 'П', 'Ñ€' => 'Р', 'Ñ' => 'С', + 'Ñ‚' => 'Т', 'у' => 'У', 'Ñ„' => 'Ф', 'Ñ…' => 'Ð¥', + 'ц' => 'Ц', 'ч' => 'Ч', 'ш' => 'Ш', 'щ' => 'Щ', + 'ÑŠ' => 'Ъ', 'Ñ‹' => 'Ы', 'ÑŒ' => 'Ь', 'Ñ' => 'Э', + 'ÑŽ' => 'Ю', 'Ñ' => 'Я', 'Ñ¡' => 'Ñ ', 'Ñ£' => 'Ñ¢', + 'Ñ¥' => 'Ѥ', 'ѧ' => 'Ѧ', 'Ñ©' => 'Ѩ', 'Ñ«' => 'Ѫ', + 'Ñ­' => 'Ѭ', 'ѯ' => 'Ñ®', 'ѱ' => 'Ѱ', 'ѳ' => 'Ѳ', + 'ѵ' => 'Ñ´', 'Ñ·' => 'Ѷ', 'ѹ' => 'Ѹ', 'Ñ»' => 'Ѻ', + 'ѽ' => 'Ѽ', 'Ñ¿' => 'Ѿ', 'Ò' => 'Ò€', 'Ò‹' => 'ÒŠ', + 'Ò' => 'ÒŒ', 'Ò' => 'ÒŽ', 'Ò‘' => 'Ò', 'Ò“' => 'Ò’', + 'Ò•' => 'Ò”', 'Ò—' => 'Ò–', 'Ò™' => 'Ò˜', 'Ò›' => 'Òš', + 'Ò' => 'Òœ', 'ÒŸ' => 'Òž', 'Ò¡' => 'Ò ', 'Ò£' => 'Ò¢', + 'Ò¥' => 'Ò¤', 'Ò§' => 'Ò¦', 'Ò©' => 'Ò¨', 'Ò«' => 'Òª', + 'Ò­' => 'Ò¬', 'Ò¯' => 'Ò®', 'Ò±' => 'Ò°', 'Ò³' => 'Ò²', + 'Òµ' => 'Ò´', 'Ò·' => 'Ò¶', 'Ò¹' => 'Ò¸', 'Ò»' => 'Òº', + 'Ò½' => 'Ò¼', 'Ò¿' => 'Ò¾', 'Ó' => 'Ó€', 'Ó‚' => 'Ó', + 'Ó„' => 'Óƒ', 'Ó†' => 'Ó…', 'Óˆ' => 'Ó‡', 'ÓŠ' => 'Ó‰', + 'ÓŒ' => 'Ó‹', 'ÓŽ' => 'Ó', 'Ó‘' => 'Ó', 'Ó“' => 'Ó’', + 'Ó•' => 'Ó”', 'Ó—' => 'Ó–', 'Ó™' => 'Ó˜', 'Ó›' => 'Óš', + 'Ó' => 'Óœ', 'ÓŸ' => 'Óž', 'Ó¡' => 'Ó ', 'Ó£' => 'Ó¢', + 'Ó¥' => 'Ó¤', 'Ó§' => 'Ó¦', 'Ó©' => 'Ó¨', 'Ó«' => 'Óª', + 'Ó­' => 'Ó¬', 'Ó¯' => 'Ó®', 'Ó±' => 'Ó°', 'Ó³' => 'Ó²', + 'Óµ' => 'Ó´', 'Ó·' => 'Ó¶', 'Ó¹' => 'Ó¸', 'Ó»' => 'Óº', + 'Ó½' => 'Ó¼', 'Ó¿' => 'Ó¾', 'Ô' => 'Ô€', 'Ôƒ' => 'Ô‚', + 'Ô…' => 'Ô„', 'Ô‡' => 'Ô†', 'Ô‰' => 'Ôˆ', 'Ô‹' => 'ÔŠ', + 'Ô' => 'ÔŒ', 'Ô' => 'ÔŽ', 'Ô‘' => 'Ô', 'Ô“' => 'Ô’', + 'Õ¡' => 'Ô±', 'Õ¢' => 'Ô²', 'Õ£' => 'Ô³', 'Õ¤' => 'Ô´', + 'Õ¥' => 'Ôµ', 'Õ¦' => 'Ô¶', 'Õ§' => 'Ô·', 'Õ¨' => 'Ô¸', + 'Õ©' => 'Ô¹', 'Õª' => 'Ôº', 'Õ«' => 'Ô»', 'Õ¬' => 'Ô¼', + 'Õ­' => 'Ô½', 'Õ®' => 'Ô¾', 'Õ¯' => 'Ô¿', 'Õ°' => 'Õ€', + 'Õ±' => 'Õ', 'Õ²' => 'Õ‚', 'Õ³' => 'Õƒ', 'Õ´' => 'Õ„', + 'Õµ' => 'Õ…', 'Õ¶' => 'Õ†', 'Õ·' => 'Õ‡', 'Õ¸' => 'Õˆ', + 'Õ¹' => 'Õ‰', 'Õº' => 'ÕŠ', 'Õ»' => 'Õ‹', 'Õ¼' => 'ÕŒ', + 'Õ½' => 'Õ', 'Õ¾' => 'ÕŽ', 'Õ¿' => 'Õ', 'Ö€' => 'Õ', + 'Ö' => 'Õ‘', 'Ö‚' => 'Õ’', 'Öƒ' => 'Õ“', 'Ö„' => 'Õ”', + 'Ö…' => 'Õ•', 'Ö†' => 'Õ–', 'Õ¥Ö‚' => 'Ö‡', 'â´€' => 'á‚ ', + 'â´' => 'á‚¡', 'â´‚' => 'á‚¢', 'â´ƒ' => 'á‚£', 'â´„' => 'Ⴄ', + 'â´…' => 'á‚¥', 'â´†' => 'Ⴆ', 'â´‡' => 'á‚§', 'â´ˆ' => 'Ⴈ', + 'â´‰' => 'á‚©', 'â´Š' => 'Ⴊ', 'â´‹' => 'á‚«', 'â´Œ' => 'Ⴌ', + 'â´' => 'á‚­', 'â´Ž' => 'á‚®', 'â´' => 'Ⴏ', 'â´' => 'á‚°', + 'â´‘' => 'Ⴑ', 'â´’' => 'Ⴒ', 'â´“' => 'Ⴓ', 'â´”' => 'á‚´', + 'â´•' => 'Ⴕ', 'â´–' => 'á‚¶', 'â´—' => 'á‚·', 'â´˜' => 'Ⴘ', + 'â´™' => 'Ⴙ', 'â´š' => 'Ⴚ', 'â´›' => 'á‚»', 'â´œ' => 'Ⴜ', + 'â´' => 'Ⴝ', 'â´ž' => 'Ⴞ', 'â´Ÿ' => 'á‚¿', 'â´ ' => 'Ⴠ', + 'â´¡' => 'áƒ', 'â´¢' => 'Ⴢ', 'â´£' => 'Ⴣ', 'â´¤' => 'Ⴤ', + 'â´¥' => 'Ⴥ', 'á¸' => 'Ḁ', 'ḃ' => 'Ḃ', 'ḅ' => 'Ḅ', + 'ḇ' => 'Ḇ', 'ḉ' => 'Ḉ', 'ḋ' => 'Ḋ', 'á¸' => 'Ḍ', + 'á¸' => 'Ḏ', 'ḑ' => 'á¸', 'ḓ' => 'Ḓ', 'ḕ' => 'Ḕ', + 'ḗ' => 'Ḗ', 'ḙ' => 'Ḙ', 'ḛ' => 'Ḛ', 'á¸' => 'Ḝ', + 'ḟ' => 'Ḟ', 'ḡ' => 'Ḡ', 'ḣ' => 'Ḣ', 'ḥ' => 'Ḥ', + 'ḧ' => 'Ḧ', 'ḩ' => 'Ḩ', 'ḫ' => 'Ḫ', 'ḭ' => 'Ḭ', + 'ḯ' => 'Ḯ', 'ḱ' => 'Ḱ', 'ḳ' => 'Ḳ', 'ḵ' => 'Ḵ', + 'ḷ' => 'Ḷ', 'ḹ' => 'Ḹ', 'ḻ' => 'Ḻ', 'ḽ' => 'Ḽ', + 'ḿ' => 'Ḿ', 'á¹' => 'á¹€', 'ṃ' => 'Ṃ', 'á¹…' => 'Ṅ', + 'ṇ' => 'Ṇ', 'ṉ' => 'Ṉ', 'ṋ' => 'Ṋ', 'á¹' => 'Ṍ', + 'á¹' => 'Ṏ', 'ṑ' => 'á¹', 'ṓ' => 'á¹’', 'ṕ' => 'á¹”', + 'á¹—' => 'á¹–', 'á¹™' => 'Ṙ', 'á¹›' => 'Ṛ', 'á¹' => 'Ṝ', + 'ṟ' => 'Ṟ', 'ṡ' => 'á¹ ', 'á¹£' => 'á¹¢', 'á¹¥' => 'Ṥ', + 'á¹§' => 'Ṧ', 'ṩ' => 'Ṩ', 'ṫ' => 'Ṫ', 'á¹­' => 'Ṭ', + 'ṯ' => 'á¹®', 'á¹±' => 'á¹°', 'á¹³' => 'á¹²', 'á¹µ' => 'á¹´', + 'á¹·' => 'á¹¶', 'á¹¹' => 'Ṹ', 'á¹»' => 'Ṻ', 'á¹½' => 'á¹¼', + 'ṿ' => 'á¹¾', 'áº' => 'Ẁ', 'ẃ' => 'Ẃ', 'ẅ' => 'Ẅ', + 'ẇ' => 'Ẇ', 'ẉ' => 'Ẉ', 'ẋ' => 'Ẋ', 'áº' => 'Ẍ', + 'áº' => 'Ẏ', 'ẑ' => 'áº', 'ẓ' => 'Ẓ', 'ẕ' => 'Ẕ', + 'ẖ' => 'ẖ', 'ẗ' => 'ẗ', 'wÌŠ' => 'ẘ', 'yÌŠ' => 'ẙ', + 'aʾ' => 'ẚ', 'ṡ' => 'ẛ', 'ạ' => 'Ạ', 'ả' => 'Ả', + 'ấ' => 'Ấ', 'ầ' => 'Ầ', 'ẩ' => 'Ẩ', 'ẫ' => 'Ẫ', + 'ậ' => 'Ậ', 'ắ' => 'Ắ', 'ằ' => 'Ằ', 'ẳ' => 'Ẳ', + 'ẵ' => 'Ẵ', 'ặ' => 'Ặ', 'ẹ' => 'Ẹ', 'ẻ' => 'Ẻ', + 'ẽ' => 'Ẽ', 'ế' => 'Ế', 'á»' => 'Ề', 'ể' => 'Ể', + 'á»…' => 'Ễ', 'ệ' => 'Ệ', 'ỉ' => 'Ỉ', 'ị' => 'Ị', + 'á»' => 'Ọ', 'á»' => 'Ỏ', 'ố' => 'á»', 'ồ' => 'á»’', + 'ổ' => 'á»”', 'á»—' => 'á»–', 'á»™' => 'Ộ', 'á»›' => 'Ớ', + 'á»' => 'Ờ', 'ở' => 'Ở', 'ỡ' => 'á» ', 'ợ' => 'Ợ', + 'ụ' => 'Ụ', 'á»§' => 'Ủ', 'ứ' => 'Ứ', 'ừ' => 'Ừ', + 'á»­' => 'Ử', 'ữ' => 'á»®', 'á»±' => 'á»°', 'ỳ' => 'Ỳ', + 'ỵ' => 'á»´', 'á»·' => 'á»¶', 'ỹ' => 'Ỹ', 'á¼€' => 'Ἀ', + 'á¼' => 'Ἁ', 'ἂ' => 'Ἂ', 'ἃ' => 'Ἃ', 'ἄ' => 'Ἄ', + 'á¼…' => 'á¼', 'ἆ' => 'Ἆ', 'ἇ' => 'á¼', 'á¼' => 'Ἐ', + 'ἑ' => 'á¼™', 'á¼’' => 'Ἒ', 'ἓ' => 'á¼›', 'á¼”' => 'Ἔ', + 'ἕ' => 'á¼', 'á¼ ' => 'Ἠ', 'ἡ' => 'Ἡ', 'á¼¢' => 'Ἢ', + 'á¼£' => 'Ἣ', 'ἤ' => 'Ἤ', 'á¼¥' => 'á¼­', 'ἦ' => 'á¼®', + 'á¼§' => 'Ἧ', 'á¼°' => 'Ἰ', 'á¼±' => 'á¼¹', 'á¼²' => 'Ἲ', + 'á¼³' => 'á¼»', 'á¼´' => 'á¼¼', 'á¼µ' => 'á¼½', 'á¼¶' => 'á¼¾', + 'á¼·' => 'Ἷ', 'á½€' => 'Ὀ', 'á½' => 'Ὁ', 'ὂ' => 'Ὂ', + 'ὃ' => 'Ὃ', 'ὄ' => 'Ὄ', 'á½…' => 'á½', 'Ï…Ì“' => 'á½', + 'ὒ' => 'á½’', 'Ï…Ì“Ì' => 'á½”', 'ὖ' => 'á½–', 'ὑ' => 'á½™', + 'ὓ' => 'á½›', 'ὕ' => 'á½', 'á½—' => 'Ὗ', 'á½ ' => 'Ὠ', + 'ὡ' => 'Ὡ', 'á½¢' => 'Ὢ', 'á½£' => 'Ὣ', 'ὤ' => 'Ὤ', + 'á½¥' => 'á½­', 'ὦ' => 'á½®', 'á½§' => 'Ὧ', 'ἀι' => 'á¾€', + 'á¼Î¹' => 'á¾', 'ἂι' => 'ᾂ', 'ἃι' => 'ᾃ', 'ἄι' => 'ᾄ', + 'ἅι' => 'á¾…', 'ἆι' => 'ᾆ', 'ἇι' => 'ᾇ', 'á¾€' => 'ᾈ', + 'á¾' => 'ᾉ', 'ᾂ' => 'ᾊ', 'ᾃ' => 'ᾋ', 'ᾄ' => 'ᾌ', + 'á¾…' => 'á¾', 'ᾆ' => 'ᾎ', 'ᾇ' => 'á¾', 'ἠι' => 'á¾', + 'ἡι' => 'ᾑ', 'ἢι' => 'á¾’', 'ἣι' => 'ᾓ', 'ἤι' => 'á¾”', + 'ἥι' => 'ᾕ', 'ἦι' => 'á¾–', 'ἧι' => 'á¾—', 'á¾' => 'ᾘ', + 'ᾑ' => 'á¾™', 'á¾’' => 'ᾚ', 'ᾓ' => 'á¾›', 'á¾”' => 'ᾜ', + 'ᾕ' => 'á¾', 'á¾–' => 'ᾞ', 'á¾—' => 'ᾟ', 'ὠι' => 'á¾ ', + 'ὡι' => 'ᾡ', 'ὢι' => 'á¾¢', 'ὣι' => 'á¾£', 'ὤι' => 'ᾤ', + 'ὥι' => 'á¾¥', 'ὦι' => 'ᾦ', 'ὧι' => 'á¾§', 'á¾ ' => 'ᾨ', + 'ᾡ' => 'ᾩ', 'á¾¢' => 'ᾪ', 'á¾£' => 'ᾫ', 'ᾤ' => 'ᾬ', + 'á¾¥' => 'á¾­', 'ᾦ' => 'á¾®', 'á¾§' => 'ᾯ', 'ὰι' => 'á¾²', + 'αι' => 'á¾³', 'άι' => 'á¾´', 'ᾶ' => 'á¾¶', 'ᾶι' => 'á¾·', + 'á¾°' => 'Ᾰ', 'á¾±' => 'á¾¹', 'á½°' => 'Ὰ', 'á½±' => 'á¾»', + 'á¾³' => 'á¾¼', 'ι' => 'á¾¾', 'ὴι' => 'á¿‚', 'ηι' => 'ῃ', + 'ήι' => 'á¿„', 'ῆ' => 'ῆ', 'ῆι' => 'ῇ', 'á½²' => 'Ὲ', + 'á½³' => 'Έ', 'á½´' => 'Ὴ', 'á½µ' => 'á¿‹', 'ῃ' => 'ῌ', + 'ῒ' => 'á¿’', 'ϊÌ' => 'á¿“', 'ῖ' => 'á¿–', 'ῗ' => 'á¿—', + 'á¿' => 'Ῐ', 'á¿‘' => 'á¿™', 'á½¶' => 'Ὶ', 'á½·' => 'á¿›', + 'ῢ' => 'á¿¢', 'ϋÌ' => 'á¿£', 'ÏÌ“' => 'ῤ', 'Ï…Í‚' => 'ῦ', + 'ῧ' => 'á¿§', 'á¿ ' => 'Ῠ', 'á¿¡' => 'á¿©', 'ὺ' => 'Ὺ', + 'á½»' => 'á¿«', 'á¿¥' => 'Ῥ', 'ὼι' => 'ῲ', 'ωι' => 'ῳ', + 'ώι' => 'á¿´', 'ῶ' => 'á¿¶', 'ῶι' => 'á¿·', 'ὸ' => 'Ὸ', + 'á½¹' => 'Ό', 'á½¼' => 'Ὼ', 'á½½' => 'á¿»', 'ῳ' => 'ῼ', + 'ω' => 'Ω', 'k' => 'K', 'Ã¥' => 'â„«', 'â…Ž' => 'Ⅎ', + 'â…°' => 'â… ', 'â…±' => 'â…¡', 'â…²' => 'â…¢', 'â…³' => 'â…£', + 'â…´' => 'â…¤', 'â…µ' => 'â…¥', 'â…¶' => 'â…¦', 'â…·' => 'â…§', + 'â…¸' => 'â…¨', 'â…¹' => 'â…©', 'â…º' => 'â…ª', 'â…»' => 'â…«', + 'â…¼' => 'â…¬', 'â…½' => 'â…­', 'â…¾' => 'â…®', 'â…¿' => 'â…¯', + 'ↄ' => 'Ↄ', 'â“' => 'â’¶', 'â“‘' => 'â’·', 'â“’' => 'â’¸', + 'â““' => 'â’¹', 'â“”' => 'â’º', 'â“•' => 'â’»', 'â“–' => 'â’¼', + 'â“—' => 'â’½', 'ⓘ' => 'â’¾', 'â“™' => 'â’¿', 'ⓚ' => 'â“€', + 'â“›' => 'â“', 'ⓜ' => 'â“‚', 'â“' => 'Ⓝ', 'ⓞ' => 'â“„', + 'ⓟ' => 'â“…', 'â“ ' => 'Ⓠ', 'â“¡' => 'Ⓡ', 'â“¢' => 'Ⓢ', + 'â“£' => 'Ⓣ', 'ⓤ' => 'Ⓤ', 'â“¥' => 'â“‹', 'ⓦ' => 'Ⓦ', + 'â“§' => 'â“', 'ⓨ' => 'Ⓨ', 'â“©' => 'â“', 'â°°' => 'â°€', + 'â°±' => 'â°', 'â°²' => 'â°‚', 'â°³' => 'â°ƒ', 'â°´' => 'â°„', + 'â°µ' => 'â°…', 'â°¶' => 'â°†', 'â°·' => 'â°‡', 'â°¸' => 'â°ˆ', + 'â°¹' => 'â°‰', 'â°º' => 'â°Š', 'â°»' => 'â°‹', 'â°¼' => 'â°Œ', + 'â°½' => 'â°', 'â°¾' => 'â°Ž', 'â°¿' => 'â°', 'â±€' => 'â°', + 'â±' => 'â°‘', 'ⱂ' => 'â°’', 'ⱃ' => 'â°“', 'ⱄ' => 'â°”', + 'â±…' => 'â°•', 'ⱆ' => 'â°–', 'ⱇ' => 'â°—', 'ⱈ' => 'â°˜', + 'ⱉ' => 'â°™', 'ⱊ' => 'â°š', 'ⱋ' => 'â°›', 'ⱌ' => 'â°œ', + 'â±' => 'â°', 'ⱎ' => 'â°ž', 'â±' => 'â°Ÿ', 'â±' => 'â° ', + 'ⱑ' => 'â°¡', 'â±’' => 'â°¢', 'ⱓ' => 'â°£', 'â±”' => 'â°¤', + 'ⱕ' => 'â°¥', 'â±–' => 'â°¦', 'â±—' => 'â°§', 'ⱘ' => 'â°¨', + 'â±™' => 'â°©', 'ⱚ' => 'â°ª', 'â±›' => 'â°«', 'ⱜ' => 'â°¬', + 'â±' => 'â°­', 'ⱞ' => 'â°®', 'ⱡ' => 'â± ', 'É«' => 'â±¢', + 'áµ½' => 'â±£', 'ɽ' => 'Ɽ', 'ⱨ' => 'â±§', 'ⱪ' => 'Ⱪ', + 'ⱬ' => 'Ⱬ', 'â±¶' => 'â±µ', 'â²' => 'â²€', 'ⲃ' => 'Ⲃ', + 'â²…' => 'Ⲅ', 'ⲇ' => 'Ⲇ', 'ⲉ' => 'Ⲉ', 'ⲋ' => 'Ⲋ', + 'â²' => 'Ⲍ', 'â²' => 'Ⲏ', 'ⲑ' => 'â²', 'ⲓ' => 'â²’', + 'ⲕ' => 'â²”', 'â²—' => 'â²–', 'â²™' => 'Ⲙ', 'â²›' => 'Ⲛ', + 'â²' => 'Ⲝ', 'ⲟ' => 'Ⲟ', 'ⲡ' => 'â² ', 'â²£' => 'â²¢', + 'â²¥' => 'Ⲥ', 'â²§' => 'Ⲧ', 'ⲩ' => 'Ⲩ', 'ⲫ' => 'Ⲫ', + 'â²­' => 'Ⲭ', 'ⲯ' => 'â²®', 'â²±' => 'â²°', 'â²³' => 'â²²', + 'â²µ' => 'â²´', 'â²·' => 'â²¶', 'â²¹' => 'Ⲹ', 'â²»' => 'Ⲻ', + 'â²½' => 'â²¼', 'ⲿ' => 'â²¾', 'â³' => 'â³€', 'ⳃ' => 'Ⳃ', + 'â³…' => 'Ⳅ', 'ⳇ' => 'Ⳇ', 'ⳉ' => 'Ⳉ', 'ⳋ' => 'Ⳋ', + 'â³' => 'Ⳍ', 'â³' => 'Ⳏ', 'ⳑ' => 'â³', 'ⳓ' => 'â³’', + 'ⳕ' => 'â³”', 'â³—' => 'â³–', 'â³™' => 'Ⳙ', 'â³›' => 'Ⳛ', + 'â³' => 'Ⳝ', 'ⳟ' => 'Ⳟ', 'ⳡ' => 'â³ ', 'â³£' => 'â³¢', + 'ff' => 'ff', 'fi' => 'ï¬', 'fl' => 'fl', 'ffi' => 'ffi', + 'ffl' => 'ffl', 'st' => 'ſt', 'st' => 'st', 'Õ´Õ¶' => 'ﬓ', + 'Õ´Õ¥' => 'ﬔ', 'Õ´Õ«' => 'ﬕ', 'Õ¾Õ¶' => 'ﬖ', 'Õ´Õ­' => 'ﬗ', + 'ï½' => 'A', 'b' => 'ï¼¢', 'c' => 'ï¼£', 'd' => 'D', + 'ï½…' => 'ï¼¥', 'f' => 'F', 'g' => 'ï¼§', 'h' => 'H', + 'i' => 'I', 'j' => 'J', 'k' => 'K', 'l' => 'L', + 'ï½' => 'ï¼­', 'n' => 'ï¼®', 'ï½' => 'O', 'ï½' => 'ï¼°', + 'q' => 'ï¼±', 'ï½’' => 'ï¼²', 's' => 'ï¼³', 'ï½”' => 'ï¼´', + 'u' => 'ï¼µ', 'ï½–' => 'ï¼¶', 'ï½—' => 'ï¼·', 'x' => 'X', + 'ï½™' => 'ï¼¹', 'z' => 'Z', 'ð¨' => 'ð€', 'ð©' => 'ð', + 'ðª' => 'ð‚', 'ð«' => 'ðƒ', 'ð¬' => 'ð„', 'ð­' => 'ð…', + 'ð®' => 'ð†', 'ð¯' => 'ð‡', 'ð°' => 'ðˆ', 'ð±' => 'ð‰', + 'ð²' => 'ðŠ', 'ð³' => 'ð‹', 'ð´' => 'ðŒ', 'ðµ' => 'ð', + 'ð¶' => 'ðŽ', 'ð·' => 'ð', 'ð¸' => 'ð', 'ð¹' => 'ð‘', + 'ðº' => 'ð’', 'ð»' => 'ð“', 'ð¼' => 'ð”', 'ð½' => 'ð•', + 'ð¾' => 'ð–', 'ð¿' => 'ð—', 'ð‘€' => 'ð˜', 'ð‘' => 'ð™', + 'ð‘‚' => 'ðš', 'ð‘ƒ' => 'ð›', 'ð‘„' => 'ðœ', 'ð‘…' => 'ð', + 'ð‘†' => 'ðž', 'ð‘‡' => 'ðŸ', 'ð‘ˆ' => 'ð ', 'ð‘‰' => 'ð¡', + 'ð‘Š' => 'ð¢', 'ð‘‹' => 'ð£', 'ð‘Œ' => 'ð¤', 'ð‘' => 'ð¥', + 'ð¦' => 'ð‘Ž', 'ð§' => 'ð‘', + ); + + return strtr($string, $case_folding); +} + +// Fixes corrupted serialized strings after a character set conversion. +function fix_serialized_columns() +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT id_action, extra + FROM {db_prefix}log_actions + WHERE action IN ({string:remove}, {string:delete})', + array( + 'remove' => 'remove', + 'delete' => 'delete', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (@unserialize($row['extra']) === false && preg_match('~^(a:3:{s:5:"topic";i:\d+;s:7:"subject";s:)(\d+):"(.+)"(;s:6:"member";s:5:"\d+";})$~', $row['extra'], $matches) === 1) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_actions + SET extra = {string:extra} + WHERE id_action = {int:current_action}', + array( + 'current_action' => $row['id_action'], + 'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4], + ) + ); + } + $smcFunc['db_free_result']($request); + + // Refresh some cached data. + updateSettings(array( + 'memberlist_updated' => time(), + )); + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Compat.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Compat.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,243 @@ += strlen($str)) + break; + + $str_array[] = substr($str, $count, $str_length); + $count += $str_length; + } + + return $str_array; + } +} + +if (!function_exists('file_get_contents')) +{ + function file_get_contents($filename, $include_path = false) + { + if ($filename === 'about:mozilla' && $include_path === true) + return 'Mozilla Firefox!'; + + $fp = fopen($filename, 'rb', $include_path); + if ($fp == false) + return false; + + if (is_file($filename)) + $data = fread($fp, filesize($filename)); + else + { + $data = ''; + while (!feof($fp)) + $data .= fread($fp, 8192); + } + fclose($fp); + + return $data; + } +} + +// Define the old SMF sha1 function. +function sha1_smf($str) +{ + // If we have mhash loaded in, use it instead! + if (function_exists('mhash') && defined('MHASH_SHA1')) + return bin2hex(mhash(MHASH_SHA1, $str)); + + $nblk = (strlen($str) + 8 >> 6) + 1; + $blks = array_pad(array(), $nblk * 16, 0); + + for ($i = 0; $i < strlen($str); $i++) + $blks[$i >> 2] |= ord($str{$i}) << (24 - ($i % 4) * 8); + + $blks[$i >> 2] |= 0x80 << (24 - ($i % 4) * 8); + + return sha1_core($blks, strlen($str) * 8); +} + +// This is the core SHA-1 calculation routine, used by sha1(). +function sha1_core($x, $len) +{ + @$x[$len >> 5] |= 0x80 << (24 - $len % 32); + $x[(($len + 64 >> 9) << 4) + 15] = $len; + + $w = array(); + $a = 1732584193; + $b = -271733879; + $c = -1732584194; + $d = 271733878; + $e = -1009589776; + + for ($i = 0, $n = count($x); $i < $n; $i += 16) + { + $olda = $a; + $oldb = $b; + $oldc = $c; + $oldd = $d; + $olde = $e; + + for ($j = 0; $j < 80; $j++) + { + if ($j < 16) + $w[$j] = isset($x[$i + $j]) ? $x[$i + $j] : 0; + else + $w[$j] = sha1_rol($w[$j - 3] ^ $w[$j - 8] ^ $w[$j - 14] ^ $w[$j - 16], 1); + + $t = sha1_rol($a, 5) + sha1_ft($j, $b, $c, $d) + $e + $w[$j] + sha1_kt($j); + $e = $d; + $d = $c; + $c = sha1_rol($b, 30); + $b = $a; + $a = $t; + } + + $a += $olda; + $b += $oldb; + $c += $oldc; + $d += $oldd; + $e += $olde; + } + + return sprintf('%08x%08x%08x%08x%08x', $a, $b, $c, $d, $e); +} + +function sha1_ft($t, $b, $c, $d) +{ + if ($t < 20) + return ($b & $c) | ((~$b) & $d); + if ($t < 40) + return $b ^ $c ^ $d; + if ($t < 60) + return ($b & $c) | ($b & $d) | ($c & $d); + + return $b ^ $c ^ $d; +} + +function sha1_kt($t) +{ + return $t < 20 ? 1518500249 : ($t < 40 ? 1859775393 : ($t < 60 ? -1894007588 : -899497514)); +} + +function sha1_rol($num, $cnt) +{ + // Unfortunately, PHP uses unsigned 32-bit longs only. So we have to kludge it a bit. + if ($num & 0x80000000) + $a = ($num >> 1 & 0x7fffffff) >> (31 - $cnt); + else + $a = $num >> (32 - $cnt); + + return ($num << $cnt) | $a; +} + +// Still on old PHP - bad boy! (the built in one would be faster.) +if (!function_exists('sha1')) +{ + function sha1($str) + { + return sha1_smf($str); + } +} + +if (!function_exists('array_combine')) +{ + function array_combine($keys, $values) + { + $ret = array(); + if (($array_error = !is_array($keys) || !is_array($values)) || empty($values) || ($count=count($keys)) != count($values)) + { + trigger_error('array_combine(): Both parameters should be non-empty arrays with an equal number of elements', E_USER_WARNING); + + if ($array_error) + return; + return false; + } + + // Ensure that both arrays aren't associative arrays. + $keys = array_values($keys); + $values = array_values($values); + + for ($i=0; $i < $count; $i++) + $ret[$keys[$i]] = $values[$i]; + + return $ret; + } +} + +if (!function_exists('array_diff_key')) +{ + function array_diff_key() + { + $arrays = func_get_args(); + $result = array_shift($arrays); + foreach ($arrays as $array) + { + foreach ($result as $key => $v) + { + if (array_key_exists($key, $array)) + { + unset($result[$key]); + } + } + } + return $result; + } +} + +if (!function_exists('mysql_real_escape_string')) +{ + function mysql_real_escape_string($string, $connection = null) + { + return mysql_escape_string($string); + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Db-mysql.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Db-mysql.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,709 @@ + 'smf_db_query', + 'db_quote' => 'smf_db_quote', + 'db_fetch_assoc' => 'mysql_fetch_assoc', + 'db_fetch_row' => 'mysql_fetch_row', + 'db_free_result' => 'mysql_free_result', + 'db_insert' => 'smf_db_insert', + 'db_insert_id' => 'smf_db_insert_id', + 'db_num_rows' => 'mysql_num_rows', + 'db_data_seek' => 'mysql_data_seek', + 'db_num_fields' => 'mysql_num_fields', + 'db_escape_string' => 'addslashes', + 'db_unescape_string' => 'stripslashes', + 'db_server_info' => 'mysql_get_server_info', + 'db_affected_rows' => 'smf_db_affected_rows', + 'db_transaction' => 'smf_db_transaction', + 'db_error' => 'mysql_error', + 'db_select_db' => 'mysql_select_db', + 'db_title' => 'MySQL', + 'db_sybase' => false, + 'db_case_sensitive' => false, + 'db_escape_wildcard_string' => 'smf_db_escape_wildcard_string', + ); + + if (!empty($db_options['persist'])) + $connection = @mysql_pconnect($db_server, $db_user, $db_passwd); + else + $connection = @mysql_connect($db_server, $db_user, $db_passwd); + + // Something's wrong, show an error if its fatal (which we assume it is) + if (!$connection) + { + if (!empty($db_options['non_fatal'])) + return null; + else + db_fatal_error(); + } + + // Select the database, unless told not to + if (empty($db_options['dont_select_db']) && !@mysql_select_db($db_name, $connection) && empty($db_options['non_fatal'])) + db_fatal_error(); + + // This makes it possible to have SMF automatically change the sql_mode and autocommit if needed. + if (isset($mysql_set_mode) && $mysql_set_mode === true) + $smcFunc['db_query']('', 'SET sql_mode = \'\', AUTOCOMMIT = 1', + array(), + false + ); + + return $connection; +} + +// Extend the database functionality. +function db_extend($type = 'extra') +{ + global $sourcedir, $db_type; + + require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-' . $db_type . '.php'); + $initFunc = 'db_' . $type . '_init'; + $initFunc(); +} + +// Fix up the prefix so it doesn't require the database to be selected. +function db_fix_prefix(&$db_prefix, $db_name) +{ + $db_prefix = is_numeric(substr($db_prefix, 0, 1)) ? $db_name . '.' . $db_prefix : '`' . $db_name . '`.' . $db_prefix; +} + +function smf_db_replacement__callback($matches) +{ + global $db_callback, $user_info, $db_prefix; + + list ($values, $connection) = $db_callback; + + if (!is_resource($connection)) + db_fatal_error(); + + if ($matches[1] === 'db_prefix') + return $db_prefix; + + if ($matches[1] === 'query_see_board') + return $user_info['query_see_board']; + + if ($matches[1] === 'query_wanna_see_board') + return $user_info['query_wanna_see_board']; + + if (!isset($matches[2])) + smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__); + + if (!isset($values[$matches[2]])) + smf_db_error_backtrace('The database value you\'re trying to insert does not exist: ' . htmlspecialchars($matches[2]), '', E_USER_ERROR, __FILE__, __LINE__); + + $replacement = $values[$matches[2]]; + + switch ($matches[1]) + { + case 'int': + if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement) + smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + return (string) (int) $replacement; + break; + + case 'string': + case 'text': + return sprintf('\'%1$s\'', mysql_real_escape_string($replacement, $connection)); + break; + + case 'array_int': + if (is_array($replacement)) + { + if (empty($replacement)) + smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + foreach ($replacement as $key => $value) + { + if (!is_numeric($value) || (string) $value !== (string) (int) $value) + smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + $replacement[$key] = (string) (int) $value; + } + + return implode(', ', $replacement); + } + else + smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + break; + + case 'array_string': + if (is_array($replacement)) + { + if (empty($replacement)) + smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + foreach ($replacement as $key => $value) + $replacement[$key] = sprintf('\'%1$s\'', mysql_real_escape_string($value, $connection)); + + return implode(', ', $replacement); + } + else + smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + break; + + case 'date': + if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1) + return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]); + else + smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + break; + + case 'float': + if (!is_numeric($replacement)) + smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + return (string) (float) $replacement; + break; + + case 'identifier': + // Backticks inside identifiers are supported as of MySQL 4.1. We don't need them for SMF. + return '`' . strtr($replacement, array('`' => '', '.' => '')) . '`'; + break; + + case 'raw': + return $replacement; + break; + + default: + smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__); + break; + } +} + +// Just like the db_query, escape and quote a string, but not executing the query. +function smf_db_quote($db_string, $db_values, $connection = null) +{ + global $db_callback, $db_connection; + + // Only bother if there's something to replace. + if (strpos($db_string, '{') !== false) + { + // This is needed by the callback function. + $db_callback = array($db_values, $connection == null ? $db_connection : $connection); + + // Do the quoting and escaping + $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); + + // Clear this global variable. + $db_callback = array(); + } + + return $db_string; +} + +// Do a query. Takes care of errors too. +function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null) +{ + global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start; + global $db_unbuffered, $db_callback, $modSettings; + + // Comments that are allowed in a query are preg_removed. + static $allowed_comments_from = array( + '~\s+~s', + '~/\*!40001 SQL_NO_CACHE \*/~', + '~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~', + '~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~', + ); + static $allowed_comments_to = array( + ' ', + '', + '', + '', + ); + + // Decide which connection to use. + $connection = $connection == null ? $db_connection : $connection; + + // One more query.... + $db_count = !isset($db_count) ? 1 : $db_count + 1; + + if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override'])) + smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__); + + // Use "ORDER BY null" to prevent Mysql doing filesorts for Group By clauses without an Order By + if (strpos($db_string, 'GROUP BY') !== false && strpos($db_string, 'ORDER BY') === false && strpos($db_string, 'INSERT INTO') === false) + { + // Add before LIMIT + if ($pos = strpos($db_string, 'LIMIT ')) + $db_string = substr($db_string, 0, $pos) . "\t\t\tORDER BY null\n" . substr($db_string, $pos, strlen($db_string)); + else + // Append it. + $db_string .= "\n\t\t\tORDER BY null"; + } + + if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false)) + { + // Pass some values to the global space for use in the callback function. + $db_callback = array($db_values, $connection); + + // Inject the values passed to this function. + $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); + + // This shouldn't be residing in global space any longer. + $db_callback = array(); + } + + // Debugging. + if (isset($db_show_debug) && $db_show_debug === true) + { + // Get the file and line number this function was called. + list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); + + // Initialize $db_cache if not already initialized. + if (!isset($db_cache)) + $db_cache = array(); + + if (!empty($_SESSION['debug_redirect'])) + { + $db_cache = array_merge($_SESSION['debug_redirect'], $db_cache); + $db_count = count($db_cache) + 1; + $_SESSION['debug_redirect'] = array(); + } + + $st = microtime(); + // Don't overload it. + $db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...'; + $db_cache[$db_count]['f'] = $file; + $db_cache[$db_count]['l'] = $line; + $db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start)); + } + + // First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over. + if (empty($modSettings['disableQueryCheck'])) + { + $clean = ''; + $old_pos = 0; + $pos = -1; + while (true) + { + $pos = strpos($db_string, '\'', $pos + 1); + if ($pos === false) + break; + $clean .= substr($db_string, $old_pos, $pos - $old_pos); + + while (true) + { + $pos1 = strpos($db_string, '\'', $pos + 1); + $pos2 = strpos($db_string, '\\', $pos + 1); + if ($pos1 === false) + break; + elseif ($pos2 == false || $pos2 > $pos1) + { + $pos = $pos1; + break; + } + + $pos = $pos2 + 1; + } + $clean .= ' %s '; + + $old_pos = $pos + 1; + } + $clean .= substr($db_string, $old_pos); + $clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean))); + + // We don't use UNION in SMF, at least so far. But it's useful for injections. + if (strpos($clean, 'union') !== false && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0) + $fail = true; + // Comments? We don't use comments in our queries, we leave 'em outside! + elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false) + $fail = true; + // Trying to change passwords, slow us down, or something? + elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0) + $fail = true; + elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0) + $fail = true; + // Sub selects? We don't use those either. + elseif (preg_match('~\([^)]*?select~s', $clean) != 0) + $fail = true; + + if (!empty($fail) && function_exists('log_error')) + smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__); + } + + if (empty($db_unbuffered)) + $ret = @mysql_query($db_string, $connection); + else + $ret = @mysql_unbuffered_query($db_string, $connection); + if ($ret === false && empty($db_values['db_error_skip'])) + $ret = smf_db_error($db_string, $connection); + + // Debugging. + if (isset($db_show_debug) && $db_show_debug === true) + $db_cache[$db_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st)); + + return $ret; +} + +function smf_db_affected_rows($connection = null) +{ + global $db_connection; + + return mysql_affected_rows($connection == null ? $db_connection : $connection); +} + +function smf_db_insert_id($table, $field = null, $connection = null) +{ + global $db_connection, $db_prefix; + + $table = str_replace('{db_prefix}', $db_prefix, $table); + + // MySQL doesn't need the table or field information. + return mysql_insert_id($connection == null ? $db_connection : $connection); +} + +// Do a transaction. +function smf_db_transaction($type = 'commit', $connection = null) +{ + global $db_connection; + + // Decide which connection to use + $connection = $connection == null ? $db_connection : $connection; + + if ($type == 'begin') + return @mysql_query('BEGIN', $connection); + elseif ($type == 'rollback') + return @mysql_query('ROLLBACK', $connection); + elseif ($type == 'commit') + return @mysql_query('COMMIT', $connection); + + return false; +} + +// Database error! +function smf_db_error($db_string, $connection = null) +{ + global $txt, $context, $sourcedir, $webmaster_email, $modSettings; + global $forum_version, $db_connection, $db_last_error, $db_persist; + global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd; + global $smcFunc; + + // Get the file and line numbers. + list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); + + // Decide which connection to use + $connection = $connection == null ? $db_connection : $connection; + + // This is the error message... + $query_error = mysql_error($connection); + $query_errno = mysql_errno($connection); + + // Error numbers: + // 1016: Can't open file '....MYI' + // 1030: Got error ??? from table handler. + // 1034: Incorrect key file for table. + // 1035: Old key file for table. + // 1205: Lock wait timeout exceeded. + // 1213: Deadlock found. + // 2006: Server has gone away. + // 2013: Lost connection to server during query. + + // Log the error. + if ($query_errno != 1213 && $query_errno != 1205 && function_exists('log_error')) + log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n$db_string" : ''), 'database', $file, $line); + + // Database error auto fixing ;). + if (function_exists('cache_get_data') && (!isset($modSettings['autoFixDatabase']) || $modSettings['autoFixDatabase'] == '1')) + { + // Force caching on, just for the error checking. + $old_cache = @$modSettings['cache_enable']; + $modSettings['cache_enable'] = '1'; + + if (($temp = cache_get_data('db_last_error', 600)) !== null) + $db_last_error = max(@$db_last_error, $temp); + + if (@$db_last_error < time() - 3600 * 24 * 3) + { + // We know there's a problem... but what? Try to auto detect. + if ($query_errno == 1030 && strpos($query_error, ' 127 ') !== false) + { + preg_match_all('~(?:[\n\r]|^)[^\']+?(?:FROM|JOIN|UPDATE|TABLE) ((?:[^\n\r(]+?(?:, )?)*)~s', $db_string, $matches); + + $fix_tables = array(); + foreach ($matches[1] as $tables) + { + $tables = array_unique(explode(',', $tables)); + foreach ($tables as $table) + { + // Now, it's still theoretically possible this could be an injection. So backtick it! + if (trim($table) != '') + $fix_tables[] = '`' . strtr(trim($table), array('`' => '')) . '`'; + } + } + + $fix_tables = array_unique($fix_tables); + } + // Table crashed. Let's try to fix it. + elseif ($query_errno == 1016) + { + if (preg_match('~\'([^\.\']+)~', $query_error, $match) != 0) + $fix_tables = array('`' . $match[1] . '`'); + } + // Indexes crashed. Should be easy to fix! + elseif ($query_errno == 1034 || $query_errno == 1035) + { + preg_match('~\'([^\']+?)\'~', $query_error, $match); + $fix_tables = array('`' . $match[1] . '`'); + } + } + + // Check for errors like 145... only fix it once every three days, and send an email. (can't use empty because it might not be set yet...) + if (!empty($fix_tables)) + { + // Subs-Admin.php for updateSettingsFile(), Subs-Post.php for sendmail(). + require_once($sourcedir . '/Subs-Admin.php'); + require_once($sourcedir . '/Subs-Post.php'); + + // Make a note of the REPAIR... + cache_put_data('db_last_error', time(), 600); + if (($temp = cache_get_data('db_last_error', 600)) === null) + updateSettingsFile(array('db_last_error' => time())); + + // Attempt to find and repair the broken table. + foreach ($fix_tables as $table) + $smcFunc['db_query']('', " + REPAIR TABLE $table", false, false); + + // And send off an email! + sendmail($webmaster_email, $txt['database_error'], $txt['tried_to_repair']); + + $modSettings['cache_enable'] = $old_cache; + + // Try the query again...? + $ret = $smcFunc['db_query']('', $db_string, false, false); + if ($ret !== false) + return $ret; + } + else + $modSettings['cache_enable'] = $old_cache; + + // Check for the "lost connection" or "deadlock found" errors - and try it just one more time. + if (in_array($query_errno, array(1205, 1213, 2006, 2013))) + { + if (in_array($query_errno, array(2006, 2013)) && $db_connection == $connection) + { + // Are we in SSI mode? If so try that username and password first + if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd)) + { + if (empty($db_persist)) + $db_connection = @mysql_connect($db_server, $ssi_db_user, $ssi_db_passwd); + else + $db_connection = @mysql_pconnect($db_server, $ssi_db_user, $ssi_db_passwd); + } + // Fall back to the regular username and password if need be + if (!$db_connection) + { + if (empty($db_persist)) + $db_connection = @mysql_connect($db_server, $db_user, $db_passwd); + else + $db_connection = @mysql_pconnect($db_server, $db_user, $db_passwd); + } + + if (!$db_connection || !@mysql_select_db($db_name, $db_connection)) + $db_connection = false; + } + + if ($db_connection) + { + // Try a deadlock more than once more. + for ($n = 0; $n < 4; $n++) + { + $ret = $smcFunc['db_query']('', $db_string, false, false); + + $new_errno = mysql_errno($db_connection); + if ($ret !== false || in_array($new_errno, array(1205, 1213))) + break; + } + + // If it failed again, shucks to be you... we're not trying it over and over. + if ($ret !== false) + return $ret; + } + } + // Are they out of space, perhaps? + elseif ($query_errno == 1030 && (strpos($query_error, ' -1 ') !== false || strpos($query_error, ' 28 ') !== false || strpos($query_error, ' 12 ') !== false)) + { + if (!isset($txt)) + $query_error .= ' - check database storage space.'; + else + { + if (!isset($txt['mysql_error_space'])) + loadLanguage('Errors'); + + $query_error .= !isset($txt['mysql_error_space']) ? ' - check database storage space.' : $txt['mysql_error_space']; + } + } + } + + // Nothing's defined yet... just die with it. + if (empty($context) || empty($txt)) + die($query_error); + + // Show an error message, if possible. + $context['error_title'] = $txt['database_error']; + if (allowedTo('admin_forum')) + $context['error_message'] = nl2br($query_error) . '
' . $txt['file'] . ': ' . $file . '
' . $txt['line'] . ': ' . $line; + else + $context['error_message'] = $txt['try_again']; + + // A database error is often the sign of a database in need of upgrade. Check forum versions, and if not identical suggest an upgrade... (not for Demo/CVS versions!) + if (allowedTo('admin_forum') && !empty($forum_version) && $forum_version != 'SMF ' . @$modSettings['smfVersion'] && strpos($forum_version, 'Demo') === false && strpos($forum_version, 'CVS') === false) + $context['error_message'] .= '

' . sprintf($txt['database_error_versions'], $forum_version, $modSettings['smfVersion']); + + if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true) + { + $context['error_message'] .= '

' . nl2br($db_string); + } + + // It's already been logged... don't log it again. + fatal_error($context['error_message'], false); +} + +// Insert some data... +function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null) +{ + global $smcFunc, $db_connection, $db_prefix; + + $connection = $connection === null ? $db_connection : $connection; + + // With nothing to insert, simply return. + if (empty($data)) + return; + + // Replace the prefix holder with the actual prefix. + $table = str_replace('{db_prefix}', $db_prefix, $table); + + // Inserting data as a single row can be done as a single array. + if (!is_array($data[array_rand($data)])) + $data = array($data); + + // Create the mold for a single row insert. + $insertData = '('; + foreach ($columns as $columnName => $type) + { + // Are we restricting the length? + if (strpos($type, 'string-') !== false) + $insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName); + else + $insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName); + } + $insertData = substr($insertData, 0, -2) . ')'; + + // Create an array consisting of only the columns. + $indexed_columns = array_keys($columns); + + // Here's where the variables are injected to the query. + $insertRows = array(); + foreach ($data as $dataRow) + $insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection); + + // Determine the method of insertion. + $queryTitle = $method == 'replace' ? 'REPLACE' : ($method == 'ignore' ? 'INSERT IGNORE' : 'INSERT'); + + // Do the insert. + $smcFunc['db_query']('', ' + ' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`) + VALUES + ' . implode(', + ', $insertRows), + array( + 'security_override' => true, + 'db_error_skip' => $table === $db_prefix . 'log_errors', + ), + $connection + ); +} + +// This function tries to work out additional error information from a back trace. +function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null) +{ + if (empty($log_message)) + $log_message = $error_message; + + if (function_exists('debug_backtrace')) + { + foreach (debug_backtrace() as $step) + { + // Found it? + if (strpos($step['function'], 'query') === false && !in_array(substr($step['function'], 0, 7), array('smf_db_', 'preg_re', 'db_erro', 'call_us')) && substr($step['function'], 0, 2) != '__') + { + $log_message .= '
Function: ' . $step['function']; + break; + } + + if (isset($step['line'])) + { + $file = $step['file']; + $line = $step['line']; + } + } + } + + // A special case - we want the file and line numbers for debugging. + if ($error_type == 'return') + return array($file, $line); + + // Is always a critical error. + if (function_exists('log_error')) + log_error($log_message, 'critical', $file, $line); + + if (function_exists('fatal_error')) + { + fatal_error($error_message, false); + + // Cannot continue... + exit; + } + elseif ($error_type) + trigger_error($error_message . ($line !== null ? '(' . basename($file) . '-' . $line . ')' : ''), $error_type); + else + trigger_error($error_message . ($line !== null ? '(' . basename($file) . '-' . $line . ')' : '')); +} + +// Escape the LIKE wildcards so that they match the character and not the wildcard. +// The optional second parameter turns human readable wildcards into SQL wildcards. +function smf_db_escape_wildcard_string($string, $translate_human_wildcards=false) +{ + $replacements = array( + '%' => '\%', + '_' => '\_', + '\\' => '\\\\', + ); + + if ($translate_human_wildcards) + $replacements += array( + '*' => '%', + ); + + return strtr($string, $replacements); +} +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Db-postgresql.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Db-postgresql.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,757 @@ + 'smf_db_query', + 'db_quote' => 'smf_db_quote', + 'db_insert' => 'smf_db_insert', + 'db_insert_id' => 'smf_db_insert_id', + 'db_fetch_assoc' => 'smf_db_fetch_assoc', + 'db_fetch_row' => 'smf_db_fetch_row', + 'db_free_result' => 'pg_free_result', + 'db_num_rows' => 'pg_num_rows', + 'db_data_seek' => 'smf_db_data_seek', + 'db_num_fields' => 'pg_num_fields', + 'db_escape_string' => 'pg_escape_string', + 'db_unescape_string' => 'smf_db_unescape_string', + 'db_server_info' => 'smf_db_version', + 'db_affected_rows' => 'smf_db_affected_rows', + 'db_transaction' => 'smf_db_transaction', + 'db_error' => 'pg_last_error', + 'db_select_db' => 'smf_db_select_db', + 'db_title' => 'PostgreSQL', + 'db_sybase' => true, + 'db_case_sensitive' => true, + 'db_escape_wildcard_string' => 'smf_db_escape_wildcard_string', + ); + + if (!empty($db_options['persist'])) + $connection = @pg_pconnect('host=' . $db_server . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\''); + else + $connection = @pg_connect( 'host=' . $db_server . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\''); + + // Something's wrong, show an error if its fatal (which we assume it is) + if (!$connection) + { + if (!empty($db_options['non_fatal'])) + { + return null; + } + else + { + db_fatal_error(); + } + } + + return $connection; +} + +// Extend the database functionality. +function db_extend ($type = 'extra') +{ + global $sourcedir, $db_type; + + require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-' . $db_type . '.php'); + $initFunc = 'db_' . $type . '_init'; + $initFunc(); +} + +// Do nothing on postgreSQL +function db_fix_prefix (&$db_prefix, $db_name) +{ + return; +} + +function smf_db_replacement__callback($matches) +{ + global $db_callback, $user_info, $db_prefix; + + list ($values, $connection) = $db_callback; + + if (!is_resource($connection)) + db_fatal_error(); + + if ($matches[1] === 'db_prefix') + return $db_prefix; + + if ($matches[1] === 'query_see_board') + return $user_info['query_see_board']; + + if ($matches[1] === 'query_wanna_see_board') + return $user_info['query_wanna_see_board']; + + if (!isset($matches[2])) + smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__); + + if (!isset($values[$matches[2]])) + smf_db_error_backtrace('The database value you\'re trying to insert does not exist: ' . htmlspecialchars($matches[2]), '', E_USER_ERROR, __FILE__, __LINE__); + + $replacement = $values[$matches[2]]; + + switch ($matches[1]) + { + case 'int': + if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement) + smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + return (string) (int) $replacement; + break; + + case 'string': + case 'text': + return sprintf('\'%1$s\'', pg_escape_string($replacement)); + break; + + case 'array_int': + if (is_array($replacement)) + { + if (empty($replacement)) + smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + foreach ($replacement as $key => $value) + { + if (!is_numeric($value) || (string) $value !== (string) (int) $value) + smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + $replacement[$key] = (string) (int) $value; + } + + return implode(', ', $replacement); + } + else + smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + break; + + case 'array_string': + if (is_array($replacement)) + { + if (empty($replacement)) + smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + foreach ($replacement as $key => $value) + $replacement[$key] = sprintf('\'%1$s\'', pg_escape_string($value)); + + return implode(', ', $replacement); + } + else + smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + break; + + case 'date': + if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1) + return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]); + else + smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + break; + + case 'float': + if (!is_numeric($replacement)) + smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + return (string) (float) $replacement; + break; + + case 'identifier': + return '`' . strtr($replacement, array('`' => '', '.' => '')) . '`'; + break; + + case 'raw': + return $replacement; + break; + + default: + smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__); + break; + } +} + +// Just like the db_query, escape and quote a string, but not executing the query. +function smf_db_quote($db_string, $db_values, $connection = null) +{ + global $db_callback, $db_connection; + + // Only bother if there's something to replace. + if (strpos($db_string, '{') !== false) + { + // This is needed by the callback function. + $db_callback = array($db_values, $connection == null ? $db_connection : $connection); + + // Do the quoting and escaping + $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); + + // Clear this global variable. + $db_callback = array(); + } + + return $db_string; +} + +// Do a query. Takes care of errors too. +function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null) +{ + global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start; + global $db_unbuffered, $db_callback, $db_last_result, $db_replace_result, $modSettings; + + // Decide which connection to use. + $connection = $connection == null ? $db_connection : $connection; + + // Special queries that need processing. + $replacements = array( + 'alter_table_boards' => array( + '~(.+)~' => '', + ), + 'alter_table_icons' => array( + '~(.+)~' => '', + ), + 'alter_table_smileys' => array( + '~(.+)~' => '', + ), + 'alter_table_spiders' => array( + '~(.+)~' => '', + ), + 'ban_suggest_error_ips' => array( + '~RLIKE~' => '~', + '~\\.~' => '\.', + ), + 'ban_suggest_message_ips' => array( + '~RLIKE~' => '~', + '~\\.~' => '\.', + ), + 'consolidate_spider_stats' => array( + '~MONTH\(log_time\), DAYOFMONTH\(log_time\)~' => 'MONTH(CAST(CAST(log_time AS abstime) AS timestamp)), DAYOFMONTH(CAST(CAST(log_time AS abstime) AS timestamp))', + ), + 'delete_subscription' => array( + '~LIMIT 1~' => '', + ), + 'display_get_post_poster' => array( + '~GROUP BY id_msg\s+HAVING~' => 'AND', + ), + 'attach_download_increase' => array( + '~LOW_PRIORITY~' => '', + ), + 'boardindex_fetch_boards' => array( + '~IFNULL\(lb.id_msg, 0\) >= b.id_msg_updated~' => 'CASE WHEN IFNULL(lb.id_msg, 0) >= b.id_msg_updated THEN 1 ELSE 0 END', + '~(.)$~' => '$1 ORDER BY b.board_order', + ), + 'get_random_number' => array( + '~RAND~' => 'RANDOM', + ), + 'insert_log_search_topics' => array( + '~NOT RLIKE~' => '!~', + ), + 'insert_log_search_results_no_index' => array( + '~NOT RLIKE~' => '!~', + ), + 'insert_log_search_results_subject' => array( + '~NOT RLIKE~' => '!~', + ), + 'messageindex_fetch_boards' => array( + '~(.)$~' => '$1 ORDER BY b.board_order', + ), + 'select_message_icons' => array( + '~(.)$~' => '$1 ORDER BY icon_order', + ), + 'set_character_set' => array( + '~SET\\s+NAMES\\s([a-zA-Z0-9\\-_]+)~' => 'SET NAMES \'$1\'', + ), + 'pm_conversation_list' => array( + '~ORDER\\s+BY\\s+\\{raw:sort\\}~' => 'ORDER BY ' . (isset($db_values['sort']) ? ($db_values['sort'] === 'pm.id_pm' ? 'MAX(pm.id_pm)' : $db_values['sort']) : ''), + ), + 'top_topic_starters' => array( + '~ORDER BY FIND_IN_SET\(id_member,(.+?)\)~' => 'ORDER BY STRPOS(\',\' || $1 || \',\', \',\' || id_member|| \',\')', + ), + 'order_by_board_order' => array( + '~(.)$~' => '$1 ORDER BY b.board_order', + ), + 'spider_check' => array( + '~(.)$~' => '$1 ORDER BY LENGTH(user_agent) DESC', + ), + 'unread_replies' => array( + '~SELECT\\s+DISTINCT\\s+t.id_topic~' => 'SELECT t.id_topic, {raw:sort}', + ), + 'profile_board_stats' => array( + '~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)', + ), + 'set_smiley_order' => array( + '~(.+)~' => '', + ), + ); + + if (isset($replacements[$identifier])) + $db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string); + + // Limits need to be a little different. + $db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string); + + if (trim($db_string) == '') + return false; + + // Comments that are allowed in a query are preg_removed. + static $allowed_comments_from = array( + '~\s+~s', + '~/\*!40001 SQL_NO_CACHE \*/~', + '~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~', + '~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~', + ); + static $allowed_comments_to = array( + ' ', + '', + '', + '', + ); + + // One more query.... + $db_count = !isset($db_count) ? 1 : $db_count + 1; + $db_replace_result = 0; + + if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override'])) + smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__); + + if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false)) + { + // Pass some values to the global space for use in the callback function. + $db_callback = array($db_values, $connection); + + // Inject the values passed to this function. + $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); + + // This shouldn't be residing in global space any longer. + $db_callback = array(); + } + + // Debugging. + if (isset($db_show_debug) && $db_show_debug === true) + { + // Get the file and line number this function was called. + list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); + + // Initialize $db_cache if not already initialized. + if (!isset($db_cache)) + $db_cache = array(); + + if (!empty($_SESSION['debug_redirect'])) + { + $db_cache = array_merge($_SESSION['debug_redirect'], $db_cache); + $db_count = count($db_cache) + 1; + $_SESSION['debug_redirect'] = array(); + } + + $st = microtime(); + // Don't overload it. + $db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...'; + $db_cache[$db_count]['f'] = $file; + $db_cache[$db_count]['l'] = $line; + $db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start)); + } + + // First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over. + if (empty($modSettings['disableQueryCheck'])) + { + $clean = ''; + $old_pos = 0; + $pos = -1; + while (true) + { + $pos = strpos($db_string, '\'', $pos + 1); + if ($pos === false) + break; + $clean .= substr($db_string, $old_pos, $pos - $old_pos); + + while (true) + { + $pos1 = strpos($db_string, '\'', $pos + 1); + $pos2 = strpos($db_string, '\\', $pos + 1); + if ($pos1 === false) + break; + elseif ($pos2 == false || $pos2 > $pos1) + { + $pos = $pos1; + break; + } + + $pos = $pos2 + 1; + } + $clean .= ' %s '; + + $old_pos = $pos + 1; + } + $clean .= substr($db_string, $old_pos); + $clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean))); + + // We don't use UNION in SMF, at least so far. But it's useful for injections. + if (strpos($clean, 'union') !== false && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0) + $fail = true; + // Comments? We don't use comments in our queries, we leave 'em outside! + elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false) + $fail = true; + // Trying to change passwords, slow us down, or something? + elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0) + $fail = true; + elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0) + $fail = true; + // Sub selects? We don't use those either. + elseif (preg_match('~\([^)]*?select~s', $clean) != 0) + $fail = true; + + if (!empty($fail) && function_exists('log_error')) + smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__); + } + + $db_last_result = @pg_query($connection, $db_string); + + if ($db_last_result === false && empty($db_values['db_error_skip'])) + $db_last_result = smf_db_error($db_string, $connection); + + // Debugging. + if (isset($db_show_debug) && $db_show_debug === true) + $db_cache[$db_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st)); + + return $db_last_result; +} + +function smf_db_affected_rows($result = null) +{ + global $db_last_result, $db_replace_result; + + if ($db_replace_result) + return $db_replace_result; + elseif ($result == null && !$db_last_result) + return 0; + + return pg_affected_rows($result == null ? $db_last_result : $result); +} + +function smf_db_insert_id($table, $field = null, $connection = null) +{ + global $db_connection, $smcFunc, $db_prefix; + + $table = str_replace('{db_prefix}', $db_prefix, $table); + + if ($connection === false) + $connection = $db_connection; + + // Try get the last ID for the auto increment field. + $request = $smcFunc['db_query']('', 'SELECT CURRVAL(\'' . $table . '_seq\') AS insertID', + array( + ) + ); + if (!$request) + return false; + list ($lastID) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return $lastID; +} + +// Do a transaction. +function smf_db_transaction($type = 'commit', $connection = null) +{ + global $db_connection; + + // Decide which connection to use + $connection = $connection == null ? $db_connection : $connection; + + if ($type == 'begin') + return @pg_query($connection, 'BEGIN'); + elseif ($type == 'rollback') + return @pg_query($connection, 'ROLLBACK'); + elseif ($type == 'commit') + return @pg_query($connection, 'COMMIT'); + + return false; +} + +// Database error! +function smf_db_error($db_string, $connection = null) +{ + global $txt, $context, $sourcedir, $webmaster_email, $modSettings; + global $forum_version, $db_connection, $db_last_error, $db_persist; + global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd; + global $smcFunc; + + // We'll try recovering the file and line number the original db query was called from. + list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); + + // Decide which connection to use + $connection = $connection == null ? $db_connection : $connection; + + // This is the error message... + $query_error = @pg_last_error($connection); + + // Log the error. + if (function_exists('log_error')) + log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" .$db_string : ''), 'database', $file, $line); + + // Nothing's defined yet... just die with it. + if (empty($context) || empty($txt)) + die($query_error); + + // Show an error message, if possible. + $context['error_title'] = $txt['database_error']; + if (allowedTo('admin_forum')) + $context['error_message'] = nl2br($query_error) . '
' . $txt['file'] . ': ' . $file . '
' . $txt['line'] . ': ' . $line; + else + $context['error_message'] = $txt['try_again']; + + // A database error is often the sign of a database in need of updgrade. Check forum versions, and if not identical suggest an upgrade... (not for Demo/CVS versions!) + if (allowedTo('admin_forum') && !empty($forum_version) && $forum_version != 'SMF ' . @$modSettings['smfVersion'] && strpos($forum_version, 'Demo') === false && strpos($forum_version, 'CVS') === false) + $context['error_message'] .= '

' . sprintf($txt['database_error_versions'], $forum_version, $modSettings['smfVersion']); + + if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true) + { + $context['error_message'] .= '

' . nl2br($db_string); + } + + // It's already been logged... don't log it again. + fatal_error($context['error_message'], false); +} + +// A PostgreSQL specific function for tracking the current row... +function smf_db_fetch_row($request, $counter = false) +{ + global $db_row_count; + + if ($counter !== false) + return pg_fetch_row($request, $counter); + + // Reset the row counter... + if (!isset($db_row_count[(int) $request])) + $db_row_count[(int) $request] = 0; + + // Return the right row. + return @pg_fetch_row($request, $db_row_count[(int) $request]++); +} + +// Get an associative array +function smf_db_fetch_assoc($request, $counter = false) +{ + global $db_row_count; + + if ($counter !== false) + return pg_fetch_assoc($request, $counter); + + // Reset the row counter... + if (!isset($db_row_count[(int) $request])) + $db_row_count[(int) $request] = 0; + + // Return the right row. + return @pg_fetch_assoc($request, $db_row_count[(int) $request]++); +} + +// Reset the pointer... +function smf_db_data_seek($request, $counter) +{ + global $db_row_count; + + $db_row_count[(int) $request] = $counter; + + return true; +} + +// Unescape an escaped string! +function smf_db_unescape_string($string) +{ + return strtr($string, array('\'\'' => '\'')); +} + +// For inserting data in a special way... +function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null) +{ + global $db_replace_result, $db_in_transact, $smcFunc, $db_connection, $db_prefix; + + $connection = $connection === null ? $db_connection : $connection; + + if (empty($data)) + return; + + if (!is_array($data[array_rand($data)])) + $data = array($data); + + // Replace the prefix holder with the actual prefix. + $table = str_replace('{db_prefix}', $db_prefix, $table); + + $priv_trans = false; + if ((count($data) > 1 || $method == 'replace') && !$db_in_transact && !$disable_trans) + { + $smcFunc['db_transaction']('begin', $connection); + $priv_trans = true; + } + + // PostgreSQL doesn't support replace: we implement a MySQL-compatible behavior instead + if ($method == 'replace') + { + $count = 0; + $where = ''; + foreach ($columns as $columnName => $type) + { + // Are we restricting the length? + if (strpos($type, 'string-') !== false) + $actualType = sprintf($columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $count); + else + $actualType = sprintf($columnName . ' = {%1$s:%2$s}, ', $type, $count); + + // A key? That's what we were looking for. + if (in_array($columnName, $keys)) + $where .= (empty($where) ? '' : ' AND ') . substr($actualType, 0, -2); + $count++; + } + + // Make it so. + if (!empty($where) && !empty($data)) + { + foreach ($data as $k => $entry) + { + $smcFunc['db_query']('', ' + DELETE FROM ' . $table . + ' WHERE ' . $where, + $entry, $connection + ); + } + } + } + + if (!empty($data)) + { + // Create the mold for a single row insert. + $insertData = '('; + foreach ($columns as $columnName => $type) + { + // Are we restricting the length? + if (strpos($type, 'string-') !== false) + $insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName); + else + $insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName); + } + $insertData = substr($insertData, 0, -2) . ')'; + + // Create an array consisting of only the columns. + $indexed_columns = array_keys($columns); + + // Here's where the variables are injected to the query. + $insertRows = array(); + foreach ($data as $dataRow) + $insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection); + + foreach ($insertRows as $entry) + // Do the insert. + $smcFunc['db_query']('', ' + INSERT INTO ' . $table . '("' . implode('", "', $indexed_columns) . '") + VALUES + ' . $entry, + array( + 'security_override' => true, + 'db_error_skip' => $method == 'ignore' || $table === $db_prefix . 'log_errors', + ), + $connection + ); + } + + if ($priv_trans) + $smcFunc['db_transaction']('commit', $connection); +} + +// Dummy function really. +function smf_db_select_db($db_name, $db_connection) +{ + return true; +} + +// Get the current version. +function smf_db_version() +{ + $version = pg_version(); + + return $version['client']; +} + +// This function tries to work out additional error information from a back trace. +function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null) +{ + if (empty($log_message)) + $log_message = $error_message; + + if (function_exists('debug_backtrace')) + { + foreach (debug_backtrace() as $step) + { + // Found it? + if (strpos($step['function'], 'query') === false && !in_array(substr($step['function'], 0, 7), array('smf_db_', 'preg_re', 'db_erro', 'call_us')) && substr($step['function'], 0, 2) != '__') + { + $log_message .= '
Function: ' . $step['function']; + break; + } + + if (isset($step['line'])) + { + $file = $step['file']; + $line = $step['line']; + } + } + } + + // A special case - we want the file and line numbers for debugging. + if ($error_type == 'return') + return array($file, $line); + + // Is always a critical error. + if (function_exists('log_error')) + log_error($log_message, 'critical', $file, $line); + + if (function_exists('fatal_error')) + { + fatal_error($error_message, $error_type); + + // Cannot continue... + exit; + } + elseif ($error_type) + trigger_error($error_message . ($line !== null ? '(' . basename($file) . '-' . $line . ')' : ''), $error_type); + else + trigger_error($error_message . ($line !== null ? '(' . basename($file) . '-' . $line . ')' : '')); +} + +// Escape the LIKE wildcards so that they match the character and not the wildcard. +// The optional second parameter turns human readable wildcards into SQL wildcards. +function smf_db_escape_wildcard_string($string, $translate_human_wildcards=false) +{ + $replacements = array( + '%' => '\%', + '_' => '\_', + '\\' => '\\\\', + ); + + if ($translate_human_wildcards) + $replacements += array( + '*' => '%', + ); + + return strtr($string, $replacements); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Db-sqlite.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Db-sqlite.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,734 @@ + 'smf_db_query', + 'db_quote' => 'smf_db_quote', + 'db_fetch_assoc' => 'sqlite_fetch_array', + 'db_fetch_row' => 'smf_db_fetch_row', + 'db_free_result' => 'smf_db_free_result', + 'db_insert' => 'smf_db_insert', + 'db_insert_id' => 'smf_db_insert_id', + 'db_num_rows' => 'sqlite_num_rows', + 'db_data_seek' => 'sqlite_seek', + 'db_num_fields' => 'sqlite_num_fields', + 'db_escape_string' => 'sqlite_escape_string', + 'db_unescape_string' => 'smf_db_unescape_string', + 'db_server_info' => 'smf_db_libversion', + 'db_affected_rows' => 'smf_db_affected_rows', + 'db_transaction' => 'smf_db_transaction', + 'db_error' => 'smf_db_last_error', + 'db_select_db' => '', + 'db_title' => 'SQLite', + 'db_sybase' => true, + 'db_case_sensitive' => true, + 'db_escape_wildcard_string' => 'smf_db_escape_wildcard_string', + ); + + if (substr($db_name, -3) != '.db') + $db_name .= '.db'; + + if (!empty($db_options['persist'])) + $connection = @sqlite_popen($db_name, 0666, $sqlite_error); + else + $connection = @sqlite_open($db_name, 0666, $sqlite_error); + + // Something's wrong, show an error if its fatal (which we assume it is) + if (!$connection) + { + if (!empty($db_options['non_fatal'])) + return null; + else + db_fatal_error(); + } + $db_in_transact = false; + + // This is frankly stupid - stop SQLite returning alias names! + @sqlite_query('PRAGMA short_column_names = 1', $connection); + + // Make some user defined functions! + sqlite_create_function($connection, 'unix_timestamp', 'smf_udf_unix_timestamp', 0); + sqlite_create_function($connection, 'inet_aton', 'smf_udf_inet_aton', 1); + sqlite_create_function($connection, 'inet_ntoa', 'smf_udf_inet_ntoa', 1); + sqlite_create_function($connection, 'find_in_set', 'smf_udf_find_in_set', 2); + sqlite_create_function($connection, 'year', 'smf_udf_year', 1); + sqlite_create_function($connection, 'month', 'smf_udf_month', 1); + sqlite_create_function($connection, 'dayofmonth', 'smf_udf_dayofmonth', 1); + sqlite_create_function($connection, 'concat', 'smf_udf_concat'); + sqlite_create_function($connection, 'locate', 'smf_udf_locate', 2); + sqlite_create_function($connection, 'regexp', 'smf_udf_regexp', 2); + + return $connection; +} + +// Extend the database functionality. +function db_extend($type = 'extra') +{ + global $sourcedir, $db_type; + + require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-' . $db_type . '.php'); + $initFunc = 'db_' . $type . '_init'; + $initFunc(); +} + +// SQLite doesn't actually need this! +function db_fix_prefix(&$db_prefix, $db_name) +{ + return false; +} + +function smf_db_replacement__callback($matches) +{ + global $db_callback, $user_info, $db_prefix; + + list ($values, $connection) = $db_callback; + + if ($matches[1] === 'db_prefix') + return $db_prefix; + + if ($matches[1] === 'query_see_board') + return $user_info['query_see_board']; + + if ($matches[1] === 'query_wanna_see_board') + return $user_info['query_wanna_see_board']; + + if (!isset($matches[2])) + smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__); + + if (!isset($values[$matches[2]])) + smf_db_error_backtrace('The database value you\'re trying to insert does not exist: ' . htmlspecialchars($matches[2]), '', E_USER_ERROR, __FILE__, __LINE__); + + $replacement = $values[$matches[2]]; + + switch ($matches[1]) + { + case 'int': + if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement) + smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + return (string) (int) $replacement; + break; + + case 'string': + case 'text': + return sprintf('\'%1$s\'', sqlite_escape_string($replacement)); + break; + + case 'array_int': + if (is_array($replacement)) + { + if (empty($replacement)) + smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + foreach ($replacement as $key => $value) + { + if (!is_numeric($value) || (string) $value !== (string) (int) $value) + smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + $replacement[$key] = (string) (int) $value; + } + + return implode(', ', $replacement); + } + else + smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + break; + + case 'array_string': + if (is_array($replacement)) + { + if (empty($replacement)) + smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + + foreach ($replacement as $key => $value) + $replacement[$key] = sprintf('\'%1$s\'', sqlite_escape_string($value)); + + return implode(', ', $replacement); + } + else + smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + break; + + case 'date': + if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1) + return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]); + else + smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + break; + + case 'float': + if (!is_numeric($replacement)) + smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); + return (string) (float) $replacement; + break; + + case 'identifier': + return '`' . strtr($replacement, array('`' => '', '.' => '')) . '`'; + break; + + case 'raw': + return $replacement; + break; + + default: + smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__); + break; + } +} + +// Just like the db_query, escape and quote a string, but not executing the query. +function smf_db_quote($db_string, $db_values, $connection = null) +{ + global $db_callback, $db_connection; + + // Only bother if there's something to replace. + if (strpos($db_string, '{') !== false) + { + // This is needed by the callback function. + $db_callback = array($db_values, $connection == null ? $db_connection : $connection); + + // Do the quoting and escaping + $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); + + // Clear this global variable. + $db_callback = array(); + } + + return $db_string; +} + +// Do a query. Takes care of errors too. +function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null) +{ + global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start; + global $db_unbuffered, $db_callback, $modSettings; + + // Decide which connection to use. + $connection = $connection == null ? $db_connection : $connection; + + // Special queries that need processing. + $replacements = array( + 'birthday_array' => array( + '~DATE_FORMAT\(([^,]+),\s*([^\)]+)\s*\)~' => 'strftime($2, $1)' + ), + 'substring' => array( + '~SUBSTRING~' => 'SUBSTR', + ), + 'truncate_table' => array( + '~TRUNCATE~i' => 'DELETE FROM', + ), + 'user_activity_by_time' => array( + '~HOUR\(FROM_UNIXTIME\((poster_time\s+\+\s+\{int:.+\})\)\)~' => 'strftime(\'%H\', datetime($1, \'unixepoch\'))', + ), + 'unread_fetch_topic_count' => array( + '~\s*SELECT\sCOUNT\(DISTINCT\st\.id_topic\),\sMIN\(t\.id_last_msg\)(.+)$~is' => 'SELECT COUNT(id_topic), MIN(id_last_msg) FROM (SELECT DISTINCT t.id_topic, t.id_last_msg $1)', + ), + 'alter_table_boards' => array( + '~(.+)~' => '', + ), + 'get_random_number' => array( + '~RAND~' => 'RANDOM', + ), + 'set_character_set' => array( + '~(.+)~' => '', + ), + 'themes_count' => array( + '~\s*SELECT\sCOUNT\(DISTINCT\sid_member\)\sAS\svalue,\sid_theme.+FROM\s(.+themes)(.+)~is' => 'SELECT COUNT(id_member) AS value, id_theme FROM (SELECT DISTINCT id_member, id_theme, variable FROM $1) $2', + ), + 'attach_download_increase' => array( + '~LOW_PRIORITY~' => '', + ), + 'pm_conversation_list' => array( + '~ORDER BY id_pm~' => 'ORDER BY MAX(pm.id_pm)', + ), + 'boardindex_fetch_boards' => array( + '~(.)$~' => '$1 ORDER BY b.board_order', + ), + 'messageindex_fetch_boards' => array( + '~(.)$~' => '$1 ORDER BY b.board_order', + ), + 'order_by_board_order' => array( + '~(.)$~' => '$1 ORDER BY b.board_order', + ), + 'spider_check' => array( + '~(.)$~' => '$1 ORDER BY LENGTH(user_agent) DESC', + ), + ); + + if (isset($replacements[$identifier])) + $db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string); + + // SQLite doesn't support count(distinct). + $db_string = trim($db_string); + $db_string = preg_replace('~^\s*SELECT\s+?COUNT\(DISTINCT\s+?(.+?)\)(\s*AS\s*(.+?))*\s*(FROM.+)~is', 'SELECT COUNT(*) $2 FROM (SELECT DISTINCT $1 $4)', $db_string); + + // Or RLIKE. + $db_string = preg_replace('~AND\s*(.+?)\s*RLIKE\s*(\{string:.+?\})~', 'AND REGEXP(\1, \2)', $db_string); + + // INSTR? No support for that buddy :( + if (preg_match('~INSTR\((.+?),\s(.+?)\)~', $db_string, $matches) === 1) + { + $db_string = preg_replace('~INSTR\((.+?),\s(.+?)\)~', '$1 LIKE $2', $db_string); + list(, $search) = explode(':', substr($matches[2], 1, -1)); + $db_values[$search] = '%' . $db_values[$search] . '%'; + } + + // Lets remove ASC and DESC from GROUP BY clause. + if (preg_match('~GROUP BY .*? (?:ASC|DESC)~is', $db_string, $matches)) + { + $replace = str_replace(array('ASC', 'DESC'), '', $matches[0]); + $db_string = str_replace($matches[0], $replace, $db_string); + } + + // We need to replace the SUBSTRING in the sort identifier. + if ($identifier == 'substring_membergroups' && isset($db_values['sort'])) + $db_values['sort'] = preg_replace('~SUBSTRING~', 'SUBSTR', $db_values['sort']); + + // SQLite doesn't support TO_DAYS but has the julianday function which can be used in the same manner. But make sure it is being used to calculate a span. + $db_string = preg_replace('~\(TO_DAYS\(([^)]+)\) - TO_DAYS\(([^)]+)\)\) AS span~', '(julianday($1) - julianday($2)) AS span', $db_string); + + // One more query.... + $db_count = !isset($db_count) ? 1 : $db_count + 1; + + if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override'])) + smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__); + + if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false)) + { + // Pass some values to the global space for use in the callback function. + $db_callback = array($db_values, $connection); + + // Inject the values passed to this function. + $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); + + // This shouldn't be residing in global space any longer. + $db_callback = array(); + } + + // Debugging. + if (isset($db_show_debug) && $db_show_debug === true) + { + // Get the file and line number this function was called. + list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); + + // Initialize $db_cache if not already initialized. + if (!isset($db_cache)) + $db_cache = array(); + + if (!empty($_SESSION['debug_redirect'])) + { + $db_cache = array_merge($_SESSION['debug_redirect'], $db_cache); + $db_count = count($db_cache) + 1; + $_SESSION['debug_redirect'] = array(); + } + + $st = microtime(); + // Don't overload it. + $db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...'; + $db_cache[$db_count]['f'] = $file; + $db_cache[$db_count]['l'] = $line; + $db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start)); + } + + $ret = @sqlite_query($db_string, $connection, SQLITE_BOTH, $err_msg); + if ($ret === false && empty($db_values['db_error_skip'])) + $ret = smf_db_error($db_string . '#!#' . $err_msg, $connection); + + // Debugging. + if (isset($db_show_debug) && $db_show_debug === true) + $db_cache[$db_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st)); + + return $ret; +} + +function smf_db_affected_rows($connection = null) +{ + global $db_connection; + + return sqlite_changes($connection == null ? $db_connection : $connection); +} + +function smf_db_insert_id($table, $field = null, $connection = null) +{ + global $db_connection, $db_prefix; + + $table = str_replace('{db_prefix}', $db_prefix, $table); + + // SQLite doesn't need the table or field information. + return sqlite_last_insert_rowid($connection == null ? $db_connection : $connection); +} + +// Keeps the connection handle. +function smf_db_last_error() +{ + global $db_connection, $sqlite_error; + + $query_errno = sqlite_last_error($db_connection); + return $query_errno || empty($sqlite_error) ? sqlite_error_string($query_errno) : $sqlite_error; +} + +// Do a transaction. +function smf_db_transaction($type = 'commit', $connection = null) +{ + global $db_connection, $db_in_transact; + + // Decide which connection to use + $connection = $connection == null ? $db_connection : $connection; + + if ($type == 'begin') + { + $db_in_transact = true; + return @sqlite_query('BEGIN', $connection); + } + elseif ($type == 'rollback') + { + $db_in_transact = false; + return @sqlite_query('ROLLBACK', $connection); + } + elseif ($type == 'commit') + { + $db_in_transact = false; + return @sqlite_query('COMMIT', $connection); + } + + return false; +} + +// Database error! +function smf_db_error($db_string, $connection = null) +{ + global $txt, $context, $sourcedir, $webmaster_email, $modSettings; + global $forum_version, $db_connection, $db_last_error, $db_persist; + global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd; + global $smcFunc; + + // We'll try recovering the file and line number the original db query was called from. + list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); + + // Decide which connection to use + $connection = $connection == null ? $db_connection : $connection; + + // This is the error message... + $query_errno = sqlite_last_error($connection); + $query_error = sqlite_error_string($query_errno); + + // Get the extra error message. + $errStart = strrpos($db_string, '#!#'); + $query_error .= '
' . substr($db_string, $errStart + 3); + $db_string = substr($db_string, 0, $errStart); + + // Log the error. + if (function_exists('log_error')) + log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" .$db_string : ''), 'database', $file, $line); + + // Sqlite optimizing - the actual error message isn't helpful or user friendly. + if (strpos($query_error, 'no_access') !== false || strpos($query_error, 'database schema has changed') !== false) + { + if (!empty($context) && !empty($txt) && !empty($txt['error_sqlite_optimizing'])) + fatal_error($txt['error_sqlite_optimizing'], false); + else + { + // Don't cache this page! + header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Cache-Control: no-cache'); + + // Send the right error codes. + header('HTTP/1.1 503 Service Temporarily Unavailable'); + header('Status: 503 Service Temporarily Unavailable'); + header('Retry-After: 3600'); + + die('Sqlite is optimizing the database, the forum can not be accessed until it has finished. Please try refreshing this page momentarily.'); + } + } + + // Nothing's defined yet... just die with it. + if (empty($context) || empty($txt)) + die($query_error); + + // Show an error message, if possible. + $context['error_title'] = $txt['database_error']; + if (allowedTo('admin_forum')) + $context['error_message'] = nl2br($query_error) . '
' . $txt['file'] . ': ' . $file . '
' . $txt['line'] . ': ' . $line; + else + $context['error_message'] = $txt['try_again']; + + // A database error is often the sign of a database in need of updgrade. Check forum versions, and if not identical suggest an upgrade... (not for Demo/CVS versions!) + if (allowedTo('admin_forum') && !empty($forum_version) && $forum_version != 'SMF ' . @$modSettings['smfVersion'] && strpos($forum_version, 'Demo') === false && strpos($forum_version, 'CVS') === false) + $context['error_message'] .= '

' . sprintf($txt['database_error_versions'], $forum_version, $modSettings['smfVersion']); + + if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true) + { + $context['error_message'] .= '

' . nl2br($db_string); + } + + // It's already been logged... don't log it again. + fatal_error($context['error_message'], false); +} + +// Insert some data... +function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null) +{ + global $db_in_transact, $db_connection, $smcFunc, $db_prefix; + + $connection = $connection === null ? $db_connection : $connection; + + if (empty($data)) + return; + + if (!is_array($data[array_rand($data)])) + $data = array($data); + + // Replace the prefix holder with the actual prefix. + $table = str_replace('{db_prefix}', $db_prefix, $table); + + $priv_trans = false; + if (count($data) > 1 && !$db_in_transact && !$disable_trans) + { + $smcFunc['db_transaction']('begin', $connection); + $priv_trans = true; + } + + if (!empty($data)) + { + // Create the mold for a single row insert. + $insertData = '('; + foreach ($columns as $columnName => $type) + { + // Are we restricting the length? + if (strpos($type, 'string-') !== false) + $insertData .= sprintf('SUBSTR({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName); + else + $insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName); + } + $insertData = substr($insertData, 0, -2) . ')'; + + // Create an array consisting of only the columns. + $indexed_columns = array_keys($columns); + + // Here's where the variables are injected to the query. + $insertRows = array(); + foreach ($data as $dataRow) + $insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection); + + foreach ($insertRows as $entry) + // Do the insert. + $smcFunc['db_query']('', + (($method === 'replace') ? 'REPLACE' : (' INSERT' . ($method === 'ignore' ? ' OR IGNORE' : ''))) . ' INTO ' . $table . '(' . implode(', ', $indexed_columns) . ') + VALUES + ' . $entry, + array( + 'security_override' => true, + 'db_error_skip' => $table === $db_prefix . 'log_errors', + ), + $connection + ); + } + + if ($priv_trans) + $smcFunc['db_transaction']('commit', $connection); +} + +// Doesn't do anything on sqlite! +function smf_db_free_result($handle = false) +{ + return true; +} + +// Make sure we return no string indexes! +function smf_db_fetch_row($handle) +{ + return sqlite_fetch_array($handle, SQLITE_NUM); +} + +// Unescape an escaped string! +function smf_db_unescape_string($string) +{ + return strtr($string, array('\'\'' => '\'')); +} + +// This function tries to work out additional error information from a back trace. +function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null) +{ + if (empty($log_message)) + $log_message = $error_message; + + if (function_exists('debug_backtrace')) + { + foreach (debug_backtrace() as $step) + { + // Found it? + if (strpos($step['function'], 'query') === false && !in_array(substr($step['function'], 0, 7), array('smf_db_', 'preg_re', 'db_erro', 'call_us')) && substr($step['function'], 0, 2) != '__') + { + $log_message .= '
Function: ' . $step['function']; + break; + } + + if (isset($step['line'])) + { + $file = $step['file']; + $line = $step['line']; + } + } + } + + // A special case - we want the file and line numbers for debugging. + if ($error_type == 'return') + return array($file, $line); + + // Is always a critical error. + if (function_exists('log_error')) + log_error($log_message, 'critical', $file, $line); + + if (function_exists('fatal_error')) + { + fatal_error($error_message, $error_type); + + // Cannot continue... + exit; + } + elseif ($error_type) + trigger_error($error_message . ($line !== null ? '(' . basename($file) . '-' . $line . ')' : ''), $error_type); + else + trigger_error($error_message . ($line !== null ? '(' . basename($file) . '-' . $line . ')' : '')); +} + +// Emulate UNIX_TIMESTAMP. +function smf_udf_unix_timestamp() +{ + return strftime('%s', 'now'); +} + +// Emulate INET_ATON. +function smf_udf_inet_aton($ip) +{ + $chunks = explode('.', $ip); + return @$chunks[0] * pow(256, 3) + @$chunks[1] * pow(256, 2) + @$chunks[2] * 256 + @$chunks[3]; +} + +// Emulate INET_NTOA. +function smf_udf_inet_ntoa($n) +{ + $t = array(0, 0, 0, 0); + $msk = 16777216.0; + $n += 0.0; + if ($n < 1) + return '0.0.0.0'; + + for ($i = 0; $i < 4; $i++) + { + $k = (int) ($n / $msk); + $n -= $msk * $k; + $t[$i] = $k; + $msk /= 256.0; + }; + + $a = join('.', $t); + return $a; +} + +// Emulate FIND_IN_SET. +function smf_udf_find_in_set($find, $groups) +{ + foreach (explode(',', $groups) as $key => $group) + { + if ($group == $find) + return $key + 1; + } + + return 0; +} + +// Emulate YEAR. +function smf_udf_year($date) +{ + return substr($date, 0, 4); +} + +// Emulate MONTH. +function smf_udf_month($date) +{ + return substr($date, 5, 2); +} + +// Emulate DAYOFMONTH. +function smf_udf_dayofmonth($date) +{ + return substr($date, 8, 2); +} + +// We need this since sqlite_libversion() doesn't take any parameters. +function smf_db_libversion($void = null) +{ + return sqlite_libversion(); +} + +// This function uses variable argument lists so that it can handle more then two parameters. +// Emulates the CONCAT function. +function smf_udf_concat() +{ + // Since we didn't specify any arguments we must get them from PHP. + $args = func_get_args(); + + // It really doesn't matter if there were 0 to 100 arguments, just slap them all together. + return implode('', $args); +} + +// We need to use PHP to locate the position in the string. +function smf_udf_locate($find, $string) +{ + return strpos($string, $find); +} + +// This is used to replace RLIKE. +function smf_udf_regexp($exp, $search) +{ + if (preg_match($exp, $match)) + return 1; + return 0; +} + +// Escape the LIKE wildcards so that they match the character and not the wildcard. +// The optional second parameter turns human readable wildcards into SQL wildcards. +function smf_db_escape_wildcard_string($string, $translate_human_wildcards=false) +{ + $replacements = array( + '%' => '\%', + '\\' => '\\\\', + ); + + if ($translate_human_wildcards) + $replacements += array( + '*' => '%', + ); + + return strtr($string, $replacements); +} +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Editor.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Editor.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2172 @@ + ';', '#smlt#' => '<', '#smgt#' => '>', '#smamp#' => '&')); + $context['message'] = bbc_to_html($_REQUEST['message']); + } + else + { + $_REQUEST['message'] = un_htmlspecialchars($_REQUEST['message']); + $_REQUEST['message'] = strtr($_REQUEST['message'], array('#smcol#' => ';', '#smlt#' => '<', '#smgt#' => '>', '#smamp#' => '&')); + + $context['message'] = html_to_bbc($_REQUEST['message']); + } + + $context['message'] = $smcFunc['htmlspecialchars']($context['message']); +} + +// Convert only the BBC that can be edited in HTML mode for the editor. +function bbc_to_html($text) +{ + global $modSettings, $smcFunc; + + // Turn line breaks back into br's. + $text = strtr($text, array("\r" => '', "\n" => '
')); + + // Prevent conversion of all bbcode inside these bbcodes. + // !!! Tie in with bbc permissions ? + foreach (array('code', 'php', 'nobbc') as $code) + { + if (strpos($text, '['. $code) !== false) + { + $parts = preg_split('~(\[/' . $code . '\]|\[' . $code . '(?:=[^\]]+)?\])~i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); + + // Only mess with stuff inside tags. + for ($i = 0, $n = count($parts); $i < $n; $i++) + { + // Value of 2 means we're inside the tag. + if ($i % 4 == 2) + $parts[$i] = strtr($parts[$i], array('[' => '[', ']' => ']', "'" => "'")); + } + // Put our humpty dumpty message back together again. + $text = implode('', $parts); + } + } + + // What tags do we allow? + $allowed_tags = array('b', 'u', 'i', 's', 'hr', 'list', 'li', 'font', 'size', 'color', 'img', 'left', 'center', 'right', 'url', 'email', 'ftp', 'sub', 'sup'); + + $text = parse_bbc($text, true, '', $allowed_tags); + + // Fix for having a line break then a thingy. + $text = strtr($text, array('
' '', "\r" => '')); + + // Note that IE doesn't understand spans really - make them something "legacy" + $working_html = array( + '~(.+?)~i' => '$1', + '~(.+?)~i' => '$1', + '~(.+?)~i' => '$2', + '~(.+?)~i' => '$2', + '~(.+?)~i' => '

$2

', + ); + $text = preg_replace(array_keys($working_html), array_values($working_html), $text); + + // Parse unique ID's and disable javascript into the smileys - using the double space. + $i = 1; + $text = preg_replace('~(?:\s| )?<(img\ssrc="' . preg_quote($modSettings['smileys_url'], '~') . '/[^<>]+?/([^<>]+?)"\s*)[^<>]*?class="smiley" />~e', '\'<\' . ' . 'stripslashes(\'$1\') . \'alt="" title="" onresizestart="return false;" id="smiley_\' . ' . "\$" . 'i++ . \'_$2" style="padding: 0 3px 0 3px;" />\'', $text); + + return $text; +} + +// The harder one - wysiwyg to BBC! +function html_to_bbc($text) +{ + global $modSettings, $smcFunc, $sourcedir, $scripturl, $context; + + // Replace newlines with spaces, as that's how browsers usually interpret them. + $text = preg_replace("~\s*[\r\n]+\s*~", ' ', $text); + + // Though some of us love paragraphs, the parser will do better with breaks. + $text = preg_replace('~

\s*?
\s*(?!<)~i', '


', $text); + + // Safari/webkit wraps lines in Wysiwyg in
's. + if ($context['browser']['is_webkit']) + $text = preg_replace(array('~]*?))?' . '>~i', '
'), array('
', ''), $text); + + // If there's a trailing break get rid of it - Firefox tends to add one. + $text = preg_replace('~$~i', '', $text); + + // Remove any formatting within code tags. + if (strpos($text, '[code') !== false) + { + $text = preg_replace('~~i', '#smf_br_spec_grudge_cool!#', $text); + $parts = preg_split('~(\[/code\]|\[code(?:=[^\]]+)?\])~i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); + + // Only mess with stuff outside [code] tags. + for ($i = 0, $n = count($parts); $i < $n; $i++) + { + // Value of 2 means we're inside the tag. + if ($i % 4 == 2) + $parts[$i] = strip_tags($parts[$i]); + } + + $text = strtr(implode('', $parts), array('#smf_br_spec_grudge_cool!#' => '
')); + } + + // Remove scripts, style and comment blocks. + $text = preg_replace('~]*[^/]?' . '>.*?~i', '', $text); + $text = preg_replace('~]*[^/]?' . '>.*?~i', '', $text); + $text = preg_replace('~\\<\\!--.*?-->~i', '', $text); + $text = preg_replace('~\\<\\!\\[CDATA\\[.*?\\]\\]\\>~i', '', $text); + + // Do the smileys ultra first! + preg_match_all('~]*?id="*smiley_\d+_([^<>]+?)[\s"/>]\s*[^<>]*?/*>(?:\s)?~i', $text, $matches); + if (!empty($matches[0])) + { + // Easy if it's not custom. + if (empty($modSettings['smiley_enable'])) + { + $smileysfrom = array('>:D', ':D', '::)', '>:(', ':)', ';)', ';D', ':(', ':o', '8)', ':P', '???', ':-[', ':-X', ':-*', ':\'(', ':-\\', '^-^', 'O0', 'C:-)', '0:)'); + $smileysto = array('evil.gif', 'cheesy.gif', 'rolleyes.gif', 'angry.gif', 'smiley.gif', 'wink.gif', 'grin.gif', 'sad.gif', 'shocked.gif', 'cool.gif', 'tongue.gif', 'huh.gif', 'embarrassed.gif', 'lipsrsealed.gif', 'kiss.gif', 'cry.gif', 'undecided.gif', 'azn.gif', 'afro.gif', 'police.gif', 'angel.gif'); + + foreach ($matches[1] as $k => $file) + { + $found = array_search($file, $smileysto); + // Note the weirdness here is to stop double spaces between smileys. + if ($found) + $matches[1][$k] = '-[]-smf_smily_start#|#' . htmlspecialchars($smileysfrom[$found]) . '-[]-smf_smily_end#|#'; + else + $matches[1][$k] = ''; + } + } + else + { + // Load all the smileys. + $names = array(); + foreach ($matches[1] as $file) + $names[] = $file; + $names = array_unique($names); + + if (!empty($names)) + { + $request = $smcFunc['db_query']('', ' + SELECT code, filename + FROM {db_prefix}smileys + WHERE filename IN ({array_string:smiley_filenames})', + array( + 'smiley_filenames' => $names, + ) + ); + $mappings = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $mappings[$row['filename']] = htmlspecialchars($row['code']); + $smcFunc['db_free_result']($request); + + foreach ($matches[1] as $k => $file) + if (isset($mappings[$file])) + $matches[1][$k] = '-[]-smf_smily_start#|#' . $mappings[$file] . '-[]-smf_smily_end#|#'; + } + } + + // Replace the tags! + $text = str_replace($matches[0], $matches[1], $text); + + // Now sort out spaces + $text = str_replace(array('-[]-smf_smily_end#|#-[]-smf_smily_start#|#', '-[]-smf_smily_end#|#', '-[]-smf_smily_start#|#'), ' ', $text); + } + + // Only try to buy more time if the client didn't quit. + if (connection_aborted() && $context['server']['is_apache']) + @apache_reset_timeout(); + + $parts = preg_split('~(<[A-Za-z]+\s*[^<>]*?style="?[^<>"]+"?[^<>]*?(?:/?)>|)~', $text, -1, PREG_SPLIT_DELIM_CAPTURE); + $replacement = ''; + $stack = array(); + + foreach ($parts as $part) + { + if (preg_match('~(<([A-Za-z]+)\s*[^<>]*?)style="?([^<>"]+)"?([^<>]*?(/?)>)~', $part, $matches) === 1) + { + // If it's being closed instantly, we can't deal with it...yet. + if ($matches[5] === '/') + continue; + else + { + // Get an array of styles that apply to this element. (The strtr is there to combat HTML generated by Word.) + $styles = explode(';', strtr($matches[3], array('"' => ''))); + $curElement = $matches[2]; + $precedingStyle = $matches[1]; + $afterStyle = $matches[4]; + $curCloseTags = ''; + $extra_attr = ''; + + foreach ($styles as $type_value_pair) + { + // Remove spaces and convert uppercase letters. + $clean_type_value_pair = strtolower(strtr(trim($type_value_pair), '=', ':')); + + // Something like 'font-weight: bold' is expected here. + if (strpos($clean_type_value_pair, ':') === false) + continue; + + // Capture the elements of a single style item (e.g. 'font-weight' and 'bold'). + list ($style_type, $style_value) = explode(':', $type_value_pair); + + $style_value = trim($style_value); + + switch (trim($style_type)) + { + case 'font-weight': + if ($style_value === 'bold') + { + $curCloseTags .= '[/b]'; + $replacement .= '[b]'; + } + break; + + case 'text-decoration': + if ($style_value == 'underline') + { + $curCloseTags .= '[/u]'; + $replacement .= '[u]'; + } + elseif ($style_value == 'line-through') + { + $curCloseTags .= '[/s]'; + $replacement .= '[s]'; + } + break; + + case 'text-align': + if ($style_value == 'left') + { + $curCloseTags .= '[/left]'; + $replacement .= '[left]'; + } + elseif ($style_value == 'center') + { + $curCloseTags .= '[/center]'; + $replacement .= '[center]'; + } + elseif ($style_value == 'right') + { + $curCloseTags .= '[/right]'; + $replacement .= '[right]'; + } + break; + + case 'font-style': + if ($style_value == 'italic') + { + $curCloseTags .= '[/i]'; + $replacement .= '[i]'; + } + break; + + case 'color': + $curCloseTags .= '[/color]'; + $replacement .= '[color=' . $style_value . ']'; + break; + + case 'font-size': + // Sometimes people put decimals where decimals should not be. + if (preg_match('~(\d)+\.\d+(p[xt])~i', $style_value, $dec_matches) === 1) + $style_value = $dec_matches[1] . $dec_matches[2]; + + $curCloseTags .= '[/size]'; + $replacement .= '[size=' . $style_value . ']'; + break; + + case 'font-family': + // Only get the first freaking font if there's a list! + if (strpos($style_value, ',') !== false) + $style_value = substr($style_value, 0, strpos($style_value, ',')); + + $curCloseTags .= '[/font]'; + $replacement .= '[font=' . strtr($style_value, array("'" => '')) . ']'; + break; + + // This is a hack for images with dimensions embedded. + case 'width': + case 'height': + if (preg_match('~[1-9]\d*~i', $style_value, $dimension) === 1) + $extra_attr .= ' ' . $style_type . '="' . $dimension[0] . '"'; + break; + + case 'list-style-type': + if (preg_match('~none|disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-alpha|upper-alpha|lower-greek|lower-latin|upper-latin|hebrew|armenian|georgian|cjk-ideographic|hiragana|katakana|hiragana-iroha|katakana-iroha~i', $style_value, $listType) === 1) + $extra_attr .= ' listtype="' . $listType[0] . '"'; + break; + } + } + + // Preserve some tags stripping the styling. + if (in_array($matches[2], array('a', 'font'))) + { + $replacement .= $precedingStyle . $afterStyle; + $curCloseTags = '' . $curCloseTags; + } + + // If there's something that still needs closing, push it to the stack. + if (!empty($curCloseTags)) + array_push($stack, array( + 'element' => strtolower($curElement), + 'closeTags' => $curCloseTags + ) + ); + elseif (!empty($extra_attr)) + $replacement .= $precedingStyle . $extra_attr . $afterStyle; + } + } + + elseif (preg_match('~~', $part, $matches) === 1) + { + // Is this the element that we've been waiting for to be closed? + if (!empty($stack) && strtolower($matches[1]) === $stack[count($stack) - 1]['element']) + { + $byebyeTag = array_pop($stack); + $replacement .= $byebyeTag['closeTags']; + } + + // Must've been something else. + else + $replacement .= $part; + } + // In all other cases, just add the part to the replacement. + else + $replacement .= $part; + } + + // Now put back the replacement in the text. + $text = $replacement; + + // We are not finished yet, request more time. + if (connection_aborted() && $context['server']['is_apache']) + @apache_reset_timeout(); + + // Let's pull out any legacy alignments. + while (preg_match('~<([A-Za-z]+)\s+[^<>]*?(align="*(left|center|right)"*)[^<>]*?(/?)>~i', $text, $matches) === 1) + { + // Find the position in the text of this tag over again. + $start_pos = strpos($text, $matches[0]); + if ($start_pos === false) + break; + + // End tag? + if ($matches[4] != '/' && strpos($text, '', $start_pos) !== false) + { + $end_length = strlen(''); + $end_pos = strpos($text, '', $start_pos); + + // Remove the align from that tag so it's never checked again. + $tag = substr($text, $start_pos, strlen($matches[0])); + $content = substr($text, $start_pos + strlen($matches[0]), $end_pos - $start_pos - strlen($matches[0])); + $tag = str_replace($matches[2], '', $tag); + + // Put the tags back into the body. + $text = substr($text, 0, $start_pos) . $tag . '[' . $matches[3] . ']' . $content . '[/' . $matches[3] . ']' . substr($text, $end_pos); + } + else + { + // Just get rid of this evil tag. + $text = substr($text, 0, $start_pos) . substr($text, $start_pos + strlen($matches[0])); + } + } + + // Let's do some special stuff for fonts - cause we all love fonts. + while (preg_match('~]*)>~i', $text, $matches) === 1) + { + // Find the position of this again. + $start_pos = strpos($text, $matches[0]); + $end_pos = false; + if ($start_pos === false) + break; + + // This must have an end tag - and we must find the right one. + $lower_text = strtolower($text); + + $start_pos_test = $start_pos + 4; + // How many starting tags must we find closing ones for first? + $start_font_tag_stack = 0; + while ($start_pos_test < strlen($text)) + { + // Where is the next starting font? + $next_start_pos = strpos($lower_text, '', $start_pos_test); + + // Did we past another starting tag before an end one? + if ($next_start_pos !== false && $next_start_pos < $next_end_pos) + { + $start_font_tag_stack++; + $start_pos_test = $next_start_pos + 4; + } + // Otherwise we have an end tag but not the right one? + elseif ($start_font_tag_stack) + { + $start_font_tag_stack--; + $start_pos_test = $next_end_pos + 4; + } + // Otherwise we're there! + else + { + $end_pos = $next_end_pos; + break; + } + } + if ($end_pos === false) + break; + + // Now work out what the attributes are. + $attribs = fetchTagAttributes($matches[1]); + $tags = array(); + foreach ($attribs as $s => $v) + { + if ($s == 'size') + $tags[] = array('[size=' . (int) trim($v) . ']', '[/size]'); + elseif ($s == 'face') + $tags[] = array('[font=' . trim(strtolower($v)) . ']', '[/font]'); + elseif ($s == 'color') + $tags[] = array('[color=' . trim(strtolower($v)) . ']', '[/color]'); + } + + // As before add in our tags. + $before = $after = ''; + foreach ($tags as $tag) + { + $before .= $tag[0]; + if (isset($tag[1])) + $after = $tag[1] . $after; + } + + // Remove the tag so it's never checked again. + $content = substr($text, $start_pos + strlen($matches[0]), $end_pos - $start_pos - strlen($matches[0])); + + // Put the tags back into the body. + $text = substr($text, 0, $start_pos) . $before . $content . $after . substr($text, $end_pos + 7); + } + + // Almost there, just a little more time. + if (connection_aborted() && $context['server']['is_apache']) + @apache_reset_timeout(); + + if (count($parts = preg_split('~<(/?)(li|ol|ul)([^>]*)>~i', $text, null, PREG_SPLIT_DELIM_CAPTURE)) > 1) + { + // A toggle that dermines whether we're directly under a
    or
      . + $inList = false; + + // Keep track of the number of nested list levels. + $listDepth = 0; + + // Map what we can expect from the HTML to what is supported by SMF. + $listTypeMapping = array( + '1' => 'decimal', + 'A' => 'upper-alpha', + 'a' => 'lower-alpha', + 'I' => 'upper-roman', + 'i' => 'lower-roman', + 'disc' => 'disc', + 'square' => 'square', + 'circle' => 'circle', + ); + + // $i: text, $i + 1: '/', $i + 2: tag, $i + 3: tail. + for ($i = 0, $numParts = count($parts) - 1; $i < $numParts; $i += 4) + { + $tag = strtolower($parts[$i + 2]); + $isOpeningTag = $parts[$i + 1] === ''; + + if ($isOpeningTag) + { + switch ($tag) + { + case 'ol': + case 'ul': + + // We have a problem, we're already in a list. + if ($inList) + { + // Inject a list opener, we'll deal with the ol/ul next loop. + array_splice($parts, $i, 0, array( + '', + '', + str_repeat("\t", $listDepth) . '[li]', + '', + )); + $numParts = count($parts) - 1; + + // The inlist status changes a bit. + $inList = false; + } + + // Just starting a new list. + else + { + $inList = true; + + if ($tag === 'ol') + $listType = 'decimal'; + elseif (preg_match('~type="?(' . implode('|', array_keys($listTypeMapping)) . ')"?~', $parts[$i + 3], $match) === 1) + $listType = $listTypeMapping[$match[1]]; + else + $listType = null; + + $listDepth++; + + $parts[$i + 2] = '[list' . ($listType === null ? '' : ' type=' . $listType) . ']' . "\n"; + $parts[$i + 3] = ''; + } + break; + + case 'li': + + // This is how it should be: a list item inside the list. + if ($inList) + { + $parts[$i + 2] = str_repeat("\t", $listDepth) . '[li]'; + $parts[$i + 3] = ''; + + // Within a list item, it's almost as if you're outside. + $inList = false; + } + + // The li is no direct child of a list. + else + { + // We are apparently in a list item. + if ($listDepth > 0) + { + $parts[$i + 2] = '[/li]' . "\n" . str_repeat("\t", $listDepth) . '[li]'; + $parts[$i + 3] = ''; + } + + // We're not even near a list. + else + { + // Quickly create a list with an item. + $listDepth++; + + $parts[$i + 2] = '[list]' . "\n\t" . '[li]'; + $parts[$i + 3] = ''; + } + } + + break; + } + } + + // Handle all the closing tags. + else + { + switch ($tag) + { + case 'ol': + case 'ul': + + // As we expected it, closing the list while we're in it. + if ($inList) + { + $inList = false; + + $listDepth--; + + $parts[$i + 1] = ''; + $parts[$i + 2] = str_repeat("\t", $listDepth) . '[/list]'; + $parts[$i + 3] = ''; + } + + else + { + // We're in a list item. + if ($listDepth > 0) + { + // Inject closure for this list item first. + // The content of $parts[$i] is left as is! + array_splice($parts, $i + 1, 0, array( + '', // $i + 1 + '[/li]' . "\n", // $i + 2 + '', // $i + 3 + '', // $i + 4 + )); + $numParts = count($parts) - 1; + + // Now that we've closed the li, we're in list space. + $inList = true; + } + + // We're not even in a list, ignore + else + { + $parts[$i + 1] = ''; + $parts[$i + 2] = ''; + $parts[$i + 3] = ''; + } + } + break; + + case 'li': + + if ($inList) + { + // There's no use for a after
        or
          , ignore. + $parts[$i + 1] = ''; + $parts[$i + 2] = ''; + $parts[$i + 3] = ''; + } + + else + { + // Remove the trailing breaks from the list item. + $parts[$i] = preg_replace('~\s*\s*$~', '', $parts[$i]); + $parts[$i + 1] = ''; + $parts[$i + 2] = '[/li]' . "\n"; + $parts[$i + 3] = ''; + + // And we're back in the [list] space. + $inList = true; + } + + break; + } + } + + // If we're in the [list] space, no content is allowed. + if ($inList && trim(preg_replace('~\s*\s*~', '', $parts[$i + 4])) !== '') + { + // Fix it by injecting an extra list item. + array_splice($parts, $i + 4, 0, array( + '', // No content. + '', // Opening tag. + 'li', // It's a
        • . + '', // No tail. + )); + $numParts = count($parts) - 1; + } + } + + $text = implode('', $parts); + + if ($inList) + { + $listDepth--; + $text .= str_repeat("\t", $listDepth) . '[/list]'; + } + + for ($i = $listDepth; $i > 0; $i--) + $text .= '[/li]' . "\n" . str_repeat("\t", $i - 1) . '[/list]'; + + } + + // I love my own image... + while (preg_match('~]*)/*>~i', $text, $matches) === 1) + { + // Find the position of the image. + $start_pos = strpos($text, $matches[0]); + if ($start_pos === false) + break; + $end_pos = $start_pos + strlen($matches[0]); + + $params = ''; + $had_params = array(); + $src = ''; + + $attrs = fetchTagAttributes($matches[1]); + foreach ($attrs as $attrib => $value) + { + if (in_array($attrib, array('width', 'height'))) + $params .= ' ' . $attrib . '=' . (int) $value; + elseif ($attrib == 'alt' && trim($value) != '') + $params .= ' alt=' . trim($value); + elseif ($attrib == 'src') + $src = trim($value); + } + + $tag = ''; + if (!empty($src)) + { + // Attempt to fix the path in case it's not present. + if (preg_match('~^https?://~i', $src) === 0 && is_array($parsedURL = parse_url($scripturl)) && isset($parsedURL['host'])) + { + $baseURL = (isset($parsedURL['scheme']) ? $parsedURL['scheme'] : 'http') . '://' . $parsedURL['host'] . (empty($parsedURL['port']) ? '' : ':' . $parsedURL['port']); + + if (substr($src, 0, 1) === '/') + $src = $baseURL . $src; + else + $src = $baseURL . (empty($parsedURL['path']) ? '/' : preg_replace('~/(?:index\\.php)?$~', '', $parsedURL['path'])) . '/' . $src; + } + + $tag = '[img' . $params . ']' . $src . '[/img]'; + } + + // Replace the tag + $text = substr($text, 0, $start_pos) . $tag . substr($text, $end_pos); + } + + // The final bits are the easy ones - tags which map to tags which map to tags - etc etc. + $tags = array( + '~~i' => '[b]', + '~~i' => '[/b]', + '~~i' => '[i]', + '~~i' => '[/i]', + '~~i' => '[u]', + '~~i' => '[/u]', + '~~i' => '[b]', + '~~i' => '[/b]', + '~~i' => '[i]', + '~~i' => '[/i]', + '~~i' => "[s]", + '~~i' => "[/s]", + '~~i' => '[s]', + '~~i' => '[/s]', + '~~i' => '[s]', + '~~i' => '[/s]', + '~~i' => '[center]', + '~~i' => '[/center]', + '~~i' => '[pre]', + '~~i' => '[/pre]', + '~~i' => '[sub]', + '~~i' => '[/sub]', + '~~i' => '[sup]', + '~~i' => '[/sup]', + '~~i' => '[tt]', + '~~i' => '[/tt]', + '~~i' => '[table]', + '~~i' => '[/table]', + '~~i' => '[tr]', + '~~i' => '[/tr]', + '~<(td|th)\s[^<>]*?colspan="?(\d{1,2})"?.*?' . '>~ie' => 'str_repeat(\'[td][/td]\', $2 - 1) . \'[td]\'', + '~<(td|th)(\s(.)*?)*?' . '>~i' => '[td]', + '~~i' => '[/td]', + '~]*?)?' . '>~i' => "\n", + '~]*>(\n)?~i' => "[hr]\n$1", + '~(\n)?\\[hr\\]~i' => "\n[hr]", + '~^\n\\[hr\\]~i' => "[hr]", + '~~i' => "<blockquote>", + '~~i' => "</blockquote>", + '~~i' => "<ins>", + '~~i' => "</ins>", + ); + $text = preg_replace(array_keys($tags), array_values($tags), $text); + + // Please give us just a little more time. + if (connection_aborted() && $context['server']['is_apache']) + @apache_reset_timeout(); + + // What about URL's - the pain in the ass of the tag world. + while (preg_match('~]*)>([^<>]*)~i', $text, $matches) === 1) + { + // Find the position of the URL. + $start_pos = strpos($text, $matches[0]); + if ($start_pos === false) + break; + $end_pos = $start_pos + strlen($matches[0]); + + $tag_type = 'url'; + $href = ''; + + $attrs = fetchTagAttributes($matches[1]); + foreach ($attrs as $attrib => $value) + { + if ($attrib == 'href') + { + $href = trim($value); + + // Are we dealing with an FTP link? + if (preg_match('~^ftps?://~', $href) === 1) + $tag_type = 'ftp'; + + // Or is this a link to an email address? + elseif (substr($href, 0, 7) == 'mailto:') + { + $tag_type = 'email'; + $href = substr($href, 7); + } + + // No http(s), so attempt to fix this potential relative URL. + elseif (preg_match('~^https?://~i', $href) === 0 && is_array($parsedURL = parse_url($scripturl)) && isset($parsedURL['host'])) + { + $baseURL = (isset($parsedURL['scheme']) ? $parsedURL['scheme'] : 'http') . '://' . $parsedURL['host'] . (empty($parsedURL['port']) ? '' : ':' . $parsedURL['port']); + + if (substr($href, 0, 1) === '/') + $href = $baseURL . $href; + else + $href = $baseURL . (empty($parsedURL['path']) ? '/' : preg_replace('~/(?:index\\.php)?$~', '', $parsedURL['path'])) . '/' . $href; + } + } + + // External URL? + if ($attrib == 'target' && $tag_type == 'url') + { + if (trim($value) == '_blank') + $tag_type == 'iurl'; + } + } + + $tag = ''; + if ($href != '') + { + if ($matches[2] == $href) + $tag = '[' . $tag_type . ']' . $href . '[/' . $tag_type . ']'; + else + $tag = '[' . $tag_type . '=' . $href . ']' . $matches[2] . '[/' . $tag_type . ']'; + } + + // Replace the tag + $text = substr($text, 0, $start_pos) . $tag . substr($text, $end_pos); + } + + $text = strip_tags($text); + + // Some tags often end up as just dummy tags - remove those. + $text = preg_replace('~\[[bisu]\]\s*\[/[bisu]\]~', '', $text); + + // Fix up entities. + $text = preg_replace('~&~i', '&#38;', $text); + + $text = legalise_bbc($text); + + return $text; +} + +// Returns an array of attributes associated with a tag. +function fetchTagAttributes($text) +{ + $attribs = array(); + $key = $value = ''; + $strpos = 0; + $tag_state = 0; // 0 = key, 1 = attribute with no string, 2 = attribute with string + for ($i = 0; $i < strlen($text); $i++) + { + // We're either moving from the key to the attribute or we're in a string and this is fine. + if ($text{$i} == '=') + { + if ($tag_state == 0) + $tag_state = 1; + elseif ($tag_state == 2) + $value .= '='; + } + // A space is either moving from an attribute back to a potential key or in a string is fine. + elseif ($text{$i} == ' ') + { + if ($tag_state == 2) + $value .= ' '; + elseif ($tag_state == 1) + { + $attribs[$key] = $value; + $key = $value = ''; + $tag_state = 0; + } + } + // A quote? + elseif ($text{$i} == '"') + { + // Must be either going into or out of a string. + if ($tag_state == 1) + $tag_state = 2; + else + $tag_state = 1; + } + // Otherwise it's fine. + else + { + if ($tag_state == 0) + $key .= $text{$i}; + else + $value .= $text{$i}; + } + } + + // Anything left? + if ($key != '' && $value != '') + $attribs[$key] = $value; + + return $attribs; +} + +function getMessageIcons($board_id) +{ + global $modSettings, $context, $txt, $settings, $smcFunc; + + if (empty($modSettings['messageIcons_enable'])) + { + loadLanguage('Post'); + + $icons = array( + array('value' => 'xx', 'name' => $txt['standard']), + array('value' => 'thumbup', 'name' => $txt['thumbs_up']), + array('value' => 'thumbdown', 'name' => $txt['thumbs_down']), + array('value' => 'exclamation', 'name' => $txt['excamation_point']), + array('value' => 'question', 'name' => $txt['question_mark']), + array('value' => 'lamp', 'name' => $txt['lamp']), + array('value' => 'smiley', 'name' => $txt['icon_smiley']), + array('value' => 'angry', 'name' => $txt['icon_angry']), + array('value' => 'cheesy', 'name' => $txt['icon_cheesy']), + array('value' => 'grin', 'name' => $txt['icon_grin']), + array('value' => 'sad', 'name' => $txt['icon_sad']), + array('value' => 'wink', 'name' => $txt['icon_wink']) + ); + + foreach ($icons as $k => $dummy) + { + $icons[$k]['url'] = $settings['images_url'] . '/post/' . $dummy['value'] . '.gif'; + $icons[$k]['is_last'] = false; + } + } + // Otherwise load the icons, and check we give the right image too... + else + { + if (($temp = cache_get_data('posting_icons-' . $board_id, 480)) == null) + { + $request = $smcFunc['db_query']('select_message_icons', ' + SELECT title, filename + FROM {db_prefix}message_icons + WHERE id_board IN (0, {int:board_id})', + array( + 'board_id' => $board_id, + ) + ); + $icon_data = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $icon_data[] = $row; + $smcFunc['db_free_result']($request); + + cache_put_data('posting_icons-' . $board_id, $icon_data, 480); + } + else + $icon_data = $temp; + + $icons = array(); + foreach ($icon_data as $icon) + { + $icons[$icon['filename']] = array( + 'value' => $icon['filename'], + 'name' => $icon['title'], + 'url' => $settings[file_exists($settings['theme_dir'] . '/images/post/' . $icon['filename'] . '.gif') ? 'images_url' : 'default_images_url'] . '/post/' . $icon['filename'] . '.gif', + 'is_last' => false, + ); + } + } + + return array_values($icons); +} + +// This is an important yet frustrating function - it attempts to clean up illegal BBC caused by browsers like Opera which don't obey the rules!!! +function legalise_bbc($text) +{ + global $modSettings; + + // Don't care about the texts that are too short. + if (strlen($text) < 3) + return $text; + + // We are going to cycle through the BBC and keep track of tags as they arise - in order. If get to a block level tag we're going to make sure it's not in a non-block level tag! + // This will keep the order of tags that are open. + $current_tags = array(); + + // This will quickly let us see if the tag is active. + $active_tags = array(); + + // A list of tags that's disabled by the admin. + $disabled = empty($modSettings['disabledBBC']) ? array() : array_flip(explode(',', strtolower($modSettings['disabledBBC']))); + + // Add flash if it's disabled as embedded tag. + if (empty($modSettings['enableEmbeddedFlash'])) + $disabled['flash'] = true; + + // Get a list of all the tags that are not disabled. + $all_tags = parse_bbc(false); + $valid_tags = array(); + $self_closing_tags = array(); + foreach ($all_tags as $tag) + { + if (!isset($disabled[$tag['tag']])) + $valid_tags[$tag['tag']] = !empty($tag['block_level']); + if (isset($tag['type']) && $tag['type'] == 'closed') + $self_closing_tags[] = $tag['tag']; + } + + // Don't worry if we're in a code/nobbc. + $in_code_nobbc = false; + + // Right - we're going to start by going through the whole lot to make sure we don't have align stuff crossed as this happens load and is stupid! + $align_tags = array('left', 'center', 'right', 'pre'); + + // Remove those align tags that are not valid. + $align_tags = array_intersect($align_tags, array_keys($valid_tags)); + + // These keep track of where we are! + if (!empty($align_tags) && count($matches = preg_split('~(\\[/?(?:' . implode('|', $align_tags) . ')\\])~', $text, -1, PREG_SPLIT_DELIM_CAPTURE)) > 1) + { + // The first one is never a tag. + $isTag = false; + + // By default we're not inside a tag too. + $insideTag = null; + + foreach ($matches as $i => $match) + { + // We're only interested in tags, not text. + if ($isTag) + { + $isClosingTag = substr($match, 1, 1) === '/'; + $tagName = substr($match, $isClosingTag ? 2 : 1, -1); + + // We're closing the exact same tag that we opened. + if ($isClosingTag && $insideTag === $tagName) + $insideTag = null; + + // We're opening a tag and we're not yet inside one either + elseif (!$isClosingTag && $insideTag === null) + $insideTag = $tagName; + + // In all other cases, this tag must be invalid + else + unset($matches[$i]); + } + + // The next one is gonna be the other one. + $isTag = !$isTag; + } + + // We're still inside a tag and had no chance for closure? + if ($insideTag !== null) + $matches[] = '[/' . $insideTag . ']'; + + // And a complete text string again. + $text = implode('', $matches); + } + + // Quickly remove any tags which are back to back. + $backToBackPattern = '~\\[(' . implode('|', array_diff(array_keys($valid_tags), array('td', 'anchor'))) . ')[^<>\\[\\]]*\\]\s*\\[/\\1\\]~'; + $lastlen = 0; + while (strlen($text) !== $lastlen) + $lastlen = strlen($text = preg_replace($backToBackPattern, '', $text)); + + // Need to sort the tags my name length. + uksort($valid_tags, 'sort_array_length'); + + // These inline tags can compete with each other regarding style. + $competing_tags = array( + 'color', + 'size', + ); + + // In case things changed above set these back to normal. + $in_code_nobbc = false; + $new_text_offset = 0; + + // These keep track of where we are! + if (count($parts = preg_split(sprintf('~(\\[)(/?)(%1$s)((?:[\\s=][^\\]\\[]*)?\\])~', implode('|', array_keys($valid_tags))), $text, -1, PREG_SPLIT_DELIM_CAPTURE)) > 1) + { + // Start with just text. + $isTag = false; + + // Start outside [nobbc] or [code] blocks. + $inCode = false; + $inNoBbc = false; + + // A buffer containing all opened inline elements. + $inlineElements = array(); + + // A buffer containing all opened block elements. + $blockElements = array(); + + // A buffer containing the opened inline elements that might compete. + $competingElements = array(); + + // $i: text, $i + 1: '[', $i + 2: '/', $i + 3: tag, $i + 4: tag tail. + for ($i = 0, $n = count($parts) - 1; $i < $n; $i += 5) + { + $tag = $parts[$i + 3]; + $isOpeningTag = $parts[$i + 2] === ''; + $isClosingTag = $parts[$i + 2] === '/'; + $isBlockLevelTag = isset($valid_tags[$tag]) && $valid_tags[$tag] && !in_array($tag, $self_closing_tags); + $isCompetingTag = in_array($tag, $competing_tags); + + // Check if this might be one of those cleaned out tags. + if ($tag === '') + continue; + + // Special case: inside [code] blocks any code is left untouched. + elseif ($tag === 'code') + { + // We're inside a code block and closing it. + if ($inCode && $isClosingTag) + { + $inCode = false; + + // Reopen tags that were closed before the code block. + if (!empty($inlineElements)) + $parts[$i + 4] .= '[' . implode('][', array_keys($inlineElements)) . ']'; + } + + // We're outside a coding and nobbc block and opening it. + elseif (!$inCode && !$inNoBbc && $isOpeningTag) + { + // If there are still inline elements left open, close them now. + if (!empty($inlineElements)) + { + $parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']'; + //$inlineElements = array(); + } + + $inCode = true; + } + + // Nothing further to do. + continue; + } + + // Special case: inside [nobbc] blocks any BBC is left untouched. + elseif ($tag === 'nobbc') + { + // We're inside a nobbc block and closing it. + if ($inNoBbc && $isClosingTag) + { + $inNoBbc = false; + + // Some inline elements might've been closed that need reopening. + if (!empty($inlineElements)) + $parts[$i + 4] .= '[' . implode('][', array_keys($inlineElements)) . ']'; + } + + // We're outside a nobbc and coding block and opening it. + elseif (!$inNoBbc && !$inCode && $isOpeningTag) + { + // Can't have inline elements still opened. + if (!empty($inlineElements)) + { + $parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']'; + //$inlineElements = array(); + } + + $inNoBbc = true; + } + + continue; + } + + // So, we're inside one of the special blocks: ignore any tag. + elseif ($inCode || $inNoBbc) + continue; + + // We're dealing with an opening tag. + if ($isOpeningTag) + { + // Everyting inside the square brackets of the opening tag. + $elementContent = $parts[$i + 3] . substr($parts[$i + 4], 0, -1); + + // A block level opening tag. + if ($isBlockLevelTag) + { + // Are there inline elements still open? + if (!empty($inlineElements)) + { + // Close all the inline tags, a block tag is coming... + $parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']'; + + // Now open them again, we're inside the block tag now. + $parts[$i + 5] = '[' . implode('][', array_keys($inlineElements)) . ']' . $parts[$i + 5]; + } + + $blockElements[] = $tag; + } + + // Inline opening tag. + elseif (!in_array($tag, $self_closing_tags)) + { + // Can't have two opening elements with the same contents! + if (isset($inlineElements[$elementContent])) + { + // Get rid of this tag. + $parts[$i + 1] = $parts[$i + 2] = $parts[$i + 3] = $parts[$i + 4] = ''; + + // Now try to find the corresponding closing tag. + $curLevel = 1; + for ($j = $i + 5, $m = count($parts) - 1; $j < $m; $j += 5) + { + // Find the tags with the same tagname + if ($parts[$j + 3] === $tag) + { + // If it's an opening tag, increase the level. + if ($parts[$j + 2] === '') + $curLevel++; + + // A closing tag, decrease the level. + else + { + $curLevel--; + + // Gotcha! Clean out this closing tag gone rogue. + if ($curLevel === 0) + { + $parts[$j + 1] = $parts[$j + 2] = $parts[$j + 3] = $parts[$j + 4] = ''; + break; + } + } + } + } + } + + // Otherwise, add this one to the list. + else + { + if ($isCompetingTag) + { + if (!isset($competingElements[$tag])) + $competingElements[$tag] = array(); + + $competingElements[$tag][] = $parts[$i + 4]; + + if (count($competingElements[$tag]) > 1) + $parts[$i] .= '[/' . $tag . ']'; + } + + $inlineElements[$elementContent] = $tag; + } + } + + } + + // Closing tag. + else + { + // Closing the block tag. + if ($isBlockLevelTag) + { + // Close the elements that should've been closed by closing this tag. + if (!empty($blockElements)) + { + $addClosingTags = array(); + while ($element = array_pop($blockElements)) + { + if ($element === $tag) + break; + + // Still a block tag was open not equal to this tag. + $addClosingTags[] = $element['type']; + } + + if (!empty($addClosingTags)) + $parts[$i + 1] = '[/' . implode('][/', array_reverse($addClosingTags)) . ']' . $parts[$i + 1]; + + // Apparently the closing tag was not found on the stack. + if (!is_string($element) || $element !== $tag) + { + // Get rid of this particular closing tag, it was never opened. + $parts[$i + 1] = substr($parts[$i + 1], 0, -1); + $parts[$i + 2] = $parts[$i + 3] = $parts[$i + 4] = ''; + continue; + } + } + else + { + // Get rid of this closing tag! + $parts[$i + 1] = $parts[$i + 2] = $parts[$i + 3] = $parts[$i + 4] = ''; + continue; + } + + // Inline elements are still left opened? + if (!empty($inlineElements)) + { + // Close them first.. + $parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']'; + + // Then reopen them. + $parts[$i + 5] = '[' . implode('][', array_keys($inlineElements)) . ']' . $parts[$i + 5]; + } + } + // Inline tag. + else + { + // Are we expecting this tag to end? + if (in_array($tag, $inlineElements)) + { + foreach (array_reverse($inlineElements, true) as $tagContentToBeClosed => $tagToBeClosed) + { + // Closing it one way or the other. + unset($inlineElements[$tagContentToBeClosed]); + + // Was this the tag we were looking for? + if ($tagToBeClosed === $tag) + break; + + // Nope, close it and look further! + else + $parts[$i] .= '[/' . $tagToBeClosed . ']'; + } + + if ($isCompetingTag && !empty($competingElements[$tag])) + { + array_pop($competingElements[$tag]); + + if (count($competingElements[$tag]) > 0) + $parts[$i + 5] = '[' . $tag . $competingElements[$tag][count($competingElements[$tag]) - 1] . $parts[$i + 5]; + } + } + + // Unexpected closing tag, ex-ter-mi-nate. + else + $parts[$i + 1] = $parts[$i + 2] = $parts[$i + 3] = $parts[$i + 4] = ''; + } + } + } + + // Close the code tags. + if ($inCode) + $parts[$i] .= '[/code]'; + + // The same for nobbc tags. + elseif ($inNoBbc) + $parts[$i] .= '[/nobbc]'; + + // Still inline tags left unclosed? Close them now, better late than never. + elseif (!empty($inlineElements)) + $parts[$i] .= '[/' . implode('][/', array_reverse($inlineElements)) . ']'; + + // Now close the block elements. + if (!empty($blockElements)) + $parts[$i] .= '[/' . implode('][/', array_reverse($blockElements)) . ']'; + + $text = implode('', $parts); + } + + // Final clean up of back to back tags. + $lastlen = 0; + while (strlen($text) !== $lastlen) + $lastlen = strlen($text = preg_replace($backToBackPattern, '', $text)); + + return $text; +} + +// A help function for legalise_bbc for sorting arrays based on length. +function sort_array_length($a, $b) +{ + return strlen($a) < strlen($b) ? 1 : -1; +} + +// Compatibility function - used in 1.1 for showing a post box. +function theme_postbox($msg) +{ + global $context; + + return template_control_richedit($context['post_box_name']); +} + +// Creates a box that can be used for richedit stuff like BBC, Smileys etc. +function create_control_richedit($editorOptions) +{ + global $txt, $modSettings, $options, $smcFunc; + global $context, $settings, $user_info, $sourcedir, $scripturl; + + // Load the Post language file... for the moment at least. + loadLanguage('Post'); + + // Every control must have a ID! + assert(isset($editorOptions['id'])); + assert(isset($editorOptions['value'])); + + // Is this the first richedit - if so we need to ensure some template stuff is initialised. + if (empty($context['controls']['richedit'])) + { + // Some general stuff. + $settings['smileys_url'] = $modSettings['smileys_url'] . '/' . $user_info['smiley_set']; + + // This really has some WYSIWYG stuff. + loadTemplate('GenericControls', $context['browser']['is_ie'] ? 'editor_ie' : 'editor'); + $context['html_headers'] .= ' + + '; + + $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new'); + if ($context['show_spellchecking']) + { + $context['html_headers'] .= ' + '; + + // Some hidden information is needed in order to make the spell checking work. + if (!isset($_REQUEST['xml'])) + $context['insert_after_template'] .= ' +
          + +
          '; + + // Also make sure that spell check works with rich edit. + $context['html_headers'] .= ' + '; + } + } + + // Start off the editor... + $context['controls']['richedit'][$editorOptions['id']] = array( + 'id' => $editorOptions['id'], + 'value' => $editorOptions['value'], + 'rich_value' => bbc_to_html($editorOptions['value']), + 'rich_active' => empty($modSettings['disable_wysiwyg']) && (!empty($options['wysiwyg_default']) || !empty($editorOptions['force_rich']) || !empty($_REQUEST[$editorOptions['id'] . '_mode'])), + 'disable_smiley_box' => !empty($editorOptions['disable_smiley_box']), + 'columns' => isset($editorOptions['columns']) ? $editorOptions['columns'] : 60, + 'rows' => isset($editorOptions['rows']) ? $editorOptions['rows'] : 12, + 'width' => isset($editorOptions['width']) ? $editorOptions['width'] : '70%', + 'height' => isset($editorOptions['height']) ? $editorOptions['height'] : '150px', + 'form' => isset($editorOptions['form']) ? $editorOptions['form'] : 'postmodify', + 'bbc_level' => !empty($editorOptions['bbc_level']) ? $editorOptions['bbc_level'] : 'full', + 'preview_type' => isset($editorOptions['preview_type']) ? (int) $editorOptions['preview_type'] : 1, + 'labels' => !empty($editorOptions['labels']) ? $editorOptions['labels'] : array(), + ); + + // Switch between default images and back... mostly in case you don't have an PersonalMessage template, but do have a Post template. + if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'defaults' && isset($settings['default_template'])) + { + $temp1 = $settings['theme_url']; + $settings['theme_url'] = $settings['default_theme_url']; + + $temp2 = $settings['images_url']; + $settings['images_url'] = $settings['default_images_url']; + + $temp3 = $settings['theme_dir']; + $settings['theme_dir'] = $settings['default_theme_dir']; + } + + if (empty($context['bbc_tags'])) + { + // The below array makes it dead easy to add images to this control. Add it to the array and everything else is done for you! + $context['bbc_tags'] = array(); + $context['bbc_tags'][] = array( + array( + 'image' => 'bold', + 'code' => 'b', + 'before' => '[b]', + 'after' => '[/b]', + 'description' => $txt['bold'], + ), + array( + 'image' => 'italicize', + 'code' => 'i', + 'before' => '[i]', + 'after' => '[/i]', + 'description' => $txt['italic'], + ), + array( + 'image' => 'underline', + 'code' => 'u', + 'before' => '[u]', + 'after' => '[/u]', + 'description' => $txt['underline'] + ), + array( + 'image' => 'strike', + 'code' => 's', + 'before' => '[s]', + 'after' => '[/s]', + 'description' => $txt['strike'] + ), + array(), + array( + 'image' => 'pre', + 'code' => 'pre', + 'before' => '[pre]', + 'after' => '[/pre]', + 'description' => $txt['preformatted'] + ), + array( + 'image' => 'left', + 'code' => 'left', + 'before' => '[left]', + 'after' => '[/left]', + 'description' => $txt['left_align'] + ), + array( + 'image' => 'center', + 'code' => 'center', + 'before' => '[center]', + 'after' => '[/center]', + 'description' => $txt['center'] + ), + array( + 'image' => 'right', + 'code' => 'right', + 'before' => '[right]', + 'after' => '[/right]', + 'description' => $txt['right_align'] + ), + ); + $context['bbc_tags'][] = array( + array( + 'image' => 'flash', + 'code' => 'flash', + 'before' => '[flash=200,200]', + 'after' => '[/flash]', + 'description' => $txt['flash'] + ), + array( + 'image' => 'img', + 'code' => 'img', + 'before' => '[img]', + 'after' => '[/img]', + 'description' => $txt['image'] + ), + array( + 'image' => 'url', + 'code' => 'url', + 'before' => '[url]', + 'after' => '[/url]', + 'description' => $txt['hyperlink'] + ), + array( + 'image' => 'email', + 'code' => 'email', + 'before' => '[email]', + 'after' => '[/email]', + 'description' => $txt['insert_email'] + ), + array( + 'image' => 'ftp', + 'code' => 'ftp', + 'before' => '[ftp]', + 'after' => '[/ftp]', + 'description' => $txt['ftp'] + ), + array(), + array( + 'image' => 'glow', + 'code' => 'glow', + 'before' => '[glow=red,2,300]', + 'after' => '[/glow]', + 'description' => $txt['glow'] + ), + array( + 'image' => 'shadow', + 'code' => 'shadow', + 'before' => '[shadow=red,left]', + 'after' => '[/shadow]', + 'description' => $txt['shadow'] + ), + array( + 'image' => 'move', + 'code' => 'move', + 'before' => '[move]', + 'after' => '[/move]', + 'description' => $txt['marquee'] + ), + array(), + array( + 'image' => 'sup', + 'code' => 'sup', + 'before' => '[sup]', + 'after' => '[/sup]', + 'description' => $txt['superscript'] + ), + array( + 'image' => 'sub', + 'code' => 'sub', + 'before' => '[sub]', + 'after' => '[/sub]', + 'description' => $txt['subscript'] + ), + array( + 'image' => 'tele', + 'code' => 'tt', + 'before' => '[tt]', + 'after' => '[/tt]', + 'description' => $txt['teletype'] + ), + array(), + array( + 'image' => 'table', + 'code' => 'table', + 'before' => '[table]\n[tr]\n[td]', + 'after' => '[/td]\n[/tr]\n[/table]', + 'description' => $txt['table'] + ), + array( + 'image' => 'code', + 'code' => 'code', + 'before' => '[code]', + 'after' => '[/code]', + 'description' => $txt['bbc_code'] + ), + array( + 'image' => 'quote', + 'code' => 'quote', + 'before' => '[quote]', + 'after' => '[/quote]', + 'description' => $txt['bbc_quote'] + ), + array(), + array( + 'image' => 'list', + 'code' => 'list', + 'before' => '[list]\n[li]', + 'after' => '[/li]\n[li][/li]\n[/list]', + 'description' => $txt['list_unordered'] + ), + array( + 'image' => 'orderlist', + 'code' => 'orderlist', + 'before' => '[list type=decimal]\n[li]', + 'after' => '[/li]\n[li][/li]\n[/list]', + 'description' => $txt['list_ordered'] + ), + array( + 'image' => 'hr', + 'code' => 'hr', + 'before' => '[hr]', + 'description' => $txt['horizontal_rule'] + ), + ); + + // Allow mods to modify BBC buttons. + call_integration_hook('integrate_bbc_buttons', array(&$context['bbc_tags'])); + + // Show the toggle? + if (empty($modSettings['disable_wysiwyg'])) + { + $context['bbc_tags'][count($context['bbc_tags']) - 1][] = array(); + $context['bbc_tags'][count($context['bbc_tags']) - 1][] = array( + 'image' => 'unformat', + 'code' => 'unformat', + 'before' => '', + 'description' => $txt['unformat_text'], + ); + $context['bbc_tags'][count($context['bbc_tags']) - 1][] = array( + 'image' => 'toggle', + 'code' => 'toggle', + 'before' => '', + 'description' => $txt['toggle_view'], + ); + } + + foreach ($context['bbc_tags'] as $row => $tagRow) + $context['bbc_tags'][$row][count($tagRow) - 1]['isLast'] = true; + } + + // Initialize smiley array... if not loaded before. + if (empty($context['smileys']) && empty($editorOptions['disable_smiley_box'])) + { + $context['smileys'] = array( + 'postform' => array(), + 'popup' => array(), + ); + + // Load smileys - don't bother to run a query if we're not using the database's ones anyhow. + if (empty($modSettings['smiley_enable']) && $user_info['smiley_set'] != 'none') + $context['smileys']['postform'][] = array( + 'smileys' => array( + array( + 'code' => ':)', + 'filename' => 'smiley.gif', + 'description' => $txt['icon_smiley'], + ), + array( + 'code' => ';)', + 'filename' => 'wink.gif', + 'description' => $txt['icon_wink'], + ), + array( + 'code' => ':D', + 'filename' => 'cheesy.gif', + 'description' => $txt['icon_cheesy'], + ), + array( + 'code' => ';D', + 'filename' => 'grin.gif', + 'description' => $txt['icon_grin'] + ), + array( + 'code' => '>:(', + 'filename' => 'angry.gif', + 'description' => $txt['icon_angry'], + ), + array( + 'code' => ':(', + 'filename' => 'sad.gif', + 'description' => $txt['icon_sad'], + ), + array( + 'code' => ':o', + 'filename' => 'shocked.gif', + 'description' => $txt['icon_shocked'], + ), + array( + 'code' => '8)', + 'filename' => 'cool.gif', + 'description' => $txt['icon_cool'], + ), + array( + 'code' => '???', + 'filename' => 'huh.gif', + 'description' => $txt['icon_huh'], + ), + array( + 'code' => '::)', + 'filename' => 'rolleyes.gif', + 'description' => $txt['icon_rolleyes'], + ), + array( + 'code' => ':P', + 'filename' => 'tongue.gif', + 'description' => $txt['icon_tongue'], + ), + array( + 'code' => ':-[', + 'filename' => 'embarrassed.gif', + 'description' => $txt['icon_embarrassed'], + ), + array( + 'code' => ':-X', + 'filename' => 'lipsrsealed.gif', + 'description' => $txt['icon_lips'], + ), + array( + 'code' => ':-\\', + 'filename' => 'undecided.gif', + 'description' => $txt['icon_undecided'], + ), + array( + 'code' => ':-*', + 'filename' => 'kiss.gif', + 'description' => $txt['icon_kiss'], + ), + array( + 'code' => ':\'(', + 'filename' => 'cry.gif', + 'description' => $txt['icon_cry'], + 'isLast' => true, + ), + ), + 'isLast' => true, + ); + elseif ($user_info['smiley_set'] != 'none') + { + if (($temp = cache_get_data('posting_smileys', 480)) == null) + { + $request = $smcFunc['db_query']('', ' + SELECT code, filename, description, smiley_row, hidden + FROM {db_prefix}smileys + WHERE hidden IN (0, 2) + ORDER BY smiley_row, smiley_order', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $row['filename'] = htmlspecialchars($row['filename']); + $row['description'] = htmlspecialchars($row['description']); + + $context['smileys'][empty($row['hidden']) ? 'postform' : 'popup'][$row['smiley_row']]['smileys'][] = $row; + } + $smcFunc['db_free_result']($request); + + foreach ($context['smileys'] as $section => $smileyRows) + { + foreach ($smileyRows as $rowIndex => $smileys) + $context['smileys'][$section][$rowIndex]['smileys'][count($smileys['smileys']) - 1]['isLast'] = true; + + if (!empty($smileyRows)) + $context['smileys'][$section][count($smileyRows) - 1]['isLast'] = true; + } + + cache_put_data('posting_smileys', $context['smileys'], 480); + } + else + $context['smileys'] = $temp; + } + } + + // Set a flag so the sub template knows what to do... + $context['show_bbc'] = !empty($modSettings['enableBBC']) && !empty($settings['show_bbc']); + + // Generate a list of buttons that shouldn't be shown - this should be the fastest way to do this. + $disabled_tags = array(); + if (!empty($modSettings['disabledBBC'])) + $disabled_tags = explode(',', $modSettings['disabledBBC']); + if (empty($modSettings['enableEmbeddedFlash'])) + $disabled_tags[] = 'flash'; + + foreach ($disabled_tags as $tag) + { + if ($tag == 'list') + $context['disabled_tags']['orderlist'] = true; + + $context['disabled_tags'][trim($tag)] = true; + } + + // Switch the URLs back... now we're back to whatever the main sub template is. (like folder in PersonalMessage.) + if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'defaults' && isset($settings['default_template'])) + { + $settings['theme_url'] = $temp1; + $settings['images_url'] = $temp2; + $settings['theme_dir'] = $temp3; + } +} + +// Create a anti-bot verification control? +function create_control_verification(&$verificationOptions, $do_test = false) +{ + global $txt, $modSettings, $options, $smcFunc; + global $context, $settings, $user_info, $sourcedir, $scripturl; + + // First verification means we need to set up some bits... + if (empty($context['controls']['verification'])) + { + // The template + loadTemplate('GenericControls'); + + // Some javascript ma'am? + if (!empty($verificationOptions['override_visual']) || (!empty($modSettings['visual_verification_type']) && !isset($verificationOptions['override_visual']))) + $context['html_headers'] .= ' + '; + + $context['use_graphic_library'] = in_array('gd', get_loaded_extensions()); + + // Skip I, J, L, O, Q, S and Z. + $context['standard_captcha_range'] = array_merge(range('A', 'H'), array('K', 'M', 'N', 'P', 'R'), range('T', 'Y')); + } + + // Always have an ID. + assert(isset($verificationOptions['id'])); + $isNew = !isset($context['controls']['verification'][$verificationOptions['id']]); + + // Log this into our collection. + if ($isNew) + $context['controls']['verification'][$verificationOptions['id']] = array( + 'id' => $verificationOptions['id'], + 'show_visual' => !empty($verificationOptions['override_visual']) || (!empty($modSettings['visual_verification_type']) && !isset($verificationOptions['override_visual'])), + 'number_questions' => isset($verificationOptions['override_qs']) ? $verificationOptions['override_qs'] : (!empty($modSettings['qa_verification_number']) ? $modSettings['qa_verification_number'] : 0), + 'max_errors' => isset($verificationOptions['max_errors']) ? $verificationOptions['max_errors'] : 3, + 'image_href' => $scripturl . '?action=verificationcode;vid=' . $verificationOptions['id'] . ';rand=' . md5(mt_rand()), + 'text_value' => '', + 'questions' => array(), + ); + $thisVerification = &$context['controls']['verification'][$verificationOptions['id']]; + + // Add javascript for the object. + if ($context['controls']['verification'][$verificationOptions['id']]['show_visual'] && !WIRELESS) + $context['insert_after_template'] .= ' + '; + + // Is there actually going to be anything? + if (empty($thisVerification['show_visual']) && empty($thisVerification['number_questions'])) + return false; + elseif (!$isNew && !$do_test) + return true; + + // If we want questions do we have a cache of all the IDs? + if (!empty($thisVerification['number_questions']) && empty($modSettings['question_id_cache'])) + { + if (($modSettings['question_id_cache'] = cache_get_data('verificationQuestionIds', 300)) == null) + { + $request = $smcFunc['db_query']('', ' + SELECT id_comment + FROM {db_prefix}log_comments + WHERE comment_type = {string:ver_test}', + array( + 'ver_test' => 'ver_test', + ) + ); + $modSettings['question_id_cache'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $modSettings['question_id_cache'][] = $row['id_comment']; + $smcFunc['db_free_result']($request); + + if (!empty($modSettings['cache_enable'])) + cache_put_data('verificationQuestionIds', $modSettings['question_id_cache'], 300); + } + } + + if (!isset($_SESSION[$verificationOptions['id'] . '_vv'])) + $_SESSION[$verificationOptions['id'] . '_vv'] = array(); + + // Do we need to refresh the verification? + if (!$do_test && (!empty($_SESSION[$verificationOptions['id'] . '_vv']['did_pass']) || empty($_SESSION[$verificationOptions['id'] . '_vv']['count']) || $_SESSION[$verificationOptions['id'] . '_vv']['count'] > 3) && empty($verificationOptions['dont_refresh'])) + $force_refresh = true; + else + $force_refresh = false; + + // This can also force a fresh, although unlikely. + if (($thisVerification['show_visual'] && empty($_SESSION[$verificationOptions['id'] . '_vv']['code'])) || ($thisVerification['number_questions'] && empty($_SESSION[$verificationOptions['id'] . '_vv']['q']))) + $force_refresh = true; + + $verification_errors = array(); + + // Start with any testing. + if ($do_test) + { + // This cannot happen! + if (!isset($_SESSION[$verificationOptions['id'] . '_vv']['count'])) + fatal_lang_error('no_access', false); + // ... nor this! + if ($thisVerification['number_questions'] && (!isset($_SESSION[$verificationOptions['id'] . '_vv']['q']) || !isset($_REQUEST[$verificationOptions['id'] . '_vv']['q']))) + fatal_lang_error('no_access', false); + + if ($thisVerification['show_visual'] && (empty($_REQUEST[$verificationOptions['id'] . '_vv']['code']) || empty($_SESSION[$verificationOptions['id'] . '_vv']['code']) || strtoupper($_REQUEST[$verificationOptions['id'] . '_vv']['code']) !== $_SESSION[$verificationOptions['id'] . '_vv']['code'])) + $verification_errors[] = 'wrong_verification_code'; + if ($thisVerification['number_questions']) + { + // Get the answers and see if they are all right! + $request = $smcFunc['db_query']('', ' + SELECT id_comment, recipient_name AS answer + FROM {db_prefix}log_comments + WHERE comment_type = {string:ver_test} + AND id_comment IN ({array_int:comment_ids})', + array( + 'ver_test' => 'ver_test', + 'comment_ids' => $_SESSION[$verificationOptions['id'] . '_vv']['q'], + ) + ); + $incorrectQuestions = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($_REQUEST[$verificationOptions['id'] . '_vv']['q'][$row['id_comment']]) || trim($smcFunc['htmlspecialchars'](strtolower($_REQUEST[$verificationOptions['id'] . '_vv']['q'][$row['id_comment']]))) != strtolower($row['answer'])) + $incorrectQuestions[] = $row['id_comment']; + } + $smcFunc['db_free_result']($request); + + if (!empty($incorrectQuestions)) + $verification_errors[] = 'wrong_verification_answer'; + } + } + + // Any errors means we refresh potentially. + if (!empty($verification_errors)) + { + if (empty($_SESSION[$verificationOptions['id'] . '_vv']['errors'])) + $_SESSION[$verificationOptions['id'] . '_vv']['errors'] = 0; + // Too many errors? + elseif ($_SESSION[$verificationOptions['id'] . '_vv']['errors'] > $thisVerification['max_errors']) + $force_refresh = true; + + // Keep a track of these. + $_SESSION[$verificationOptions['id'] . '_vv']['errors']++; + } + + // Are we refreshing then? + if ($force_refresh) + { + // Assume nothing went before. + $_SESSION[$verificationOptions['id'] . '_vv']['count'] = 0; + $_SESSION[$verificationOptions['id'] . '_vv']['errors'] = 0; + $_SESSION[$verificationOptions['id'] . '_vv']['did_pass'] = false; + $_SESSION[$verificationOptions['id'] . '_vv']['q'] = array(); + $_SESSION[$verificationOptions['id'] . '_vv']['code'] = ''; + + // Generating a new image. + if ($thisVerification['show_visual']) + { + // Are we overriding the range? + $character_range = !empty($verificationOptions['override_range']) ? $verificationOptions['override_range'] : $context['standard_captcha_range']; + + for ($i = 0; $i < 6; $i++) + $_SESSION[$verificationOptions['id'] . '_vv']['code'] .= $character_range[array_rand($character_range)]; + } + + // Getting some new questions? + if ($thisVerification['number_questions']) + { + // Pick some random IDs + $questionIDs = array(); + if ($thisVerification['number_questions'] == 1) + $questionIDs[] = $modSettings['question_id_cache'][array_rand($modSettings['question_id_cache'], $thisVerification['number_questions'])]; + else + foreach (array_rand($modSettings['question_id_cache'], $thisVerification['number_questions']) as $index) + $questionIDs[] = $modSettings['question_id_cache'][$index]; + } + } + else + { + // Same questions as before. + $questionIDs = !empty($_SESSION[$verificationOptions['id'] . '_vv']['q']) ? $_SESSION[$verificationOptions['id'] . '_vv']['q'] : array(); + $thisVerification['text_value'] = !empty($_REQUEST[$verificationOptions['id'] . '_vv']['code']) ? $smcFunc['htmlspecialchars']($_REQUEST[$verificationOptions['id'] . '_vv']['code']) : ''; + } + + // Have we got some questions to load? + if (!empty($questionIDs)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_comment, body AS question + FROM {db_prefix}log_comments + WHERE comment_type = {string:ver_test} + AND id_comment IN ({array_int:comment_ids})', + array( + 'ver_test' => 'ver_test', + 'comment_ids' => $questionIDs, + ) + ); + $_SESSION[$verificationOptions['id'] . '_vv']['q'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $thisVerification['questions'][] = array( + 'id' => $row['id_comment'], + 'q' => parse_bbc($row['question']), + 'is_error' => !empty($incorrectQuestions) && in_array($row['id_comment'], $incorrectQuestions), + // Remember a previous submission? + 'a' => isset($_REQUEST[$verificationOptions['id'] . '_vv'], $_REQUEST[$verificationOptions['id'] . '_vv']['q'], $_REQUEST[$verificationOptions['id'] . '_vv']['q'][$row['id_comment']]) ? $smcFunc['htmlspecialchars']($_REQUEST[$verificationOptions['id'] . '_vv']['q'][$row['id_comment']]) : '', + ); + $_SESSION[$verificationOptions['id'] . '_vv']['q'][] = $row['id_comment']; + } + $smcFunc['db_free_result']($request); + } + + $_SESSION[$verificationOptions['id'] . '_vv']['count'] = empty($_SESSION[$verificationOptions['id'] . '_vv']['count']) ? 1 : $_SESSION[$verificationOptions['id'] . '_vv']['count'] + 1; + + // Return errors if we have them. + if (!empty($verification_errors)) + return $verification_errors; + // If we had a test that one, make a note. + elseif ($do_test) + $_SESSION[$verificationOptions['id'] . '_vv']['did_pass'] = true; + + // Say that everything went well chaps. + return true; +} + +// This keeps track of all registered handling functions for auto suggest functionality and passes execution to them. +function AutoSuggestHandler($checkRegistered = null) +{ + global $context; + + // These are all registered types. + $searchTypes = array( + 'member' => 'Member', + ); + + // If we're just checking the callback function is registered return true or false. + if ($checkRegistered != null) + return isset($searchTypes[$checkRegistered]) && function_exists('AutoSuggest_Search_' . $checkRegistered); + + checkSession('get'); + loadTemplate('Xml'); + + // Any parameters? + $context['search_param'] = isset($_REQUEST['search_param']) ? unserialize(base64_decode($_REQUEST['search_param'])) : array(); + + if (isset($_REQUEST['suggest_type'], $_REQUEST['search']) && isset($searchTypes[$_REQUEST['suggest_type']])) + { + $function = 'AutoSuggest_Search_' . $searchTypes[$_REQUEST['suggest_type']]; + $context['sub_template'] = 'generic_xml'; + $context['xml_data'] = $function(); + } +} + +// Search for a member - by real_name or member_name by default. +function AutoSuggest_Search_Member() +{ + global $user_info, $txt, $smcFunc, $context; + + $_REQUEST['search'] = trim($smcFunc['strtolower']($_REQUEST['search'])) . '*'; + $_REQUEST['search'] = strtr($_REQUEST['search'], array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_', '&' => '&')); + + // Find the member. + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE real_name LIKE {string:search}' . (!empty($context['search_param']['buddies']) ? ' + AND id_member IN ({array_int:buddy_list})' : '') . ' + AND is_activated IN (1, 11) + LIMIT ' . ($smcFunc['strlen']($_REQUEST['search']) <= 2 ? '100' : '800'), + array( + 'buddy_list' => $user_info['buddies'], + 'search' => $_REQUEST['search'], + ) + ); + $xml_data = array( + 'items' => array( + 'identifier' => 'item', + 'children' => array(), + ), + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $row['real_name'] = strtr($row['real_name'], array('&' => '&', '<' => '<', '>' => '>', '"' => '"')); + + $xml_data['items']['children'][] = array( + 'attributes' => array( + 'id' => $row['id_member'], + ), + 'value' => $row['real_name'], + ); + } + $smcFunc['db_free_result']($request); + + return $xml_data; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Graphics.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Graphics.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1045 @@ + $memID)); + + $id_folder = !empty($modSettings['currentAttachmentUploadDir']) ? $modSettings['currentAttachmentUploadDir'] : 1; + $avatar_hash = empty($modSettings['custom_avatar_enabled']) ? getAttachmentFilename($destName, false, null, true) : ''; + $smcFunc['db_insert']('', + '{db_prefix}attachments', + array( + 'id_member' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-255', 'fileext' => 'string-8', 'size' => 'int', + 'id_folder' => 'int', + ), + array( + $memID, empty($modSettings['custom_avatar_enabled']) ? 0 : 1, $destName, $avatar_hash, $ext, 1, + $id_folder, + ), + array('id_attach') + ); + $attachID = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach'); + // Retain this globally in case the script wants it. + $modSettings['new_avatar_data'] = array( + 'id' => $attachID, + 'filename' => $destName, + 'type' => empty($modSettings['custom_avatar_enabled']) ? 0 : 1, + ); + + $destName = (empty($modSettings['custom_avatar_enabled']) ? (is_array($modSettings['attachmentUploadDir']) ? $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']] : $modSettings['attachmentUploadDir']) : $modSettings['custom_avatar_dir']) . '/' . $destName . '.tmp'; + + // Resize it. + if (!empty($modSettings['avatar_download_png'])) + $success = resizeImageFile($url, $destName, $max_width, $max_height, 3); + else + $success = resizeImageFile($url, $destName, $max_width, $max_height); + + // Remove the .tmp extension. + $destName = substr($destName, 0, -4); + + if ($success) + { + // Walk the right path. + if (!empty($modSettings['currentAttachmentUploadDir'])) + { + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + $path = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']]; + } + else + $path = $modSettings['attachmentUploadDir']; + + // Remove the .tmp extension from the attachment. + if (rename($destName . '.tmp', empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash)) + { + $destName = empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash; + list ($width, $height) = getimagesize($destName); + $mime_type = 'image/' . $ext; + + // Write filesize in the database. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET size = {int:filesize}, width = {int:width}, height = {int:height}, + mime_type = {string:mime_type} + WHERE id_attach = {int:current_attachment}', + array( + 'filesize' => filesize($destName), + 'width' => (int) $width, + 'height' => (int) $height, + 'current_attachment' => $attachID, + 'mime_type' => $mime_type, + ) + ); + return true; + } + else + return false; + } + else + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}attachments + WHERE id_attach = {int:current_attachment}', + array( + 'current_attachment' => $attachID, + ) + ); + + @unlink($destName . '.tmp'); + return false; + } +} + +function createThumbnail($source, $max_width, $max_height) +{ + global $modSettings; + + $destName = $source . '_thumb.tmp'; + + // Do the actual resize. + if (!empty($modSettings['attachment_thumb_png'])) + $success = resizeImageFile($source, $destName, $max_width, $max_height, 3); + else + $success = resizeImageFile($source, $destName, $max_width, $max_height); + + // Okay, we're done with the temporary stuff. + $destName = substr($destName, 0, -4); + + if ($success && @rename($destName . '.tmp', $destName)) + return true; + else + { + @unlink($destName . '.tmp'); + @touch($destName); + return false; + } +} + +function reencodeImage($fileName, $preferred_format = 0) +{ + // There is nothing we can do without GD, sorry! + if (!checkGD()) + return false; + + if (!resizeImageFile($fileName, $fileName . '.tmp', null, null, $preferred_format)) + { + if (file_exists($fileName . '.tmp')) + unlink($fileName . '.tmp'); + + return false; + } + + if (!unlink($fileName)) + return false; + + if (!rename($fileName . '.tmp', $fileName)) + return false; + + return true; +} + +function checkImageContents($fileName, $extensiveCheck = false) +{ + $fp = fopen($fileName, 'rb'); + if (!$fp) + fatal_lang_error('attach_timeout'); + + $prev_chunk = ''; + while (!feof($fp)) + { + $cur_chunk = fread($fp, 8192); + + // Though not exhaustive lists, better safe than sorry. + if (!empty($extensiveCheck)) + { + // Paranoid check. Some like it that way. + if (preg_match('~(iframe|\\<\\?|\\<%|html|eval|body|script\W|[CF]WS[\x01-\x0C])~i', $prev_chunk . $cur_chunk) === 1) + { + fclose($fp); + return false; + } + } + else + { + // Check for potential infection + if (preg_match('~(iframe|html|eval|body|script\W|[CF]WS[\x01-\x0C])~i', $prev_chunk . $cur_chunk) === 1) + { + fclose($fp); + return false; + } + } + $prev_chunk = $cur_chunk; + } + fclose($fp); + + return true; +} + +function checkGD() +{ + global $gd2; + + // Check to see if GD is installed and what version. + if (($extensionFunctions = get_extension_funcs('gd')) === false) + return false; + + // Also determine if GD2 is installed and store it in a global. + $gd2 = in_array('imagecreatetruecolor', $extensionFunctions) && function_exists('imagecreatetruecolor'); + + return true; +} + +function resizeImageFile($source, $destination, $max_width, $max_height, $preferred_format = 0) +{ + global $sourcedir; + + // Nothing to do without GD + if (!checkGD()) + return false; + + static $default_formats = array( + '1' => 'gif', + '2' => 'jpeg', + '3' => 'png', + '6' => 'bmp', + '15' => 'wbmp' + ); + + require_once($sourcedir . '/Subs-Package.php'); + @ini_set('memory_limit', '90M'); + + $success = false; + + // Get the image file, we have to work with something after all + $fp_destination = fopen($destination, 'wb'); + if ($fp_destination && substr($source, 0, 7) == 'http://') + { + $fileContents = fetch_web_data($source); + + fwrite($fp_destination, $fileContents); + fclose($fp_destination); + + $sizes = @getimagesize($destination); + } + elseif ($fp_destination) + { + $sizes = @getimagesize($source); + + $fp_source = fopen($source, 'rb'); + if ($fp_source !== false) + { + while (!feof($fp_source)) + fwrite($fp_destination, fread($fp_source, 8192)); + fclose($fp_source); + } + else + $sizes = array(-1, -1, -1); + fclose($fp_destination); + } + // We can't get to the file. + else + $sizes = array(-1, -1, -1); + + // Gif? That might mean trouble if gif support is not available. + if ($sizes[2] == 1 && !function_exists('imagecreatefromgif') && function_exists('imagecreatefrompng')) + { + // Download it to the temporary file... use the special gif library... and save as png. + if ($img = @gif_loadFile($destination) && gif_outputAsPng($img, $destination)) + $sizes[2] = 3; + } + + // A known and supported format? + if (isset($default_formats[$sizes[2]]) && function_exists('imagecreatefrom' . $default_formats[$sizes[2]])) + { + $imagecreatefrom = 'imagecreatefrom' . $default_formats[$sizes[2]]; + if ($src_img = @$imagecreatefrom($destination)) + { + resizeImage($src_img, $destination, imagesx($src_img), imagesy($src_img), $max_width === null ? imagesx($src_img) : $max_width, $max_height === null ? imagesy($src_img) : $max_height, true, $preferred_format); + $success = true; + } + } + + return $success; +} + +function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $max_height, $force_resize = false, $preferred_format = 0) +{ + global $gd2, $modSettings; + + // Without GD, no image resizing at all. + if (!checkGD()) + return false; + + $success = false; + + // Determine whether to resize to max width or to max height (depending on the limits.) + if (!empty($max_width) || !empty($max_height)) + { + if (!empty($max_width) && (empty($max_height) || $src_height * $max_width / $src_width <= $max_height)) + { + $dst_width = $max_width; + $dst_height = floor($src_height * $max_width / $src_width); + } + elseif (!empty($max_height)) + { + $dst_width = floor($src_width * $max_height / $src_height); + $dst_height = $max_height; + } + + // Don't bother resizing if it's already smaller... + if (!empty($dst_width) && !empty($dst_height) && ($dst_width < $src_width || $dst_height < $src_height || $force_resize)) + { + // (make a true color image, because it just looks better for resizing.) + if ($gd2) + { + $dst_img = imagecreatetruecolor($dst_width, $dst_height); + + // Deal nicely with a PNG - because we can. + if ((!empty($preferred_format)) && ($preferred_format == 3)) + { + imagealphablending($dst_img, false); + if (function_exists('imagesavealpha')) + imagesavealpha($dst_img, true); + } + } + else + $dst_img = imagecreate($dst_width, $dst_height); + + // Resize it! + if ($gd2) + imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height); + else + imagecopyresamplebicubic($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height); + } + else + $dst_img = $src_img; + } + else + $dst_img = $src_img; + + // Save the image as ... + if (!empty($preferred_format) && ($preferred_format == 3) && function_exists('imagepng')) + $success = imagepng($dst_img, $destName); + elseif (!empty($preferred_format) && ($preferred_format == 1) && function_exists('imagegif')) + $success = imagegif($dst_img, $destName); + elseif (function_exists('imagejpeg')) + $success = imagejpeg($dst_img, $destName); + + // Free the memory. + imagedestroy($src_img); + if ($dst_img != $src_img) + imagedestroy($dst_img); + + return $success; +} + +function imagecopyresamplebicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) +{ + $palsize = imagecolorstotal($src_img); + for ($i = 0; $i < $palsize; $i++) + { + $colors = imagecolorsforindex($src_img, $i); + imagecolorallocate($dst_img, $colors['red'], $colors['green'], $colors['blue']); + } + + $scaleX = ($src_w - 1) / $dst_w; + $scaleY = ($src_h - 1) / $dst_h; + + $scaleX2 = (int) $scaleX / 2; + $scaleY2 = (int) $scaleY / 2; + + for ($j = $src_y; $j < $dst_h; $j++) + { + $sY = (int) $j * $scaleY; + $y13 = $sY + $scaleY2; + + for ($i = $src_x; $i < $dst_w; $i++) + { + $sX = (int) $i * $scaleX; + $x34 = $sX + $scaleX2; + + $color1 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $y13)); + $color2 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $sY)); + $color3 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $y13)); + $color4 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $sY)); + + $red = ($color1['red'] + $color2['red'] + $color3['red'] + $color4['red']) / 4; + $green = ($color1['green'] + $color2['green'] + $color3['green'] + $color4['green']) / 4; + $blue = ($color1['blue'] + $color2['blue'] + $color3['blue'] + $color4['blue']) / 4; + + $color = imagecolorresolve($dst_img, $red, $green, $blue); + if ($color == -1) + { + if ($palsize++ < 256) + imagecolorallocate($dst_img, $red, $green, $blue); + $color = imagecolorclosest($dst_img, $red, $green, $blue); + } + + imagesetpixel($dst_img, $i + $dst_x - $src_x, $j + $dst_y - $src_y, $color); + } + } +} + +if (!function_exists('imagecreatefrombmp')) +{ + function imagecreatefrombmp($filename) + { + global $gd2; + + $fp = fopen($filename, 'rb'); + + $errors = error_reporting(0); + + $header = unpack('vtype/Vsize/Vreserved/Voffset', fread($fp, 14)); + $info = unpack('Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vncolor/Vcolorimportant', fread($fp, 40)); + + if ($header['type'] != 0x4D42) + false; + + if ($gd2) + $dst_img = imagecreatetruecolor($info['width'], $info['height']); + else + $dst_img = imagecreate($info['width'], $info['height']); + + $palette_size = $header['offset'] - 54; + $info['ncolor'] = $palette_size / 4; + + $palette = array(); + + $palettedata = fread($fp, $palette_size); + $n = 0; + for ($j = 0; $j < $palette_size; $j++) + { + $b = ord($palettedata{$j++}); + $g = ord($palettedata{$j++}); + $r = ord($palettedata{$j++}); + + $palette[$n++] = imagecolorallocate($dst_img, $r, $g, $b); + } + + $scan_line_size = ($info['bits'] * $info['width'] + 7) >> 3; + $scan_line_align = $scan_line_size & 3 ? 4 - ($scan_line_size & 3) : 0; + + for ($y = 0, $l = $info['height'] - 1; $y < $info['height']; $y++, $l--) + { + fseek($fp, $header['offset'] + ($scan_line_size + $scan_line_align) * $l); + $scan_line = fread($fp, $scan_line_size); + + if (strlen($scan_line) < $scan_line_size) + continue; + + if ($info['bits'] == 32) + { + $x = 0; + for ($j = 0; $j < $scan_line_size; $x++) + { + $b = ord($scan_line{$j++}); + $g = ord($scan_line{$j++}); + $r = ord($scan_line{$j++}); + $j++; + + $color = imagecolorexact($dst_img, $r, $g, $b); + if ($color == -1) + { + $color = imagecolorallocate($dst_img, $r, $g, $b); + + // Gah! Out of colors? Stupid GD 1... try anyhow. + if ($color == -1) + $color = imagecolorclosest($dst_img, $r, $g, $b); + } + + imagesetpixel($dst_img, $x, $y, $color); + } + } + elseif ($info['bits'] == 24) + { + $x = 0; + for ($j = 0; $j < $scan_line_size; $x++) + { + $b = ord($scan_line{$j++}); + $g = ord($scan_line{$j++}); + $r = ord($scan_line{$j++}); + + $color = imagecolorexact($dst_img, $r, $g, $b); + if ($color == -1) + { + $color = imagecolorallocate($dst_img, $r, $g, $b); + + // Gah! Out of colors? Stupid GD 1... try anyhow. + if ($color == -1) + $color = imagecolorclosest($dst_img, $r, $g, $b); + } + + imagesetpixel($dst_img, $x, $y, $color); + } + } + elseif ($info['bits'] == 16) + { + $x = 0; + for ($j = 0; $j < $scan_line_size; $x++) + { + $b1 = ord($scan_line{$j++}); + $b2 = ord($scan_line{$j++}); + + $word = $b2 * 256 + $b1; + + $b = (($word & 31) * 255) / 31; + $g = ((($word >> 5) & 31) * 255) / 31; + $r = ((($word >> 10) & 31) * 255) / 31; + + // Scale the image colors up properly. + $color = imagecolorexact($dst_img, $r, $g, $b); + if ($color == -1) + { + $color = imagecolorallocate($dst_img, $r, $g, $b); + + // Gah! Out of colors? Stupid GD 1... try anyhow. + if ($color == -1) + $color = imagecolorclosest($dst_img, $r, $g, $b); + } + + imagesetpixel($dst_img, $x, $y, $color); + } + } + elseif ($info['bits'] == 8) + { + $x = 0; + for ($j = 0; $j < $scan_line_size; $x++) + imagesetpixel($dst_img, $x, $y, $palette[ord($scan_line{$j++})]); + } + elseif ($info['bits'] == 4) + { + $x = 0; + for ($j = 0; $j < $scan_line_size; $x++) + { + $byte = ord($scan_line{$j++}); + + imagesetpixel($dst_img, $x, $y, $palette[(int) ($byte / 16)]); + if (++$x < $info['width']) + imagesetpixel($dst_img, $x, $y, $palette[$byte & 15]); + } + } + else + { + // Sorry, I'm just not going to do monochrome :P. + } + } + + fclose($fp); + + error_reporting($errors); + + return $dst_img; + } +} + +function gif_loadFile($lpszFileName, $iIndex = 0) +{ + // The classes needed are in this file. + loadClassFile('Class-Graphics.php'); + $gif = new gif_file(); + + if (!$gif->loadFile($lpszFileName, $iIndex)) + return false; + + return $gif; +} + +function gif_outputAsPng($gif, $lpszFileName, $background_color = -1) +{ + if (!isset($gif) || @get_class($gif) != 'cgif' || !$gif->loaded || $lpszFileName == '') + return false; + + $fd = $gif->get_png_data($background_color); + if (strlen($fd) <= 0) + return false; + + if (!($fh = @fopen($lpszFileName, 'wb'))) + return false; + + @fwrite($fh, $fd, strlen($fd)); + @fflush($fh); + @fclose($fh); + + return true; +} + +// Create the image for the visual verification code. +function showCodeImage($code) +{ + global $settings, $user_info, $modSettings; + + /* + Note: The higher the value of visual_verification_type the harder the verification is - from 0 as disabled through to 4 as "Very hard". + */ + + // What type are we going to be doing? + $imageType = $modSettings['visual_verification_type']; + // Special case to allow the admin center to show samples. + if ($user_info['is_admin'] && isset($_GET['type'])) + $imageType = (int) $_GET['type']; + + // Some quick references for what we do. + // Do we show no, low or high noise? + $noiseType = $imageType == 3 ? 'low' : ($imageType == 4 ? 'high' : ($imageType == 5 ? 'extreme' : 'none')); + // Can we have more than one font in use? + $varyFonts = $imageType > 3 ? true : false; + // Just a plain white background? + $simpleBGColor = $imageType < 3 ? true : false; + // Plain black foreground? + $simpleFGColor = $imageType == 0 ? true : false; + // High much to rotate each character. + $rotationType = $imageType == 1 ? 'none' : ($imageType > 3 ? 'low' : 'high'); + // Do we show some characters inversed? + $showReverseChars = $imageType > 3 ? true : false; + // Special case for not showing any characters. + $disableChars = $imageType == 0 ? true : false; + // What do we do with the font colors. Are they one color, close to one color or random? + $fontColorType = $imageType == 1 ? 'plain' : ($imageType > 3 ? 'random' : 'cyclic'); + // Are the fonts random sizes? + $fontSizeRandom = $imageType > 3 ? true : false; + // How much space between characters? + $fontHorSpace = $imageType > 3 ? 'high' : ($imageType == 1 ? 'medium' : 'minus'); + // Where do characters sit on the image? (Fixed position or random/very random) + $fontVerPos = $imageType == 1 ? 'fixed' : ($imageType > 3 ? 'vrandom' : 'random'); + // Make font semi-transparent? + $fontTrans = $imageType == 2 || $imageType == 3 ? true : false; + // Give the image a border? + $hasBorder = $simpleBGColor; + + // Is this GD2? Needed for pixel size. + $testGD = get_extension_funcs('gd'); + $gd2 = in_array('imagecreatetruecolor', $testGD) && function_exists('imagecreatetruecolor'); + unset($testGD); + + // The amount of pixels inbetween characters. + $character_spacing = 1; + + // What color is the background - generally white unless we're on "hard". + if ($simpleBGColor) + $background_color = array(255, 255, 255); + else + $background_color = isset($settings['verification_background']) ? $settings['verification_background'] : array(236, 237, 243); + + // The color of the characters shown (red, green, blue). + if ($simpleFGColor) + $foreground_color = array(0, 0, 0); + else + { + $foreground_color = array(64, 101, 136); + + // Has the theme author requested a custom color? + if (isset($settings['verification_foreground'])) + $foreground_color = $settings['verification_foreground']; + } + + if (!is_dir($settings['default_theme_dir'] . '/fonts')) + return false; + + // Get a list of the available fonts. + $font_dir = dir($settings['default_theme_dir'] . '/fonts'); + $font_list = array(); + $ttfont_list = array(); + while ($entry = $font_dir->read()) + { + if (preg_match('~^(.+)\.gdf$~', $entry, $matches) === 1) + $font_list[] = $entry; + elseif (preg_match('~^(.+)\.ttf$~', $entry, $matches) === 1) + $ttfont_list[] = $entry; + } + + if (empty($font_list)) + return false; + + // For non-hard things don't even change fonts. + if (!$varyFonts) + { + $font_list = array($font_list[0]); + // Try use Screenge if we can - it looks good! + if (in_array('Screenge.ttf', $ttfont_list)) + $ttfont_list = array('Screenge.ttf'); + else + $ttfont_list = empty($ttfont_list) ? array() : array($ttfont_list[0]); + + } + + // Create a list of characters to be shown. + $characters = array(); + $loaded_fonts = array(); + for ($i = 0; $i < strlen($code); $i++) + { + $characters[$i] = array( + 'id' => $code{$i}, + 'font' => array_rand($font_list), + ); + + $loaded_fonts[$characters[$i]['font']] = null; + } + + // Load all fonts and determine the maximum font height. + foreach ($loaded_fonts as $font_index => $dummy) + $loaded_fonts[$font_index] = imageloadfont($settings['default_theme_dir'] . '/fonts/' . $font_list[$font_index]); + + // Determine the dimensions of each character. + $total_width = $character_spacing * strlen($code) + 20; + $max_height = 0; + foreach ($characters as $char_index => $character) + { + $characters[$char_index]['width'] = imagefontwidth($loaded_fonts[$character['font']]); + $characters[$char_index]['height'] = imagefontheight($loaded_fonts[$character['font']]); + + $max_height = max($characters[$char_index]['height'] + 5, $max_height); + $total_width += $characters[$char_index]['width']; + } + + // Create an image. + $code_image = $gd2 ? imagecreatetruecolor($total_width, $max_height) : imagecreate($total_width, $max_height); + + // Draw the background. + $bg_color = imagecolorallocate($code_image, $background_color[0], $background_color[1], $background_color[2]); + imagefilledrectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $bg_color); + + // Randomize the foreground color a little. + for ($i = 0; $i < 3; $i++) + $foreground_color[$i] = mt_rand(max($foreground_color[$i] - 3, 0), min($foreground_color[$i] + 3, 255)); + $fg_color = imagecolorallocate($code_image, $foreground_color[0], $foreground_color[1], $foreground_color[2]); + + // Color for the dots. + for ($i = 0; $i < 3; $i++) + $dotbgcolor[$i] = $background_color[$i] < $foreground_color[$i] ? mt_rand(0, max($foreground_color[$i] - 20, 0)) : mt_rand(min($foreground_color[$i] + 20, 255), 255); + $randomness_color = imagecolorallocate($code_image, $dotbgcolor[0], $dotbgcolor[1], $dotbgcolor[2]); + + // Some squares/rectanges for new extreme level + if ($noiseType == 'extreme') + { + for ($i = 0; $i < rand(1, 5); $i++) + { + $x1 = rand(0, $total_width / 4); + $x2 = $x1 + round(rand($total_width / 4, $total_width)); + $y1 = rand(0, $max_height); + $y2 = $y1 + round(rand(0, $max_height / 3)); + imagefilledrectangle($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color); + } + } + + // Fill in the characters. + if (!$disableChars) + { + $cur_x = 0; + foreach ($characters as $char_index => $character) + { + // Can we use true type fonts? + $can_do_ttf = function_exists('imagettftext'); + + // How much rotation will we give? + if ($rotationType == 'none') + $angle = 0; + else + $angle = mt_rand(-100, 100) / ($rotationType == 'high' ? 6 : 10); + + // What color shall we do it? + if ($fontColorType == 'cyclic') + { + // Here we'll pick from a set of acceptance types. + $colors = array( + array(10, 120, 95), + array(46, 81, 29), + array(4, 22, 154), + array(131, 9, 130), + array(0, 0, 0), + array(143, 39, 31), + ); + if (!isset($last_index)) + $last_index = -1; + $new_index = $last_index; + while ($last_index == $new_index) + $new_index = mt_rand(0, count($colors) - 1); + $char_fg_color = $colors[$new_index]; + $last_index = $new_index; + } + elseif ($fontColorType == 'random') + $char_fg_color = array(mt_rand(max($foreground_color[0] - 2, 0), $foreground_color[0]), mt_rand(max($foreground_color[1] - 2, 0), $foreground_color[1]), mt_rand(max($foreground_color[2] - 2, 0), $foreground_color[2])); + else + $char_fg_color = array($foreground_color[0], $foreground_color[1], $foreground_color[2]); + + if (!empty($can_do_ttf)) + { + // GD2 handles font size differently. + if ($fontSizeRandom) + $font_size = $gd2 ? mt_rand(17, 19) : mt_rand(18, 25); + else + $font_size = $gd2 ? 18 : 24; + + // Work out the sizes - also fix the character width cause TTF not quite so wide! + $font_x = $fontHorSpace == 'minus' && $cur_x > 0 ? $cur_x - 3 : $cur_x + 5; + $font_y = $max_height - ($fontVerPos == 'vrandom' ? mt_rand(2, 8) : ($fontVerPos == 'random' ? mt_rand(3, 5) : 5)); + + // What font face? + if (!empty($ttfont_list)) + $fontface = $settings['default_theme_dir'] . '/fonts/' . $ttfont_list[mt_rand(0, count($ttfont_list) - 1)]; + + // What color are we to do it in? + $is_reverse = $showReverseChars ? mt_rand(0, 1) : false; + $char_color = function_exists('imagecolorallocatealpha') && $fontTrans ? imagecolorallocatealpha($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2], 50) : imagecolorallocate($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2]); + + $fontcord = @imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $char_color, $fontface, $character['id']); + if (empty($fontcord)) + $can_do_ttf = false; + elseif ($is_reverse) + { + imagefilledpolygon($code_image, $fontcord, 4, $fg_color); + // Put the character back! + imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $randomness_color, $fontface, $character['id']); + } + + if ($can_do_ttf) + $cur_x = max($fontcord[2], $fontcord[4]) + ($angle == 0 ? 0 : 3); + } + + if (!$can_do_ttf) + { + // Rotating the characters a little... + if (function_exists('imagerotate')) + { + $char_image = $gd2 ? imagecreatetruecolor($character['width'], $character['height']) : imagecreate($character['width'], $character['height']); + $char_bgcolor = imagecolorallocate($char_image, $background_color[0], $background_color[1], $background_color[2]); + imagefilledrectangle($char_image, 0, 0, $character['width'] - 1, $character['height'] - 1, $char_bgcolor); + imagechar($char_image, $loaded_fonts[$character['font']], 0, 0, $character['id'], imagecolorallocate($char_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2])); + $rotated_char = imagerotate($char_image, mt_rand(-100, 100) / 10, $char_bgcolor); + imagecopy($code_image, $rotated_char, $cur_x, 0, 0, 0, $character['width'], $character['height']); + imagedestroy($rotated_char); + imagedestroy($char_image); + } + + // Sorry, no rotation available. + else + imagechar($code_image, $loaded_fonts[$character['font']], $cur_x, floor(($max_height - $character['height']) / 2), $character['id'], imagecolorallocate($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2])); + $cur_x += $character['width'] + $character_spacing; + } + } + } + // If disabled just show a cross. + else + { + imageline($code_image, 0, 0, $total_width, $max_height, $fg_color); + imageline($code_image, 0, $max_height, $total_width, 0, $fg_color); + } + + // Make the background color transparent on the hard image. + if (!$simpleBGColor) + imagecolortransparent($code_image, $bg_color); + if ($hasBorder) + imagerectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $fg_color); + + // Add some noise to the background? + if ($noiseType != 'none') + { + for ($i = mt_rand(0, 2); $i < $max_height; $i += mt_rand(1, 2)) + for ($j = mt_rand(0, 10); $j < $total_width; $j += mt_rand(1, 10)) + imagesetpixel($code_image, $j, $i, mt_rand(0, 1) ? $fg_color : $randomness_color); + + // Put in some lines too? + if ($noiseType != 'extreme') + { + $num_lines = $noiseType == 'high' ? mt_rand(3, 7) : mt_rand(2, 5); + for ($i = 0; $i < $num_lines; $i++) + { + if (mt_rand(0, 1)) + { + $x1 = mt_rand(0, $total_width); + $x2 = mt_rand(0, $total_width); + $y1 = 0; $y2 = $max_height; + } + else + { + $y1 = mt_rand(0, $max_height); + $y2 = mt_rand(0, $max_height); + $x1 = 0; $x2 = $total_width; + } + imagesetthickness($code_image, mt_rand(1, 2)); + imageline($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color); + } + } + else + { + // Put in some ellipse + $num_ellipse = $noiseType == 'extreme' ? mt_rand(6, 12) : mt_rand(2, 6); + for ($i = 0; $i < $num_ellipse; $i++) + { + $x1 = round(rand(($total_width / 4) * -1, $total_width + ($total_width / 4))); + $x2 = round(rand($total_width / 2, 2 * $total_width)); + $y1 = round(rand(($max_height / 4) * -1, $max_height + ($max_height / 4))); + $y2 = round(rand($max_height / 2, 2 * $max_height)); + imageellipse($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color); + } + } + } + + // Show the image. + if (function_exists('imagegif')) + { + header('Content-type: image/gif'); + imagegif($code_image); + } + else + { + header('Content-type: image/png'); + imagepng($code_image); + } + + // Bail out. + imagedestroy($code_image); + die(); +} + +// Create a letter for the visual verification code. +function showLetterImage($letter) +{ + global $settings; + + if (!is_dir($settings['default_theme_dir'] . '/fonts')) + return false; + + // Get a list of the available font directories. + $font_dir = dir($settings['default_theme_dir'] . '/fonts'); + $font_list = array(); + while ($entry = $font_dir->read()) + if ($entry[0] !== '.' && is_dir($settings['default_theme_dir'] . '/fonts/' . $entry) && file_exists($settings['default_theme_dir'] . '/fonts/' . $entry . '.gdf')) + $font_list[] = $entry; + + if (empty($font_list)) + return false; + + // Pick a random font. + $random_font = $font_list[array_rand($font_list)]; + + // Check if the given letter exists. + if (!file_exists($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.gif')) + return false; + + // Include it! + header('Content-type: image/gif'); + include($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.gif'); + + // Nothing more to come. + die(); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-List.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-List.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,235 @@ + $_REQUEST[$request_var_sort], + 'desc' => isset($_REQUEST[$request_var_desc]) && isset($listOptions['columns'][$_REQUEST[$request_var_sort]]['sort']['reverse']), + ); + else + $list_context['sort'] = array( + 'id' => $listOptions['default_sort_col'], + 'desc' => (!empty($listOptions['default_sort_dir']) && $listOptions['default_sort_dir'] == 'desc') || (!empty($listOptions['columns'][$listOptions['default_sort_col']]['sort']['default']) && substr($listOptions['columns'][$listOptions['default_sort_col']]['sort']['default'], -4, 4) == 'desc') ? true : false, + ); + + // Set the database column sort. + $sort = $listOptions['columns'][$list_context['sort']['id']]['sort'][$list_context['sort']['desc'] ? 'reverse' : 'default']; + } + + $list_context['start_var_name'] = isset($listOptions['start_var_name']) ? $listOptions['start_var_name'] : 'start'; + // In some cases the full list must be shown, regardless of the amount of items. + if (empty($listOptions['items_per_page'])) + { + $list_context['start'] = 0; + $list_context['items_per_page'] = 0; + } + // With items per page set, calculate total number of items and page index. + else + { + // First get an impression of how many items to expect. + if (isset($listOptions['get_count']['file'])) + require_once($listOptions['get_count']['file']); + $list_context['total_num_items'] = call_user_func_array($listOptions['get_count']['function'], empty($listOptions['get_count']['params']) ? array() : $listOptions['get_count']['params']); + + // Default the start to the beginning...sounds logical. + $list_context['start'] = isset($_REQUEST[$list_context['start_var_name']]) ? (int) $_REQUEST[$list_context['start_var_name']] : 0; + $list_context['items_per_page'] = $listOptions['items_per_page']; + + // Then create a page index. + $list_context['page_index'] = constructPageIndex($listOptions['base_href'] . (empty($list_context['sort']) ? '' : ';' . $request_var_sort . '=' . $list_context['sort']['id'] . ($list_context['sort']['desc'] ? ';' . $request_var_desc : '')) . ($list_context['start_var_name'] != 'start' ? ';' . $list_context['start_var_name'] . '=%1$d' : ''), $list_context['start'], $list_context['total_num_items'], $list_context['items_per_page'], $list_context['start_var_name'] != 'start'); + } + + // Prepare the headers of the table. + $list_context['headers'] = array(); + foreach ($listOptions['columns'] as $column_id => $column) + $list_context['headers'][] = array( + 'id' => $column_id, + 'label' => isset($column['header']['eval']) ? eval($column['header']['eval']) : (isset($column['header']['value']) ? $column['header']['value'] : ''), + 'href' => empty($listOptions['default_sort_col']) || empty($column['sort']) ? '' : $listOptions['base_href'] . ';' . $request_var_sort . '=' . $column_id . ($column_id === $list_context['sort']['id'] && !$list_context['sort']['desc'] && isset($column['sort']['reverse']) ? ';' . $request_var_desc : '') . (empty($list_context['start']) ? '' : ';' . $list_context['start_var_name'] . '=' . $list_context['start']), + 'sort_image' => empty($listOptions['default_sort_col']) || empty($column['sort']) || $column_id !== $list_context['sort']['id'] ? null : ($list_context['sort']['desc'] ? 'down' : 'up'), + 'class' => isset($column['header']['class']) ? $column['header']['class'] : '', + 'style' => isset($column['header']['style']) ? $column['header']['style'] : '', + 'colspan' => isset($column['header']['colspan']) ? $column['header']['colspan'] : '', + ); + + // We know the amount of columns, might be useful for the template. + $list_context['num_columns'] = count($listOptions['columns']); + $list_context['width'] = isset($listOptions['width']) ? $listOptions['width'] : '0'; + + // Get the file with the function for the item list. + if (isset($listOptions['get_items']['file'])) + require_once($listOptions['get_items']['file']); + + // Call the function and include which items we want and in what order. + $list_items = call_user_func_array($listOptions['get_items']['function'], array_merge(array($list_context['start'], $list_context['items_per_page'], $sort), empty($listOptions['get_items']['params']) ? array() : $listOptions['get_items']['params'])); + + // Loop through the list items to be shown and construct the data values. + $list_context['rows'] = array(); + foreach ($list_items as $item_id => $list_item) + { + $cur_row = array(); + foreach ($listOptions['columns'] as $column_id => $column) + { + $cur_data = array(); + + // A value straight from the database? + if (isset($column['data']['db'])) + $cur_data['value'] = $list_item[$column['data']['db']]; + + // Take the value from the database and make it HTML safe. + elseif (isset($column['data']['db_htmlsafe'])) + $cur_data['value'] = htmlspecialchars($list_item[$column['data']['db_htmlsafe']]); + + // Using sprintf is probably the most readable way of injecting data. + elseif (isset($column['data']['sprintf'])) + { + $params = array(); + foreach ($column['data']['sprintf']['params'] as $sprintf_param => $htmlsafe) + $params[] = $htmlsafe ? htmlspecialchars($list_item[$sprintf_param]) : $list_item[$sprintf_param]; + $cur_data['value'] = vsprintf($column['data']['sprintf']['format'], $params); + } + + // The most flexible way probably is applying a custom function. + elseif (isset($column['data']['function'])) + $cur_data['value'] = $column['data']['function']($list_item); + + // A modified value (inject the database values). + elseif (isset($column['data']['eval'])) + $cur_data['value'] = eval(preg_replace('~%([a-zA-Z0-9\-_]+)%~', '$list_item[\'$1\']', $column['data']['eval'])); + + // A literal value. + elseif (isset($column['data']['value'])) + $cur_data['value'] = $column['data']['value']; + + // Empty value. + else + $cur_data['value'] = ''; + + // Allow for basic formatting. + if (!empty($column['data']['comma_format'])) + $cur_data['value'] = comma_format($cur_data['value']); + elseif (!empty($column['data']['timeformat'])) + $cur_data['value'] = timeformat($cur_data['value']); + + // Set a style class for this column? + if (isset($column['data']['class'])) + $cur_data['class'] = $column['data']['class']; + + // Fully customized styling for the cells in this column only. + if (isset($column['data']['style'])) + $cur_data['style'] = $column['data']['style']; + + // Add the data cell properties to the current row. + $cur_row[$column_id] = $cur_data; + } + + // Insert the row into the list. + $list_context['rows'][$item_id] = $cur_row; + } + + // The title is currently optional. + if (isset($listOptions['title'])) + $list_context['title'] = $listOptions['title']; + + // In case there's a form, share it with the template context. + if (isset($listOptions['form'])) + { + $list_context['form'] = $listOptions['form']; + + if (!isset($list_context['form']['hidden_fields'])) + $list_context['form']['hidden_fields'] = array(); + + // Always add a session check field. + $list_context['form']['hidden_fields'][$context['session_var']] = $context['session_id']; + + // Include the starting page as hidden field? + if (!empty($list_context['form']['include_start']) && !empty($list_context['start'])) + $list_context['form']['hidden_fields'][$list_context['start_var_name']] = $list_context['start']; + + // If sorting needs to be the same after submitting, add the parameter. + if (!empty($list_context['form']['include_sort']) && !empty($list_context['sort'])) + { + $list_context['form']['hidden_fields']['sort'] = $list_context['sort']['id']; + if ($list_context['sort']['desc']) + $list_context['form']['hidden_fields']['desc'] = 1; + } + } + + // Wanna say something nice in case there are no items? + if (isset($listOptions['no_items_label'])) + { + $list_context['no_items_label'] = $listOptions['no_items_label']; + $list_context['no_items_align'] = isset($listOptions['no_items_align']) ? $listOptions['no_items_align'] : ''; + } + + // A list can sometimes need a few extra rows above and below. + if (isset($listOptions['additional_rows'])) + { + $list_context['additional_rows'] = array(); + foreach ($listOptions['additional_rows'] as $row) + { + if (empty($row)) + continue; + + // Supported row positions: top_of_list, after_title, + // above_column_headers, below_table_data, bottom_of_list. + if (!isset($list_context['additional_rows'][$row['position']])) + $list_context['additional_rows'][$row['position']] = array(); + $list_context['additional_rows'][$row['position']][] = $row; + } + } + + // Add an option for inline JavaScript. + if (isset($listOptions['javascript'])) + $list_context['javascript'] = $listOptions['javascript']; + + // We want a menu. + if (isset($listOptions['list_menu'])) + $list_context['list_menu'] = $listOptions['list_menu']; + + // Make sure the template is loaded. + loadTemplate('GenericList'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Membergroups.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Membergroups.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,757 @@ + $value) + $groups[$key] = (int) $value; + } + + // Some groups are protected (guests, administrators, moderators, newbies). + $protected_groups = array(-1, 0, 1, 3, 4); + + // There maybe some others as well. + if (!allowedTo('admin_forum')) + { + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}membergroups + WHERE group_type = {int:is_protected}', + array( + 'is_protected' => 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $protected_groups[] = $row['id_group']; + $smcFunc['db_free_result']($request); + } + + // Make sure they don't delete protected groups! + $groups = array_diff($groups, array_unique($protected_groups)); + if (empty($groups)) + return false; + + // Log the deletion. + $request = $smcFunc['db_query']('', ' + SELECT group_name + FROM {db_prefix}membergroups + WHERE id_group IN ({array_int:group_list})', + array( + 'group_list' => $groups, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + logAction('delete_group', array('group' => $row['group_name']), 'admin'); + $smcFunc['db_free_result']($request); + + // Remove the membergroups themselves. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}membergroups + WHERE id_group IN ({array_int:group_list})', + array( + 'group_list' => $groups, + ) + ); + + // Remove the permissions of the membergroups. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}permissions + WHERE id_group IN ({array_int:group_list})', + array( + 'group_list' => $groups, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}board_permissions + WHERE id_group IN ({array_int:group_list})', + array( + 'group_list' => $groups, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}group_moderators + WHERE id_group IN ({array_int:group_list})', + array( + 'group_list' => $groups, + ) + ); + + // Delete any outstanding requests. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_group_requests + WHERE id_group IN ({array_int:group_list})', + array( + 'group_list' => $groups, + ) + ); + + // Update the primary groups of members. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_group = {int:regular_group} + WHERE id_group IN ({array_int:group_list})', + array( + 'group_list' => $groups, + 'regular_group' => 0, + ) + ); + + // Update any inherited groups (Lose inheritance). + $smcFunc['db_query']('', ' + UPDATE {db_prefix}membergroups + SET id_parent = {int:uninherited} + WHERE id_parent IN ({array_int:group_list})', + array( + 'group_list' => $groups, + 'uninherited' => -2, + ) + ); + + // Update the additional groups of members. + $request = $smcFunc['db_query']('', ' + SELECT id_member, additional_groups + FROM {db_prefix}members + WHERE FIND_IN_SET({raw:additional_groups_explode}, additional_groups) != 0', + array( + 'additional_groups_explode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $groups), + ) + ); + $updates = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $updates[$row['additional_groups']][] = $row['id_member']; + $smcFunc['db_free_result']($request); + + foreach ($updates as $additional_groups => $memberArray) + updateMemberData($memberArray, array('additional_groups' => implode(',', array_diff(explode(',', $additional_groups), $groups)))); + + // No boards can provide access to these membergroups anymore. + $request = $smcFunc['db_query']('', ' + SELECT id_board, member_groups + FROM {db_prefix}boards + WHERE FIND_IN_SET({raw:member_groups_explode}, member_groups) != 0', + array( + 'member_groups_explode' => implode(', member_groups) != 0 OR FIND_IN_SET(', $groups), + ) + ); + $updates = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $updates[$row['member_groups']][] = $row['id_board']; + $smcFunc['db_free_result']($request); + + foreach ($updates as $member_groups => $boardArray) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET member_groups = {string:member_groups} + WHERE id_board IN ({array_int:board_lists})', + array( + 'board_lists' => $boardArray, + 'member_groups' => implode(',', array_diff(explode(',', $member_groups), $groups)), + ) + ); + + // Recalculate the post groups, as they likely changed. + updateStats('postgroups'); + + // Make a note of the fact that the cache may be wrong. + $settings_update = array('settings_updated' => time()); + // Have we deleted the spider group? + if (isset($modSettings['spider_group']) && in_array($modSettings['spider_group'], $groups)) + $settings_update['spider_group'] = 0; + + updateSettings($settings_update); + + // It was a success. + return true; +} + +// Remove one or more members from one or more membergroups. +function removeMembersFromGroups($members, $groups = null, $permissionCheckDone = false) +{ + global $smcFunc, $user_info, $modSettings; + + // You're getting nowhere without this permission, unless of course you are the group's moderator. + if (!$permissionCheckDone) + isAllowedTo('manage_membergroups'); + + // Assume something will happen. + updateSettings(array('settings_updated' => time())); + + // Cleaning the input. + if (!is_array($members)) + $members = array((int) $members); + else + { + $members = array_unique($members); + + // Cast the members to integer. + foreach ($members as $key => $value) + $members[$key] = (int) $value; + } + + // Before we get started, let's check we won't leave the admin group empty! + if ($groups === null || $groups == 1 || (is_array($groups) && in_array(1, $groups))) + { + $admins = array(); + listMembergroupMembers_Href($admins, 1); + + // Remove any admins if there are too many. + $non_changing_admins = array_diff(array_keys($admins), $members); + + if (empty($non_changing_admins)) + $members = array_diff($members, array_keys($admins)); + } + + // Just in case. + if (empty($members)) + return false; + elseif ($groups === null) + { + // Wanna remove all groups from these members? That's easy. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET + id_group = {int:regular_member}, + additional_groups = {string:blank_string} + WHERE id_member IN ({array_int:member_list})' . (allowedTo('admin_forum') ? '' : ' + AND id_group != {int:admin_group} + AND FIND_IN_SET({int:admin_group}, additional_groups) = 0'), + array( + 'member_list' => $members, + 'regular_member' => 0, + 'admin_group' => 1, + 'blank_string' => '', + ) + ); + + updateStats('postgroups', $members); + + // Log what just happened. + foreach ($members as $member) + logAction('removed_all_groups', array('member' => $member), 'admin'); + + return true; + } + elseif (!is_array($groups)) + $groups = array((int) $groups); + else + { + $groups = array_unique($groups); + + // Make sure all groups are integer. + foreach ($groups as $key => $value) + $groups[$key] = (int) $value; + } + + // Fetch a list of groups members cannot be assigned to explicitely, and the group names of the ones we want. + $implicitGroups = array(-1, 0, 3); + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name, min_posts + FROM {db_prefix}membergroups + WHERE id_group IN ({array_int:group_list})', + array( + 'group_list' => $groups, + ) + ); + $group_names = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['min_posts'] != -1) + $implicitGroups[] = $row['id_group']; + else + $group_names[$row['id_group']] = $row['group_name']; + } + $smcFunc['db_free_result']($request); + + // Now get rid of those groups. + $groups = array_diff($groups, $implicitGroups); + + // Don't forget the protected groups. + if (!allowedTo('admin_forum')) + { + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}membergroups + WHERE group_type = {int:is_protected}', + array( + 'is_protected' => 1, + ) + ); + $protected_groups = array(1); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $protected_groups[] = $row['id_group']; + $smcFunc['db_free_result']($request); + + // If you're not an admin yourself, you can't touch protected groups! + $groups = array_diff($groups, array_unique($protected_groups)); + } + + // Only continue if there are still groups and members left. + if (empty($groups) || empty($members)) + return false; + + // First, reset those who have this as their primary group - this is the easy one. + $log_inserts = array(); + $request = $smcFunc['db_query']('', ' + SELECT id_member, id_group + FROM {db_prefix}members AS members + WHERE id_group IN ({array_int:group_list}) + AND id_member IN ({array_int:member_list})', + array( + 'group_list' => $groups, + 'member_list' => $members, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $log_inserts[] = array( + time(), 3, $user_info['id'], $user_info['ip'], 'removed_from_group', + 0, 0, 0, serialize(array('group' => $group_names[$row['id_group']], 'member' => $row['id_member'])), + ); + $smcFunc['db_free_result']($request); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_group = {int:regular_member} + WHERE id_group IN ({array_int:group_list}) + AND id_member IN ({array_int:member_list})', + array( + 'group_list' => $groups, + 'member_list' => $members, + 'regular_member' => 0, + ) + ); + + // Those who have it as part of their additional group must be updated the long way... sadly. + $request = $smcFunc['db_query']('', ' + SELECT id_member, additional_groups + FROM {db_prefix}members + WHERE (FIND_IN_SET({raw:additional_groups_implode}, additional_groups) != 0) + AND id_member IN ({array_int:member_list}) + LIMIT ' . count($members), + array( + 'member_list' => $members, + 'additional_groups_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $groups), + ) + ); + $updates = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // What log entries must we make for this one, eh? + foreach (explode(',', $row['additional_groups']) as $group) + if (in_array($group, $groups)) + $log_inserts[] = array( + time(), 3, $user_info['id'], $user_info['ip'], 'removed_from_group', + 0, 0, 0, serialize(array('group' => $group_names[$group], 'member' => $row['id_member'])), + ); + + $updates[$row['additional_groups']][] = $row['id_member']; + } + $smcFunc['db_free_result']($request); + + foreach ($updates as $additional_groups => $memberArray) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET additional_groups = {string:additional_groups} + WHERE id_member IN ({array_int:member_list})', + array( + 'member_list' => $memberArray, + 'additional_groups' => implode(',', array_diff(explode(',', $additional_groups), $groups)), + ) + ); + + // Their post groups may have changed now... + updateStats('postgroups', $members); + + // Do the log. + if (!empty($log_inserts) && !empty($modSettings['modlog_enabled'])) + $smcFunc['db_insert']('', + '{db_prefix}log_actions', + array( + 'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', + 'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534', + ), + $log_inserts, + array('id_action') + ); + + // Mission successful. + return true; +} + +// Add one or more members to a membergroup. +/* Supported types: + - only_primary - Assigns a membergroup as primary membergroup, but only + if a member has not yet a primary membergroup assigned, + unless the member is already part of the membergroup. + - only_additional - Assigns a membergroup to the additional membergroups, + unless the member is already part of the membergroup. + - force_primary - Assigns a membergroup as primary membergroup no matter + what the previous primary membergroup was. + - auto - Assigns a membergroup to the primary group if it's still + available. If not, assign it to the additional group. */ +function addMembersToGroup($members, $group, $type = 'auto', $permissionCheckDone = false) +{ + global $smcFunc, $user_info, $modSettings; + + // Show your licence, but only if it hasn't been done yet. + if (!$permissionCheckDone) + isAllowedTo('manage_membergroups'); + + // Make sure we don't keep old stuff cached. + updateSettings(array('settings_updated' => time())); + + if (!is_array($members)) + $members = array((int) $members); + else + { + $members = array_unique($members); + + // Make sure all members are integer. + foreach ($members as $key => $value) + $members[$key] = (int) $value; + } + $group = (int) $group; + + // Some groups just don't like explicitly having members. + $implicitGroups = array(-1, 0, 3); + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name, min_posts + FROM {db_prefix}membergroups + WHERE id_group = {int:current_group}', + array( + 'current_group' => $group, + ) + ); + $group_names = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['min_posts'] != -1) + $implicitGroups[] = $row['id_group']; + else + $group_names[$row['id_group']] = $row['group_name']; + } + $smcFunc['db_free_result']($request); + + // Sorry, you can't join an implicit group. + if (in_array($group, $implicitGroups) || empty($members)) + return false; + + // Only admins can add admins... + if (!allowedTo('admin_forum') && $group == 1) + return false; + // ... and assign protected groups! + elseif (!allowedTo('admin_forum')) + { + $request = $smcFunc['db_query']('', ' + SELECT group_type + FROM {db_prefix}membergroups + WHERE id_group = {int:current_group} + LIMIT {int:limit}', + array( + 'current_group' => $group, + 'limit' => 1, + ) + ); + list ($is_protected) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // Is it protected? + if ($is_protected == 1) + return false; + } + + // Do the actual updates. + if ($type == 'only_additional') + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET additional_groups = CASE WHEN additional_groups = {string:blank_string} THEN {string:id_group_string} ELSE CONCAT(additional_groups, {string:id_group_string_extend}) END + WHERE id_member IN ({array_int:member_list}) + AND id_group != {int:id_group} + AND FIND_IN_SET({int:id_group}, additional_groups) = 0', + array( + 'member_list' => $members, + 'id_group' => $group, + 'id_group_string' => (string) $group, + 'id_group_string_extend' => ',' . $group, + 'blank_string' => '', + ) + ); + elseif ($type == 'only_primary' || $type == 'force_primary') + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_group = {int:id_group} + WHERE id_member IN ({array_int:member_list})' . ($type == 'force_primary' ? '' : ' + AND id_group = {int:regular_group} + AND FIND_IN_SET({int:id_group}, additional_groups) = 0'), + array( + 'member_list' => $members, + 'id_group' => $group, + 'regular_group' => 0, + ) + ); + elseif ($type == 'auto') + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET + id_group = CASE WHEN id_group = {int:regular_group} THEN {int:id_group} ELSE id_group END, + additional_groups = CASE WHEN id_group = {int:id_group} THEN additional_groups + WHEN additional_groups = {string:blank_string} THEN {string:id_group_string} + ELSE CONCAT(additional_groups, {string:id_group_string_extend}) END + WHERE id_member IN ({array_int:member_list}) + AND id_group != {int:id_group} + AND FIND_IN_SET({int:id_group}, additional_groups) = 0', + array( + 'member_list' => $members, + 'regular_group' => 0, + 'id_group' => $group, + 'blank_string' => '', + 'id_group_string' => (string) $group, + 'id_group_string_extend' => ',' . $group, + ) + ); + // Ack!!? What happened? + else + trigger_error('addMembersToGroup(): Unknown type \'' . $type . '\'', E_USER_WARNING); + + // Update their postgroup statistics. + updateStats('postgroups', $members); + + // Log the data. + $log_inserts = array(); + foreach ($members as $member) + $log_inserts[] = array( + time(), 3, $user_info['id'], $user_info['ip'], 'added_to_group', + 0, 0, 0, serialize(array('group' => $group_names[$group], 'member' => $member)), + ); + + if (!empty($log_inserts) && !empty($modSettings['modlog_enabled'])) + $smcFunc['db_insert']('', + '{db_prefix}log_actions', + array( + 'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', + 'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534', + ), + $log_inserts, + array('id_action') + ); + + return true; +} + +function listMembergroupMembers_Href(&$members, $membergroup, $limit = null) +{ + global $scripturl, $txt, $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE id_group = {int:id_group} OR FIND_IN_SET({int:id_group}, additional_groups) != 0' . ($limit === null ? '' : ' + LIMIT ' . ($limit + 1)), + array( + 'id_group' => $membergroup, + ) + ); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[$row['id_member']] = '' . $row['real_name'] . ''; + $smcFunc['db_free_result']($request); + + // If there are more than $limit members, add a 'more' link. + if ($limit !== null && count($members) > $limit) + { + array_pop($members); + return true; + } + else + return false; +} + +// Retrieve a list of (visible) membergroups used by the cache. +function cache_getMembergroupList() +{ + global $scripturl, $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT id_group, group_name, online_color + FROM {db_prefix}membergroups + WHERE min_posts = {int:min_posts} + AND hidden = {int:not_hidden} + AND id_group != {int:mod_group} + AND online_color != {string:blank_string} + ORDER BY group_name', + array( + 'min_posts' => -1, + 'not_hidden' => 0, + 'mod_group' => 3, + 'blank_string' => '', + ) + ); + $groupCache = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $groupCache[] = '' . $row['group_name'] . ''; + $smcFunc['db_free_result']($request); + + return array( + 'data' => $groupCache, + 'expires' => time() + 3600, + 'refresh_eval' => 'return $GLOBALS[\'modSettings\'][\'settings_updated\'] > ' . time() . ';', + ); +} + +function list_getMembergroups($start, $items_per_page, $sort, $membergroup_type) +{ + global $txt, $scripturl, $context, $settings, $smcFunc; + + $groups = array(); + + // Get the basic group data. + $request = $smcFunc['db_query']('substring_membergroups', ' + SELECT id_group, group_name, min_posts, online_color, stars, 0 AS num_members + FROM {db_prefix}membergroups + WHERE min_posts ' . ($membergroup_type === 'post_count' ? '!=' : '=') . ' -1' . (allowedTo('admin_forum') ? '' : ' + AND group_type != {int:is_protected}') . ' + ORDER BY {raw:sort}', + array( + 'is_protected' => 1, + 'sort' => $sort, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $groups[$row['id_group']] = array( + 'id_group' => $row['id_group'], + 'group_name' => $row['group_name'], + 'min_posts' => $row['min_posts'], + 'online_color' => $row['online_color'], + 'stars' => $row['stars'], + 'num_members' => $row['num_members'], + ); + $smcFunc['db_free_result']($request); + + // If we found any membergroups, get the amount of members in them. + if (!empty($groups)) + { + if ($membergroup_type === 'post_count') + { + $query = $smcFunc['db_query']('', ' + SELECT id_post_group AS id_group, COUNT(*) AS num_members + FROM {db_prefix}members + WHERE id_post_group IN ({array_int:group_list}) + GROUP BY id_post_group', + array( + 'group_list' => array_keys($groups), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $groups[$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + } + + else + { + $query = $smcFunc['db_query']('', ' + SELECT id_group, COUNT(*) AS num_members + FROM {db_prefix}members + WHERE id_group IN ({array_int:group_list}) + GROUP BY id_group', + array( + 'group_list' => array_keys($groups), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $groups[$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + + $query = $smcFunc['db_query']('', ' + SELECT mg.id_group, COUNT(*) AS num_members + FROM {db_prefix}membergroups AS mg + INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_string} + AND mem.id_group != mg.id_group + AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0) + WHERE mg.id_group IN ({array_int:group_list}) + GROUP BY mg.id_group', + array( + 'group_list' => array_keys($groups), + 'blank_string' => '', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($query)) + $groups[$row['id_group']]['num_members'] += $row['num_members']; + $smcFunc['db_free_result']($query); + } + } + + // Apply manual sorting if the 'number of members' column is selected. + if (substr($sort, 0, 1) == '1' || strpos($sort, ', 1') !== false) + { + $sort_ascending = strpos($sort, 'DESC') === false; + + foreach ($groups as $group) + $sort_array[] = $group['id_group'] != 3 ? (int) $group['num_members'] : -1; + + array_multisort($sort_array, $sort_ascending ? SORT_ASC : SORT_DESC, SORT_REGULAR, $groups); + } + + return $groups; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Members.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Members.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1392 @@ + $v) + $users[$k] = (int) $v; + + // Deleting more than one? You can't have more than one account... + isAllowedTo('profile_remove_any'); + } + + // Get their names for logging purposes. + $request = $smcFunc['db_query']('', ' + SELECT id_member, member_name, CASE WHEN id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0 THEN 1 ELSE 0 END AS is_admin + FROM {db_prefix}members + WHERE id_member IN ({array_int:user_list}) + LIMIT ' . count($users), + array( + 'user_list' => $users, + 'admin_group' => 1, + ) + ); + $admins = array(); + $user_log_details = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['is_admin']) + $admins[] = $row['id_member']; + $user_log_details[$row['id_member']] = array($row['id_member'], $row['member_name']); + } + $smcFunc['db_free_result']($request); + + if (empty($user_log_details)) + return; + + // Make sure they aren't trying to delete administrators if they aren't one. But don't bother checking if it's just themself. + if (!empty($admins) && ($check_not_admin || (!allowedTo('admin_forum') && (count($users) != 1 || $users[0] != $user_info['id'])))) + { + $users = array_diff($users, $admins); + foreach ($admins as $id) + unset($user_log_details[$id]); + } + + // No one left? + if (empty($users)) + return; + + // Log the action - regardless of who is deleting it. + $log_inserts = array(); + foreach ($user_log_details as $user) + { + // Integration rocks! + call_integration_hook('integrate_delete_member', array($user[0])); + + // Add it to the administration log for future reference. + $log_inserts[] = array( + time(), 3, $user_info['id'], $user_info['ip'], 'delete_member', + 0, 0, 0, serialize(array('member' => $user[0], 'name' => $user[1], 'member_acted' => $user_info['name'])), + ); + + // Remove any cached data if enabled. + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2) + cache_put_data('user_settings-' . $user[0], null, 60); + } + + // Do the actual logging... + if (!empty($log_inserts) && !empty($modSettings['modlog_enabled'])) + $smcFunc['db_insert']('', + '{db_prefix}log_actions', + array( + 'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', + 'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534', + ), + $log_inserts, + array('id_action') + ); + + // Make these peoples' posts guest posts. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET id_member = {int:guest_id}, poster_email = {string:blank_email} + WHERE id_member IN ({array_int:users})', + array( + 'guest_id' => 0, + 'blank_email' => '', + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}polls + SET id_member = {int:guest_id} + WHERE id_member IN ({array_int:users})', + array( + 'guest_id' => 0, + 'users' => $users, + ) + ); + + // Make these peoples' posts guest first posts and last posts. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET id_member_started = {int:guest_id} + WHERE id_member_started IN ({array_int:users})', + array( + 'guest_id' => 0, + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET id_member_updated = {int:guest_id} + WHERE id_member_updated IN ({array_int:users})', + array( + 'guest_id' => 0, + 'users' => $users, + ) + ); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_actions + SET id_member = {int:guest_id} + WHERE id_member IN ({array_int:users})', + array( + 'guest_id' => 0, + 'users' => $users, + ) + ); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_banned + SET id_member = {int:guest_id} + WHERE id_member IN ({array_int:users})', + array( + 'guest_id' => 0, + 'users' => $users, + ) + ); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_errors + SET id_member = {int:guest_id} + WHERE id_member IN ({array_int:users})', + array( + 'guest_id' => 0, + 'users' => $users, + ) + ); + + // Delete the member. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}members + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + + // Delete the logs... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_actions + WHERE id_log = {int:log_type} + AND id_member IN ({array_int:users})', + array( + 'log_type' => 2, + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_boards + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_comments + WHERE id_recipient IN ({array_int:users}) + AND comment_type = {string:warntpl}', + array( + 'users' => $users, + 'warntpl' => 'warntpl', + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_group_requests + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_karma + WHERE id_target IN ({array_int:users}) + OR id_executor IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_mark_read + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_notify + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_online + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_subscribed + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_topics + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}collapsed_categories + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + + // Make their votes appear as guest votes - at least it keeps the totals right. + //!!! Consider adding back in cookie protection. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_polls + SET id_member = {int:guest_id} + WHERE id_member IN ({array_int:users})', + array( + 'guest_id' => 0, + 'users' => $users, + ) + ); + + // Delete personal messages. + require_once($sourcedir . '/PersonalMessage.php'); + deleteMessages(null, null, $users); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}personal_messages + SET id_member_from = {int:guest_id} + WHERE id_member_from IN ({array_int:users})', + array( + 'guest_id' => 0, + 'users' => $users, + ) + ); + + // They no longer exist, so we don't know who it was sent to. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}pm_recipients + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + + // Delete avatar. + require_once($sourcedir . '/ManageAttachments.php'); + removeAttachments(array('id_member' => $users)); + + // It's over, no more moderation for you. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}moderators + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}group_moderators + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + + // If you don't exist we can't ban you. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}ban_items + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + + // Remove individual theme settings. + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE id_member IN ({array_int:users})', + array( + 'users' => $users, + ) + ); + + // These users are nobody's buddy nomore. + $request = $smcFunc['db_query']('', ' + SELECT id_member, pm_ignore_list, buddy_list + FROM {db_prefix}members + WHERE FIND_IN_SET({raw:pm_ignore_list}, pm_ignore_list) != 0 OR FIND_IN_SET({raw:buddy_list}, buddy_list) != 0', + array( + 'pm_ignore_list' => implode(', pm_ignore_list) != 0 OR FIND_IN_SET(', $users), + 'buddy_list' => implode(', buddy_list) != 0 OR FIND_IN_SET(', $users), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET + pm_ignore_list = {string:pm_ignore_list}, + buddy_list = {string:buddy_list} + WHERE id_member = {int:id_member}', + array( + 'id_member' => $row['id_member'], + 'pm_ignore_list' => implode(',', array_diff(explode(',', $row['pm_ignore_list']), $users)), + 'buddy_list' => implode(',', array_diff(explode(',', $row['buddy_list']), $users)), + ) + ); + $smcFunc['db_free_result']($request); + + // Make sure no member's birthday is still sticking in the calendar... + updateSettings(array( + 'calendar_updated' => time(), + )); + + updateStats('member'); +} + +function registerMember(&$regOptions, $return_errors = false) +{ + global $scripturl, $txt, $modSettings, $context, $sourcedir; + global $user_info, $options, $settings, $smcFunc; + + loadLanguage('Login'); + + // We'll need some external functions. + require_once($sourcedir . '/Subs-Auth.php'); + require_once($sourcedir . '/Subs-Post.php'); + + // Put any errors in here. + $reg_errors = array(); + + // Registration from the admin center, let them sweat a little more. + if ($regOptions['interface'] == 'admin') + { + is_not_guest(); + isAllowedTo('moderate_forum'); + } + // If you're an admin, you're special ;). + elseif ($regOptions['interface'] == 'guest') + { + // You cannot register twice... + if (empty($user_info['is_guest'])) + redirectexit(); + + // Make sure they didn't just register with this session. + if (!empty($_SESSION['just_registered']) && empty($modSettings['disableRegisterCheck'])) + fatal_lang_error('register_only_once', false); + } + + // What method of authorization are we going to use? + if (empty($regOptions['auth_method']) || !in_array($regOptions['auth_method'], array('password', 'openid'))) + { + if (!empty($regOptions['openid'])) + $regOptions['auth_method'] = 'openid'; + else + $regOptions['auth_method'] = 'password'; + } + + // No name?! How can you register with no name? + if (empty($regOptions['username'])) + $reg_errors[] = array('lang', 'need_username'); + + // Spaces and other odd characters are evil... + $regOptions['username'] = preg_replace('~[\t\n\r\x0B\0' . ($context['utf8'] ? ($context['server']['complex_preg_chars'] ? '\x{A0}' : "\xC2\xA0") : '\xA0') . ']+~' . ($context['utf8'] ? 'u' : ''), ' ', $regOptions['username']); + + // Don't use too long a name. + if ($smcFunc['strlen']($regOptions['username']) > 25) + $reg_errors[] = array('lang', 'error_long_name'); + + // Only these characters are permitted. + if (preg_match('~[<>&"\'=\\\\]~', preg_replace('~&#(?:\\d{1,7}|x[0-9a-fA-F]{1,6});~', '', $regOptions['username'])) != 0 || $regOptions['username'] == '_' || $regOptions['username'] == '|' || strpos($regOptions['username'], '[code') !== false || strpos($regOptions['username'], '[/code') !== false) + $reg_errors[] = array('lang', 'error_invalid_characters_username'); + + if ($smcFunc['strtolower']($regOptions['username']) === $smcFunc['strtolower']($txt['guest_title'])) + $reg_errors[] = array('lang', 'username_reserved', 'general', array($txt['guest_title'])); + + // !!! Separate the sprintf? + if (empty($regOptions['email']) || preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $regOptions['email']) === 0 || strlen($regOptions['email']) > 255) + $reg_errors[] = array('done', sprintf($txt['valid_email_needed'], $smcFunc['htmlspecialchars']($regOptions['username']))); + + if (!empty($regOptions['check_reserved_name']) && isReservedName($regOptions['username'], 0, false)) + { + if ($regOptions['password'] == 'chocolate cake') + $reg_errors[] = array('done', 'Sorry, I don\'t take bribes... you\'ll need to come up with a different name.'); + $reg_errors[] = array('done', '(' . htmlspecialchars($regOptions['username']) . ') ' . $txt['name_in_use']); + } + + // Generate a validation code if it's supposed to be emailed. + $validation_code = ''; + if ($regOptions['require'] == 'activation') + $validation_code = generateValidationCode(); + + // If you haven't put in a password generate one. + if ($regOptions['interface'] == 'admin' && $regOptions['password'] == '' && $regOptions['auth_method'] == 'password') + { + mt_srand(time() + 1277); + $regOptions['password'] = generateValidationCode(); + $regOptions['password_check'] = $regOptions['password']; + } + // Does the first password match the second? + elseif ($regOptions['password'] != $regOptions['password_check'] && $regOptions['auth_method'] == 'password') + $reg_errors[] = array('lang', 'passwords_dont_match'); + + // That's kind of easy to guess... + if ($regOptions['password'] == '') + { + if ($regOptions['auth_method'] == 'password') + $reg_errors[] = array('lang', 'no_password'); + else + $regOptions['password'] = sha1(mt_rand()); + } + + // Now perform hard password validation as required. + if (!empty($regOptions['check_password_strength'])) + { + $passwordError = validatePassword($regOptions['password'], $regOptions['username'], array($regOptions['email'])); + + // Password isn't legal? + if ($passwordError != null) + $reg_errors[] = array('lang', 'profile_error_password_' . $passwordError); + } + + // If they are using an OpenID that hasn't been verified yet error out. + // !!! Change this so they can register without having to attempt a login first + if ($regOptions['auth_method'] == 'openid' && (empty($_SESSION['openid']['verified']) || $_SESSION['openid']['openid_uri'] != $regOptions['openid'])) + $reg_errors[] = array('lang', 'openid_not_verified'); + + // You may not be allowed to register this email. + if (!empty($regOptions['check_email_ban'])) + isBannedEmail($regOptions['email'], 'cannot_register', $txt['ban_register_prohibited']); + + // Check if the email address is in use. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE email_address = {string:email_address} + OR email_address = {string:username} + LIMIT 1', + array( + 'email_address' => $regOptions['email'], + 'username' => $regOptions['username'], + ) + ); + // !!! Separate the sprintf? + if ($smcFunc['db_num_rows']($request) != 0) + $reg_errors[] = array('lang', 'email_in_use', false, array(htmlspecialchars($regOptions['email']))); + $smcFunc['db_free_result']($request); + + // If we found any errors we need to do something about it right away! + foreach ($reg_errors as $key => $error) + { + /* Note for each error: + 0 = 'lang' if it's an index, 'done' if it's clear text. + 1 = The text/index. + 2 = Whether to log. + 3 = sprintf data if necessary. */ + if ($error[0] == 'lang') + loadLanguage('Errors'); + $message = $error[0] == 'lang' ? (empty($error[3]) ? $txt[$error[1]] : vsprintf($txt[$error[1]], $error[3])) : $error[1]; + + // What to do, what to do, what to do. + if ($return_errors) + { + if (!empty($error[2])) + log_error($message, $error[2]); + $reg_errors[$key] = $message; + } + else + fatal_error($message, empty($error[2]) ? false : $error[2]); + } + + // If there's any errors left return them at once! + if (!empty($reg_errors)) + return $reg_errors; + + $reservedVars = array( + 'actual_theme_url', + 'actual_images_url', + 'base_theme_dir', + 'base_theme_url', + 'default_images_url', + 'default_theme_dir', + 'default_theme_url', + 'default_template', + 'images_url', + 'number_recent_posts', + 'smiley_sets_default', + 'theme_dir', + 'theme_id', + 'theme_layers', + 'theme_templates', + 'theme_url', + ); + + // Can't change reserved vars. + if (isset($regOptions['theme_vars']) && array_intersect($regOptions['theme_vars'], $reservedVars) != array()) + fatal_lang_error('no_theme'); + + // Some of these might be overwritten. (the lower ones that are in the arrays below.) + $regOptions['register_vars'] = array( + 'member_name' => $regOptions['username'], + 'email_address' => $regOptions['email'], + 'passwd' => sha1(strtolower($regOptions['username']) . $regOptions['password']), + 'password_salt' => substr(md5(mt_rand()), 0, 4) , + 'posts' => 0, + 'date_registered' => time(), + 'member_ip' => $regOptions['interface'] == 'admin' ? '127.0.0.1' : $user_info['ip'], + 'member_ip2' => $regOptions['interface'] == 'admin' ? '127.0.0.1' : $_SERVER['BAN_CHECK_IP'], + 'validation_code' => $validation_code, + 'real_name' => $regOptions['username'], + 'personal_text' => $modSettings['default_personal_text'], + 'pm_email_notify' => 1, + 'id_theme' => 0, + 'id_post_group' => 4, + 'lngfile' => '', + 'buddy_list' => '', + 'pm_ignore_list' => '', + 'message_labels' => '', + 'website_title' => '', + 'website_url' => '', + 'location' => '', + 'icq' => '', + 'aim' => '', + 'yim' => '', + 'msn' => '', + 'time_format' => '', + 'signature' => '', + 'avatar' => '', + 'usertitle' => '', + 'secret_question' => '', + 'secret_answer' => '', + 'additional_groups' => '', + 'ignore_boards' => '', + 'smiley_set' => '', + 'openid_uri' => (!empty($regOptions['openid']) ? $regOptions['openid'] : ''), + ); + + // Setup the activation status on this new account so it is correct - firstly is it an under age account? + if ($regOptions['require'] == 'coppa') + { + $regOptions['register_vars']['is_activated'] = 5; + // !!! This should be changed. To what should be it be changed?? + $regOptions['register_vars']['validation_code'] = ''; + } + // Maybe it can be activated right away? + elseif ($regOptions['require'] == 'nothing') + $regOptions['register_vars']['is_activated'] = 1; + // Maybe it must be activated by email? + elseif ($regOptions['require'] == 'activation') + $regOptions['register_vars']['is_activated'] = 0; + // Otherwise it must be awaiting approval! + else + $regOptions['register_vars']['is_activated'] = 3; + + if (isset($regOptions['memberGroup'])) + { + // Make sure the id_group will be valid, if this is an administator. + $regOptions['register_vars']['id_group'] = $regOptions['memberGroup'] == 1 && !allowedTo('admin_forum') ? 0 : $regOptions['memberGroup']; + + // Check if this group is assignable. + $unassignableGroups = array(-1, 3); + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}membergroups + WHERE min_posts != {int:min_posts}' . (allowedTo('admin_forum') ? '' : ' + OR group_type = {int:is_protected}'), + array( + 'min_posts' => -1, + 'is_protected' => 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $unassignableGroups[] = $row['id_group']; + $smcFunc['db_free_result']($request); + + if (in_array($regOptions['register_vars']['id_group'], $unassignableGroups)) + $regOptions['register_vars']['id_group'] = 0; + } + + // ICQ cannot be zero. + if (isset($regOptions['extra_register_vars']['icq']) && empty($regOptions['extra_register_vars']['icq'])) + $regOptions['extra_register_vars']['icq'] = ''; + + // Integrate optional member settings to be set. + if (!empty($regOptions['extra_register_vars'])) + foreach ($regOptions['extra_register_vars'] as $var => $value) + $regOptions['register_vars'][$var] = $value; + + // Integrate optional user theme options to be set. + $theme_vars = array(); + if (!empty($regOptions['theme_vars'])) + foreach ($regOptions['theme_vars'] as $var => $value) + $theme_vars[$var] = $value; + + // Call an optional function to validate the users' input. + call_integration_hook('integrate_register', array(&$regOptions, &$theme_vars)); + + // Right, now let's prepare for insertion. + $knownInts = array( + 'date_registered', 'posts', 'id_group', 'last_login', 'instant_messages', 'unread_messages', + 'new_pm', 'pm_prefs', 'gender', 'hide_email', 'show_online', 'pm_email_notify', 'karma_good', 'karma_bad', + 'notify_announcements', 'notify_send_body', 'notify_regularity', 'notify_types', + 'id_theme', 'is_activated', 'id_msg_last_visit', 'id_post_group', 'total_time_logged_in', 'warning', + ); + $knownFloats = array( + 'time_offset', + ); + + $column_names = array(); + $values = array(); + foreach ($regOptions['register_vars'] as $var => $val) + { + $type = 'string'; + if (in_array($var, $knownInts)) + $type = 'int'; + elseif (in_array($var, $knownFloats)) + $type = 'float'; + elseif ($var == 'birthdate') + $type = 'date'; + + $column_names[$var] = $type; + $values[$var] = $val; + } + + // Register them into the database. + $smcFunc['db_insert']('', + '{db_prefix}members', + $column_names, + $values, + array('id_member') + ); + $memberID = $smcFunc['db_insert_id']('{db_prefix}members', 'id_member'); + + // Update the number of members and latest member's info - and pass the name, but remove the 's. + if ($regOptions['register_vars']['is_activated'] == 1) + updateStats('member', $memberID, $regOptions['register_vars']['real_name']); + else + updateStats('member'); + + // Theme variables too? + if (!empty($theme_vars)) + { + $inserts = array(); + foreach ($theme_vars as $var => $val) + $inserts[] = array($memberID, $var, $val); + $smcFunc['db_insert']('insert', + '{db_prefix}themes', + array('id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + $inserts, + array('id_member', 'variable') + ); + } + + // If it's enabled, increase the registrations for today. + trackStats(array('registers' => '+')); + + // Administrative registrations are a bit different... + if ($regOptions['interface'] == 'admin') + { + if ($regOptions['require'] == 'activation') + $email_message = 'admin_register_activate'; + elseif (!empty($regOptions['send_welcome_email'])) + $email_message = 'admin_register_immediate'; + + if (isset($email_message)) + { + $replacements = array( + 'REALNAME' => $regOptions['register_vars']['real_name'], + 'USERNAME' => $regOptions['username'], + 'PASSWORD' => $regOptions['password'], + 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', + 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $memberID . ';code=' . $validation_code, + 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $memberID, + 'ACTIVATIONCODE' => $validation_code, + ); + + $emaildata = loadEmailTemplate($email_message, $replacements); + + sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + } + + // All admins are finished here. + return $memberID; + } + + // Can post straight away - welcome them to your fantastic community... + if ($regOptions['require'] == 'nothing') + { + if (!empty($regOptions['send_welcome_email'])) + { + $replacements = array( + 'REALNAME' => $regOptions['register_vars']['real_name'], + 'USERNAME' => $regOptions['username'], + 'PASSWORD' => $regOptions['password'], + 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', + 'OPENID' => !empty($regOptions['openid']) ? $regOptions['openid'] : '', + ); + $emaildata = loadEmailTemplate('register_' . ($regOptions['auth_method'] == 'openid' ? 'openid_' : '') . 'immediate', $replacements); + sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + } + + // Send admin their notification. + adminNotify('standard', $memberID, $regOptions['username']); + } + // Need to activate their account - or fall under COPPA. + elseif ($regOptions['require'] == 'activation' || $regOptions['require'] == 'coppa') + { + $replacements = array( + 'REALNAME' => $regOptions['register_vars']['real_name'], + 'USERNAME' => $regOptions['username'], + 'PASSWORD' => $regOptions['password'], + 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', + 'OPENID' => !empty($regOptions['openid']) ? $regOptions['openid'] : '', + ); + + if ($regOptions['require'] == 'activation') + $replacements += array( + 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $memberID . ';code=' . $validation_code, + 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $memberID, + 'ACTIVATIONCODE' => $validation_code, + ); + else + $replacements += array( + 'COPPALINK' => $scripturl . '?action=coppa;u=' . $memberID, + ); + + $emaildata = loadEmailTemplate('register_' . ($regOptions['auth_method'] == 'openid' ? 'openid_' : '') . ($regOptions['require'] == 'activation' ? 'activate' : 'coppa'), $replacements); + + sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + } + // Must be awaiting approval. + else + { + $replacements = array( + 'REALNAME' => $regOptions['register_vars']['real_name'], + 'USERNAME' => $regOptions['username'], + 'PASSWORD' => $regOptions['password'], + 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', + 'OPENID' => !empty($regOptions['openid']) ? $regOptions['openid'] : '', + ); + + $emaildata = loadEmailTemplate('register_' . ($regOptions['auth_method'] == 'openid' ? 'openid_' : '') . 'pending', $replacements); + + sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + + // Admin gets informed here... + adminNotify('approval', $memberID, $regOptions['username']); + } + + // Okay, they're for sure registered... make sure the session is aware of this for security. (Just married :P!) + $_SESSION['just_registered'] = 1; + + return $memberID; +} + +// Check if a name is in the reserved words list. (name, current member id, name/username?.) +function isReservedName($name, $current_ID_MEMBER = 0, $is_name = true, $fatal = true) +{ + global $user_info, $modSettings, $smcFunc, $context; + + // No cheating with entities please. + $replaceEntities = create_function('$string', ' + $num = substr($string, 0, 1) === \'x\' ? hexdec(substr($string, 1)) : (int) $string; + if ($num === 0x202E || $num === 0x202D) return \'\'; if (in_array($num, array(0x22, 0x26, 0x27, 0x3C, 0x3E))) return \'&#\' . $num . \';\';' . + (empty($context['utf8']) ? 'return $num < 0x20 ? \'\' : ($num < 0x80 ? chr($num) : \'&#\' . $string . \';\');' : ' + return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) ? \'\' : ($num < 0x80 ? chr($num) : ($num < 0x800 ? chr(192 | $num >> 6) . chr(128 | $num & 63) : ($num < 0x10000 ? chr(224 | $num >> 12) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63) : chr(240 | $num >> 18) . chr(128 | $num >> 12 & 63) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63))));') + ); + + $name = preg_replace('~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~e', '$replaceEntities(\'\\2\')', $name); + $checkName = $smcFunc['strtolower']($name); + + // Administrators are never restricted ;). + if (!allowedTo('moderate_forum') && ((!empty($modSettings['reserveName']) && $is_name) || !empty($modSettings['reserveUser']) && !$is_name)) + { + $reservedNames = explode("\n", $modSettings['reserveNames']); + // Case sensitive check? + $checkMe = empty($modSettings['reserveCase']) ? $checkName : $name; + + // Check each name in the list... + foreach ($reservedNames as $reserved) + { + if ($reserved == '') + continue; + + // The admin might've used entities too, level the playing field. + $reservedCheck = preg_replace('~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~e', '$replaceEntities(\'\\2\')', $reserved); + + // Case sensitive name? + if (empty($modSettings['reserveCase'])) + $reservedCheck = $smcFunc['strtolower']($reservedCheck); + + // If it's not just entire word, check for it in there somewhere... + if ($checkMe == $reservedCheck || ($smcFunc['strpos']($checkMe, $reservedCheck) !== false && empty($modSettings['reserveWord']))) + if ($fatal) + fatal_lang_error('username_reserved', 'password', array($reserved)); + else + return true; + } + + $censor_name = $name; + if (censorText($censor_name) != $name) + if ($fatal) + fatal_lang_error('name_censored', 'password', array($name)); + else + return true; + } + + // Characters we just shouldn't allow, regardless. + foreach (array('*') as $char) + if (strpos($checkName, $char) !== false) + if ($fatal) + fatal_lang_error('username_reserved', 'password', array($char)); + else + return true; + + // Get rid of any SQL parts of the reserved name... + $checkName = strtr($name, array('_' => '\\_', '%' => '\\%')); + + // Make sure they don't want someone else's name. + $request = $smcFunc['db_query']('', ' + SELECT id_member + FROM {db_prefix}members + WHERE ' . (empty($current_ID_MEMBER) ? '' : 'id_member != {int:current_member} + AND ') . '(real_name LIKE {string:check_name} OR member_name LIKE {string:check_name}) + LIMIT 1', + array( + 'current_member' => $current_ID_MEMBER, + 'check_name' => $checkName, + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + { + $smcFunc['db_free_result']($request); + return true; + } + + // Does name case insensitive match a member group name? + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}membergroups + WHERE group_name LIKE {string:check_name} + LIMIT 1', + array( + 'check_name' => $checkName, + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + { + $smcFunc['db_free_result']($request); + return true; + } + + // Okay, they passed. + return false; +} + +// Get a list of groups that have a given permission (on a given board). +function groupsAllowedTo($permission, $board_id = null) +{ + global $modSettings, $board_info, $smcFunc; + + // Admins are allowed to do anything. + $member_groups = array( + 'allowed' => array(1), + 'denied' => array(), + ); + + // Assume we're dealing with regular permissions (like profile_view_own). + if ($board_id === null) + { + $request = $smcFunc['db_query']('', ' + SELECT id_group, add_deny + FROM {db_prefix}permissions + WHERE permission = {string:permission}', + array( + 'permission' => $permission, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $member_groups[$row['add_deny'] === '1' ? 'allowed' : 'denied'][] = $row['id_group']; + $smcFunc['db_free_result']($request); + } + + // Otherwise it's time to look at the board. + else + { + // First get the profile of the given board. + if (isset($board_info['id']) && $board_info['id'] == $board_id) + $profile_id = $board_info['profile']; + elseif ($board_id !== 0) + { + $request = $smcFunc['db_query']('', ' + SELECT id_profile + FROM {db_prefix}boards + WHERE id_board = {int:id_board} + LIMIT 1', + array( + 'id_board' => $board_id, + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + fatal_lang_error('no_board'); + list ($profile_id) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + $profile_id = 1; + + $request = $smcFunc['db_query']('', ' + SELECT bp.id_group, bp.add_deny + FROM {db_prefix}board_permissions AS bp + WHERE bp.permission = {string:permission} + AND bp.id_profile = {int:profile_id}', + array( + 'profile_id' => $profile_id, + 'permission' => $permission, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $member_groups[$row['add_deny'] === '1' ? 'allowed' : 'denied'][] = $row['id_group']; + $smcFunc['db_free_result']($request); + } + + // Denied is never allowed. + $member_groups['allowed'] = array_diff($member_groups['allowed'], $member_groups['denied']); + + return $member_groups; +} + +// Get a list of members that have a given permission (on a given board). +function membersAllowedTo($permission, $board_id = null) +{ + global $smcFunc; + + $member_groups = groupsAllowedTo($permission, $board_id); + + $include_moderators = in_array(3, $member_groups['allowed']) && $board_id !== null; + $member_groups['allowed'] = array_diff($member_groups['allowed'], array(3)); + + $exclude_moderators = in_array(3, $member_groups['denied']) && $board_id !== null; + $member_groups['denied'] = array_diff($member_groups['denied'], array(3)); + + $request = $smcFunc['db_query']('', ' + SELECT mem.id_member + FROM {db_prefix}members AS mem' . ($include_moderators || $exclude_moderators ? ' + LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_member = mem.id_member AND mods.id_board = {int:board_id})' : '') . ' + WHERE (' . ($include_moderators ? 'mods.id_member IS NOT NULL OR ' : '') . 'mem.id_group IN ({array_int:member_groups_allowed}) OR FIND_IN_SET({raw:member_group_allowed_implode}, mem.additional_groups) != 0)' . (empty($member_groups['denied']) ? '' : ' + AND NOT (' . ($exclude_moderators ? 'mods.id_member IS NOT NULL OR ' : '') . 'mem.id_group IN ({array_int:member_groups_denied}) OR FIND_IN_SET({raw:member_group_denied_implode}, mem.additional_groups) != 0)'), + array( + 'member_groups_allowed' => $member_groups['allowed'], + 'member_groups_denied' => $member_groups['denied'], + 'board_id' => $board_id, + 'member_group_allowed_implode' => implode(', mem.additional_groups) != 0 OR FIND_IN_SET(', $member_groups['allowed']), + 'member_group_denied_implode' => implode(', mem.additional_groups) != 0 OR FIND_IN_SET(', $member_groups['denied']), + ) + ); + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[] = $row['id_member']; + $smcFunc['db_free_result']($request); + + return $members; +} + +// This function is used to reassociate members with relevant posts. +function reattributePosts($memID, $email = false, $membername = false, $post_count = false) +{ + global $smcFunc; + + // Firstly, if email and username aren't passed find out the members email address and name. + if ($email === false && $membername === false) + { + $request = $smcFunc['db_query']('', ' + SELECT email_address, member_name + FROM {db_prefix}members + WHERE id_member = {int:memID} + LIMIT 1', + array( + 'memID' => $memID, + ) + ); + list ($email, $membername) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // If they want the post count restored then we need to do some research. + if ($post_count) + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND b.count_posts = {int:count_posts}) + WHERE m.id_member = {int:guest_id} + AND m.approved = {int:is_approved} + AND m.icon != {string:recycled_icon}' . (empty($email) ? '' : ' + AND m.poster_email = {string:email_address}') . (empty($membername) ? '' : ' + AND m.poster_name = {string:member_name}'), + array( + 'count_posts' => 0, + 'guest_id' => 0, + 'email_address' => $email, + 'member_name' => $membername, + 'is_approved' => 1, + 'recycled_icon' => 'recycled', + ) + ); + list ($messageCount) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + updateMemberData($memID, array('posts' => 'posts + ' . $messageCount)); + } + + $query_parts = array(); + if (!empty($email)) + $query_parts[] = 'poster_email = {string:email_address}'; + if (!empty($membername)) + $query_parts[] = 'poster_name = {string:member_name}'; + $query = implode(' AND ', $query_parts); + + // Finally, update the posts themselves! + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET id_member = {int:memID} + WHERE ' . $query, + array( + 'memID' => $memID, + 'email_address' => $email, + 'member_name' => $membername, + ) + ); + + return $smcFunc['db_affected_rows'](); +} + +// This simple function adds/removes the passed user from the current users buddy list. +function BuddyListToggle() +{ + global $user_info; + + checkSession('get'); + + isAllowedTo('profile_identity_own'); + is_not_guest(); + + if (empty($_REQUEST['u'])) + fatal_lang_error('no_access', false); + $_REQUEST['u'] = (int) $_REQUEST['u']; + + // Remove if it's already there... + if (in_array($_REQUEST['u'], $user_info['buddies'])) + $user_info['buddies'] = array_diff($user_info['buddies'], array($_REQUEST['u'])); + // ...or add if it's not and if it's not you. + elseif ($user_info['id'] != $_REQUEST['u']) + $user_info['buddies'][] = (int) $_REQUEST['u']; + + // Update the settings. + updateMemberData($user_info['id'], array('buddy_list' => implode(',', $user_info['buddies']))); + + // Redirect back to the profile + redirectexit('action=profile;u=' . $_REQUEST['u']); +} + +function list_getMembers($start, $items_per_page, $sort, $where, $where_params = array(), $get_duplicates = false) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('', ' + SELECT + mem.id_member, mem.member_name, mem.real_name, mem.email_address, mem.icq, mem.aim, mem.yim, mem.msn, mem.member_ip, mem.member_ip2, mem.last_login, + mem.posts, mem.is_activated, mem.date_registered, mem.id_group, mem.additional_groups, mg.group_name + FROM {db_prefix}members AS mem + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group) + WHERE ' . $where . ' + ORDER BY {raw:sort} + LIMIT {int:start}, {int:per_page}', + array_merge($where_params, array( + 'sort' => $sort, + 'start' => $start, + 'per_page' => $items_per_page, + )) + ); + + $members = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $members[] = $row; + $smcFunc['db_free_result']($request); + + // If we want duplicates pass the members array off. + if ($get_duplicates) + populateDuplicateMembers($members); + + return $members; +} + +function list_getNumMembers($where, $where_params = array()) +{ + global $smcFunc, $modSettings; + + // We know how many members there are in total. + if (empty($where) || $where == '1') + $num_members = $modSettings['totalMembers']; + + // The database knows the amount when there are extra conditions. + else + { + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}members AS mem + WHERE ' . $where, + array_merge($where_params, array( + )) + ); + list ($num_members) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + return $num_members; +} + +function populateDuplicateMembers(&$members) +{ + global $smcFunc; + + // This will hold all the ip addresses. + $ips = array(); + foreach ($members as $key => $member) + { + // Create the duplicate_members element. + $members[$key]['duplicate_members'] = array(); + + // Store the IPs. + if (!empty($member['member_ip'])) + $ips[] = $member['member_ip']; + if (!empty($member['member_ip2'])) + $ips[] = $member['member_ip2']; + } + + $ips = array_unique($ips); + + if (empty($ips)) + return false; + + // Fetch all members with this IP address, we'll filter out the current ones in a sec. + $request = $smcFunc['db_query']('', ' + SELECT + id_member, member_name, email_address, member_ip, member_ip2, is_activated + FROM {db_prefix}members + WHERE member_ip IN ({array_string:ips}) + OR member_ip2 IN ({array_string:ips})', + array( + 'ips' => $ips, + ) + ); + $duplicate_members = array(); + $duplicate_ids = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + //$duplicate_ids[] = $row['id_member']; + + $member_context = array( + 'id' => $row['id_member'], + 'name' => $row['member_name'], + 'email' => $row['email_address'], + 'is_banned' => $row['is_activated'] > 10, + 'ip' => $row['member_ip'], + 'ip2' => $row['member_ip2'], + ); + + if (in_array($row['member_ip'], $ips)) + $duplicate_members[$row['member_ip']][] = $member_context; + if ($row['member_ip'] != $row['member_ip2'] && in_array($row['member_ip2'], $ips)) + $duplicate_members[$row['member_ip2']][] = $member_context; + } + $smcFunc['db_free_result']($request); + + // Also try to get a list of messages using these ips. + $request = $smcFunc['db_query']('', ' + SELECT + m.poster_ip, mem.id_member, mem.member_name, mem.email_address, mem.is_activated + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_member != 0 + ' . (!empty($duplicate_ids) ? 'AND m.id_member NOT IN ({array_int:duplicate_ids})' : '') . ' + AND m.poster_ip IN ({array_string:ips})', + array( + 'duplicate_ids' => $duplicate_ids, + 'ips' => $ips, + ) + ); + + $had_ips = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Don't collect lots of the same. + if (isset($had_ips[$row['poster_ip']]) && in_array($row['id_member'], $had_ips[$row['poster_ip']])) + continue; + $had_ips[$row['poster_ip']][] = $row['id_member']; + + $duplicate_members[$row['poster_ip']][] = array( + 'id' => $row['id_member'], + 'name' => $row['member_name'], + 'email' => $row['email_address'], + 'is_banned' => $row['is_activated'] > 10, + 'ip' => $row['poster_ip'], + 'ip2' => $row['poster_ip'], + ); + } + $smcFunc['db_free_result']($request); + + // Now we have all the duplicate members, stick them with their respective member in the list. + if (!empty($duplicate_members)) + foreach ($members as $key => $member) + { + if (isset($duplicate_members[$member['member_ip']])) + $members[$key]['duplicate_members'] = $duplicate_members[$member['member_ip']]; + if ($member['member_ip'] != $member['member_ip2'] && isset($duplicate_members[$member['member_ip2']])) + $members[$key]['duplicate_members'] = array_merge($member['duplicate_members'], $duplicate_members[$member['member_ip2']]); + + // Check we don't have lots of the same member. + $member_track = array($member['id_member']); + foreach ($members[$key]['duplicate_members'] as $duplicate_id_member => $duplicate_member) + { + if (in_array($duplicate_member['id'], $member_track)) + { + unset($members[$key]['duplicate_members'][$duplicate_id_member]); + continue; + } + + $member_track[] = $duplicate_member['id']; + } + } +} + +// Generate a random validation code. +function generateValidationCode() +{ + global $smcFunc, $modSettings; + + $request = $smcFunc['db_query']('get_random_number', ' + SELECT RAND()', + array( + ) + ); + + list ($dbRand) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + return substr(preg_replace('/\W/', '', sha1(microtime() . mt_rand() . $dbRand . $modSettings['rand_seed'])), 0, 10); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-MembersOnline.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-MembersOnline.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,256 @@ + array(), + 'list_users_online' => array(), + 'online_groups' => array(), + 'num_guests' => 0, + 'num_spiders' => 0, + 'num_buddies' => 0, + 'num_users_hidden' => 0, + 'num_users_online' => 0, + ); + + // Get any spiders if enabled. + $spiders = array(); + $spider_finds = array(); + if (!empty($modSettings['show_spider_online']) && ($modSettings['show_spider_online'] < 3 || allowedTo('admin_forum')) && !empty($modSettings['spider_name_cache'])) + $spiders = unserialize($modSettings['spider_name_cache']); + + // Load the users online right now. + $request = $smcFunc['db_query']('', ' + SELECT + lo.id_member, lo.log_time, lo.id_spider, mem.real_name, mem.member_name, mem.show_online, + mg.online_color, mg.id_group, mg.group_name + FROM {db_prefix}log_online AS lo + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lo.id_member) + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_mem_group} THEN mem.id_post_group ELSE mem.id_group END)', + array( + 'reg_mem_group' => 0, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($row['real_name'])) + { + // Do we think it's a spider? + if ($row['id_spider'] && isset($spiders[$row['id_spider']])) + { + $spider_finds[$row['id_spider']] = isset($spider_finds[$row['id_spider']]) ? $spider_finds[$row['id_spider']] + 1 : 1; + $membersOnlineStats['num_spiders']++; + } + // Guests are only nice for statistics. + $membersOnlineStats['num_guests']++; + + continue; + } + + elseif (empty($row['show_online']) && empty($membersOnlineOptions['show_hidden'])) + { + // Just increase the stats and don't add this hidden user to any list. + $membersOnlineStats['num_users_hidden']++; + continue; + } + + // Some basic color coding... + if (!empty($row['online_color'])) + $link = '' . $row['real_name'] . ''; + else + $link = '' . $row['real_name'] . ''; + + // Buddies get counted and highlighted. + $is_buddy = in_array($row['id_member'], $user_info['buddies']); + if ($is_buddy) + { + $membersOnlineStats['num_buddies']++; + $link = '' . $link . ''; + } + + // A lot of useful information for each member. + $membersOnlineStats['users_online'][$row[$membersOnlineOptions['sort']] . $row['member_name']] = array( + 'id' => $row['id_member'], + 'username' => $row['member_name'], + 'name' => $row['real_name'], + 'group' => $row['id_group'], + 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => $link, + 'is_buddy' => $is_buddy, + 'hidden' => empty($row['show_online']), + 'is_last' => false, + ); + + // This is the compact version, simply implode it to show. + $membersOnlineStats['list_users_online'][$row[$membersOnlineOptions['sort']] . $row['member_name']] = empty($row['show_online']) ? '' . $link . '' : $link; + + // Store all distinct (primary) membergroups that are shown. + if (!isset($membersOnlineStats['online_groups'][$row['id_group']])) + $membersOnlineStats['online_groups'][$row['id_group']] = array( + 'id' => $row['id_group'], + 'name' => $row['group_name'], + 'color' => $row['online_color'] + ); + } + $smcFunc['db_free_result']($request); + + // If there are spiders only and we're showing the detail, add them to the online list - at the bottom. + if (!empty($spider_finds) && $modSettings['show_spider_online'] > 1) + foreach ($spider_finds as $id => $count) + { + $link = $spiders[$id] . ($count > 1 ? ' (' . $count . ')' : ''); + $sort = $membersOnlineOptions['sort'] = 'log_time' && $membersOnlineOptions['reverse_sort'] ? 0 : 'zzz_'; + $membersOnlineStats['users_online'][$sort . $spiders[$id]] = array( + 'id' => 0, + 'username' => $spiders[$id], + 'name' => $link, + 'group' => $txt['spiders'], + 'href' => '', + 'link' => $link, + 'is_buddy' => false, + 'hidden' => false, + 'is_last' => false, + ); + $membersOnlineStats['list_users_online'][$sort . $spiders[$id]] = $link; + } + + // Time to sort the list a bit. + if (!empty($membersOnlineStats['users_online'])) + { + // Determine the sort direction. + $sortFunction = empty($membersOnlineOptions['reverse_sort']) ? 'ksort' : 'krsort'; + + // Sort the two lists. + $sortFunction($membersOnlineStats['users_online']); + $sortFunction($membersOnlineStats['list_users_online']); + + // Mark the last list item as 'is_last'. + $userKeys = array_keys($membersOnlineStats['users_online']); + $membersOnlineStats['users_online'][end($userKeys)]['is_last'] = true; + } + + // Also sort the membergroups. + ksort($membersOnlineStats['online_groups']); + + // Hidden and non-hidden members make up all online members. + $membersOnlineStats['num_users_online'] = count($membersOnlineStats['users_online']) + $membersOnlineStats['num_users_hidden'] - (isset($modSettings['show_spider_online']) && $modSettings['show_spider_online'] > 1 ? count($spider_finds) : 0); + + return $membersOnlineStats; +} + +// Check if the number of users online is a record and store it. +function trackStatsUsersOnline($total_users_online) +{ + global $modSettings, $smcFunc; + + $settingsToUpdate = array(); + + // More members on now than ever were? Update it! + if (!isset($modSettings['mostOnline']) || $total_users_online >= $modSettings['mostOnline']) + $settingsToUpdate = array( + 'mostOnline' => $total_users_online, + 'mostDate' => time() + ); + + $date = strftime('%Y-%m-%d', forum_time(false)); + + // No entry exists for today yet? + if (!isset($modSettings['mostOnlineUpdated']) || $modSettings['mostOnlineUpdated'] != $date) + { + $request = $smcFunc['db_query']('', ' + SELECT most_on + FROM {db_prefix}log_activity + WHERE date = {date:date} + LIMIT 1', + array( + 'date' => $date, + ) + ); + + // The log_activity hasn't got an entry for today? + if ($smcFunc['db_num_rows']($request) === 0) + { + $smcFunc['db_insert']('ignore', + '{db_prefix}log_activity', + array('date' => 'date', 'most_on' => 'int'), + array($date, $total_users_online), + array('date') + ); + } + // There's an entry in log_activity on today... + else + { + list ($modSettings['mostOnlineToday']) = $smcFunc['db_fetch_row']($request); + + if ($total_users_online > $modSettings['mostOnlineToday']) + trackStats(array('most_on' => $total_users_online)); + + $total_users_online = max($total_users_online, $modSettings['mostOnlineToday']); + } + $smcFunc['db_free_result']($request); + + $settingsToUpdate['mostOnlineUpdated'] = $date; + $settingsToUpdate['mostOnlineToday'] = $total_users_online; + } + + // Highest number of users online today? + elseif ($total_users_online > $modSettings['mostOnlineToday']) + { + trackStats(array('most_on' => $total_users_online)); + $settingsToUpdate['mostOnlineToday'] = $total_users_online; + } + + if (!empty($settingsToUpdate)) + updateSettings($settingsToUpdate); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Menu.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Menu.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,300 @@ + 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + array( + array( + $user_info['id'], + $settings['theme_id'], + 'use_sidebar_menu', + empty($options['use_sidebar_menu']) ? '1' : '0', + ), + ), + array('id_member', 'id_theme', 'variable') + ); + + // Clear the theme settings cache for this user. + $themes = explode(',', $modSettings['knownThemes']); + foreach ($themes as $theme) + cache_put_data('theme_settings-' . $theme . ':' . $user_info['id'], null, 60); + + // Redirect as this seems to work best. + $redirect_url = isset($menuOptions['toggle_redirect_url']) ? $menuOptions['toggle_redirect_url'] : 'action=' . (isset($_GET['action']) ? $_GET['action'] : 'admin') . ';area=' . (isset($_GET['area']) ? $_GET['area'] : 'index') . ';sa=' . (isset($_GET['sa']) ? $_GET['sa'] : 'settings') . (isset($_GET['u']) ? ';u=' . $_GET['u'] : '') . ';' . $context['session_var'] . '=' . $context['session_id']; + redirectexit($redirect_url); + } + + // Work out where we should get our images from. + $context['menu_image_path'] = file_exists($settings['theme_dir'] . '/images/admin/change_menu.png') ? $settings['images_url'] . '/admin' : $settings['default_images_url'] . '/admin'; + + /* Note menuData is array of form: + + Possible fields: + For Section: + string $title: Section title. + bool $enabled: Should section be shown? + array $areas: Array of areas within this section. + array $permission: Permission required to access the whole section. + + For Areas: + array $permission: Array of permissions to determine who can access this area. + string $label: Optional text string for link (Otherwise $txt[$index] will be used) + string $file: Name of source file required for this area. + string $function: Function to call when area is selected. + string $custom_url: URL to use for this menu item. + bool $enabled: Should this area even be accessible? + bool $hidden: Should this area be visible? + string $select: If set this item will not be displayed - instead the item indexed here shall be. + array $subsections: Array of subsections from this area. + + For Subsections: + string 0: Text label for this subsection. + array 1: Array of permissions to check for this subsection. + bool 2: Is this the default subaction - if not set for any will default to first... + bool enabled: Bool to say whether this should be enabled or not. + */ + + // Every menu gets a unique ID, these are shown in first in, first out order. + $context['max_menu_id'] = isset($context['max_menu_id']) ? $context['max_menu_id'] + 1 : 1; + + // This will be all the data for this menu - and we'll make a shortcut to it to aid readability here. + $context['menu_data_' . $context['max_menu_id']] = array(); + $menu_context = &$context['menu_data_' . $context['max_menu_id']]; + + // What is the general action of this menu (i.e. $scripturl?action=XXXX. + $menu_context['current_action'] = isset($menuOptions['action']) ? $menuOptions['action'] : $context['current_action']; + + // What is the current area selected? + if (isset($menuOptions['current_area']) || isset($_GET['area'])) + $menu_context['current_area'] = isset($menuOptions['current_area']) ? $menuOptions['current_area'] : $_GET['area']; + + // Build a list of additional parameters that should go in the URL. + $menu_context['extra_parameters'] = ''; + if (!empty($menuOptions['extra_url_parameters'])) + foreach ($menuOptions['extra_url_parameters'] as $key => $value) + $menu_context['extra_parameters'] .= ';' . $key . '=' . $value; + + // Only include the session ID in the URL if it's strictly necessary. + if (empty($menuOptions['disable_url_session_check'])) + $menu_context['extra_parameters'] .= ';' . $context['session_var'] . '=' . $context['session_id']; + + $include_data = array(); + + // Now setup the context correctly. + foreach ($menuData as $section_id => $section) + { + // Is this enabled - or has as permission check - which fails? + if ((isset($section['enabled']) && $section['enabled'] == false) || (isset($section['permission']) && !allowedTo($section['permission']))) + continue; + + // Now we cycle through the sections to pick the right area. + foreach ($section['areas'] as $area_id => $area) + { + // Can we do this? + if ((!isset($area['enabled']) || $area['enabled'] != false) && (empty($area['permission']) || allowedTo($area['permission']))) + { + // Add it to the context... if it has some form of name! + if (isset($area['label']) || (isset($txt[$area_id]) && !isset($area['select']))) + { + // If we haven't got an area then the first valid one is our choice. + if (!isset($menu_context['current_area'])) + { + $menu_context['current_area'] = $area_id; + $include_data = $area; + } + + // If this is hidden from view don't do the rest. + if (empty($area['hidden'])) + { + // First time this section? + if (!isset($menu_context['sections'][$section_id])) + $menu_context['sections'][$section_id]['title'] = $section['title']; + + $menu_context['sections'][$section_id]['areas'][$area_id] = array('label' => isset($area['label']) ? $area['label'] : $txt[$area_id]); + // We'll need the ID as well... + $menu_context['sections'][$section_id]['id'] = $section_id; + // Does it have a custom URL? + if (isset($area['custom_url'])) + $menu_context['sections'][$section_id]['areas'][$area_id]['url'] = $area['custom_url']; + + // Does this area have its own icon? + if (!isset($area['force_menu_into_arms_of_another_menu']) && $user_info['name'] == 'iamanoompaloompa') + $menu_context['sections'][$section_id]['areas'][$area_id] = unserialize(base64_decode('YTozOntzOjU6ImxhYmVsIjtzOjEyOiJPb21wYSBMb29tcGEiO3M6MzoidXJsIjtzOjQzOiJodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL09vbXBhX0xvb21wYXM/IjtzOjQ6Imljb24iO3M6ODY6IjxpbWcgc3JjPSJodHRwOi8vd3d3LnNpbXBsZW1hY2hpbmVzLm9yZy9pbWFnZXMvb29tcGEuZ2lmIiBhbHQ9IkknbSBhbiBPb21wYSBMb29tcGEiIC8+Ijt9')); + elseif (isset($area['icon'])) + $menu_context['sections'][$section_id]['areas'][$area_id]['icon'] = '  '; + else + $menu_context['sections'][$section_id]['areas'][$area_id]['icon'] = ''; + + // Did it have subsections? + if (!empty($area['subsections'])) + { + $menu_context['sections'][$section_id]['areas'][$area_id]['subsections'] = array(); + $first_sa = $last_sa = null; + foreach ($area['subsections'] as $sa => $sub) + { + if ((empty($sub[1]) || allowedTo($sub[1])) && (!isset($sub['enabled']) || !empty($sub['enabled']))) + { + if ($first_sa == null) + $first_sa = $sa; + + $menu_context['sections'][$section_id]['areas'][$area_id]['subsections'][$sa] = array('label' => $sub[0]); + // Custom URL? + if (isset($sub['url'])) + $menu_context['sections'][$section_id]['areas'][$area_id]['subsections'][$sa]['url'] = $sub['url']; + + // A bit complicated - but is this set? + if ($menu_context['current_area'] == $area_id) + { + // Save which is the first... + if (empty($first_sa)) + $first_sa = $sa; + + // Is this the current subsection? + if (isset($_REQUEST['sa']) && $_REQUEST['sa'] == $sa) + $menu_context['current_subsection'] = $sa; + // Otherwise is it the default? + elseif (!isset($menu_context['current_subsection']) && !empty($sub[2])) + $menu_context['current_subsection'] = $sa; + } + + // Let's assume this is the last, for now. + $last_sa = $sa; + } + // Mark it as disabled... + else + $menu_context['sections'][$section_id]['areas'][$area_id]['subsections'][$sa]['disabled'] = true; + } + + // Set which one is first, last and selected in the group. + if (!empty($menu_context['sections'][$section_id]['areas'][$area_id]['subsections'])) + { + $menu_context['sections'][$section_id]['areas'][$area_id]['subsections'][$context['right_to_left'] ? $last_sa : $first_sa]['is_first'] = true; + $menu_context['sections'][$section_id]['areas'][$area_id]['subsections'][$context['right_to_left'] ? $first_sa : $last_sa]['is_last'] = true; + + if ($menu_context['current_area'] == $area_id && !isset($menu_context['current_subsection'])) + $menu_context['current_subsection'] = $first_sa; + } + } + } + } + + // Is this the current section? + if ($menu_context['current_area'] == $area_id && empty($found_section)) + { + // Only do this once? + $found_section = true; + + // Update the context if required - as we can have areas pretending to be others. ;) + $menu_context['current_section'] = $section_id; + $menu_context['current_area'] = isset($area['select']) ? $area['select'] : $area_id; + + // This will be the data we return. + $include_data = $area; + } + // Make sure we have something in case it's an invalid area. + elseif (empty($found_section) && empty($include_data)) + { + $menu_context['current_section'] = $section_id; + $backup_area = isset($area['select']) ? $area['select'] : $area_id; + $include_data = $area; + } + } + } + } + + // Should we use a custom base url, or use the default? + $menu_context['base_url'] = isset($menuOptions['base_url']) ? $menuOptions['base_url'] : $scripturl . '?action=' . $menu_context['current_action']; + + // What about the toggle url? + $menu_context['toggle_url'] = isset($menuOptions['toggle_url']) ? $menuOptions['toggle_url'] : $menu_context['base_url'] . (!empty($menu_context['current_area']) ? ';area=' . $menu_context['current_area'] : '') . (!empty($menu_context['current_subsection']) ? ';sa=' . $menu_context['current_subsection'] : '') . $menu_context['extra_parameters'] . ';togglebar'; + + // If we didn't find the area we were looking for go to a default one. + if (isset($backup_area) && empty($found_section)) + $menu_context['current_area'] = $backup_area; + + // If still no data then return - nothing to show! + if (empty($menu_context['sections'])) + { + // Never happened! + $context['max_menu_id']--; + if ($context['max_menu_id'] == 0) + unset($context['max_menu_id']); + + return false; + } + + // What type of menu is this? + if (empty($menuOptions['menu_type'])) + { + $menuOptions['menu_type'] = '_' . (empty($options['use_sidebar_menu']) ? 'dropdown' : 'sidebar'); + $menu_context['can_toggle_drop_down'] = !$user_info['is_guest'] && isset($settings['theme_version']) && $settings['theme_version'] >= 2.0; + } + else + $menu_context['can_toggle_drop_down'] = !empty($menuOptions['can_toggle_drop_down']); + + // Almost there - load the template and add to the template layers. + if (!WIRELESS) + { + loadTemplate(isset($menuOptions['template_name']) ? $menuOptions['template_name'] : 'GenericMenu'); + $menu_context['layer_name'] = (isset($menuOptions['layer_name']) ? $menuOptions['layer_name'] : 'generic_menu') . $menuOptions['menu_type']; + $context['template_layers'][] = $menu_context['layer_name']; + } + + // Check we had something - for sanity sake. + if (empty($include_data)) + return false; + + // Finally - return information on the selected item. + $include_data += array( + 'current_action' => $menu_context['current_action'], + 'current_area' => $menu_context['current_area'], + 'current_section' => $menu_context['current_section'], + 'current_subsection' => !empty($menu_context['current_subsection']) ? $menu_context['current_subsection'] : '', + ); + + return $include_data; +} + +// Delete a menu. +function destroyMenu($menu_id = 'last') +{ + global $context; + + $menu_name = $menu_id == 'last' && isset($context['max_menu_id']) && isset($context['menu_data_' . $context['max_menu_id']]) ? 'menu_data_' . $context['max_menu_id'] : 'menu_data_' . $menu_id; + if (!isset($context[$menu_name])) + return false; + + $layer_index = array_search($context[$menu_name]['layer_name'], $context['template_layers']); + if ($layer_index !== false) + unset($context['template_layers'][$layer_index]); + + unset($context[$menu_name]); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-MessageIndex.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-MessageIndex.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,84 @@ + $row['id_cat'], + 'name' => $row['cat_name'], + 'boards' => array(), + ); + + $return_value[$row['id_cat']]['boards'][] = array( + 'id' => $row['id_board'], + 'name' => $row['board_name'], + 'child_level' => $row['child_level'], + 'selected' => isset($boardListOptions['selected_board']) && $boardListOptions['selected_board'] == $row['id_board'], + ); + } + } + $smcFunc['db_free_result']($request); + + return $return_value; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-OpenID.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-OpenID.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,609 @@ + $_GET, + 'post' => $_POST, + 'openid_uri' => $openid_url, + 'cookieTime' => $modSettings['cookieTime'], + ); + + $parameters = array( + 'openid.mode=checkid_setup', + 'openid.trust_root=' . urlencode($scripturl), + 'openid.identity=' . urlencode(empty($response_data['delegate']) ? $openid_url : $response_data['delegate']), + 'openid.assoc_handle=' . urlencode($assoc['handle']), + 'openid.return_to=' . urlencode($scripturl . '?action=openidreturn&sa=' . (!empty($return_action) ? $return_action : $_REQUEST['action']) . '&t=' . $request_time . (!empty($save_fields) ? '&sf=' . base64_encode(serialize($save_fields)) : '')), + ); + + // If they are logging in but don't yet have an account or they are registering, let's request some additional information + if (($_REQUEST['action'] == 'login2' && !smf_openid_member_exists($openid_url)) || ($_REQUEST['action'] == 'register' || $_REQUEST['action'] == 'register2')) + { + // Email is required. + $parameters[] = 'openid.sreg.required=email'; + // The rest is just optional. + $parameters[] = 'openid.sreg.optional=nickname,dob,gender'; + } + + $redir_url = $response_data['server'] . '?' . implode('&', $parameters); + + if ($return) + return $redir_url; + else + redirectexit($redir_url); +} + +// Revalidate a user using OpenID. Note that this function will not return when authentication is required. +function smf_openID_revalidate() +{ + global $user_settings; + + if (isset($_SESSION['openid_revalidate_time']) && $_SESSION['openid_revalidate_time'] > time() - 60) + { + unset($_SESSION['openid_revalidate_time']); + return true; + } + else + smf_openID_validate($user_settings['openid_uri'], false, null, 'revalidate'); + + // We shouldn't get here. + trigger_error('Hacking attempt...', E_USER_ERROR); +} + +function smf_openID_getAssociation($server, $handle = null, $no_delete = false) +{ + global $smcFunc; + + if (!$no_delete) + { + // Delete the already expired associations. + $smcFunc['db_query']('openid_delete_assoc_old', ' + DELETE FROM {db_prefix}openid_assoc + WHERE expires <= {int:current_time}', + array( + 'current_time' => time(), + ) + ); + } + + // Get the association that has the longest lifetime from now. + $request = $smcFunc['db_query']('openid_select_assoc', ' + SELECT server_url, handle, secret, issued, expires, assoc_type + FROM {db_prefix}openid_assoc + WHERE server_url = {string:server_url}' . ($handle === null ? '' : ' + AND handle = {string:handle}') . ' + ORDER BY expires DESC', + array( + 'server_url' => $server, + 'handle' => $handle, + ) + ); + + if ($smcFunc['db_num_rows']($request) == 0) + return null; + + $return = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + return $return; +} + +function smf_openID_makeAssociation($server) +{ + global $smcFunc, $modSettings, $p; + + $parameters = array( + 'openid.mode=associate', + ); + + // We'll need to get our keys for the Diffie-Hellman key exchange. + $dh_keys = smf_openID_setup_DH(); + + // If we don't support DH we'll have to see if the provider will accept no encryption. + if ($dh_keys === false) + $parameters[] = 'openid.session_type='; + else + { + $parameters[] = 'openid.session_type=DH-SHA1'; + $parameters[] = 'openid.dh_consumer_public=' . urlencode(base64_encode(long_to_binary($dh_keys['public']))); + $parameters[] = 'openid.assoc_type=HMAC-SHA1'; + } + + // The data to post to the server. + $post_data = implode('&', $parameters); + $data = fetch_web_data($server, $post_data); + + // Parse the data given. + preg_match_all('~^([^:]+):(.+)$~m', $data, $matches); + $assoc_data = array(); + + foreach ($matches[1] as $key => $match) + $assoc_data[$match] = $matches[2][$key]; + + if (!isset($assoc_data['assoc_type']) || (empty($assoc_data['mac_key']) && empty($assoc_data['enc_mac_key']))) + fatal_lang_error('openid_server_bad_response'); + + // Clean things up a bit. + $handle = isset($assoc_data['assoc_handle']) ? $assoc_data['assoc_handle'] : ''; + $issued = time(); + $expires = $issued + min((int)$assoc_data['expires_in'], 60); + $assoc_type = isset($assoc_data['assoc_type']) ? $assoc_data['assoc_type'] : ''; + + // !!! Is this really needed? + foreach (array('dh_server_public', 'enc_mac_key') as $key) + if (isset($assoc_data[$key])) + $assoc_data[$key] = str_replace(' ', '+', $assoc_data[$key]); + + // Figure out the Diffie-Hellman secret. + if (!empty($assoc_data['enc_mac_key'])) + { + $dh_secret = bcpowmod(binary_to_long(base64_decode($assoc_data['dh_server_public'])), $dh_keys['private'], $p); + $secret = base64_encode(binary_xor(sha1_raw(long_to_binary($dh_secret)), base64_decode($assoc_data['enc_mac_key']))); + } + else + $secret = $assoc_data['mac_key']; + + // Store the data + $smcFunc['db_insert']('replace', + '{db_prefix}openid_assoc', + array('server_url' => 'string', 'handle' => 'string', 'secret' => 'string', 'issued' => 'int', 'expires' => 'int', 'assoc_type' => 'string'), + array($server, $handle, $secret, $issued, $expires, $assoc_type), + array('server_url', 'handle') + ); + + return array( + 'server' => $server, + 'handle' => $assoc_data['assoc_handle'], + 'secret' => $secret, + 'issued' => $issued, + 'expires' => $expires, + 'assoc_type' => $assoc_data['assoc_type'], + ); +} + +function smf_openID_removeAssociation($handle) +{ + global $smcFunc; + + $smcFunc['db_query']('openid_remove_association', ' + DELETE FROM {db_prefix}openid_assoc + WHERE handle = {string:handle}', + array( + 'handle' => $handle, + ) + ); +} + +function smf_openID_return() +{ + global $smcFunc, $user_info, $user_profile, $sourcedir, $modSettings, $context, $sc, $user_settings; + + // Is OpenID even enabled? + if (empty($modSettings['enableOpenID'])) + fatal_lang_error('no_access', false); + + if (!isset($_GET['openid_mode'])) + fatal_lang_error('openid_return_no_mode', false); + + // !!! Check for error status! + if ($_GET['openid_mode'] != 'id_res') + fatal_lang_error('openid_not_resolved'); + + // SMF has this annoying habit of removing the + from the base64 encoding. So lets put them back. + foreach (array('openid_assoc_handle', 'openid_invalidate_handle', 'openid_sig', 'sf') as $key) + if (isset($_GET[$key])) + $_GET[$key] = str_replace(' ', '+', $_GET[$key]); + + // Did they tell us to remove any associations? + if (!empty($_GET['openid_invalidate_handle'])) + smf_openid_removeAssociation($_GET['openid_invalidate_handle']); + + $server_info = smf_openid_getServerInfo($_GET['openid_identity']); + + // Get the association data. + $assoc = smf_openID_getAssociation($server_info['server'], $_GET['openid_assoc_handle'], true); + if ($assoc === null) + fatal_lang_error('openid_no_assoc'); + + $secret = base64_decode($assoc['secret']); + + $signed = explode(',', $_GET['openid_signed']); + $verify_str = ''; + foreach ($signed as $sign) + { + $verify_str .= $sign . ':' . strtr($_GET['openid_' . str_replace('.', '_', $sign)], array('&' => '&')) . "\n"; + } + + $verify_str = base64_encode(sha1_hmac($verify_str, $secret)); + + if ($verify_str != $_GET['openid_sig']) + { + fatal_lang_error('openid_sig_invalid', 'critical'); + } + + if (!isset($_SESSION['openid']['saved_data'][$_GET['t']])) + fatal_lang_error('openid_load_data'); + + $openid_uri = $_SESSION['openid']['saved_data'][$_GET['t']]['openid_uri']; + $modSettings['cookieTime'] = $_SESSION['openid']['saved_data'][$_GET['t']]['cookieTime']; + + if (empty($openid_uri)) + fatal_lang_error('openid_load_data'); + + // Any save fields to restore? + $context['openid_save_fields'] = isset($_GET['sf']) ? unserialize(base64_decode($_GET['sf'])) : array(); + + // Is there a user with this OpenID_uri? + $result = $smcFunc['db_query']('', ' + SELECT passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, + openid_uri + FROM {db_prefix}members + WHERE openid_uri = {string:openid_uri}', + array( + 'openid_uri' => $openid_uri, + ) + ); + + $member_found = $smcFunc['db_num_rows']($result); + + if (!$member_found && isset($_GET['sa']) && $_GET['sa'] == 'change_uri' && !empty($_SESSION['new_openid_uri']) && $_SESSION['new_openid_uri'] == $openid_uri) + { + // Update the member. + updateMemberData($user_settings['id_member'], array('openid_uri' => $openid_uri)); + + unset($_SESSION['new_openid_uri']); + $_SESSION['openid'] = array( + 'verified' => true, + 'openid_uri' => $openid_uri, + ); + + // Send them back to profile. + redirectexit('action=profile;area=authentication;updated'); + } + elseif (!$member_found) + { + // Store the received openid info for the user when returned to the registration page. + $_SESSION['openid'] = array( + 'verified' => true, + 'openid_uri' => $openid_uri, + ); + if (isset($_GET['openid_sreg_nickname'])) + $_SESSION['openid']['nickname'] = $_GET['openid_sreg_nickname']; + if (isset($_GET['openid_sreg_email'])) + $_SESSION['openid']['email'] = $_GET['openid_sreg_email']; + if (isset($_GET['openid_sreg_dob'])) + $_SESSION['openid']['dob'] = $_GET['openid_sreg_dob']; + if (isset($_GET['openid_sreg_gender'])) + $_SESSION['openid']['gender'] = $_GET['openid_sreg_gender']; + + // Were we just verifying the registration state? + if (isset($_GET['sa']) && $_GET['sa'] == 'register2') + { + require_once($sourcedir . '/Register.php'); + return Register2(true); + } + else + redirectexit('action=register'); + } + elseif (isset($_GET['sa']) && $_GET['sa'] == 'revalidate' && $user_settings['openid_uri'] == $openid_uri) + { + $_SESSION['openid_revalidate_time'] = time(); + + // Restore the get data. + require_once($sourcedir . '/Subs-Auth.php'); + $_SESSION['openid']['saved_data'][$_GET['t']]['get']['openid_restore_post'] = $_GET['t']; + $query_string = construct_query_string($_SESSION['openid']['saved_data'][$_GET['t']]['get']); + + redirectexit($query_string); + } + else + { + $user_settings = $smcFunc['db_fetch_assoc']($result); + $smcFunc['db_free_result']($result); + + $user_settings['passwd'] = sha1(strtolower($user_settings['member_name']) . $secret); + $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); + + updateMemberData($user_settings['id_member'], array('passwd' => $user_settings['passwd'], 'password_salt' => $user_settings['password_salt'])); + + // Cleanup on Aisle 5. + $_SESSION['openid'] = array( + 'verified' => true, + 'openid_uri' => $openid_uri, + ); + + require_once($sourcedir . '/LogInOut.php'); + + if (!checkActivation()) + return; + + DoLogin(); + } +} + +function smf_openID_canonize($uri) +{ + // !!! Add in discovery. + + if (strpos($uri, 'http://') !== 0 && strpos($uri, 'https://') !== 0) + $uri = 'http://' . $uri; + + if (strpos(substr($uri, strpos($uri, '://') + 3), '/') === false) + $uri .= '/'; + + return $uri; +} + +function smf_openid_member_exists($url) +{ + global $smcFunc; + + $request = $smcFunc['db_query']('openid_member_exists', ' + SELECT mem.id_member, mem.member_name + FROM {db_prefix}members AS mem + WHERE mem.openid_uri = {string:openid_uri}', + array( + 'openid_uri' => $url, + ) + ); + $member = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + return $member; +} + +// Prepare for a Diffie-Hellman key exchange. +function smf_openID_setup_DH($regenerate = false) +{ + global $p, $g; + + // First off, do we have BC Math available? + if (!function_exists('bcpow')) + return false; + + // Defined in OpenID spec. + $p = '155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443'; + $g = '2'; + + // Make sure the scale is set. + bcscale(0); + + return smf_openID_get_keys($regenerate); +} + +function smf_openID_get_keys($regenerate) +{ + global $modSettings, $p, $g; + + // Ok lets take the easy way out, are their any keys already defined for us? They are changed in the daily maintenance scheduled task. + if (!empty($modSettings['dh_keys']) && !$regenerate) + { + // Sweeeet! + list ($public, $private) = explode("\n", $modSettings['dh_keys']); + return array( + 'public' => base64_decode($public), + 'private' => base64_decode($private), + ); + } + + // Dang it, now I have to do math. And it's not just ordinary math, its the evil big interger math. This will take a few seconds. + $private = smf_openid_generate_private_key(); + $public = bcpowmod($g, $private, $p); + + // Now that we did all that work, lets save it so we don't have to keep doing it. + $keys = array('dh_keys' => base64_encode($public) . "\n" . base64_encode($private)); + updateSettings($keys); + + return array( + 'public' => $public, + 'private' => $private, + ); +} + +function smf_openid_generate_private_key() +{ + global $p; + static $cache = array(); + + $byte_string = long_to_binary($p); + + if (isset($cache[$byte_string])) + list ($dup, $num_bytes) = $cache[$byte_string]; + else + { + $num_bytes = strlen($byte_string) - ($byte_string[0] == "\x00" ? 1 : 0); + + $max_rand = bcpow(256, $num_bytes); + + $dup = bcmod($max_rand, $num_bytes); + + $cache[$byte_string] = array($dup, $num_bytes); + } + + do + { + $str = ''; + for ($i = 0; $i < $num_bytes; $i += 4) + $str .= pack('L', mt_rand()); + + $bytes = "\x00" . $str; + + $num = binary_to_long($bytes); + } while (bccomp($num, $dup) < 0); + + return bcadd(bcmod($num, $p), 1); +} + +function smf_openID_getServerInfo($openid_url) +{ + global $sourcedir; + + require_once($sourcedir . '/Subs-Package.php'); + + // Get the html and parse it for the openid variable which will tell us where to go. + $webdata = fetch_web_data($openid_url); + + if (empty($webdata)) + return false; + + $response_data = array(); + + // Some OpenID servers have strange but still valid HTML which makes our job hard. + if (preg_match_all('~~i', $webdata, $link_matches) == 0) + fatal_lang_error('openid_server_bad_response'); + + foreach ($link_matches[1] as $link_match) + { + if (preg_match('~rel="([\s\S]*?)"~i', $link_match, $rel_match) == 0 || preg_match('~href="([\s\S]*?)"~i', $link_match, $href_match) == 0) + continue; + + $rels = preg_split('~\s+~', $rel_match[1]); + foreach ($rels as $rel) + if (preg_match('~openid2?\.(server|delegate|provider)~i', $rel, $match) != 0) + $response_data[$match[1]] = $href_match[1]; + } + + if (empty($response_data['server'])) + if (empty($response_data['provider'])) + fatal_lang_error('openid_server_bad_response'); + else + $response_data['server'] = $response_data['provider']; + + return $response_data; +} + +function sha1_hmac($data, $key) +{ + + if (strlen($key) > 64) + $key = sha1_raw($key); + + // Pad the key if need be. + $key = str_pad($key, 64, chr(0x00)); + $ipad = str_repeat(chr(0x36), 64); + $opad = str_repeat(chr(0x5c), 64); + $hash1 = sha1_raw(($key ^ $ipad) . $data); + $hmac = sha1_raw(($key ^ $opad) . $hash1); + return $hmac; +} + +function sha1_raw($text) +{ + if (version_compare(PHP_VERSION, '5.0.0') >= 0) + return sha1($text, true); + + $hex = sha1($text); + $raw = ''; + for ($i = 0; $i < 40; $i += 2) + { + $hexcode = substr($hex, $i, 2); + $charcode = (int) base_convert($hexcode, 16, 10); + $raw .= chr($charcode); + } + + return $raw; +} + +function binary_to_long($str) +{ + $bytes = array_merge(unpack('C*', $str)); + + $n = 0; + + foreach ($bytes as $byte) + { + $n = bcmul($n, 256); + $n = bcadd($n, $byte); + } + + return $n; +} + +function long_to_binary($value) +{ + $cmp = bccomp($value, 0); + if ($cmp < 0) + fatal_error('Only non-negative integers allowed.'); + + if ($cmp == 0) + return "\x00"; + + $bytes = array(); + + while (bccomp($value, 0) > 0) + { + array_unshift($bytes, bcmod($value, 256)); + $value = bcdiv($value, 256); + } + + if ($bytes && ($bytes[0] > 127)) + array_unshift($bytes, 0); + + $return = ''; + foreach ($bytes as $byte) + $return .= pack('C', $byte); + + return $return; +} + +function binary_xor($num1, $num2) +{ + $return = ''; + + for ($i = 0; $i < strlen($num2); $i++) + $return .= $num1[$i] ^ $num2[$i]; + + return $return; +} + +// PHP 4 didn't have bcpowmod. +if (!function_exists('bcpowmod') && function_exists('bcpow')) +{ + function bcpowmod($num1, $num2, $num3) + { + return bcmod(bcpow($num1, $num2), $num3); + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Package.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Package.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2995 @@ + $v) + { + if (in_array($k, $octdec)) + $current[$k] = octdec(trim($v)); + else + $current[$k] = trim($v); + } + + $checksum = 256; + for ($i = 0; $i < 148; $i++) + $checksum += ord($header{$i}); + for ($i = 156; $i < 512; $i++) + $checksum += ord($header{$i}); + + if ($current['checksum'] != $checksum) + break; + + $size = ceil($current['size'] / 512); + $current['data'] = substr($data, ++$offset << 9, $current['size']); + $offset += $size; + + // Not a directory and doesn't exist already... + if (substr($current['filename'], -1, 1) != '/' && !file_exists($destination . '/' . $current['filename'])) + $write_this = true; + // File exists... check if it is newer. + elseif (substr($current['filename'], -1, 1) != '/') + $write_this = $overwrite || filemtime($destination . '/' . $current['filename']) < $current['mtime']; + // Folder... create. + elseif ($destination !== null && !$single_file) + { + // Protect from accidental parent directory writing... + $current['filename'] = strtr($current['filename'], array('../' => '', '/..' => '')); + + if (!file_exists($destination . '/' . $current['filename'])) + mktree($destination . '/' . $current['filename'], 0777); + $write_this = false; + } + else + $write_this = false; + + if ($write_this && $destination !== null) + { + if (strpos($current['filename'], '/') !== false && !$single_file) + mktree($destination . '/' . dirname($current['filename']), 0777); + + // Is this the file we're looking for? + if ($single_file && ($destination == $current['filename'] || $destination == '*/' . basename($current['filename']))) + return $current['data']; + // If we're looking for another file, keep going. + elseif ($single_file) + continue; + // Looking for restricted files? + elseif ($files_to_extract !== null && !in_array($current['filename'], $files_to_extract)) + continue; + + package_put_contents($destination . '/' . $current['filename'], $current['data']); + } + + if (substr($current['filename'], -1, 1) != '/') + $return[] = array( + 'filename' => $current['filename'], + 'md5' => md5($current['data']), + 'preview' => substr($current['data'], 0, 100), + 'size' => $current['size'], + 'skipped' => false + ); + } + + if ($destination !== null && !$single_file) + package_flush_cache(); + + if ($single_file) + return false; + else + return $return; +} + +// Extract zip data. If destination is null, return a listing. +function read_zip_data($data, $destination, $single_file = false, $overwrite = false, $files_to_extract = null) +{ + umask(0); + if ($destination !== null && !file_exists($destination) && !$single_file) + mktree($destination, 0777); + + // Look for the PK header... + if (substr($data, 0, 2) != 'PK') + return false; + + // Find the central whosamawhatsit at the end; if there's a comment it's a pain. + if (substr($data, -22, 4) == 'PK' . chr(5) . chr(6)) + $p = -22; + else + { + // Have to find where the comment begins, ugh. + for ($p = -22; $p > -strlen($data); $p--) + { + if (substr($data, $p, 4) == 'PK' . chr(5) . chr(6)) + break; + } + } + + $return = array(); + + // Get the basic zip file info. + $zip_info = unpack('vfiles/Vsize/Voffset', substr($data, $p + 10, 10)); + + $p = $zip_info['offset']; + for ($i = 0; $i < $zip_info['files']; $i++) + { + // Make sure this is a file entry... + if (substr($data, $p, 4) != 'PK' . chr(1) . chr(2)) + return false; + + // Get all the important file information. + $file_info = unpack('Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', substr($data, $p + 16, 30)); + $file_info['filename'] = substr($data, $p + 46, $file_info['filename_len']); + + // Skip all the information we don't care about anyway. + $p += 46 + $file_info['filename_len'] + $file_info['extra_len'] + $file_info['comment_len']; + + // If this is a file, and it doesn't exist.... happy days! + if (substr($file_info['filename'], -1, 1) != '/' && !file_exists($destination . '/' . $file_info['filename'])) + $write_this = true; + // If the file exists, we may not want to overwrite it. + elseif (substr($file_info['filename'], -1, 1) != '/') + $write_this = $overwrite; + // This is a directory, so we're gonna want to create it. (probably...) + elseif ($destination !== null && !$single_file) + { + // Just a little accident prevention, don't mind me. + $file_info['filename'] = strtr($file_info['filename'], array('../' => '', '/..' => '')); + + if (!file_exists($destination . '/' . $file_info['filename'])) + mktree($destination . '/' . $file_info['filename'], 0777); + $write_this = false; + } + else + $write_this = false; + + // Check that the data is there and does exist. + if (substr($data, $file_info['offset'], 4) != 'PK' . chr(3) . chr(4)) + return false; + + // Get the actual compressed data. + $file_info['data'] = substr($data, $file_info['offset'] + 30 + $file_info['filename_len'], $file_info['compressed_size']); + + // Only inflate it if we need to ;). + if ($file_info['compressed_size'] != $file_info['size']) + $file_info['data'] = @gzinflate($file_info['data']); + + // Okay! We can write this file, looks good from here... + if ($write_this && $destination !== null) + { + if (strpos($file_info['filename'], '/') !== false && !$single_file) + mktree($destination . '/' . dirname($file_info['filename']), 0777); + + // If we're looking for a specific file, and this is it... ka-bam, baby. + if ($single_file && ($destination == $file_info['filename'] || $destination == '*/' . basename($file_info['filename']))) + return $file_info['data']; + // Oh? Another file. Fine. You don't like this file, do you? I know how it is. Yeah... just go away. No, don't apologize. I know this file's just not *good enough* for you. + elseif ($single_file) + continue; + // Don't really want this? + elseif ($files_to_extract !== null && !in_array($file_info['filename'], $files_to_extract)) + continue; + + package_put_contents($destination . '/' . $file_info['filename'], $file_info['data']); + } + + if (substr($file_info['filename'], -1, 1) != '/') + $return[] = array( + 'filename' => $file_info['filename'], + 'md5' => md5($file_info['data']), + 'preview' => substr($file_info['data'], 0, 100), + 'size' => $file_info['size'], + 'skipped' => false + ); + } + + if ($destination !== null && !$single_file) + package_flush_cache(); + + if ($single_file) + return false; + else + return $return; +} + +// Checks the existence of a remote file since file_exists() does not do remote. +function url_exists($url) +{ + $a_url = parse_url($url); + + if (!isset($a_url['scheme'])) + return false; + + // Attempt to connect... + $temp = ''; + $fid = fsockopen($a_url['host'], !isset($a_url['port']) ? 80 : $a_url['port'], $temp, $temp, 8); + if (!$fid) + return false; + + fputs($fid, 'HEAD ' . $a_url['path'] . ' HTTP/1.0' . "\r\n" . 'Host: ' . $a_url['host'] . "\r\n\r\n"); + $head = fread($fid, 1024); + fclose($fid); + + return preg_match('~^HTTP/.+\s+(20[01]|30[127])~i', $head) == 1; +} + +// Load the installed packages. +function loadInstalledPackages() +{ + global $boarddir, $smcFunc; + + // First, check that the database is valid, installed.list is still king. + $install_file = implode('', file($boarddir . '/Packages/installed.list')); + if (trim($install_file) == '') + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_packages + SET install_state = {int:not_installed}', + array( + 'not_installed' => 0, + ) + ); + + // Don't have anything left, so send an empty array. + return array(); + } + + // Load the packages from the database - note this is ordered by install time to ensure latest package uninstalled first. + $request = $smcFunc['db_query']('', ' + SELECT id_install, package_id, filename, name, version + FROM {db_prefix}log_packages + WHERE install_state != {int:not_installed} + ORDER BY time_installed DESC', + array( + 'not_installed' => 0, + ) + ); + $installed = array(); + $found = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Already found this? If so don't add it twice! + if (in_array($row['package_id'], $found)) + continue; + + $found[] = $row['package_id']; + + $installed[] = array( + 'id' => $row['id_install'], + 'name' => $row['name'], + 'filename' => $row['filename'], + 'package_id' => $row['package_id'], + 'version' => $row['version'], + ); + } + $smcFunc['db_free_result']($request); + + return $installed; +} + +function getPackageInfo($gzfilename) +{ + global $boarddir; + + // Extract package-info.xml from downloaded file. (*/ is used because it could be in any directory.) + if (strpos($gzfilename, 'http://') !== false) + $packageInfo = read_tgz_data(fetch_web_data($gzfilename, '', true), '*/package-info.xml', true); + else + { + if (!file_exists($boarddir . '/Packages/' . $gzfilename)) + return 'package_get_error_not_found'; + + if (is_file($boarddir . '/Packages/' . $gzfilename)) + $packageInfo = read_tgz_file($boarddir . '/Packages/' . $gzfilename, '*/package-info.xml', true); + elseif (file_exists($boarddir . '/Packages/' . $gzfilename . '/package-info.xml')) + $packageInfo = file_get_contents($boarddir . '/Packages/' . $gzfilename . '/package-info.xml'); + else + return 'package_get_error_missing_xml'; + } + + // Nothing? + if (empty($packageInfo)) + return 'package_get_error_is_zero'; + + // Parse package-info.xml into an xmlArray. + loadClassFile('Class-Package.php'); + $packageInfo = new xmlArray($packageInfo); + + // !!! Error message of some sort? + if (!$packageInfo->exists('package-info[0]')) + return 'package_get_error_packageinfo_corrupt'; + + $packageInfo = $packageInfo->path('package-info[0]'); + + $package = $packageInfo->to_array(); + $package['xml'] = $packageInfo; + $package['filename'] = $gzfilename; + + if (!isset($package['type'])) + $package['type'] = 'modification'; + + return $package; +} + +// Create a chmod control for chmoding files. +function create_chmod_control($chmodFiles = array(), $chmodOptions = array(), $restore_write_status = false) +{ + global $context, $modSettings, $package_ftp, $boarddir, $txt, $sourcedir, $scripturl; + + // If we're restoring the status of existing files prepare the data. + if ($restore_write_status && isset($_SESSION['pack_ftp']) && !empty($_SESSION['pack_ftp']['original_perms'])) + { + function list_restoreFiles($dummy1, $dummy2, $dummy3, $do_change) + { + global $txt; + + $restore_files = array(); + foreach ($_SESSION['pack_ftp']['original_perms'] as $file => $perms) + { + // Check the file still exists, and the permissions were indeed different than now. + $file_permissions = @fileperms($file); + if (!file_exists($file) || $file_permissions == $perms) + { + unset($_SESSION['pack_ftp']['original_perms'][$file]); + continue; + } + + // Are we wanting to change the permission? + if ($do_change && isset($_POST['restore_files']) && in_array($file, $_POST['restore_files'])) + { + // Use FTP if we have it. + if (!empty($package_ftp)) + { + $ftp_file = strtr($file, array($_SESSION['pack_ftp']['root'] => '')); + $package_ftp->chmod($ftp_file, $perms); + } + else + @chmod($file, $perms); + + $new_permissions = @fileperms($file); + $result = $new_permissions == $perms ? 'success' : 'failure'; + unset($_SESSION['pack_ftp']['original_perms'][$file]); + } + elseif ($do_change) + { + $new_permissions = ''; + $result = 'skipped'; + unset($_SESSION['pack_ftp']['original_perms'][$file]); + } + + // Record the results! + $restore_files[] = array( + 'path' => $file, + 'old_perms_raw' => $perms, + 'old_perms' => substr(sprintf('%o', $perms), -4), + 'cur_perms' => substr(sprintf('%o', $file_permissions), -4), + 'new_perms' => isset($new_permissions) ? substr(sprintf('%o', $new_permissions), -4) : '', + 'result' => isset($result) ? $result : '', + 'writable_message' => '' . (@is_writable($file) ? $txt['package_file_perms_writable'] : $txt['package_file_perms_not_writable']) . '', + ); + } + + return $restore_files; + } + + $listOptions = array( + 'id' => 'restore_file_permissions', + 'title' => $txt['package_restore_permissions'], + 'get_items' => array( + 'function' => 'list_restoreFiles', + 'params' => array( + !empty($_POST['restore_perms']), + ), + ), + 'columns' => array( + 'path' => array( + 'header' => array( + 'value' => $txt['package_restore_permissions_filename'], + ), + 'data' => array( + 'db' => 'path', + 'class' => 'smalltext', + ), + ), + 'old_perms' => array( + 'header' => array( + 'value' => $txt['package_restore_permissions_orig_status'], + ), + 'data' => array( + 'db' => 'old_perms', + 'class' => 'smalltext', + ), + ), + 'cur_perms' => array( + 'header' => array( + 'value' => $txt['package_restore_permissions_cur_status'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + $formatTxt = $rowData[\'result\'] == \'\' || $rowData[\'result\'] == \'skipped\' ? $txt[\'package_restore_permissions_pre_change\'] : $txt[\'package_restore_permissions_post_change\']; + return sprintf($formatTxt, $rowData[\'cur_perms\'], $rowData[\'new_perms\'], $rowData[\'writable_message\']); + '), + 'class' => 'smalltext', + ), + ), + 'check' => array( + 'header' => array( + 'value' => '', + ), + 'data' => array( + 'sprintf' => array( + 'format' => '', + 'params' => array( + 'path' => false, + ), + ), + 'style' => 'text-align: center', + ), + ), + 'result' => array( + 'header' => array( + 'value' => $txt['package_restore_permissions_result'], + ), + 'data' => array( + 'function' => create_function('$rowData', ' + global $txt; + + return $txt[\'package_restore_permissions_action_\' . $rowData[\'result\']]; + '), + 'class' => 'smalltext', + ), + ), + ), + 'form' => array( + 'href' => !empty($chmodOptions['destination_url']) ? $chmodOptions['destination_url'] : $scripturl . '?action=admin;area=packages;sa=perms;restore;' . $context['session_var'] . '=' . $context['session_id'], + ), + 'additional_rows' => array( + array( + 'position' => 'below_table_data', + 'value' => '', + 'class' => 'titlebg', + 'style' => 'text-align: right;', + ), + array( + 'position' => 'after_title', + 'value' => '' . $txt['package_restore_permissions_desc'] . '', + 'class' => 'windowbg2', + ), + ), + ); + + // Work out what columns and the like to show. + if (!empty($_POST['restore_perms'])) + { + $listOptions['additional_rows'][1]['value'] = sprintf($txt['package_restore_permissions_action_done'], $scripturl . '?action=admin;area=packages;sa=perms;' . $context['session_var'] . '=' . $context['session_id']); + unset($listOptions['columns']['check'], $listOptions['form'], $listOptions['additional_rows'][0]); + + $context['sub_template'] = 'show_list'; + $context['default_list'] = 'restore_file_permissions'; + } + else + { + unset($listOptions['columns']['result']); + } + + // Create the list for display. + require_once($sourcedir . '/Subs-List.php'); + createList($listOptions); + + // If we just restored permissions then whereever we are, we are now done and dusted. + if (!empty($_POST['restore_perms'])) + obExit(); + } + // Otherwise, it's entirely irrelevant? + elseif ($restore_write_status) + return true; + + // This is where we report what we got up to. + $return_data = array( + 'files' => array( + 'writable' => array(), + 'notwritable' => array(), + ), + ); + + // If we have some FTP information already, then let's assume it was required and try to get ourselves connected. + if (!empty($_SESSION['pack_ftp']['connected'])) + { + // Load the file containing the ftp_connection class. + loadClassFile('Class-Package.php'); + + $package_ftp = new ftp_connection($_SESSION['pack_ftp']['server'], $_SESSION['pack_ftp']['port'], $_SESSION['pack_ftp']['username'], package_crypt($_SESSION['pack_ftp']['password'])); + } + + // Just got a submission did we? + if (empty($package_ftp) && isset($_POST['ftp_username'])) + { + loadClassFile('Class-Package.php'); + $ftp = new ftp_connection($_POST['ftp_server'], $_POST['ftp_port'], $_POST['ftp_username'], $_POST['ftp_password']); + + // We're connected, jolly good! + if ($ftp->error === false) + { + // Common mistake, so let's try to remedy it... + if (!$ftp->chdir($_POST['ftp_path'])) + { + $ftp_error = $ftp->last_message; + $ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $_POST['ftp_path'])); + } + + if (!in_array($_POST['ftp_path'], array('', '/'))) + { + $ftp_root = strtr($boarddir, array($_POST['ftp_path'] => '')); + if (substr($ftp_root, -1) == '/' && ($_POST['ftp_path'] == '' || substr($_POST['ftp_path'], 0, 1) == '/')) + $ftp_root = substr($ftp_root, 0, -1); + } + else + $ftp_root = $boarddir; + + $_SESSION['pack_ftp'] = array( + 'server' => $_POST['ftp_server'], + 'port' => $_POST['ftp_port'], + 'username' => $_POST['ftp_username'], + 'password' => package_crypt($_POST['ftp_password']), + 'path' => $_POST['ftp_path'], + 'root' => $ftp_root, + 'connected' => true, + ); + + if (!isset($modSettings['package_path']) || $modSettings['package_path'] != $_POST['ftp_path']) + updateSettings(array('package_path' => $_POST['ftp_path'])); + + // This is now the primary connection. + $package_ftp = $ftp; + } + } + + // Now try to simply make the files writable, with whatever we might have. + if (!empty($chmodFiles)) + { + foreach ($chmodFiles as $k => $file) + { + // Sometimes this can somehow happen maybe? + if (empty($file)) + unset($chmodFiles[$k]); + // Already writable? + elseif (@is_writable($file)) + $return_data['files']['writable'][] = $file; + else + { + // Now try to change that. + $return_data['files'][package_chmod($file, 'writable', true) ? 'writable' : 'notwritable'][] = $file; + } + } + } + + // Have we still got nasty files which ain't writable? Dear me we need more FTP good sir. + if (empty($package_ftp) && (!empty($return_data['files']['notwritable']) || !empty($chmodOptions['force_find_error']))) + { + if (!isset($ftp) || $ftp->error !== false) + { + if (!isset($ftp)) + { + loadClassFile('Class-Package.php'); + $ftp = new ftp_connection(null); + } + elseif ($ftp->error !== false && !isset($ftp_error)) + $ftp_error = $ftp->last_message === null ? '' : $ftp->last_message; + + list ($username, $detect_path, $found_path) = $ftp->detect_path($boarddir); + + if ($found_path) + $_POST['ftp_path'] = $detect_path; + elseif (!isset($_POST['ftp_path'])) + $_POST['ftp_path'] = isset($modSettings['package_path']) ? $modSettings['package_path'] : $detect_path; + + if (!isset($_POST['ftp_username'])) + $_POST['ftp_username'] = $username; + } + + $context['package_ftp'] = array( + 'server' => isset($_POST['ftp_server']) ? $_POST['ftp_server'] : (isset($modSettings['package_server']) ? $modSettings['package_server'] : 'localhost'), + 'port' => isset($_POST['ftp_port']) ? $_POST['ftp_port'] : (isset($modSettings['package_port']) ? $modSettings['package_port'] : '21'), + 'username' => isset($_POST['ftp_username']) ? $_POST['ftp_username'] : (isset($modSettings['package_username']) ? $modSettings['package_username'] : ''), + 'path' => $_POST['ftp_path'], + 'error' => empty($ftp_error) ? null : $ftp_error, + 'destination' => !empty($chmodOptions['destination_url']) ? $chmodOptions['destination_url'] : '', + ); + + // Which files failed? + if (!isset($context['notwritable_files'])) + $context['notwritable_files'] = array(); + $context['notwritable_files'] = array_merge($context['notwritable_files'], $return_data['files']['notwritable']); + + // Sent here to die? + if (!empty($chmodOptions['crash_on_error'])) + { + $context['page_title'] = $txt['package_ftp_necessary']; + $context['sub_template'] = 'ftp_required'; + obExit(); + } + } + + return $return_data; +} + +function packageRequireFTP($destination_url, $files = null, $return = false) +{ + global $context, $modSettings, $package_ftp, $boarddir, $txt; + + // Try to make them writable the manual way. + if ($files !== null) + { + foreach ($files as $k => $file) + { + // If this file doesn't exist, then we actually want to look at the directory, no? + if (!file_exists($file)) + $file = dirname($file); + + // This looks odd, but it's an attempt to work around PHP suExec. + if (!@is_writable($file)) + @chmod($file, 0755); + if (!@is_writable($file)) + @chmod($file, 0777); + if (!@is_writable(dirname($file))) + @chmod($file, 0755); + if (!@is_writable(dirname($file))) + @chmod($file, 0777); + + $fp = is_dir($file) ? @opendir($file) : @fopen($file, 'rb'); + if (@is_writable($file) && $fp) + { + unset($files[$k]); + if (!is_dir($file)) + fclose($fp); + else + closedir($fp); + } + } + + // No FTP required! + if (empty($files)) + return array(); + } + + // They've opted to not use FTP, and try anyway. + if (isset($_SESSION['pack_ftp']) && $_SESSION['pack_ftp'] == false) + { + if ($files === null) + return array(); + + foreach ($files as $k => $file) + { + // This looks odd, but it's an attempt to work around PHP suExec. + if (!file_exists($file)) + { + mktree(dirname($file), 0755); + @touch($file); + @chmod($file, 0755); + } + + if (!@is_writable($file)) + @chmod($file, 0777); + if (!@is_writable(dirname($file))) + @chmod(dirname($file), 0777); + + if (@is_writable($file)) + unset($files[$k]); + } + + return $files; + } + elseif (isset($_SESSION['pack_ftp'])) + { + // Load the file containing the ftp_connection class. + loadClassFile('Class-Package.php'); + + $package_ftp = new ftp_connection($_SESSION['pack_ftp']['server'], $_SESSION['pack_ftp']['port'], $_SESSION['pack_ftp']['username'], package_crypt($_SESSION['pack_ftp']['password'])); + + if ($files === null) + return array(); + + foreach ($files as $k => $file) + { + $ftp_file = strtr($file, array($_SESSION['pack_ftp']['root'] => '')); + + // This looks odd, but it's an attempt to work around PHP suExec. + if (!file_exists($file)) + { + mktree(dirname($file), 0755); + $package_ftp->create_file($ftp_file); + $package_ftp->chmod($ftp_file, 0755); + } + + if (!@is_writable($file)) + $package_ftp->chmod($ftp_file, 0777); + if (!@is_writable(dirname($file))) + $package_ftp->chmod(dirname($ftp_file), 0777); + + if (@is_writable($file)) + unset($files[$k]); + } + + return $files; + } + + if (isset($_POST['ftp_none'])) + { + $_SESSION['pack_ftp'] = false; + + $files = packageRequireFTP($destination_url, $files, $return); + return $files; + } + elseif (isset($_POST['ftp_username'])) + { + loadClassFile('Class-Package.php'); + $ftp = new ftp_connection($_POST['ftp_server'], $_POST['ftp_port'], $_POST['ftp_username'], $_POST['ftp_password']); + + if ($ftp->error === false) + { + // Common mistake, so let's try to remedy it... + if (!$ftp->chdir($_POST['ftp_path'])) + { + $ftp_error = $ftp->last_message; + $ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $_POST['ftp_path'])); + } + } + } + + if (!isset($ftp) || $ftp->error !== false) + { + if (!isset($ftp)) + { + loadClassFile('Class-Package.php'); + $ftp = new ftp_connection(null); + } + elseif ($ftp->error !== false && !isset($ftp_error)) + $ftp_error = $ftp->last_message === null ? '' : $ftp->last_message; + + list ($username, $detect_path, $found_path) = $ftp->detect_path($boarddir); + + if ($found_path) + $_POST['ftp_path'] = $detect_path; + elseif (!isset($_POST['ftp_path'])) + $_POST['ftp_path'] = isset($modSettings['package_path']) ? $modSettings['package_path'] : $detect_path; + + if (!isset($_POST['ftp_username'])) + $_POST['ftp_username'] = $username; + + $context['package_ftp'] = array( + 'server' => isset($_POST['ftp_server']) ? $_POST['ftp_server'] : (isset($modSettings['package_server']) ? $modSettings['package_server'] : 'localhost'), + 'port' => isset($_POST['ftp_port']) ? $_POST['ftp_port'] : (isset($modSettings['package_port']) ? $modSettings['package_port'] : '21'), + 'username' => isset($_POST['ftp_username']) ? $_POST['ftp_username'] : (isset($modSettings['package_username']) ? $modSettings['package_username'] : ''), + 'path' => $_POST['ftp_path'], + 'error' => empty($ftp_error) ? null : $ftp_error, + 'destination' => $destination_url, + ); + + // If we're returning dump out here. + if ($return) + return $files; + + $context['page_title'] = $txt['package_ftp_necessary']; + $context['sub_template'] = 'ftp_required'; + obExit(); + } + else + { + if (!in_array($_POST['ftp_path'], array('', '/'))) + { + $ftp_root = strtr($boarddir, array($_POST['ftp_path'] => '')); + if (substr($ftp_root, -1) == '/' && ($_POST['ftp_path'] == '' || substr($_POST['ftp_path'], 0, 1) == '/')) + $ftp_root = substr($ftp_root, 0, -1); + } + else + $ftp_root = $boarddir; + + $_SESSION['pack_ftp'] = array( + 'server' => $_POST['ftp_server'], + 'port' => $_POST['ftp_port'], + 'username' => $_POST['ftp_username'], + 'password' => package_crypt($_POST['ftp_password']), + 'path' => $_POST['ftp_path'], + 'root' => $ftp_root, + ); + + if (!isset($modSettings['package_path']) || $modSettings['package_path'] != $_POST['ftp_path']) + updateSettings(array('package_path' => $_POST['ftp_path'])); + + $files = packageRequireFTP($destination_url, $files, $return); + } + + return $files; +} + +// Parses a package-info.xml file - method can be 'install', 'upgrade', or 'uninstall'. +function parsePackageInfo(&$packageXML, $testing_only = true, $method = 'install', $previous_version = '') +{ + global $boarddir, $forum_version, $context, $temp_path, $language; + + // Mayday! That action doesn't exist!! + if (empty($packageXML) || !$packageXML->exists($method)) + return array(); + + // We haven't found the package script yet... + $script = false; + $the_version = strtr($forum_version, array('SMF ' => '')); + + // Emulation support... + if (!empty($_SESSION['version_emulate'])) + $the_version = $_SESSION['version_emulate']; + + // Get all the versions of this method and find the right one. + $these_methods = $packageXML->set($method); + foreach ($these_methods as $this_method) + { + // They specified certain versions this part is for. + if ($this_method->exists('@for')) + { + // Don't keep going if this won't work for this version of SMF. + if (!matchPackageVersion($the_version, $this_method->fetch('@for'))) + continue; + } + + // Upgrades may go from a certain old version of the mod. + if ($method == 'upgrade' && $this_method->exists('@from')) + { + // Well, this is for the wrong old version... + if (!matchPackageVersion($previous_version, $this_method->fetch('@from'))) + continue; + } + + // We've found it! + $script = $this_method; + break; + } + + // Bad news, a matching script wasn't found! + if ($script === false) + return array(); + + // Find all the actions in this method - in theory, these should only be allowed actions. (* means all.) + $actions = $script->set('*'); + $return = array(); + + $temp_auto = 0; + $temp_path = $boarddir . '/Packages/temp/' . (isset($context['base_path']) ? $context['base_path'] : ''); + + $context['readmes'] = array(); + // This is the testing phase... nothing shall be done yet. + foreach ($actions as $action) + { + $actionType = $action->name(); + + if ($actionType == 'readme' || $actionType == 'code' || $actionType == 'database' || $actionType == 'modification' || $actionType == 'redirect') + { + // Allow for translated readme files. + if ($actionType == 'readme') + { + if ($action->exists('@lang')) + { + // Auto-select a readme language based on either request variable or current language. + if ((isset($_REQUEST['readme']) && $action->fetch('@lang') == $_REQUEST['readme']) || (!isset($_REQUEST['readme']) && $action->fetch('@lang') == $language)) + { + // In case the user put the readme blocks in the wrong order. + if (isset($context['readmes']['selected']) && $context['readmes']['selected'] == 'default') + $context['readmes'][] = 'default'; + + $context['readmes']['selected'] = htmlspecialchars($action->fetch('@lang')); + } + else + { + // We don't want this readme now, but we'll allow the user to select to read it. + $context['readmes'][] = htmlspecialchars($action->fetch('@lang')); + continue; + } + } + // Fallback readme. Without lang parameter. + else + { + + // Already selected a readme. + if (isset($context['readmes']['selected'])) + { + $context['readmes'][] = 'default'; + continue; + } + else + $context['readmes']['selected'] = 'default'; + } + } + + // !!! TODO: Make sure the file actually exists? Might not work when testing? + if ($action->exists('@type') && $action->fetch('@type') == 'inline') + { + $filename = $temp_path . '$auto_' . $temp_auto++ . ($actionType == 'readme' || $actionType == 'redirect' ? '.txt' : ($actionType == 'code' || $actionType == 'database' ? '.php' : '.mod')); + package_put_contents($filename, $action->fetch('.')); + $filename = strtr($filename, array($temp_path => '')); + } + else + $filename = $action->fetch('.'); + + $return[] = array( + 'type' => $actionType, + 'filename' => $filename, + 'description' => '', + 'reverse' => $action->exists('@reverse') && $action->fetch('@reverse') == 'true', + 'boardmod' => $action->exists('@format') && $action->fetch('@format') == 'boardmod', + 'redirect_url' => $action->exists('@url') ? $action->fetch('@url') : '', + 'redirect_timeout' => $action->exists('@timeout') ? (int) $action->fetch('@timeout') : '', + 'parse_bbc' => $action->exists('@parsebbc') && $action->fetch('@parsebbc') == 'true', + 'language' => ($actionType == 'readme' && $action->exists('@lang') && $action->fetch('@lang') == $language) ? $language : '', + ); + + continue; + } + elseif ($actionType == 'error') + { + $return[] = array( + 'type' => 'error', + ); + } + + $this_action = &$return[]; + $this_action = array( + 'type' => $actionType, + 'filename' => $action->fetch('@name'), + 'description' => $action->fetch('.') + ); + + // If there is a destination, make sure it makes sense. + if (substr($actionType, 0, 6) != 'remove') + { + $this_action['unparsed_destination'] = $action->fetch('@destination'); + $this_action['destination'] = parse_path($action->fetch('@destination')) . '/' . basename($this_action['filename']); + } + else + { + $this_action['unparsed_filename'] = $this_action['filename']; + $this_action['filename'] = parse_path($this_action['filename']); + } + + // If we're moving or requiring (copying) a file. + if (substr($actionType, 0, 4) == 'move' || substr($actionType, 0, 7) == 'require') + { + if ($action->exists('@from')) + $this_action['source'] = parse_path($action->fetch('@from')); + else + $this_action['source'] = $temp_path . $this_action['filename']; + } + + // Check if these things can be done. (chmod's etc.) + if ($actionType == 'create-dir') + { + if (!mktree($this_action['destination'], false)) + { + $temp = $this_action['destination']; + while (!file_exists($temp) && strlen($temp) > 1) + $temp = dirname($temp); + + $return[] = array( + 'type' => 'chmod', + 'filename' => $temp + ); + } + } + elseif ($actionType == 'create-file') + { + if (!mktree(dirname($this_action['destination']), false)) + { + $temp = dirname($this_action['destination']); + while (!file_exists($temp) && strlen($temp) > 1) + $temp = dirname($temp); + + $return[] = array( + 'type' => 'chmod', + 'filename' => $temp + ); + } + + if (!is_writable($this_action['destination']) && (file_exists($this_action['destination']) || !is_writable(dirname($this_action['destination'])))) + $return[] = array( + 'type' => 'chmod', + 'filename' => $this_action['destination'] + ); + } + elseif ($actionType == 'require-dir') + { + if (!mktree($this_action['destination'], false)) + { + $temp = $this_action['destination']; + while (!file_exists($temp) && strlen($temp) > 1) + $temp = dirname($temp); + + $return[] = array( + 'type' => 'chmod', + 'filename' => $temp + ); + } + } + elseif ($actionType == 'require-file') + { + if ($action->exists('@theme')) + $this_action['theme_action'] = $action->fetch('@theme'); + + if (!mktree(dirname($this_action['destination']), false)) + { + $temp = dirname($this_action['destination']); + while (!file_exists($temp) && strlen($temp) > 1) + $temp = dirname($temp); + + $return[] = array( + 'type' => 'chmod', + 'filename' => $temp + ); + } + + if (!is_writable($this_action['destination']) && (file_exists($this_action['destination']) || !is_writable(dirname($this_action['destination'])))) + $return[] = array( + 'type' => 'chmod', + 'filename' => $this_action['destination'] + ); + } + elseif ($actionType == 'move-dir' || $actionType == 'move-file') + { + if (!mktree(dirname($this_action['destination']), false)) + { + $temp = dirname($this_action['destination']); + while (!file_exists($temp) && strlen($temp) > 1) + $temp = dirname($temp); + + $return[] = array( + 'type' => 'chmod', + 'filename' => $temp + ); + } + + if (!is_writable($this_action['destination']) && (file_exists($this_action['destination']) || !is_writable(dirname($this_action['destination'])))) + $return[] = array( + 'type' => 'chmod', + 'filename' => $this_action['destination'] + ); + } + elseif ($actionType == 'remove-dir') + { + if (!is_writable($this_action['filename']) && file_exists($this_action['destination'])) + $return[] = array( + 'type' => 'chmod', + 'filename' => $this_action['filename'] + ); + } + elseif ($actionType == 'remove-file') + { + if (!is_writable($this_action['filename']) && file_exists($this_action['filename'])) + $return[] = array( + 'type' => 'chmod', + 'filename' => $this_action['filename'] + ); + } + } + + // Only testing - just return a list of things to be done. + if ($testing_only) + return $return; + + umask(0); + + $failure = false; + $not_done = array(array('type' => '!')); + foreach ($return as $action) + { + if ($action['type'] == 'modification' || $action['type'] == 'code' || $action['type'] == 'database' || $action['type'] == 'redirect') + $not_done[] = $action; + + if ($action['type'] == 'create-dir') + { + if (!mktree($action['destination'], 0755) || !is_writable($action['destination'])) + $failure |= !mktree($action['destination'], 0777); + } + elseif ($action['type'] == 'create-file') + { + if (!mktree(dirname($action['destination']), 0755) || !is_writable(dirname($action['destination']))) + $failure |= !mktree(dirname($action['destination']), 0777); + + // Create an empty file. + package_put_contents($action['destination'], package_get_contents($action['source']), $testing_only); + + if (!file_exists($action['destination'])) + $failure = true; + } + elseif ($action['type'] == 'require-dir') + { + copytree($action['source'], $action['destination']); + // Any other theme folders? + if (!empty($context['theme_copies']) && !empty($context['theme_copies'][$action['type']][$action['destination']])) + foreach ($context['theme_copies'][$action['type']][$action['destination']] as $theme_destination) + copytree($action['source'], $theme_destination); + } + elseif ($action['type'] == 'require-file') + { + if (!mktree(dirname($action['destination']), 0755) || !is_writable(dirname($action['destination']))) + $failure |= !mktree(dirname($action['destination']), 0777); + + package_put_contents($action['destination'], package_get_contents($action['source']), $testing_only); + + $failure |= !copy($action['source'], $action['destination']); + + // Any other theme files? + if (!empty($context['theme_copies']) && !empty($context['theme_copies'][$action['type']][$action['destination']])) + foreach ($context['theme_copies'][$action['type']][$action['destination']] as $theme_destination) + { + if (!mktree(dirname($theme_destination), 0755) || !is_writable(dirname($theme_destination))) + $failure |= !mktree(dirname($theme_destination), 0777); + + package_put_contents($theme_destination, package_get_contents($action['source']), $testing_only); + + $failure |= !copy($action['source'], $theme_destination); + } + } + elseif ($action['type'] == 'move-file') + { + if (!mktree(dirname($action['destination']), 0755) || !is_writable(dirname($action['destination']))) + $failure |= !mktree(dirname($action['destination']), 0777); + + $failure |= !rename($action['source'], $action['destination']); + } + elseif ($action['type'] == 'move-dir') + { + if (!mktree($action['destination'], 0755) || !is_writable($action['destination'])) + $failure |= !mktree($action['destination'], 0777); + + $failure |= !rename($action['source'], $action['destination']); + } + elseif ($action['type'] == 'remove-dir') + { + deltree($action['filename']); + + // Any other theme folders? + if (!empty($context['theme_copies']) && !empty($context['theme_copies'][$action['type']][$action['filename']])) + foreach ($context['theme_copies'][$action['type']][$action['filename']] as $theme_destination) + deltree($theme_destination); + } + elseif ($action['type'] == 'remove-file') + { + // Make sure the file exists before deleting it. + if (file_exists($action['filename'])) + { + package_chmod($action['filename']); + $failure |= !unlink($action['filename']); + } + // The file that was supposed to be deleted couldn't be found. + else + $failure = true; + + // Any other theme folders? + if (!empty($context['theme_copies']) && !empty($context['theme_copies'][$action['type']][$action['filename']])) + foreach ($context['theme_copies'][$action['type']][$action['filename']] as $theme_destination) + if (file_exists($theme_destination)) + $failure |= !unlink($theme_destination); + else + $failure = true; + } + } + + return $not_done; +} + +// This function tries to match $version into any of the ranges given in $versions +function matchPackageVersion($version, $versions) +{ + // Make sure everything is lowercase and clean of spaces and unpleasant history. + $version = str_replace(array(' ', '2.0rc1-1'), array('', '2.0rc1.1'), strtolower($version)); + $versions = explode(',', str_replace(array(' ', '2.0rc1-1'), array('', '2.0rc1.1'), strtolower($versions))); + + // Perhaps we do accept anything? + if (in_array('all', $versions)) + return true; + + // Loop through each version. + foreach ($versions as $for) + { + // Wild card spotted? + if (strpos($for, '*') !== false) + $for = str_replace('*', '0dev0', $for) . '-' . str_replace('*', '999', $for); + + // Do we have a range? + if (strpos($for, '-') !== false) + { + list ($lower, $upper) = explode('-', $for); + + // Compare the version against lower and upper bounds. + if (compareVersions($version, $lower) > -1 && compareVersions($version, $upper) < 1) + return true; + } + // Otherwise check if they are equal... + elseif (compareVersions($version, $for) === 0) + return true; + } + + return false; +} + +// The geek version of versioning checks for dummies, which basically compares two versions. +function compareVersions($version1, $version2) +{ + static $categories; + + $versions = array(); + foreach (array(1 => $version1, $version2) as $id => $version) + { + // Clean the version and extract the version parts. + $clean = str_replace(array(' ', '2.0rc1-1'), array('', '2.0rc1.1'), strtolower($version)); + preg_match('~(\d+)(?:\.(\d+|))?(?:\.)?(\d+|)(?:(alpha|beta|rc)(\d+|)(?:\.)?(\d+|))?(?:(dev))?(\d+|)~', $clean, $parts); + + // Build an array of parts. + $versions[$id] = array( + 'major' => (int) $parts[1], + 'minor' => !empty($parts[2]) ? (int) $parts[2] : 0, + 'patch' => !empty($parts[3]) ? (int) $parts[3] : 0, + 'type' => empty($parts[4]) ? 'stable' : $parts[4], + 'type_major' => !empty($parts[6]) ? (int) $parts[5] : 0, + 'type_minor' => !empty($parts[6]) ? (int) $parts[6] : 0, + 'dev' => !empty($parts[7]), + ); + } + + // Are they the same, perhaps? + if ($versions[1] === $versions[2]) + return 0; + + // Get version numbering categories... + if (!isset($categories)) + $categories = array_keys($versions[1]); + + // Loop through each category. + foreach ($categories as $category) + { + // Is there something for us to calculate? + if ($versions[1][$category] !== $versions[2][$category]) + { + // Dev builds are a problematic exception. + // (stable) dev < (stable) but (unstable) dev = (unstable) + if ($category == 'type') + return $versions[1][$category] > $versions[2][$category] ? ($versions[1]['dev'] ? -1 : 1) : ($versions[2]['dev'] ? 1 : -1); + elseif ($category == 'dev') + return $versions[1]['dev'] ? ($versions[2]['type'] == 'stable' ? -1 : 0) : ($versions[1]['type'] == 'stable' ? 1 : 0); + // Otherwise a simple comparison. + else + return $versions[1][$category] > $versions[2][$category] ? 1 : -1; + } + } + + // They are the same! + return 0; +} + +function parse_path($path) +{ + global $modSettings, $boarddir, $sourcedir, $settings, $temp_path; + + $dirs = array( + '\\' => '/', + '$boarddir' => $boarddir, + '$sourcedir' => $sourcedir, + '$avatardir' => $modSettings['avatar_directory'], + '$avatars_dir' => $modSettings['avatar_directory'], + '$themedir' => $settings['default_theme_dir'], + '$imagesdir' => $settings['default_theme_dir'] . '/' . basename($settings['default_images_url']), + '$themes_dir' => $boarddir . '/Themes', + '$languagedir' => $settings['default_theme_dir'] . '/languages', + '$languages_dir' => $settings['default_theme_dir'] . '/languages', + '$smileysdir' => $modSettings['smileys_dir'], + '$smileys_dir' => $modSettings['smileys_dir'], + ); + + // do we parse in a package directory? + if (!empty($temp_path)) + $dirs['$package'] = $temp_path; + + if (strlen($path) == 0) + trigger_error('parse_path(): There should never be an empty filename', E_USER_ERROR); + + return strtr($path, $dirs); +} + +function deltree($dir, $delete_dir = true) +{ + global $package_ftp; + + if (!file_exists($dir)) + return; + + $current_dir = @opendir($dir); + if ($current_dir == false) + { + if ($delete_dir && isset($package_ftp)) + { + $ftp_file = strtr($dir, array($_SESSION['pack_ftp']['root'] => '')); + if (!is_writable($dir . '/' . $entryname)) + $package_ftp->chmod($ftp_file, 0777); + $package_ftp->unlink($ftp_file); + } + + return; + } + + while ($entryname = readdir($current_dir)) + { + if (in_array($entryname, array('.', '..'))) + continue; + + if (is_dir($dir . '/' . $entryname)) + deltree($dir . '/' . $entryname); + else + { + // Here, 755 doesn't really matter since we're deleting it anyway. + if (isset($package_ftp)) + { + $ftp_file = strtr($dir . '/' . $entryname, array($_SESSION['pack_ftp']['root'] => '')); + + if (!is_writable($dir . '/' . $entryname)) + $package_ftp->chmod($ftp_file, 0777); + $package_ftp->unlink($ftp_file); + } + else + { + if (!is_writable($dir . '/' . $entryname)) + @chmod($dir . '/' . $entryname, 0777); + unlink($dir . '/' . $entryname); + } + } + } + + closedir($current_dir); + + if ($delete_dir) + { + if (isset($package_ftp)) + { + $ftp_file = strtr($dir, array($_SESSION['pack_ftp']['root'] => '')); + if (!is_writable($dir . '/' . $entryname)) + $package_ftp->chmod($ftp_file, 0777); + $package_ftp->unlink($ftp_file); + } + else + { + if (!is_writable($dir)) + @chmod($dir, 0777); + @rmdir($dir); + } + } +} + +function mktree($strPath, $mode) +{ + global $package_ftp; + + if (is_dir($strPath)) + { + if (!is_writable($strPath) && $mode !== false) + { + if (isset($package_ftp)) + $package_ftp->chmod(strtr($strPath, array($_SESSION['pack_ftp']['root'] => '')), $mode); + else + @chmod($strPath, $mode); + } + + $test = @opendir($strPath); + if ($test) + { + closedir($test); + return is_writable($strPath); + } + else + return false; + } + // Is this an invalid path and/or we can't make the directory? + if ($strPath == dirname($strPath) || !mktree(dirname($strPath), $mode)) + return false; + + if (!is_writable(dirname($strPath)) && $mode !== false) + { + if (isset($package_ftp)) + $package_ftp->chmod(dirname(strtr($strPath, array($_SESSION['pack_ftp']['root'] => ''))), $mode); + else + @chmod(dirname($strPath), $mode); + } + + if ($mode !== false && isset($package_ftp)) + return $package_ftp->create_dir(strtr($strPath, array($_SESSION['pack_ftp']['root'] => ''))); + elseif ($mode === false) + { + $test = @opendir(dirname($strPath)); + if ($test) + { + closedir($test); + return true; + } + else + return false; + } + else + { + @mkdir($strPath, $mode); + $test = @opendir($strPath); + if ($test) + { + closedir($test); + return true; + } + else + return false; + } +} + +function copytree($source, $destination) +{ + global $package_ftp; + + if (!file_exists($destination) || !is_writable($destination)) + mktree($destination, 0755); + if (!is_writable($destination)) + mktree($destination, 0777); + + $current_dir = opendir($source); + if ($current_dir == false) + return; + + while ($entryname = readdir($current_dir)) + { + if (in_array($entryname, array('.', '..'))) + continue; + + if (isset($package_ftp)) + $ftp_file = strtr($destination . '/' . $entryname, array($_SESSION['pack_ftp']['root'] => '')); + + if (is_file($source . '/' . $entryname)) + { + if (isset($package_ftp) && !file_exists($destination . '/' . $entryname)) + $package_ftp->create_file($ftp_file); + elseif (!file_exists($destination . '/' . $entryname)) + @touch($destination . '/' . $entryname); + } + + package_chmod($destination . '/' . $entryname); + + if (is_dir($source . '/' . $entryname)) + copytree($source . '/' . $entryname, $destination . '/' . $entryname); + elseif (file_exists($destination . '/' . $entryname)) + package_put_contents($destination . '/' . $entryname, package_get_contents($source . '/' . $entryname)); + else + copy($source . '/' . $entryname, $destination . '/' . $entryname); + } + + closedir($current_dir); +} + +function listtree($path, $sub_path = '') +{ + $data = array(); + + $dir = @dir($path . $sub_path); + if (!$dir) + return array(); + while ($entry = $dir->read()) + { + if ($entry == '.' || $entry == '..') + continue; + + if (is_dir($path . $sub_path . '/' . $entry)) + $data = array_merge($data, listtree($path, $sub_path . '/' . $entry)); + else + $data[] = array( + 'filename' => $sub_path == '' ? $entry : $sub_path . '/' . $entry, + 'size' => filesize($path . $sub_path . '/' . $entry), + 'skipped' => false, + ); + } + $dir->close(); + + return $data; +} + +// Parse an xml based modification file. +function parseModification($file, $testing = true, $undo = false, $theme_paths = array()) +{ + global $boarddir, $sourcedir, $settings, $txt, $modSettings, $package_ftp; + + @set_time_limit(600); + loadClassFile('Class-Package.php'); + $xml = new xmlArray(strtr($file, array("\r" => ''))); + $actions = array(); + $everything_found = true; + + if (!$xml->exists('modification') || !$xml->exists('modification/file')) + { + $actions[] = array( + 'type' => 'error', + 'filename' => '-', + 'debug' => $txt['package_modification_malformed'] + ); + return $actions; + } + + // Get the XML data. + $files = $xml->set('modification/file'); + + // Use this for holding all the template changes in this mod. + $template_changes = array(); + // This is needed to hold the long paths, as they can vary... + $long_changes = array(); + + // First, we need to build the list of all the files likely to get changed. + foreach ($files as $file) + { + // What is the filename we're currently on? + $filename = parse_path(trim($file->fetch('@name'))); + + // Now, we need to work out whether this is even a template file... + foreach ($theme_paths as $id => $theme) + { + // If this filename is relative, if so take a guess at what it should be. + $real_filename = $filename; + if (strpos($filename, 'Themes') === 0) + $real_filename = $boarddir . '/' . $filename; + + if (strpos($real_filename, $theme['theme_dir']) === 0) + { + $template_changes[$id][] = substr($real_filename, strlen($theme['theme_dir']) + 1); + $long_changes[$id][] = $filename; + } + } + } + + // Custom themes to add. + $custom_themes_add = array(); + + // If we have some template changes, we need to build a master link of what new ones are required for the custom themes. + if (!empty($template_changes[1])) + { + foreach ($theme_paths as $id => $theme) + { + // Default is getting done anyway, so no need for involvement here. + if ($id == 1) + continue; + + // For every template, do we want it? Yea, no, maybe? + foreach ($template_changes[1] as $index => $template_file) + { + // What, it exists and we haven't already got it?! Lordy, get it in! + if (file_exists($theme['theme_dir'] . '/' . $template_file) && (!isset($template_changes[$id]) || !in_array($template_file, $template_changes[$id]))) + { + // Now let's add it to the "todo" list. + $custom_themes_add[$long_changes[1][$index]][$id] = $theme['theme_dir'] . '/' . $template_file; + } + } + } + } + + foreach ($files as $file) + { + // This is the actual file referred to in the XML document... + $files_to_change = array( + 1 => parse_path(trim($file->fetch('@name'))), + ); + + // Sometimes though, we have some additional files for other themes, if we have add them to the mix. + if (isset($custom_themes_add[$files_to_change[1]])) + $files_to_change += $custom_themes_add[$files_to_change[1]]; + + // Now, loop through all the files we're changing, and, well, change them ;) + foreach ($files_to_change as $theme => $working_file) + { + if ($working_file[0] != '/' && $working_file[1] != ':') + { + trigger_error('parseModification(): The filename \'' . $working_file . '\' is not a full path!', E_USER_WARNING); + + $working_file = $boarddir . '/' . $working_file; + } + + // Doesn't exist - give an error or what? + if (!file_exists($working_file) && (!$file->exists('@error') || !in_array(trim($file->fetch('@error')), array('ignore', 'skip')))) + { + $actions[] = array( + 'type' => 'missing', + 'filename' => $working_file, + 'debug' => $txt['package_modification_missing'] + ); + + $everything_found = false; + continue; + } + // Skip the file if it doesn't exist. + elseif (!file_exists($working_file) && $file->exists('@error') && trim($file->fetch('@error')) == 'skip') + { + $actions[] = array( + 'type' => 'skipping', + 'filename' => $working_file, + ); + continue; + } + // Okay, we're creating this file then...? + elseif (!file_exists($working_file)) + $working_data = ''; + // Phew, it exists! Load 'er up! + else + $working_data = str_replace("\r", '', package_get_contents($working_file)); + + $actions[] = array( + 'type' => 'opened', + 'filename' => $working_file + ); + + $operations = $file->exists('operation') ? $file->set('operation') : array(); + foreach ($operations as $operation) + { + // Convert operation to an array. + $actual_operation = array( + 'searches' => array(), + 'error' => $operation->exists('@error') && in_array(trim($operation->fetch('@error')), array('ignore', 'fatal', 'required')) ? trim($operation->fetch('@error')) : 'fatal', + ); + + // The 'add' parameter is used for all searches in this operation. + $add = $operation->exists('add') ? $operation->fetch('add') : ''; + + // Grab all search items of this operation (in most cases just 1). + $searches = $operation->set('search'); + foreach ($searches as $i => $search) + $actual_operation['searches'][] = array( + 'position' => $search->exists('@position') && in_array(trim($search->fetch('@position')), array('before', 'after', 'replace', 'end')) ? trim($search->fetch('@position')) : 'replace', + 'is_reg_exp' => $search->exists('@regexp') && trim($search->fetch('@regexp')) === 'true', + 'loose_whitespace' => $search->exists('@whitespace') && trim($search->fetch('@whitespace')) === 'loose', + 'search' => $search->fetch('.'), + 'add' => $add, + 'preg_search' => '', + 'preg_replace' => '', + ); + + // At least one search should be defined. + if (empty($actual_operation['searches'])) + { + $actions[] = array( + 'type' => 'failure', + 'filename' => $working_file, + 'search' => $search['search'], + 'is_custom' => $theme > 1 ? $theme : 0, + ); + + // Skip to the next operation. + continue; + } + + // Reverse the operations in case of undoing stuff. + if ($undo) + { + foreach ($actual_operation['searches'] as $i => $search) + { + + // Reverse modification of regular expressions are not allowed. + if ($search['is_reg_exp']) + { + if ($actual_operation['error'] === 'fatal') + $actions[] = array( + 'type' => 'failure', + 'filename' => $working_file, + 'search' => $search['search'], + 'is_custom' => $theme > 1 ? $theme : 0, + ); + + // Continue to the next operation. + continue 2; + } + + // The replacement is now the search subject... + if ($search['position'] === 'replace' || $search['position'] === 'end') + $actual_operation['searches'][$i]['search'] = $search['add']; + else + { + // Reversing a before/after modification becomes a replacement. + $actual_operation['searches'][$i]['position'] = 'replace'; + + if ($search['position'] === 'before') + $actual_operation['searches'][$i]['search'] .= $search['add']; + elseif ($search['position'] === 'after') + $actual_operation['searches'][$i]['search'] = $search['add'] . $search['search']; + } + + // ...and the search subject is now the replacement. + $actual_operation['searches'][$i]['add'] = $search['search']; + } + } + + // Sort the search list so the replaces come before the add before/after's. + if (count($actual_operation['searches']) !== 1) + { + $replacements = array(); + + foreach ($actual_operation['searches'] as $i => $search) + { + if ($search['position'] === 'replace') + { + $replacements[] = $search; + unset($actual_operation['searches'][$i]); + } + } + $actual_operation['searches'] = array_merge($replacements, $actual_operation['searches']); + } + + // Create regular expression replacements from each search. + foreach ($actual_operation['searches'] as $i => $search) + { + // Not much needed if the search subject is already a regexp. + if ($search['is_reg_exp']) + $actual_operation['searches'][$i]['preg_search'] = $search['search']; + else + { + // Make the search subject fit into a regular expression. + $actual_operation['searches'][$i]['preg_search'] = preg_quote($search['search'], '~'); + + // Using 'loose', a random amount of tabs and spaces may be used. + if ($search['loose_whitespace']) + $actual_operation['searches'][$i]['preg_search'] = preg_replace('~[ \t]+~', '[ \t]+', $actual_operation['searches'][$i]['preg_search']); + } + + // Shuzzup. This is done so we can safely use a regular expression. ($0 is bad!!) + $actual_operation['searches'][$i]['preg_replace'] = strtr($search['add'], array('$' => '[$PACK' . 'AGE1$]', '\\' => '[$PACK' . 'AGE2$]')); + + // Before, so the replacement comes after the search subject :P + if ($search['position'] === 'before') + { + $actual_operation['searches'][$i]['preg_search'] = '(' . $actual_operation['searches'][$i]['preg_search'] . ')'; + $actual_operation['searches'][$i]['preg_replace'] = '$1' . $actual_operation['searches'][$i]['preg_replace']; + } + + // After, after what? + elseif ($search['position'] === 'after') + { + $actual_operation['searches'][$i]['preg_search'] = '(' . $actual_operation['searches'][$i]['preg_search'] . ')'; + $actual_operation['searches'][$i]['preg_replace'] .= '$1'; + } + + // Position the replacement at the end of the file (or just before the closing PHP tags). + elseif ($search['position'] === 'end') + { + if ($undo) + { + $actual_operation['searches'][$i]['preg_replace'] = ''; + } + else + { + $actual_operation['searches'][$i]['preg_search'] = '(\\n\\?\\>)?$'; + $actual_operation['searches'][$i]['preg_replace'] .= '$1'; + } + } + + // Testing 1, 2, 3... + $failed = preg_match('~' . $actual_operation['searches'][$i]['preg_search'] . '~s', $working_data) === 0; + + // Nope, search pattern not found. + if ($failed && $actual_operation['error'] === 'fatal') + { + $actions[] = array( + 'type' => 'failure', + 'filename' => $working_file, + 'search' => $actual_operation['searches'][$i]['preg_search'], + 'search_original' => $actual_operation['searches'][$i]['search'], + 'replace_original' => $actual_operation['searches'][$i]['add'], + 'position' => $search['position'], + 'is_custom' => $theme > 1 ? $theme : 0, + 'failed' => $failed, + ); + + $everything_found = false; + continue; + } + + // Found, but in this case, that means failure! + elseif (!$failed && $actual_operation['error'] === 'required') + { + $actions[] = array( + 'type' => 'failure', + 'filename' => $working_file, + 'search' => $actual_operation['searches'][$i]['preg_search'], + 'search_original' => $actual_operation['searches'][$i]['search'], + 'replace_original' => $actual_operation['searches'][$i]['add'], + 'position' => $search['position'], + 'is_custom' => $theme > 1 ? $theme : 0, + 'failed' => $failed, + ); + + $everything_found = false; + continue; + } + + // Replace it into nothing? That's not an option...unless it's an undoing end. + if ($search['add'] === '' && ($search['position'] !== 'end' || !$undo)) + continue; + + // Finally, we're doing some replacements. + $working_data = preg_replace('~' . $actual_operation['searches'][$i]['preg_search'] . '~s', $actual_operation['searches'][$i]['preg_replace'], $working_data, 1); + + $actions[] = array( + 'type' => 'replace', + 'filename' => $working_file, + 'search' => $actual_operation['searches'][$i]['preg_search'], + 'replace' => $actual_operation['searches'][$i]['preg_replace'], + 'search_original' => $actual_operation['searches'][$i]['search'], + 'replace_original' => $actual_operation['searches'][$i]['add'], + 'position' => $search['position'], + 'failed' => $failed, + 'ignore_failure' => $failed && $actual_operation['error'] === 'ignore', + 'is_custom' => $theme > 1 ? $theme : 0, + ); + } + } + + // Fix any little helper symbols ;). + $working_data = strtr($working_data, array('[$PACK' . 'AGE1$]' => '$', '[$PACK' . 'AGE2$]' => '\\')); + + package_chmod($working_file); + + if ((file_exists($working_file) && !is_writable($working_file)) || (!file_exists($working_file) && !is_writable(dirname($working_file)))) + $actions[] = array( + 'type' => 'chmod', + 'filename' => $working_file + ); + + if (basename($working_file) == 'Settings_bak.php') + continue; + + if (!$testing && !empty($modSettings['package_make_backups']) && file_exists($working_file)) + { + // No, no, not Settings.php! + if (basename($working_file) == 'Settings.php') + @copy($working_file, dirname($working_file) . '/Settings_bak.php'); + else + @copy($working_file, $working_file . '~'); + } + + // Always call this, even if in testing, because it won't really be written in testing mode. + package_put_contents($working_file, $working_data, $testing); + + $actions[] = array( + 'type' => 'saved', + 'filename' => $working_file, + 'is_custom' => $theme > 1 ? $theme : 0, + ); + } + } + + $actions[] = array( + 'type' => 'result', + 'status' => $everything_found + ); + + return $actions; +} + +// Parses a BoardMod format mod file... +function parseBoardMod($file, $testing = true, $undo = false, $theme_paths = array()) +{ + global $boarddir, $sourcedir, $settings, $txt, $modSettings; + + @set_time_limit(600); + $file = strtr($file, array("\r" => '')); + + $working_file = null; + $working_search = null; + $working_data = ''; + $replace_with = null; + + $actions = array(); + $everything_found = true; + + // This holds all the template changes in the standard mod file. + $template_changes = array(); + // This is just the temporary file. + $temp_file = $file; + // This holds the actual changes on a step counter basis. + $temp_changes = array(); + $counter = 0; + $step_counter = 0; + + // Before we do *anything*, let's build a list of what we're editing, as it's going to be used for other theme edits. + while (preg_match('~<(edit file|file|search|search for|add|add after|replace|add before|add above|above|before)>\n(.*?)\n~is', $temp_file, $code_match) != 0) + { + $counter++; + + // Get rid of the old stuff. + $temp_file = substr_replace($temp_file, '', strpos($temp_file, $code_match[0]), strlen($code_match[0])); + + // No interest to us? + if ($code_match[1] != 'edit file' && $code_match[1] != 'file') + { + // It's a step, let's add that to the current steps. + if (isset($temp_changes[$step_counter])) + $temp_changes[$step_counter]['changes'][] = $code_match[0]; + continue; + } + + // We've found a new edit - let's make ourself heard, kind of. + $step_counter = $counter; + $temp_changes[$step_counter] = array( + 'title' => $code_match[0], + 'changes' => array(), + ); + + $filename = parse_path($code_match[2]); + + // Now, is this a template file, and if so, which? + foreach ($theme_paths as $id => $theme) + { + // If this filename is relative, if so take a guess at what it should be. + if (strpos($filename, 'Themes') === 0) + $filename = $boarddir . '/' . $filename; + + if (strpos($filename, $theme['theme_dir']) === 0) + $template_changes[$id][$counter] = substr($filename, strlen($theme['theme_dir']) + 1); + } + } + + // Anything above $counter must be for custom themes. + $custom_template_begin = $counter; + // Reference for what theme ID this action belongs to. + $theme_id_ref = array(); + + // Now we know what templates we need to touch, cycle through each theme and work out what we need to edit. + if (!empty($template_changes[1])) + { + foreach ($theme_paths as $id => $theme) + { + // Don't do default, it means nothing to me. + if ($id == 1) + continue; + + // Now, for each file do we need to edit it? + foreach ($template_changes[1] as $pos => $template_file) + { + // It does? Add it to the list darlin'. + if (file_exists($theme['theme_dir'] . '/' . $template_file) && (!isset($template_changes[$id][$pos]) || !in_array($template_file, $template_changes[$id][$pos]))) + { + // Actually add it to the mod file too, so we can see that it will work ;) + if (!empty($temp_changes[$pos]['changes'])) + { + $file .= "\n\n" . '' . "\n" . $theme['theme_dir'] . '/' . $template_file . "\n" . '' . "\n\n" . implode("\n\n", $temp_changes[$pos]['changes']); + $theme_id_ref[$counter] = $id; + $counter += 1 + count($temp_changes[$pos]['changes']); + } + } + } + } + } + + $counter = 0; + $is_custom = 0; + while (preg_match('~<(edit file|file|search|search for|add|add after|replace|add before|add above|above|before)>\n(.*?)\n~is', $file, $code_match) != 0) + { + // This is for working out what we should be editing. + $counter++; + + // Edit a specific file. + if ($code_match[1] == 'file' || $code_match[1] == 'edit file') + { + // Backup the old file. + if ($working_file !== null) + { + package_chmod($working_file); + + // Don't even dare. + if (basename($working_file) == 'Settings_bak.php') + continue; + + if (!is_writable($working_file)) + $actions[] = array( + 'type' => 'chmod', + 'filename' => $working_file + ); + + if (!$testing && !empty($modSettings['package_make_backups']) && file_exists($working_file)) + { + if (basename($working_file) == 'Settings.php') + @copy($working_file, dirname($working_file) . '/Settings_bak.php'); + else + @copy($working_file, $working_file . '~'); + } + + package_put_contents($working_file, $working_data, $testing); + } + + if ($working_file !== null) + $actions[] = array( + 'type' => 'saved', + 'filename' => $working_file, + 'is_custom' => $is_custom, + ); + + // Is this "now working on" file a theme specific one? + $is_custom = isset($theme_id_ref[$counter - 1]) ? $theme_id_ref[$counter - 1] : 0; + + // Make sure the file exists! + $working_file = parse_path($code_match[2]); + + if ($working_file[0] != '/' && $working_file[1] != ':') + { + trigger_error('parseBoardMod(): The filename \'' . $working_file . '\' is not a full path!', E_USER_WARNING); + + $working_file = $boarddir . '/' . $working_file; + } + + if (!file_exists($working_file)) + { + $places_to_check = array($boarddir, $sourcedir, $settings['default_theme_dir'], $settings['default_theme_dir'] . '/languages'); + + foreach ($places_to_check as $place) + if (file_exists($place . '/' . $working_file)) + { + $working_file = $place . '/' . $working_file; + break; + } + } + + if (file_exists($working_file)) + { + // Load the new file. + $working_data = str_replace("\r", '', package_get_contents($working_file)); + + $actions[] = array( + 'type' => 'opened', + 'filename' => $working_file + ); + } + else + { + $actions[] = array( + 'type' => 'missing', + 'filename' => $working_file + ); + + $working_file = null; + $everything_found = false; + } + + // Can't be searching for something... + $working_search = null; + } + // Search for a specific string. + elseif (($code_match[1] == 'search' || $code_match[1] == 'search for') && $working_file !== null) + { + if ($working_search !== null) + { + $actions[] = array( + 'type' => 'error', + 'filename' => $working_file + ); + + $everything_found = false; + } + + $working_search = $code_match[2]; + } + // Must've already loaded a search string. + elseif ($working_search !== null) + { + // This is the base string.... + $replace_with = $code_match[2]; + + // Add this afterward... + if ($code_match[1] == 'add' || $code_match[1] == 'add after') + $replace_with = $working_search . "\n" . $replace_with; + // Add this beforehand. + elseif ($code_match[1] == 'before' || $code_match[1] == 'add before' || $code_match[1] == 'above' || $code_match[1] == 'add above') + $replace_with .= "\n" . $working_search; + // Otherwise.. replace with $replace_with ;). + } + + // If we have a search string, replace string, and open file.. + if ($working_search !== null && $replace_with !== null && $working_file !== null) + { + // Make sure it's somewhere in the string. + if ($undo) + { + $temp = $replace_with; + $replace_with = $working_search; + $working_search = $temp; + } + + if (strpos($working_data, $working_search) !== false) + { + $working_data = str_replace($working_search, $replace_with, $working_data); + + $actions[] = array( + 'type' => 'replace', + 'filename' => $working_file, + 'search' => $working_search, + 'replace' => $replace_with, + 'search_original' => $working_search, + 'replace_original' => $replace_with, + 'position' => $code_match[1] == 'replace' ? 'replace' : ($code_match[1] == 'add' || $code_match[1] == 'add after' ? 'before' : 'after'), + 'is_custom' => $is_custom, + 'failed' => false, + ); + } + // It wasn't found! + else + { + $actions[] = array( + 'type' => 'failure', + 'filename' => $working_file, + 'search' => $working_search, + 'is_custom' => $is_custom, + 'search_original' => $working_search, + 'replace_original' => $replace_with, + 'position' => $code_match[1] == 'replace' ? 'replace' : ($code_match[1] == 'add' || $code_match[1] == 'add after' ? 'before' : 'after'), + 'is_custom' => $is_custom, + 'failed' => true, + ); + + $everything_found = false; + } + + // These don't hold any meaning now. + $working_search = null; + $replace_with = null; + } + + // Get rid of the old tag. + $file = substr_replace($file, '', strpos($file, $code_match[0]), strlen($code_match[0])); + } + + // Backup the old file. + if ($working_file !== null) + { + package_chmod($working_file); + + if (!is_writable($working_file)) + $actions[] = array( + 'type' => 'chmod', + 'filename' => $working_file + ); + + if (!$testing && !empty($modSettings['package_make_backups']) && file_exists($working_file)) + { + if (basename($working_file) == 'Settings.php') + @copy($working_file, dirname($working_file) . '/Settings_bak.php'); + else + @copy($working_file, $working_file . '~'); + } + + package_put_contents($working_file, $working_data, $testing); + } + + if ($working_file !== null) + $actions[] = array( + 'type' => 'saved', + 'filename' => $working_file, + 'is_custom' => $is_custom, + ); + + $actions[] = array( + 'type' => 'result', + 'status' => $everything_found + ); + + return $actions; +} + +function package_get_contents($filename) +{ + global $package_cache, $modSettings; + + if (!isset($package_cache)) + { + // Windows doesn't seem to care about the memory_limit. + if (!empty($modSettings['package_disable_cache']) || ini_set('memory_limit', '128M') !== false || strpos(strtolower(PHP_OS), 'win') !== false) + $package_cache = array(); + else + $package_cache = false; + } + + if (strpos($filename, 'Packages/') !== false || $package_cache === false || !isset($package_cache[$filename])) + return file_get_contents($filename); + else + return $package_cache[$filename]; +} + +function package_put_contents($filename, $data, $testing = false) +{ + global $package_ftp, $package_cache, $modSettings; + static $text_filetypes = array('php', 'txt', '.js', 'css', 'vbs', 'tml', 'htm'); + + if (!isset($package_cache)) + { + // Try to increase the memory limit - we don't want to run out of ram! + if (!empty($modSettings['package_disable_cache']) || ini_set('memory_limit', '128M') !== false || strpos(strtolower(PHP_OS), 'win') !== false) + $package_cache = array(); + else + $package_cache = false; + } + + if (isset($package_ftp)) + $ftp_file = strtr($filename, array($_SESSION['pack_ftp']['root'] => '')); + + if (!file_exists($filename) && isset($package_ftp)) + $package_ftp->create_file($ftp_file); + elseif (!file_exists($filename)) + @touch($filename); + + package_chmod($filename); + + if (!$testing && (strpos($filename, 'Packages/') !== false || $package_cache === false)) + { + $fp = @fopen($filename, in_array(substr($filename, -3), $text_filetypes) ? 'w' : 'wb'); + + // We should show an error message or attempt a rollback, no? + if (!$fp) + return false; + + fwrite($fp, $data); + fclose($fp); + } + elseif (strpos($filename, 'Packages/') !== false || $package_cache === false) + return strlen($data); + else + { + $package_cache[$filename] = $data; + + // Permission denied, eh? + $fp = @fopen($filename, 'r+'); + if (!$fp) + return false; + fclose($fp); + } + + return strlen($data); +} + +function package_flush_cache($trash = false) +{ + global $package_ftp, $package_cache; + static $text_filetypes = array('php', 'txt', '.js', 'css', 'vbs', 'tml', 'htm'); + + if (empty($package_cache)) + return; + + // First, let's check permissions! + foreach ($package_cache as $filename => $data) + { + if (isset($package_ftp)) + $ftp_file = strtr($filename, array($_SESSION['pack_ftp']['root'] => '')); + + if (!file_exists($filename) && isset($package_ftp)) + $package_ftp->create_file($ftp_file); + elseif (!file_exists($filename)) + @touch($filename); + + package_chmod($filename); + + $fp = fopen($filename, 'r+'); + if (!$fp && !$trash) + { + // We should have package_chmod()'d them before, no?! + trigger_error('package_flush_cache(): some files are still not writable', E_USER_WARNING); + return; + } + fclose($fp); + } + + if ($trash) + { + $package_cache = array(); + return; + } + + foreach ($package_cache as $filename => $data) + { + $fp = fopen($filename, in_array(substr($filename, -3), $text_filetypes) ? 'w' : 'wb'); + fwrite($fp, $data); + fclose($fp); + } + + $package_cache = array(); +} + +// Try to make a file writable. Return true if it worked, false if it didn't. +function package_chmod($filename, $perm_state = 'writable', $track_change = false) +{ + global $package_ftp; + + if (file_exists($filename) && is_writable($filename) && $perm_state == 'writable') + return true; + + // Start off checking without FTP. + if (!isset($package_ftp) || $package_ftp === false) + { + for ($i = 0; $i < 2; $i++) + { + $chmod_file = $filename; + + // Start off with a less agressive test. + if ($i == 0) + { + // If this file doesn't exist, then we actually want to look at whatever parent directory does. + $subTraverseLimit = 2; + while (!file_exists($chmod_file) && $subTraverseLimit) + { + $chmod_file = dirname($chmod_file); + $subTraverseLimit--; + } + + // Keep track of the writable status here. + $file_permissions = @fileperms($chmod_file); + } + else + { + // This looks odd, but it's an attempt to work around PHP suExec. + if (!file_exists($chmod_file) && $perm_state == 'writable') + { + $file_permissions = @fileperms(dirname($chmod_file)); + + mktree(dirname($chmod_file), 0755); + @touch($chmod_file); + @chmod($chmod_file, 0755); + } + else + $file_permissions = @fileperms($chmod_file); + } + + // This looks odd, but it's another attempt to work around PHP suExec. + if ($perm_state != 'writable') + @chmod($chmod_file, $perm_state == 'execute' ? 0755 : 0644); + else + { + if (!@is_writable($chmod_file)) + @chmod($chmod_file, 0755); + if (!@is_writable($chmod_file)) + @chmod($chmod_file, 0777); + if (!@is_writable(dirname($chmod_file))) + @chmod($chmod_file, 0755); + if (!@is_writable(dirname($chmod_file))) + @chmod($chmod_file, 0777); + } + + // The ultimate writable test. + if ($perm_state == 'writable') + { + $fp = is_dir($chmod_file) ? @opendir($chmod_file) : @fopen($chmod_file, 'rb'); + if (@is_writable($chmod_file) && $fp) + { + if (!is_dir($chmod_file)) + fclose($fp); + else + closedir($fp); + + // It worked! + if ($track_change) + $_SESSION['pack_ftp']['original_perms'][$chmod_file] = $file_permissions; + + return true; + } + } + elseif ($perm_state != 'writable' && isset($_SESSION['pack_ftp']['original_perms'][$chmod_file])) + unset($_SESSION['pack_ftp']['original_perms'][$chmod_file]); + } + + // If we're here we're a failure. + return false; + } + // Otherwise we do have FTP? + elseif ($package_ftp !== false && !empty($_SESSION['pack_ftp'])) + { + $ftp_file = strtr($filename, array($_SESSION['pack_ftp']['root'] => '')); + + // This looks odd, but it's an attempt to work around PHP suExec. + if (!file_exists($filename) && $perm_state == 'writable') + { + $file_permissions = @fileperms(dirname($filename)); + + mktree(dirname($filename), 0755); + $package_ftp->create_file($ftp_file); + $package_ftp->chmod($ftp_file, 0755); + } + else + $file_permissions = @fileperms($filename); + + if ($perm_state != 'writable') + { + $package_ftp->chmod($ftp_file, $perm_state == 'execute' ? 0755 : 0644); + } + else + { + if (!@is_writable($filename)) + $package_ftp->chmod($ftp_file, 0777); + if (!@is_writable(dirname($filename))) + $package_ftp->chmod(dirname($ftp_file), 0777); + } + + if (@is_writable($filename)) + { + if ($track_change) + $_SESSION['pack_ftp']['original_perms'][$filename] = $file_permissions; + + return true; + } + elseif ($perm_state != 'writable' && isset($_SESSION['pack_ftp']['original_perms'][$filename])) + unset($_SESSION['pack_ftp']['original_perms'][$filename]); + } + + // Oh dear, we failed if we get here. + return false; +} + +function package_crypt($pass) +{ + $n = strlen($pass); + + $salt = session_id(); + while (strlen($salt) < $n) + $salt .= session_id(); + + for ($i = 0; $i < $n; $i++) + $pass{$i} = chr(ord($pass{$i}) ^ (ord($salt{$i}) - 32)); + + return $pass; +} + +function package_create_backup($id = 'backup') +{ + global $sourcedir, $boarddir, $smcFunc; + + $files = array(); + + $base_files = array('index.php', 'SSI.php', 'agreement.txt', 'ssi_examples.php', 'ssi_examples.shtml'); + foreach ($base_files as $file) + { + if (file_exists($boarddir . '/' . $file)) + $files[realpath($boarddir . '/' . $file)] = array( + empty($_REQUEST['use_full_paths']) ? $file : $boarddir . '/' . $file, + stat($boarddir . '/' . $file) + ); + } + + $dirs = array( + $sourcedir => empty($_REQUEST['use_full_paths']) ? 'Sources/' : strtr($sourcedir . '/', '\\', '/') + ); + + $request = $smcFunc['db_query']('', ' + SELECT value + FROM {db_prefix}themes + WHERE id_member = {int:no_member} + AND variable = {string:theme_dir}', + array( + 'no_member' => 0, + 'theme_dir' => 'theme_dir', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $dirs[$row['value']] = empty($_REQUEST['use_full_paths']) ? 'Themes/' . basename($row['value']) . '/' : strtr($row['value'] . '/', '\\', '/'); + $smcFunc['db_free_result']($request); + + while (!empty($dirs)) + { + list ($dir, $dest) = each($dirs); + unset($dirs[$dir]); + + $listing = @dir($dir); + if (!$listing) + continue; + while ($entry = $listing->read()) + { + if (preg_match('~^(\.{1,2}|CVS|backup.*|help|images|.*\~)$~', $entry) != 0) + continue; + + $filepath = realpath($dir . '/' . $entry); + if (isset($files[$filepath])) + continue; + + $stat = stat($dir . '/' . $entry); + if ($stat['mode'] & 040000) + { + $files[$filepath] = array($dest . $entry . '/', $stat); + $dirs[$dir . '/' . $entry] = $dest . $entry . '/'; + } + else + $files[$filepath] = array($dest . $entry, $stat); + } + $listing->close(); + } + + if (!file_exists($boarddir . '/Packages/backups')) + mktree($boarddir . '/Packages/backups', 0777); + if (!is_writable($boarddir . '/Packages/backups')) + package_chmod($boarddir . '/Packages/backups'); + $output_file = $boarddir . '/Packages/backups/' . strftime('%Y-%m-%d_') . preg_replace('~[$\\\\/:<>|?*"\']~', '', $id); + $output_ext = '.tar' . (function_exists('gzopen') ? '.gz' : ''); + + if (file_exists($output_file . $output_ext)) + { + $i = 2; + while (file_exists($output_file . '_' . $i . $output_ext)) + $i++; + $output_file = $output_file . '_' . $i . $output_ext; + } + else + $output_file .= $output_ext; + + @set_time_limit(300); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + if (function_exists('gzopen')) + { + $fwrite = 'gzwrite'; + $fclose = 'gzclose'; + $output = gzopen($output_file, 'wb'); + } + else + { + $fwrite = 'fwrite'; + $fclose = 'fclose'; + $output = fopen($output_file, 'wb'); + } + + foreach ($files as $real_file => $file) + { + if (!file_exists($real_file)) + continue; + + $stat = $file[1]; + if (substr($file[0], -1) == '/') + $stat['size'] = 0; + + $current = pack('a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12', $file[0], decoct($stat['mode']), sprintf('%06d', decoct($stat['uid'])), sprintf('%06d', decoct($stat['gid'])), decoct($stat['size']), decoct($stat['mtime']), '', 0, '', '', '', '', '', '', '', '', ''); + + $checksum = 256; + for ($i = 0; $i < 512; $i++) + $checksum += ord($current{$i}); + + $fwrite($output, substr($current, 0, 148) . pack('a8', decoct($checksum)) . substr($current, 156, 511)); + + if ($stat['size'] == 0) + continue; + + $fp = fopen($real_file, 'rb'); + while (!feof($fp)) + $fwrite($output, fread($fp, 16384)); + fclose($fp); + + $fwrite($output, pack('a' . (512 - $stat['size'] % 512), '')); + } + + $fwrite($output, pack('a1024', '')); + $fclose($output); +} + +// Get the contents of a URL, irrespective of allow_url_fopen. +function fetch_web_data($url, $post_data = '', $keep_alive = false, $redirection_level = 0) +{ + global $webmaster_email; + static $keep_alive_dom = null, $keep_alive_fp = null; + + preg_match('~^(http|ftp)(s)?://([^/:]+)(:(\d+))?(.+)$~', $url, $match); + + // An FTP url. We should try connecting and RETRieving it... + if (empty($match[1])) + return false; + elseif ($match[1] == 'ftp') + { + // Include the file containing the ftp_connection class. + loadClassFile('Class-Package.php'); + + // Establish a connection and attempt to enable passive mode. + $ftp = new ftp_connection(($match[2] ? 'ssl://' : '') . $match[3], empty($match[5]) ? 21 : $match[5], 'anonymous', $webmaster_email); + if ($ftp->error !== false || !$ftp->passive()) + return false; + + // I want that one *points*! + fwrite($ftp->connection, 'RETR ' . $match[6] . "\r\n"); + + // Since passive mode worked (or we would have returned already!) open the connection. + $fp = @fsockopen($ftp->pasv['ip'], $ftp->pasv['port'], $err, $err, 5); + if (!$fp) + return false; + + // The server should now say something in acknowledgement. + $ftp->check_response(150); + + $data = ''; + while (!feof($fp)) + $data .= fread($fp, 4096); + fclose($fp); + + // All done, right? Good. + $ftp->check_response(226); + $ftp->close(); + } + // This is more likely; a standard HTTP URL. + elseif (isset($match[1]) && $match[1] == 'http') + { + if ($keep_alive && $match[3] == $keep_alive_dom) + $fp = $keep_alive_fp; + if (empty($fp)) + { + // Open the socket on the port we want... + $fp = @fsockopen(($match[2] ? 'ssl://' : '') . $match[3], empty($match[5]) ? ($match[2] ? 443 : 80) : $match[5], $err, $err, 5); + if (!$fp) + return false; + } + + if ($keep_alive) + { + $keep_alive_dom = $match[3]; + $keep_alive_fp = $fp; + } + + // I want this, from there, and I'm not going to be bothering you for more (probably.) + if (empty($post_data)) + { + fwrite($fp, 'GET ' . $match[6] . ' HTTP/1.0' . "\r\n"); + fwrite($fp, 'Host: ' . $match[3] . (empty($match[5]) ? ($match[2] ? ':443' : '') : ':' . $match[5]) . "\r\n"); + fwrite($fp, 'User-Agent: PHP/SMF' . "\r\n"); + if ($keep_alive) + fwrite($fp, 'Connection: Keep-Alive' . "\r\n\r\n"); + else + fwrite($fp, 'Connection: close' . "\r\n\r\n"); + } + else + { + fwrite($fp, 'POST ' . $match[6] . ' HTTP/1.0' . "\r\n"); + fwrite($fp, 'Host: ' . $match[3] . (empty($match[5]) ? ($match[2] ? ':443' : '') : ':' . $match[5]) . "\r\n"); + fwrite($fp, 'User-Agent: PHP/SMF' . "\r\n"); + if ($keep_alive) + fwrite($fp, 'Connection: Keep-Alive' . "\r\n"); + else + fwrite($fp, 'Connection: close' . "\r\n"); + fwrite($fp, 'Content-Type: application/x-www-form-urlencoded' . "\r\n"); + fwrite($fp, 'Content-Length: ' . strlen($post_data) . "\r\n\r\n"); + fwrite($fp, $post_data); + } + + $response = fgets($fp, 768); + + // Redirect in case this location is permanently or temporarily moved. + if ($redirection_level < 3 && preg_match('~^HTTP/\S+\s+30[127]~i', $response) === 1) + { + $header = ''; + $location = ''; + while (!feof($fp) && trim($header = fgets($fp, 4096)) != '') + if (strpos($header, 'Location:') !== false) + $location = trim(substr($header, strpos($header, ':') + 1)); + + if (empty($location)) + return false; + else + { + if (!$keep_alive) + fclose($fp); + return fetch_web_data($location, $post_data, $keep_alive, $redirection_level + 1); + } + } + + // Make sure we get a 200 OK. + elseif (preg_match('~^HTTP/\S+\s+20[01]~i', $response) === 0) + return false; + + // Skip the headers... + while (!feof($fp) && trim($header = fgets($fp, 4096)) != '') + { + if (preg_match('~content-length:\s*(\d+)~i', $header, $match) != 0) + $content_length = $match[1]; + elseif (preg_match('~connection:\s*close~i', $header) != 0) + { + $keep_alive_dom = null; + $keep_alive = false; + } + + continue; + } + + $data = ''; + if (isset($content_length)) + { + while (!feof($fp) && strlen($data) < $content_length) + $data .= fread($fp, $content_length - strlen($data)); + } + else + { + while (!feof($fp)) + $data .= fread($fp, 4096); + } + + if (!$keep_alive) + fclose($fp); + } + else + { + // Umm, this shouldn't happen? + trigger_error('fetch_web_data(): Bad URL', E_USER_NOTICE); + $data = false; + } + + return $data; +} + +// crc32 doesn't work as expected on 64-bit functions - make our own. +// http://www.php.net/crc32#79567 +if (!function_exists('smf_crc32')) +{ + function smf_crc32($number) + { + $crc = crc32($number); + + if ($crc & 0x80000000) + { + $crc ^= 0xffffffff; + $crc += 1; + $crc = -$crc; + } + + return $crc; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Post.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Post.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,3238 @@ + \'[\', \']\' => \']\', \':\' => \':\', \'@\' => \'@\')) . \'[/nobbc]\'', $message); + + // Remove \r's... they're evil! + $message = strtr($message, array("\r" => '')); + + // You won't believe this - but too many periods upsets apache it seems! + $message = preg_replace('~\.{100,}~', '...', $message); + + // Trim off trailing quotes - these often happen by accident. + while (substr($message, -7) == '[quote]') + $message = substr($message, 0, -7); + while (substr($message, 0, 8) == '[/quote]') + $message = substr($message, 8); + + // Find all code blocks, work out whether we'd be parsing them, then ensure they are all closed. + $in_tag = false; + $had_tag = false; + $codeopen = 0; + if (preg_match_all('~(\[(/)*code(?:=[^\]]+)?\])~is', $message, $matches)) + foreach ($matches[0] as $index => $dummy) + { + // Closing? + if (!empty($matches[2][$index])) + { + // If it's closing and we're not in a tag we need to open it... + if (!$in_tag) + $codeopen = true; + // Either way we ain't in one any more. + $in_tag = false; + } + // Opening tag... + else + { + $had_tag = true; + // If we're in a tag don't do nought! + if (!$in_tag) + $in_tag = true; + } + } + + // If we have an open tag, close it. + if ($in_tag) + $message .= '[/code]'; + // Open any ones that need to be open, only if we've never had a tag. + if ($codeopen && !$had_tag) + $message = '[code]' . $message; + + // Now that we've fixed all the code tags, let's fix the img and url tags... + $parts = preg_split('~(\[/code\]|\[code(?:=[^\]]+)?\])~i', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + + // The regular expression non breaking space has many versions. + $non_breaking_space = $context['utf8'] ? ($context['server']['complex_preg_chars'] ? '\x{A0}' : "\xC2\xA0") : '\xA0'; + + // Only mess with stuff outside [code] tags. + for ($i = 0, $n = count($parts); $i < $n; $i++) + { + // It goes 0 = outside, 1 = begin tag, 2 = inside, 3 = close tag, repeat. + if ($i % 4 == 0) + { + fixTags($parts[$i]); + + // Replace /me.+?\n with [me=name]dsf[/me]\n. + if (strpos($user_info['name'], '[') !== false || strpos($user_info['name'], ']') !== false || strpos($user_info['name'], '\'') !== false || strpos($user_info['name'], '"') !== false) + $parts[$i] = preg_replace('~(\A|\n)/me(?: | )([^\n]*)(?:\z)?~i', '$1[me="' . $user_info['name'] . '"]$2[/me]', $parts[$i]); + else + $parts[$i] = preg_replace('~(\A|\n)/me(?: | )([^\n]*)(?:\z)?~i', '$1[me=' . $user_info['name'] . ']$2[/me]', $parts[$i]); + + if (!$previewing && strpos($parts[$i], '[html]') !== false) + { + if (allowedTo('admin_forum')) + $parts[$i] = preg_replace('~\[html\](.+?)\[/html\]~ise', '\'[html]\' . strtr(un_htmlspecialchars(\'$1\'), array("\n" => \' \', \' \' => \' \', \'[\' => \'[\', \']\' => \']\')) . \'[/html]\'', $parts[$i]); + + // We should edit them out, or else if an admin edits the message they will get shown... + else + { + while (strpos($parts[$i], '[html]') !== false) + $parts[$i] = preg_replace('~\[[/]?html\]~i', '', $parts[$i]); + } + } + + // Let's look at the time tags... + $parts[$i] = preg_replace('~\[time(?:=(absolute))*\](.+?)\[/time\]~ie', '\'[time]\' . (is_numeric(\'$2\') || @strtotime(\'$2\') == 0 ? \'$2\' : strtotime(\'$2\') - (\'$1\' == \'absolute\' ? 0 : (($modSettings[\'time_offset\'] + $user_info[\'time_offset\']) * 3600))) . \'[/time]\'', $parts[$i]); + + // Change the color specific tags to [color=the color]. + $parts[$i] = preg_replace('~\[(black|blue|green|red|white)\]~', '[color=$1]', $parts[$i]); // First do the opening tags. + $parts[$i] = preg_replace('~\[/(black|blue|green|red|white)\]~', '[/color]', $parts[$i]); // And now do the closing tags + + // Make sure all tags are lowercase. + $parts[$i] = preg_replace('~\[([/]?)(list|li|table|tr|td)((\s[^\]]+)*)\]~ie', '\'[$1\' . strtolower(\'$2\') . \'$3]\'', $parts[$i]); + + $list_open = substr_count($parts[$i], '[list]') + substr_count($parts[$i], '[list '); + $list_close = substr_count($parts[$i], '[/list]'); + if ($list_close - $list_open > 0) + $parts[$i] = str_repeat('[list]', $list_close - $list_open) . $parts[$i]; + if ($list_open - $list_close > 0) + $parts[$i] = $parts[$i] . str_repeat('[/list]', $list_open - $list_close); + + $mistake_fixes = array( + // Find [table]s not followed by [tr]. + '~\[table\](?![\s' . $non_breaking_space . ']*\[tr\])~s' . ($context['utf8'] ? 'u' : '') => '[table][tr]', + // Find [tr]s not followed by [td]. + '~\[tr\](?![\s' . $non_breaking_space . ']*\[td\])~s' . ($context['utf8'] ? 'u' : '') => '[tr][td]', + // Find [/td]s not followed by something valid. + '~\[/td\](?![\s' . $non_breaking_space . ']*(?:\[td\]|\[/tr\]|\[/table\]))~s' . ($context['utf8'] ? 'u' : '') => '[/td][/tr]', + // Find [/tr]s not followed by something valid. + '~\[/tr\](?![\s' . $non_breaking_space . ']*(?:\[tr\]|\[/table\]))~s' . ($context['utf8'] ? 'u' : '') => '[/tr][/table]', + // Find [/td]s incorrectly followed by [/table]. + '~\[/td\][\s' . $non_breaking_space . ']*\[/table\]~s' . ($context['utf8'] ? 'u' : '') => '[/td][/tr][/table]', + // Find [table]s, [tr]s, and [/td]s (possibly correctly) followed by [td]. + '~\[(table|tr|/td)\]([\s' . $non_breaking_space . ']*)\[td\]~s' . ($context['utf8'] ? 'u' : '') => '[$1]$2[_td_]', + // Now, any [td]s left should have a [tr] before them. + '~\[td\]~s' => '[tr][td]', + // Look for [tr]s which are correctly placed. + '~\[(table|/tr)\]([\s' . $non_breaking_space . ']*)\[tr\]~s' . ($context['utf8'] ? 'u' : '') => '[$1]$2[_tr_]', + // Any remaining [tr]s should have a [table] before them. + '~\[tr\]~s' => '[table][tr]', + // Look for [/td]s followed by [/tr]. + '~\[/td\]([\s' . $non_breaking_space . ']*)\[/tr\]~s' . ($context['utf8'] ? 'u' : '') => '[/td]$1[_/tr_]', + // Any remaining [/tr]s should have a [/td]. + '~\[/tr\]~s' => '[/td][/tr]', + // Look for properly opened [li]s which aren't closed. + '~\[li\]([^\[\]]+?)\[li\]~s' => '[li]$1[_/li_][_li_]', + '~\[li\]([^\[\]]+?)\[/list\]~s' => '[_li_]$1[_/li_][/list]', + '~\[li\]([^\[\]]+?)$~s' => '[li]$1[/li]', + // Lists - find correctly closed items/lists. + '~\[/li\]([\s' . $non_breaking_space . ']*)\[/list\]~s' . ($context['utf8'] ? 'u' : '') => '[_/li_]$1[/list]', + // Find list items closed and then opened. + '~\[/li\]([\s' . $non_breaking_space . ']*)\[li\]~s' . ($context['utf8'] ? 'u' : '') => '[_/li_]$1[_li_]', + // Now, find any [list]s or [/li]s followed by [li]. + '~\[(list(?: [^\]]*?)?|/li)\]([\s' . $non_breaking_space . ']*)\[li\]~s' . ($context['utf8'] ? 'u' : '') => '[$1]$2[_li_]', + // Allow for sub lists. + '~\[/li\]([\s' . $non_breaking_space . ']*)\[list\]~' . ($context['utf8'] ? 'u' : '') => '[_/li_]$1[list]', + '~\[/list\]([\s' . $non_breaking_space . ']*)\[li\]~' . ($context['utf8'] ? 'u' : '') => '[/list]$1[_li_]', + // Any remaining [li]s weren't inside a [list]. + '~\[li\]~' => '[list][li]', + // Any remaining [/li]s weren't before a [/list]. + '~\[/li\]~' => '[/li][/list]', + // Put the correct ones back how we found them. + '~\[_(li|/li|td|tr|/tr)_\]~' => '[$1]', + // Images with no real url. + '~\[img\]https?://.{0,7}\[/img\]~' => '', + ); + + // Fix up some use of tables without [tr]s, etc. (it has to be done more than once to catch it all.) + for ($j = 0; $j < 3; $j++) + $parts[$i] = preg_replace(array_keys($mistake_fixes), $mistake_fixes, $parts[$i]); + + // Now we're going to do full scale table checking... + $table_check = $parts[$i]; + $table_offset = 0; + $table_array = array(); + $table_order = array( + 'table' => 'td', + 'tr' => 'table', + 'td' => 'tr', + ); + while (preg_match('~\[(/)*(table|tr|td)\]~', $table_check, $matches) != false) + { + // Keep track of where this is. + $offset = strpos($table_check, $matches[0]); + $remove_tag = false; + + // Is it opening? + if ($matches[1] != '/') + { + // If the previous table tag isn't correct simply remove it. + if ((!empty($table_array) && $table_array[0] != $table_order[$matches[2]]) || (empty($table_array) && $matches[2] != 'table')) + $remove_tag = true; + // Record this was the last tag. + else + array_unshift($table_array, $matches[2]); + } + // Otherwise is closed! + else + { + // Only keep the tag if it's closing the right thing. + if (empty($table_array) || ($table_array[0] != $matches[2])) + $remove_tag = true; + else + array_shift($table_array); + } + + // Removing? + if ($remove_tag) + { + $parts[$i] = substr($parts[$i], 0, $table_offset + $offset) . substr($parts[$i], $table_offset + strlen($matches[0]) + $offset); + // We've lost some data. + $table_offset -= strlen($matches[0]); + } + + // Remove everything up to here. + $table_offset += $offset + strlen($matches[0]); + $table_check = substr($table_check, $offset + strlen($matches[0])); + } + + // Close any remaining table tags. + foreach ($table_array as $tag) + $parts[$i] .= '[/' . $tag . ']'; + } + } + + // Put it back together! + if (!$previewing) + $message = strtr(implode('', $parts), array(' ' => '  ', "\n" => '
          ', $context['utf8'] ? "\xC2\xA0" : "\xA0" => ' ')); + else + $message = strtr(implode('', $parts), array(' ' => '  ', $context['utf8'] ? "\xC2\xA0" : "\xA0" => ' ')); + + // Now let's quickly clean up things that will slow our parser (which are common in posted code.) + $message = strtr($message, array('[]' => '[]', '['' => '['')); +} + +// This is very simple, and just removes things done by preparsecode. +function un_preparsecode($message) +{ + global $smcFunc; + + $parts = preg_split('~(\[/code\]|\[code(?:=[^\]]+)?\])~i', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + + // We're going to unparse only the stuff outside [code]... + for ($i = 0, $n = count($parts); $i < $n; $i++) + { + // If $i is a multiple of four (0, 4, 8, ...) then it's not a code section... + if ($i % 4 == 0) + { + $parts[$i] = preg_replace('~\[html\](.+?)\[/html\]~ie', '\'[html]\' . strtr(htmlspecialchars(\'$1\', ENT_QUOTES), array(\'\\"\' => \'"\', \'&#13;\' => \'
          \', \'&#32;\' => \' \', \'&#91;\' => \'[\', \'&#93;\' => \']\')) . \'[/html]\'', $parts[$i]); + // $parts[$i] = preg_replace('~\[html\](.+?)\[/html\]~ie', '\'[html]\' . strtr(htmlspecialchars(\'$1\', ENT_QUOTES), array(\'\\"\' => \'"\', \'&#13;\' => \'
          \', \'&#32;\' => \' \', \'&#38;\' => \'&\', \'&#91;\' => \'[\', \'&#93;\' => \']\')) . \'[/html]\'', $parts[$i]); + + // Attempt to un-parse the time to something less awful. + $parts[$i] = preg_replace('~\[time\](\d{0,10})\[/time\]~ie', '\'[time]\' . timeformat(\'$1\', false) . \'[/time]\'', $parts[$i]); + } + } + + // Change breaks back to \n's and &nsbp; back to spaces. + return preg_replace('~~', "\n", str_replace(' ', ' ', implode('', $parts))); +} + +// Fix any URLs posted - ie. remove 'javascript:'. +function fixTags(&$message) +{ + global $modSettings; + + // WARNING: Editing the below can cause large security holes in your forum. + // Edit only if you are sure you know what you are doing. + + $fixArray = array( + // [img]http://...[/img] or [img width=1]http://...[/img] + array( + 'tag' => 'img', + 'protocols' => array('http', 'https'), + 'embeddedUrl' => false, + 'hasEqualSign' => false, + 'hasExtra' => true, + ), + // [url]http://...[/url] + array( + 'tag' => 'url', + 'protocols' => array('http', 'https'), + 'embeddedUrl' => true, + 'hasEqualSign' => false, + ), + // [url=http://...]name[/url] + array( + 'tag' => 'url', + 'protocols' => array('http', 'https'), + 'embeddedUrl' => true, + 'hasEqualSign' => true, + ), + // [iurl]http://...[/iurl] + array( + 'tag' => 'iurl', + 'protocols' => array('http', 'https'), + 'embeddedUrl' => true, + 'hasEqualSign' => false, + ), + // [iurl=http://...]name[/iurl] + array( + 'tag' => 'iurl', + 'protocols' => array('http', 'https'), + 'embeddedUrl' => true, + 'hasEqualSign' => true, + ), + // [ftp]ftp://...[/ftp] + array( + 'tag' => 'ftp', + 'protocols' => array('ftp', 'ftps'), + 'embeddedUrl' => true, + 'hasEqualSign' => false, + ), + // [ftp=ftp://...]name[/ftp] + array( + 'tag' => 'ftp', + 'protocols' => array('ftp', 'ftps'), + 'embeddedUrl' => true, + 'hasEqualSign' => true, + ), + // [flash]http://...[/flash] + array( + 'tag' => 'flash', + 'protocols' => array('http', 'https'), + 'embeddedUrl' => false, + 'hasEqualSign' => false, + 'hasExtra' => true, + ), + ); + + // Fix each type of tag. + foreach ($fixArray as $param) + fixTag($message, $param['tag'], $param['protocols'], $param['embeddedUrl'], $param['hasEqualSign'], !empty($param['hasExtra'])); + + // Now fix possible security problems with images loading links automatically... + $message = preg_replace('~(\[img.*?\])(.+?)\[/img\]~eis', '\'$1\' . preg_replace(\'~action(=|%3d)(?!dlattach)~i\', \'action-\', \'$2\') . \'[/img]\'', $message); + + // Limit the size of images posted? + if (!empty($modSettings['max_image_width']) || !empty($modSettings['max_image_height'])) + { + // Find all the img tags - with or without width and height. + preg_match_all('~\[img(\s+width=\d+)?(\s+height=\d+)?(\s+width=\d+)?\](.+?)\[/img\]~is', $message, $matches, PREG_PATTERN_ORDER); + + $replaces = array(); + foreach ($matches[0] as $match => $dummy) + { + // If the width was after the height, handle it. + $matches[1][$match] = !empty($matches[3][$match]) ? $matches[3][$match] : $matches[1][$match]; + + // Now figure out if they had a desired height or width... + $desired_width = !empty($matches[1][$match]) ? (int) substr(trim($matches[1][$match]), 6) : 0; + $desired_height = !empty($matches[2][$match]) ? (int) substr(trim($matches[2][$match]), 7) : 0; + + // One was omitted, or both. We'll have to find its real size... + if (empty($desired_width) || empty($desired_height)) + { + list ($width, $height) = url_image_size(un_htmlspecialchars($matches[4][$match])); + + // They don't have any desired width or height! + if (empty($desired_width) && empty($desired_height)) + { + $desired_width = $width; + $desired_height = $height; + } + // Scale it to the width... + elseif (empty($desired_width) && !empty($height)) + $desired_width = (int) (($desired_height * $width) / $height); + // Scale if to the height. + elseif (!empty($width)) + $desired_height = (int) (($desired_width * $height) / $width); + } + + // If the width and height are fine, just continue along... + if ($desired_width <= $modSettings['max_image_width'] && $desired_height <= $modSettings['max_image_height']) + continue; + + // Too bad, it's too wide. Make it as wide as the maximum. + if ($desired_width > $modSettings['max_image_width'] && !empty($modSettings['max_image_width'])) + { + $desired_height = (int) (($modSettings['max_image_width'] * $desired_height) / $desired_width); + $desired_width = $modSettings['max_image_width']; + } + + // Now check the height, as well. Might have to scale twice, even... + if ($desired_height > $modSettings['max_image_height'] && !empty($modSettings['max_image_height'])) + { + $desired_width = (int) (($modSettings['max_image_height'] * $desired_width) / $desired_height); + $desired_height = $modSettings['max_image_height']; + } + + $replaces[$matches[0][$match]] = '[img' . (!empty($desired_width) ? ' width=' . $desired_width : '') . (!empty($desired_height) ? ' height=' . $desired_height : '') . ']' . $matches[4][$match] . '[/img]'; + } + + // If any img tags were actually changed... + if (!empty($replaces)) + $message = strtr($message, $replaces); + } +} + +// Fix a specific class of tag - ie. url with =. +function fixTag(&$message, $myTag, $protocols, $embeddedUrl = false, $hasEqualSign = false, $hasExtra = false) +{ + global $boardurl, $scripturl; + + if (preg_match('~^([^:]+://[^/]+)~', $boardurl, $match) != 0) + $domain_url = $match[1]; + else + $domain_url = $boardurl . '/'; + + $replaces = array(); + + if ($hasEqualSign) + preg_match_all('~\[(' . $myTag . ')=([^\]]*?)\](?:(.+?)\[/(' . $myTag . ')\])?~is', $message, $matches); + else + preg_match_all('~\[(' . $myTag . ($hasExtra ? '(?:[^\]]*?)' : '') . ')\](.+?)\[/(' . $myTag . ')\]~is', $message, $matches); + + foreach ($matches[0] as $k => $dummy) + { + // Remove all leading and trailing whitespace. + $replace = trim($matches[2][$k]); + $this_tag = $matches[1][$k]; + $this_close = $hasEqualSign ? (empty($matches[4][$k]) ? '' : $matches[4][$k]) : $matches[3][$k]; + + $found = false; + foreach ($protocols as $protocol) + { + $found = strncasecmp($replace, $protocol . '://', strlen($protocol) + 3) === 0; + if ($found) + break; + } + + if (!$found && $protocols[0] == 'http') + { + if (substr($replace, 0, 1) == '/') + $replace = $domain_url . $replace; + elseif (substr($replace, 0, 1) == '?') + $replace = $scripturl . $replace; + elseif (substr($replace, 0, 1) == '#' && $embeddedUrl) + { + $replace = '#' . preg_replace('~[^A-Za-z0-9_\-#]~', '', substr($replace, 1)); + $this_tag = 'iurl'; + $this_close = 'iurl'; + } + else + $replace = $protocols[0] . '://' . $replace; + } + elseif (!$found && $protocols[0] == 'ftp') + $replace = $protocols[0] . '://' . preg_replace('~^(?!ftps?)[^:]+://~', '', $replace); + elseif (!$found) + $replace = $protocols[0] . '://' . $replace; + + if ($hasEqualSign && $embeddedUrl) + $replaces[$matches[0][$k]] = '[' . $this_tag . '=' . $replace . ']' . (empty($matches[4][$k]) ? '' : $matches[3][$k] . '[/' . $this_close . ']'); + elseif ($hasEqualSign) + $replaces['[' . $matches[1][$k] . '=' . $matches[2][$k] . ']'] = '[' . $this_tag . '=' . $replace . ']'; + elseif ($embeddedUrl) + $replaces['[' . $matches[1][$k] . ']' . $matches[2][$k] . '[/' . $matches[3][$k] . ']'] = '[' . $this_tag . '=' . $replace . ']' . $matches[2][$k] . '[/' . $this_close . ']'; + else + $replaces['[' . $matches[1][$k] . ']' . $matches[2][$k] . '[/' . $matches[3][$k] . ']'] = '[' . $this_tag . ']' . $replace . '[/' . $this_close . ']'; + } + + foreach ($replaces as $k => $v) + { + if ($k == $v) + unset($replaces[$k]); + } + + if (!empty($replaces)) + $message = strtr($message, $replaces); +} + +// Send off an email. +function sendmail($to, $subject, $message, $from = null, $message_id = null, $send_html = false, $priority = 3, $hotmail_fix = null, $is_private = false) +{ + global $webmaster_email, $context, $modSettings, $txt, $scripturl; + global $smcFunc; + + // Use sendmail if it's set or if no SMTP server is set. + $use_sendmail = empty($modSettings['mail_type']) || $modSettings['smtp_host'] == ''; + + // Line breaks need to be \r\n only in windows or for SMTP. + $line_break = $context['server']['is_windows'] || !$use_sendmail ? "\r\n" : "\n"; + + // So far so good. + $mail_result = true; + + // If the recipient list isn't an array, make it one. + $to_array = is_array($to) ? $to : array($to); + + // Once upon a time, Hotmail could not interpret non-ASCII mails. + // In honour of those days, it's still called the 'hotmail fix'. + if ($hotmail_fix === null) + { + $hotmail_to = array(); + foreach ($to_array as $i => $to_address) + { + if (preg_match('~@(att|comcast|bellsouth)\.[a-zA-Z\.]{2,6}$~i', $to_address) === 1) + { + $hotmail_to[] = $to_address; + $to_array = array_diff($to_array, array($to_address)); + } + } + + // Call this function recursively for the hotmail addresses. + if (!empty($hotmail_to)) + $mail_result = sendmail($hotmail_to, $subject, $message, $from, $message_id, $send_html, $priority, true); + + // The remaining addresses no longer need the fix. + $hotmail_fix = false; + + // No other addresses left? Return instantly. + if (empty($to_array)) + return $mail_result; + } + + // Get rid of entities. + $subject = un_htmlspecialchars($subject); + // Make the message use the proper line breaks. + $message = str_replace(array("\r", "\n"), array('', $line_break), $message); + + // Make sure hotmail mails are sent as HTML so that HTML entities work. + if ($hotmail_fix && !$send_html) + { + $send_html = true; + $message = strtr($message, array($line_break => '
          ' . $line_break)); + $message = preg_replace('~(' . preg_quote($scripturl, '~') . '(?:[?/][\w\-_%\.,\?&;=#]+)?)~', '$1', $message); + } + + list (, $from_name) = mimespecialchars(addcslashes($from !== null ? $from : $context['forum_name'], '<>()\'\\"'), true, $hotmail_fix, $line_break); + list (, $subject) = mimespecialchars($subject, true, $hotmail_fix, $line_break); + + // Construct the mail headers... + $headers = 'From: "' . $from_name . '" <' . (empty($modSettings['mail_from']) ? $webmaster_email : $modSettings['mail_from']) . '>' . $line_break; + $headers .= $from !== null ? 'Reply-To: <' . $from . '>' . $line_break : ''; + $headers .= 'Return-Path: ' . (empty($modSettings['mail_from']) ? $webmaster_email : $modSettings['mail_from']) . $line_break; + $headers .= 'Date: ' . gmdate('D, d M Y H:i:s') . ' -0000' . $line_break; + + if ($message_id !== null && empty($modSettings['mail_no_message_id'])) + $headers .= 'Message-ID: <' . md5($scripturl . microtime()) . '-' . $message_id . strstr(empty($modSettings['mail_from']) ? $webmaster_email : $modSettings['mail_from'], '@') . '>' . $line_break; + $headers .= 'X-Mailer: SMF' . $line_break; + + // Pass this to the integration before we start modifying the output -- it'll make it easier later. + if (in_array(false, call_integration_hook('integrate_outgoing_email', array(&$subject, &$message, &$headers)), true)) + return false; + + // Save the original message... + $orig_message = $message; + + // The mime boundary separates the different alternative versions. + $mime_boundary = 'SMF-' . md5($message . time()); + + // Using mime, as it allows to send a plain unencoded alternative. + $headers .= 'Mime-Version: 1.0' . $line_break; + $headers .= 'Content-Type: multipart/alternative; boundary="' . $mime_boundary . '"' . $line_break; + $headers .= 'Content-Transfer-Encoding: 7bit' . $line_break; + + // Sending HTML? Let's plop in some basic stuff, then. + if ($send_html) + { + $no_html_message = un_htmlspecialchars(strip_tags(strtr($orig_message, array('' => $line_break)))); + + // But, then, dump it and use a plain one for dinosaur clients. + list(, $plain_message) = mimespecialchars($no_html_message, false, true, $line_break); + $message = $plain_message . $line_break . '--' . $mime_boundary . $line_break; + + // This is the plain text version. Even if no one sees it, we need it for spam checkers. + list($charset, $plain_charset_message, $encoding) = mimespecialchars($no_html_message, false, false, $line_break); + $message .= 'Content-Type: text/plain; charset=' . $charset . $line_break; + $message .= 'Content-Transfer-Encoding: ' . $encoding . $line_break . $line_break; + $message .= $plain_charset_message . $line_break . '--' . $mime_boundary . $line_break; + + // This is the actual HTML message, prim and proper. If we wanted images, they could be inlined here (with multipart/related, etc.) + list($charset, $html_message, $encoding) = mimespecialchars($orig_message, false, $hotmail_fix, $line_break); + $message .= 'Content-Type: text/html; charset=' . $charset . $line_break; + $message .= 'Content-Transfer-Encoding: ' . ($encoding == '' ? '7bit' : $encoding) . $line_break . $line_break; + $message .= $html_message . $line_break . '--' . $mime_boundary . '--'; + } + // Text is good too. + else + { + // Send a plain message first, for the older web clients. + list(, $plain_message) = mimespecialchars($orig_message, false, true, $line_break); + $message = $plain_message . $line_break . '--' . $mime_boundary . $line_break; + + // Now add an encoded message using the forum's character set. + list ($charset, $encoded_message, $encoding) = mimespecialchars($orig_message, false, false, $line_break); + $message .= 'Content-Type: text/plain; charset=' . $charset . $line_break; + $message .= 'Content-Transfer-Encoding: ' . $encoding . $line_break . $line_break; + $message .= $encoded_message . $line_break . '--' . $mime_boundary . '--'; + } + + // Are we using the mail queue, if so this is where we butt in... + if (!empty($modSettings['mail_queue']) && $priority != 0) + return AddMailQueue(false, $to_array, $subject, $message, $headers, $send_html, $priority, $is_private); + + // If it's a priority mail, send it now - note though that this should NOT be used for sending many at once. + elseif (!empty($modSettings['mail_queue']) && !empty($modSettings['mail_limit'])) + { + list ($last_mail_time, $mails_this_minute) = @explode('|', $modSettings['mail_recent']); + if (empty($mails_this_minute) || time() > $last_mail_time + 60) + $new_queue_stat = time() . '|' . 1; + else + $new_queue_stat = $last_mail_time . '|' . ((int) $mails_this_minute + 1); + + updateSettings(array('mail_recent' => $new_queue_stat)); + } + + // SMTP or sendmail? + if ($use_sendmail) + { + $subject = strtr($subject, array("\r" => '', "\n" => '')); + if (!empty($modSettings['mail_strip_carriage'])) + { + $message = strtr($message, array("\r" => '')); + $headers = strtr($headers, array("\r" => '')); + } + + foreach ($to_array as $to) + { + if (!mail(strtr($to, array("\r" => '', "\n" => '')), $subject, $message, $headers)) + { + log_error(sprintf($txt['mail_send_unable'], $to)); + $mail_result = false; + } + + // Wait, wait, I'm still sending here! + @set_time_limit(300); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + } + } + else + $mail_result = $mail_result && smtp_mail($to_array, $subject, $message, $headers); + + // Everything go smoothly? + return $mail_result; +} + +// Add an email to the mail queue. +function AddMailQueue($flush = false, $to_array = array(), $subject = '', $message = '', $headers = '', $send_html = false, $priority = 3, $is_private = false) +{ + global $context, $modSettings, $smcFunc; + + static $cur_insert = array(); + static $cur_insert_len = 0; + + if ($cur_insert_len == 0) + $cur_insert = array(); + + // If we're flushing, make the final inserts - also if we're near the MySQL length limit! + if (($flush || $cur_insert_len > 800000) && !empty($cur_insert)) + { + // Only do these once. + $cur_insert_len = 0; + + // Dump the data... + $smcFunc['db_insert']('', + '{db_prefix}mail_queue', + array( + 'time_sent' => 'int', 'recipient' => 'string-255', 'body' => 'string-65534', 'subject' => 'string-255', + 'headers' => 'string-65534', 'send_html' => 'int', 'priority' => 'int', 'private' => 'int', + ), + $cur_insert, + array('id_mail') + ); + + $cur_insert = array(); + $context['flush_mail'] = false; + } + + // If we're flushing we're done. + if ($flush) + { + $nextSendTime = time() + 10; + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}settings + SET value = {string:nextSendTime} + WHERE variable = {string:mail_next_send} + AND value = {string:no_outstanding}', + array( + 'nextSendTime' => $nextSendTime, + 'mail_next_send' => 'mail_next_send', + 'no_outstanding' => '0', + ) + ); + + return true; + } + + // Ensure we tell obExit to flush. + $context['flush_mail'] = true; + + foreach ($to_array as $to) + { + // Will this insert go over MySQL's limit? + $this_insert_len = strlen($to) + strlen($message) + strlen($headers) + 700; + + // Insert limit of 1M (just under the safety) is reached? + if ($this_insert_len + $cur_insert_len > 1000000) + { + // Flush out what we have so far. + $smcFunc['db_insert']('', + '{db_prefix}mail_queue', + array( + 'time_sent' => 'int', 'recipient' => 'string-255', 'body' => 'string-65534', 'subject' => 'string-255', + 'headers' => 'string-65534', 'send_html' => 'int', 'priority' => 'int', 'private' => 'int', + ), + $cur_insert, + array('id_mail') + ); + + // Clear this out. + $cur_insert = array(); + $cur_insert_len = 0; + } + + // Now add the current insert to the array... + $cur_insert[] = array(time(), (string) $to, (string) $message, (string) $subject, (string) $headers, ($send_html ? 1 : 0), $priority, (int) $is_private); + $cur_insert_len += $this_insert_len; + } + + // If they are using SSI there is a good chance obExit will never be called. So lets be nice and flush it for them. + if (SMF === 'SSI') + return AddMailQueue(true); + + return true; +} + +// Send off a personal message. +function sendpm($recipients, $subject, $message, $store_outbox = false, $from = null, $pm_head = 0) +{ + global $scripturl, $txt, $user_info, $language; + global $modSettings, $smcFunc; + + // Make sure the PM language file is loaded, we might need something out of it. + loadLanguage('PersonalMessage'); + + $onBehalf = $from !== null; + + // Initialize log array. + $log = array( + 'failed' => array(), + 'sent' => array() + ); + + if ($from === null) + $from = array( + 'id' => $user_info['id'], + 'name' => $user_info['name'], + 'username' => $user_info['username'] + ); + // Probably not needed. /me something should be of the typer. + else + $user_info['name'] = $from['name']; + + // This is the one that will go in their inbox. + $htmlmessage = $smcFunc['htmlspecialchars']($message, ENT_QUOTES); + $htmlsubject = $smcFunc['htmlspecialchars']($subject); + preparsecode($htmlmessage); + + // Integrated PMs + call_integration_hook('integrate_personal_message', array($recipients, $from['username'], $subject, $message)); + + // Get a list of usernames and convert them to IDs. + $usernames = array(); + foreach ($recipients as $rec_type => $rec) + { + foreach ($rec as $id => $member) + { + if (!is_numeric($recipients[$rec_type][$id])) + { + $recipients[$rec_type][$id] = $smcFunc['strtolower'](trim(preg_replace('/[<>&"\'=\\\]/', '', $recipients[$rec_type][$id]))); + $usernames[$recipients[$rec_type][$id]] = 0; + } + } + } + if (!empty($usernames)) + { + $request = $smcFunc['db_query']('pm_find_username', ' + SELECT id_member, member_name + FROM {db_prefix}members + WHERE ' . ($smcFunc['db_case_sensitive'] ? 'LOWER(member_name)' : 'member_name') . ' IN ({array_string:usernames})', + array( + 'usernames' => array_keys($usernames), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + if (isset($usernames[$smcFunc['strtolower']($row['member_name'])])) + $usernames[$smcFunc['strtolower']($row['member_name'])] = $row['id_member']; + $smcFunc['db_free_result']($request); + + // Replace the usernames with IDs. Drop usernames that couldn't be found. + foreach ($recipients as $rec_type => $rec) + foreach ($rec as $id => $member) + { + if (is_numeric($recipients[$rec_type][$id])) + continue; + + if (!empty($usernames[$member])) + $recipients[$rec_type][$id] = $usernames[$member]; + else + { + $log['failed'][$id] = sprintf($txt['pm_error_user_not_found'], $recipients[$rec_type][$id]); + unset($recipients[$rec_type][$id]); + } + } + } + + // Make sure there are no duplicate 'to' members. + $recipients['to'] = array_unique($recipients['to']); + + // Only 'bcc' members that aren't already in 'to'. + $recipients['bcc'] = array_diff(array_unique($recipients['bcc']), $recipients['to']); + + // Combine 'to' and 'bcc' recipients. + $all_to = array_merge($recipients['to'], $recipients['bcc']); + + // Check no-one will want it deleted right away! + $request = $smcFunc['db_query']('', ' + SELECT + id_member, criteria, is_or + FROM {db_prefix}pm_rules + WHERE id_member IN ({array_int:to_members}) + AND delete_pm = {int:delete_pm}', + array( + 'to_members' => $all_to, + 'delete_pm' => 1, + ) + ); + $deletes = array(); + // Check whether we have to apply anything... + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $criteria = unserialize($row['criteria']); + // Note we don't check the buddy status, cause deletion from buddy = madness! + $delete = false; + foreach ($criteria as $criterium) + { + $match = false; + if (($criterium['t'] == 'mid' && $criterium['v'] == $from['id']) || ($criterium['t'] == 'gid' && in_array($criterium['v'], $user_info['groups'])) || ($criterium['t'] == 'sub' && strpos($subject, $criterium['v']) !== false) || ($criterium['t'] == 'msg' && strpos($message, $criterium['v']) !== false)) + $delete = true; + // If we're adding and one criteria don't match then we stop! + elseif (!$row['is_or']) + { + $delete = false; + break; + } + } + if ($delete) + $deletes[$row['id_member']] = 1; + } + $smcFunc['db_free_result']($request); + + // Load the membergrounp message limits. + //!!! Consider caching this? + static $message_limit_cache = array(); + if (!allowedTo('moderate_forum') && empty($message_limit_cache)) + { + $request = $smcFunc['db_query']('', ' + SELECT id_group, max_messages + FROM {db_prefix}membergroups', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $message_limit_cache[$row['id_group']] = $row['max_messages']; + $smcFunc['db_free_result']($request); + } + + // Load the groups that are allowed to read PMs. + $allowed_groups = array(); + $disallowed_groups = array(); + $request = $smcFunc['db_query']('', ' + SELECT id_group, add_deny + FROM {db_prefix}permissions + WHERE permission = {string:read_permission}', + array( + 'read_permission' => 'pm_read', + ) + ); + + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (empty($row['add_deny'])) + $disallowed_groups[] = $row['id_group']; + else + $allowed_groups[] = $row['id_group']; + } + + $smcFunc['db_free_result']($request); + + if (empty($modSettings['permission_enable_deny'])) + $disallowed_groups = array(); + + $request = $smcFunc['db_query']('', ' + SELECT + member_name, real_name, id_member, email_address, lngfile, + pm_email_notify, instant_messages,' . (allowedTo('moderate_forum') ? ' 0' : ' + (pm_receive_from = {int:admins_only}' . (empty($modSettings['enable_buddylist']) ? '' : ' OR + (pm_receive_from = {int:buddies_only} AND FIND_IN_SET({string:from_id}, buddy_list) = 0) OR + (pm_receive_from = {int:not_on_ignore_list} AND FIND_IN_SET({string:from_id}, pm_ignore_list) != 0)') . ')') . ' AS ignored, + FIND_IN_SET({string:from_id}, buddy_list) != 0 AS is_buddy, is_activated, + additional_groups, id_group, id_post_group + FROM {db_prefix}members + WHERE id_member IN ({array_int:recipients}) + ORDER BY lngfile + LIMIT {int:count_recipients}', + array( + 'not_on_ignore_list' => 1, + 'buddies_only' => 2, + 'admins_only' => 3, + 'recipients' => $all_to, + 'count_recipients' => count($all_to), + 'from_id' => $from['id'], + ) + ); + $notifications = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Don't do anything for members to be deleted! + if (isset($deletes[$row['id_member']])) + continue; + + // We need to know this members groups. + $groups = explode(',', $row['additional_groups']); + $groups[] = $row['id_group']; + $groups[] = $row['id_post_group']; + + $message_limit = -1; + // For each group see whether they've gone over their limit - assuming they're not an admin. + if (!in_array(1, $groups)) + { + foreach ($groups as $id) + { + if (isset($message_limit_cache[$id]) && $message_limit != 0 && $message_limit < $message_limit_cache[$id]) + $message_limit = $message_limit_cache[$id]; + } + + if ($message_limit > 0 && $message_limit <= $row['instant_messages']) + { + $log['failed'][$row['id_member']] = sprintf($txt['pm_error_data_limit_reached'], $row['real_name']); + unset($all_to[array_search($row['id_member'], $all_to)]); + continue; + } + + // Do they have any of the allowed groups? + if (count(array_intersect($allowed_groups, $groups)) == 0 || count(array_intersect($disallowed_groups, $groups)) != 0) + { + $log['failed'][$row['id_member']] = sprintf($txt['pm_error_user_cannot_read'], $row['real_name']); + unset($all_to[array_search($row['id_member'], $all_to)]); + continue; + } + } + + // Note that PostgreSQL can return a lowercase t/f for FIND_IN_SET + if (!empty($row['ignored']) && $row['ignored'] != 'f' && $row['id_member'] != $from['id']) + { + $log['failed'][$row['id_member']] = sprintf($txt['pm_error_ignored_by_user'], $row['real_name']); + unset($all_to[array_search($row['id_member'], $all_to)]); + continue; + } + + // If the receiving account is banned (>=10) or pending deletion (4), refuse to send the PM. + if ($row['is_activated'] >= 10 || ($row['is_activated'] == 4 && !$user_info['is_admin'])) + { + $log['failed'][$row['id_member']] = sprintf($txt['pm_error_user_cannot_read'], $row['real_name']); + unset($all_to[array_search($row['id_member'], $all_to)]); + continue; + } + + // Send a notification, if enabled - taking the buddy list into account. + if (!empty($row['email_address']) && ($row['pm_email_notify'] == 1 || ($row['pm_email_notify'] > 1 && (!empty($modSettings['enable_buddylist']) && $row['is_buddy']))) && $row['is_activated'] == 1) + $notifications[empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']][] = $row['email_address']; + + $log['sent'][$row['id_member']] = sprintf(isset($txt['pm_successfully_sent']) ? $txt['pm_successfully_sent'] : '', $row['real_name']); + } + $smcFunc['db_free_result']($request); + + // Only 'send' the message if there are any recipients left. + if (empty($all_to)) + return $log; + + // Insert the message itself and then grab the last insert id. + $smcFunc['db_insert']('', + '{db_prefix}personal_messages', + array( + 'id_pm_head' => 'int', 'id_member_from' => 'int', 'deleted_by_sender' => 'int', + 'from_name' => 'string-255', 'msgtime' => 'int', 'subject' => 'string-255', 'body' => 'string-65534', + ), + array( + $pm_head, $from['id'], ($store_outbox ? 0 : 1), + $from['username'], time(), $htmlsubject, $htmlmessage, + ), + array('id_pm') + ); + $id_pm = $smcFunc['db_insert_id']('{db_prefix}personal_messages', 'id_pm'); + + // Add the recipients. + if (!empty($id_pm)) + { + // If this is new we need to set it part of it's own conversation. + if (empty($pm_head)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}personal_messages + SET id_pm_head = {int:id_pm_head} + WHERE id_pm = {int:id_pm_head}', + array( + 'id_pm_head' => $id_pm, + ) + ); + + // Some people think manually deleting personal_messages is fun... it's not. We protect against it though :) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}pm_recipients + WHERE id_pm = {int:id_pm}', + array( + 'id_pm' => $id_pm, + ) + ); + + $insertRows = array(); + foreach ($all_to as $to) + { + $insertRows[] = array($id_pm, $to, in_array($to, $recipients['bcc']) ? 1 : 0, isset($deletes[$to]) ? 1 : 0, 1); + } + + $smcFunc['db_insert']('insert', + '{db_prefix}pm_recipients', + array( + 'id_pm' => 'int', 'id_member' => 'int', 'bcc' => 'int', 'deleted' => 'int', 'is_new' => 'int' + ), + $insertRows, + array('id_pm', 'id_member') + ); + } + + censorText($message); + censorText($subject); + $message = trim(un_htmlspecialchars(strip_tags(strtr(parse_bbc(htmlspecialchars($message), false), array('
          ' => "\n", '' => "\n", '
        • ' => "\n", '[' => '[', ']' => ']'))))); + + foreach ($notifications as $lang => $notification_list) + { + // Make sure to use the right language. + loadLanguage('index+PersonalMessage', $lang, false); + + // Replace the right things in the message strings. + $mailsubject = str_replace(array('SUBJECT', 'SENDER'), array($subject, un_htmlspecialchars($from['name'])), $txt['new_pm_subject']); + $mailmessage = str_replace(array('SUBJECT', 'MESSAGE', 'SENDER'), array($subject, $message, un_htmlspecialchars($from['name'])), $txt['pm_email']); + $mailmessage .= "\n\n" . $txt['instant_reply'] . ' ' . $scripturl . '?action=pm;sa=send;f=inbox;pmsg=' . $id_pm . ';quote;u=' . $from['id']; + + // Off the notification email goes! + sendmail($notification_list, $mailsubject, $mailmessage, null, 'p' . $id_pm, false, 2, null, true); + } + + // Back to what we were on before! + loadLanguage('index+PersonalMessage'); + + // Add one to their unread and read message counts. + foreach ($all_to as $k => $id) + if (isset($deletes[$id])) + unset($all_to[$k]); + if (!empty($all_to)) + updateMemberData($all_to, array('instant_messages' => '+', 'unread_messages' => '+', 'new_pm' => 1)); + + return $log; +} + +// Prepare text strings for sending as email body or header. +function mimespecialchars($string, $with_charset = true, $hotmail_fix = false, $line_break = "\r\n", $custom_charset = null) +{ + global $context; + + $charset = $custom_charset !== null ? $custom_charset : $context['character_set']; + + // This is the fun part.... + if (preg_match_all('~&#(\d{3,8});~', $string, $matches) !== 0 && !$hotmail_fix) + { + // Let's, for now, assume there are only 'ish characters. + $simple = true; + + foreach ($matches[1] as $entity) + if ($entity > 128) + $simple = false; + unset($matches); + + if ($simple) + $string = preg_replace('~&#(\d{3,8});~e', 'chr(\'$1\')', $string); + else + { + // Try to convert the string to UTF-8. + if (!$context['utf8'] && function_exists('iconv')) + { + $newstring = @iconv($context['character_set'], 'UTF-8', $string); + if ($newstring) + $string = $newstring; + } + + $fixchar = create_function('$n', ' + if ($n < 128) + return chr($n); + elseif ($n < 2048) + return chr(192 | $n >> 6) . chr(128 | $n & 63); + elseif ($n < 65536) + return chr(224 | $n >> 12) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63); + else + return chr(240 | $n >> 18) . chr(128 | $n >> 12 & 63) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63);'); + + $string = preg_replace('~&#(\d{3,8});~e', '$fixchar(\'$1\')', $string); + + // Unicode, baby. + $charset = 'UTF-8'; + } + } + + // Convert all special characters to HTML entities...just for Hotmail :-\ + if ($hotmail_fix && ($context['utf8'] || function_exists('iconv') || $context['character_set'] === 'ISO-8859-1')) + { + if (!$context['utf8'] && function_exists('iconv')) + { + $newstring = @iconv($context['character_set'], 'UTF-8', $string); + if ($newstring) + $string = $newstring; + } + + $entityConvert = create_function('$c', ' + if (strlen($c) === 1 && ord($c[0]) <= 0x7F) + return $c; + elseif (strlen($c) === 2 && ord($c[0]) >= 0xC0 && ord($c[0]) <= 0xDF) + return "&#" . (((ord($c[0]) ^ 0xC0) << 6) + (ord($c[1]) ^ 0x80)) . ";"; + elseif (strlen($c) === 3 && ord($c[0]) >= 0xE0 && ord($c[0]) <= 0xEF) + return "&#" . (((ord($c[0]) ^ 0xE0) << 12) + ((ord($c[1]) ^ 0x80) << 6) + (ord($c[2]) ^ 0x80)) . ";"; + elseif (strlen($c) === 4 && ord($c[0]) >= 0xF0 && ord($c[0]) <= 0xF7) + return "&#" . (((ord($c[0]) ^ 0xF0) << 18) + ((ord($c[1]) ^ 0x80) << 12) + ((ord($c[2]) ^ 0x80) << 6) + (ord($c[3]) ^ 0x80)) . ";"; + else + return "";'); + + // Convert all 'special' characters to HTML entities. + return array($charset, preg_replace('~([\x80-' . ($context['server']['complex_preg_chars'] ? '\x{10FFFF}' : "\xF7\xBF\xBF\xBF") . '])~eu', '$entityConvert(\'\1\')', $string), '7bit'); + } + + // We don't need to mess with the subject line if no special characters were in it.. + elseif (!$hotmail_fix && preg_match('~([^\x09\x0A\x0D\x20-\x7F])~', $string) === 1) + { + // Base64 encode. + $string = base64_encode($string); + + // Show the characterset and the transfer-encoding for header strings. + if ($with_charset) + $string = '=?' . $charset . '?B?' . $string . '?='; + + // Break it up in lines (mail body). + else + $string = chunk_split($string, 76, $line_break); + + return array($charset, $string, 'base64'); + } + + else + return array($charset, $string, '7bit'); +} + +// Send an email via SMTP. +function smtp_mail($mail_to_array, $subject, $message, $headers) +{ + global $modSettings, $webmaster_email, $txt; + + $modSettings['smtp_host'] = trim($modSettings['smtp_host']); + + // Try POP3 before SMTP? + // !!! There's no interface for this yet. + if ($modSettings['mail_type'] == 2 && $modSettings['smtp_username'] != '' && $modSettings['smtp_password'] != '') + { + $socket = fsockopen($modSettings['smtp_host'], 110, $errno, $errstr, 2); + if (!$socket && (substr($modSettings['smtp_host'], 0, 5) == 'smtp.' || substr($modSettings['smtp_host'], 0, 11) == 'ssl://smtp.')) + $socket = fsockopen(strtr($modSettings['smtp_host'], array('smtp.' => 'pop.')), 110, $errno, $errstr, 2); + + if ($socket) + { + fgets($socket, 256); + fputs($socket, 'USER ' . $modSettings['smtp_username'] . "\r\n"); + fgets($socket, 256); + fputs($socket, 'PASS ' . base64_decode($modSettings['smtp_password']) . "\r\n"); + fgets($socket, 256); + fputs($socket, 'QUIT' . "\r\n"); + + fclose($socket); + } + } + + // Try to connect to the SMTP server... if it doesn't exist, only wait three seconds. + if (!$socket = fsockopen($modSettings['smtp_host'], empty($modSettings['smtp_port']) ? 25 : $modSettings['smtp_port'], $errno, $errstr, 3)) + { + // Maybe we can still save this? The port might be wrong. + if (substr($modSettings['smtp_host'], 0, 4) == 'ssl:' && (empty($modSettings['smtp_port']) || $modSettings['smtp_port'] == 25)) + { + if ($socket = fsockopen($modSettings['smtp_host'], 465, $errno, $errstr, 3)) + log_error($txt['smtp_port_ssl']); + } + + // Unable to connect! Don't show any error message, but just log one and try to continue anyway. + if (!$socket) + { + log_error($txt['smtp_no_connect'] . ': ' . $errno . ' : ' . $errstr); + return false; + } + } + + // Wait for a response of 220, without "-" continuer. + if (!server_parse(null, $socket, '220')) + return false; + + if ($modSettings['mail_type'] == 1 && $modSettings['smtp_username'] != '' && $modSettings['smtp_password'] != '') + { + // !!! These should send the CURRENT server's name, not the mail server's! + + // EHLO could be understood to mean encrypted hello... + if (server_parse('EHLO ' . $modSettings['smtp_host'], $socket, null) == '250') + { + if (!server_parse('AUTH LOGIN', $socket, '334')) + return false; + // Send the username and password, encoded. + if (!server_parse(base64_encode($modSettings['smtp_username']), $socket, '334')) + return false; + // The password is already encoded ;) + if (!server_parse($modSettings['smtp_password'], $socket, '235')) + return false; + } + elseif (!server_parse('HELO ' . $modSettings['smtp_host'], $socket, '250')) + return false; + } + else + { + // Just say "helo". + if (!server_parse('HELO ' . $modSettings['smtp_host'], $socket, '250')) + return false; + } + + // Fix the message for any lines beginning with a period! (the first is ignored, you see.) + $message = strtr($message, array("\r\n" . '.' => "\r\n" . '..')); + + // !! Theoretically, we should be able to just loop the RCPT TO. + $mail_to_array = array_values($mail_to_array); + foreach ($mail_to_array as $i => $mail_to) + { + // Reset the connection to send another email. + if ($i != 0) + { + if (!server_parse('RSET', $socket, '250')) + return false; + } + + // From, to, and then start the data... + if (!server_parse('MAIL FROM: <' . (empty($modSettings['mail_from']) ? $webmaster_email : $modSettings['mail_from']) . '>', $socket, '250')) + return false; + if (!server_parse('RCPT TO: <' . $mail_to . '>', $socket, '250')) + return false; + if (!server_parse('DATA', $socket, '354')) + return false; + fputs($socket, 'Subject: ' . $subject . "\r\n"); + if (strlen($mail_to) > 0) + fputs($socket, 'To: <' . $mail_to . '>' . "\r\n"); + fputs($socket, $headers . "\r\n\r\n"); + fputs($socket, $message . "\r\n"); + + // Send a ., or in other words "end of data". + if (!server_parse('.', $socket, '250')) + return false; + + // Almost done, almost done... don't stop me just yet! + @set_time_limit(300); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + } + fputs($socket, 'QUIT' . "\r\n"); + fclose($socket); + + return true; +} + +// Parse a message to the SMTP server. +function server_parse($message, $socket, $response) +{ + global $txt; + + if ($message !== null) + fputs($socket, $message . "\r\n"); + + // No response yet. + $server_response = ''; + + while (substr($server_response, 3, 1) != ' ') + if (!($server_response = fgets($socket, 256))) + { + // !!! Change this message to reflect that it may mean bad user/password/server issues/etc. + log_error($txt['smtp_bad_response']); + return false; + } + + if ($response === null) + return substr($server_response, 0, 3); + + if (substr($server_response, 0, 3) != $response) + { + log_error($txt['smtp_error'] . $server_response); + return false; + } + + return true; +} + +function SpellCheck() +{ + global $txt, $context, $smcFunc; + + // A list of "words" we know about but pspell doesn't. + $known_words = array('smf', 'php', 'mysql', 'www', 'gif', 'jpeg', 'png', 'http', 'smfisawesome', 'grandia', 'terranigma', 'rpgs'); + + loadLanguage('Post'); + loadTemplate('Post'); + + // Okay, this looks funny, but it actually fixes a weird bug. + ob_start(); + $old = error_reporting(0); + + // See, first, some windows machines don't load pspell properly on the first try. Dumb, but this is a workaround. + pspell_new('en'); + + // Next, the dictionary in question may not exist. So, we try it... but... + $pspell_link = pspell_new($txt['lang_dictionary'], $txt['lang_spelling'], '', strtr($context['character_set'], array('iso-' => 'iso', 'ISO-' => 'iso')), PSPELL_FAST | PSPELL_RUN_TOGETHER); + + // Most people don't have anything but English installed... So we use English as a last resort. + if (!$pspell_link) + $pspell_link = pspell_new('en', '', '', '', PSPELL_FAST | PSPELL_RUN_TOGETHER); + + error_reporting($old); + ob_end_clean(); + + if (!isset($_POST['spellstring']) || !$pspell_link) + die; + + // Construct a bit of Javascript code. + $context['spell_js'] = ' + var txt = {"done": "' . $txt['spellcheck_done'] . '"}; + var mispstr = window.opener.document.forms[spell_formname][spell_fieldname].value; + var misps = Array('; + + // Get all the words (Javascript already separated them). + $alphas = explode("\n", strtr($_POST['spellstring'], array("\r" => ''))); + + $found_words = false; + for ($i = 0, $n = count($alphas); $i < $n; $i++) + { + // Words are sent like 'word|offset_begin|offset_end'. + $check_word = explode('|', $alphas[$i]); + + // If the word is a known word, or spelled right... + if (in_array($smcFunc['strtolower']($check_word[0]), $known_words) || pspell_check($pspell_link, $check_word[0]) || !isset($check_word[2])) + continue; + + // Find the word, and move up the "last occurance" to here. + $found_words = true; + + // Add on the javascript for this misspelling. + $context['spell_js'] .= ' + new misp("' . strtr($check_word[0], array('\\' => '\\\\', '"' => '\\"', '<' => '', '>' => '')) . '", ' . (int) $check_word[1] . ', ' . (int) $check_word[2] . ', ['; + + // If there are suggestions, add them in... + $suggestions = pspell_suggest($pspell_link, $check_word[0]); + if (!empty($suggestions)) + { + // But first check they aren't going to be censored - no naughty words! + foreach ($suggestions as $k => $word) + if ($suggestions[$k] != censorText($word)) + unset($suggestions[$k]); + + if (!empty($suggestions)) + $context['spell_js'] .= '"' . implode('", "', $suggestions) . '"'; + } + + $context['spell_js'] .= ']),'; + } + + // If words were found, take off the last comma. + if ($found_words) + $context['spell_js'] = substr($context['spell_js'], 0, -1); + + $context['spell_js'] .= ' + );'; + + // And instruct the template system to just show the spellcheck sub template. + $context['template_layers'] = array(); + $context['sub_template'] = 'spellcheck'; +} + +// Notify members that something has happened to a topic they marked! +function sendNotifications($topics, $type, $exclude = array(), $members_only = array()) +{ + global $txt, $scripturl, $language, $user_info; + global $modSettings, $sourcedir, $context, $smcFunc; + + // Can't do it if there's no topics. + if (empty($topics)) + return; + // It must be an array - it must! + if (!is_array($topics)) + $topics = array($topics); + + // Get the subject and body... + $result = $smcFunc['db_query']('', ' + SELECT mf.subject, ml.body, ml.id_member, t.id_last_msg, t.id_topic, + IFNULL(mem.real_name, ml.poster_name) AS poster_name + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) + INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ml.id_member) + WHERE t.id_topic IN ({array_int:topic_list}) + LIMIT 1', + array( + 'topic_list' => $topics, + ) + ); + $topicData = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Clean it up. + censorText($row['subject']); + censorText($row['body']); + $row['subject'] = un_htmlspecialchars($row['subject']); + $row['body'] = trim(un_htmlspecialchars(strip_tags(strtr(parse_bbc($row['body'], false, $row['id_last_msg']), array('
          ' => "\n", '' => "\n", '' => "\n", '[' => '[', ']' => ']'))))); + + $topicData[$row['id_topic']] = array( + 'subject' => $row['subject'], + 'body' => $row['body'], + 'last_id' => $row['id_last_msg'], + 'topic' => $row['id_topic'], + 'name' => $user_info['name'], + 'exclude' => '', + ); + } + $smcFunc['db_free_result']($result); + + // Work out any exclusions... + foreach ($topics as $key => $id) + if (isset($topicData[$id]) && !empty($exclude[$key])) + $topicData[$id]['exclude'] = (int) $exclude[$key]; + + // Nada? + if (empty($topicData)) + trigger_error('sendNotifications(): topics not found', E_USER_NOTICE); + + $topics = array_keys($topicData); + // Just in case they've gone walkies. + if (empty($topics)) + return; + + // Insert all of these items into the digest log for those who want notifications later. + $digest_insert = array(); + foreach ($topicData as $id => $data) + $digest_insert[] = array($data['topic'], $data['last_id'], $type, (int) $data['exclude']); + $smcFunc['db_insert']('', + '{db_prefix}log_digest', + array( + 'id_topic' => 'int', 'id_msg' => 'int', 'note_type' => 'string', 'exclude' => 'int', + ), + $digest_insert, + array() + ); + + // Find the members with notification on for this topic. + $members = $smcFunc['db_query']('', ' + SELECT + mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.lngfile, + ln.sent, mem.id_group, mem.additional_groups, b.member_groups, mem.id_post_group, t.id_member_started, + ln.id_topic + FROM {db_prefix}log_notify AS ln + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member) + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE ln.id_topic IN ({array_int:topic_list}) + AND mem.notify_types < {int:notify_types} + AND mem.notify_regularity < {int:notify_regularity} + AND mem.is_activated = {int:is_activated} + AND ln.id_member != {int:current_member}' . + (empty($members_only) ? '' : ' AND ln.id_member IN ({array_int:members_only})') . ' + ORDER BY mem.lngfile', + array( + 'current_member' => $user_info['id'], + 'topic_list' => $topics, + 'notify_types' => $type == 'reply' ? '4' : '3', + 'notify_regularity' => 2, + 'is_activated' => 1, + 'members_only' => is_array($members_only) ? $members_only : array($members_only), + ) + ); + $sent = 0; + while ($row = $smcFunc['db_fetch_assoc']($members)) + { + // Don't do the excluded... + if ($topicData[$row['id_topic']]['exclude'] == $row['id_member']) + continue; + + // Easier to check this here... if they aren't the topic poster do they really want to know? + if ($type != 'reply' && $row['notify_types'] == 2 && $row['id_member'] != $row['id_member_started']) + continue; + + if ($row['id_group'] != 1) + { + $allowed = explode(',', $row['member_groups']); + $row['additional_groups'] = explode(',', $row['additional_groups']); + $row['additional_groups'][] = $row['id_group']; + $row['additional_groups'][] = $row['id_post_group']; + + if (count(array_intersect($allowed, $row['additional_groups'])) == 0) + continue; + } + + $needed_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']; + if (empty($current_language) || $current_language != $needed_language) + $current_language = loadLanguage('Post', $needed_language, false); + + $message_type = 'notification_' . $type; + $replacements = array( + 'TOPICSUBJECT' => $topicData[$row['id_topic']]['subject'], + 'POSTERNAME' => un_htmlspecialchars($topicData[$row['id_topic']]['name']), + 'TOPICLINK' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new', + 'UNSUBSCRIBELINK' => $scripturl . '?action=notify;topic=' . $row['id_topic'] . '.0', + ); + + if ($type == 'remove') + unset($replacements['TOPICLINK'], $replacements['UNSUBSCRIBELINK']); + // Do they want the body of the message sent too? + if (!empty($row['notify_send_body']) && $type == 'reply' && empty($modSettings['disallow_sendBody'])) + { + $message_type .= '_body'; + $replacements['MESSAGE'] = $topicData[$row['id_topic']]['body']; + } + if (!empty($row['notify_regularity']) && $type == 'reply') + $message_type .= '_once'; + + // Send only if once is off or it's on and it hasn't been sent. + if ($type != 'reply' || empty($row['notify_regularity']) || empty($row['sent'])) + { + $emaildata = loadEmailTemplate($message_type, $replacements, $needed_language); + sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, 'm' . $topicData[$row['id_topic']]['last_id']); + $sent++; + } + } + $smcFunc['db_free_result']($members); + + if (isset($current_language) && $current_language != $user_info['language']) + loadLanguage('Post'); + + // Sent! + if ($type == 'reply' && !empty($sent)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_notify + SET sent = {int:is_sent} + WHERE id_topic IN ({array_int:topic_list}) + AND id_member != {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'topic_list' => $topics, + 'is_sent' => 1, + ) + ); + + // For approvals we need to unsend the exclusions (This *is* the quickest way!) + if (!empty($sent) && !empty($exclude)) + { + foreach ($topicData as $id => $data) + if ($data['exclude']) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_notify + SET sent = {int:not_sent} + WHERE id_topic = {int:id_topic} + AND id_member = {int:id_member}', + array( + 'not_sent' => 0, + 'id_topic' => $id, + 'id_member' => $data['exclude'], + ) + ); + } +} + +// Create a post, either as new topic (id_topic = 0) or in an existing one. +// The input parameters of this function assume: +// - Strings have been escaped. +// - Integers have been cast to integer. +// - Mandatory parameters are set. +function createPost(&$msgOptions, &$topicOptions, &$posterOptions) +{ + global $user_info, $txt, $modSettings, $smcFunc, $context; + + // Set optional parameters to the default value. + $msgOptions['icon'] = empty($msgOptions['icon']) ? 'xx' : $msgOptions['icon']; + $msgOptions['smileys_enabled'] = !empty($msgOptions['smileys_enabled']); + $msgOptions['attachments'] = empty($msgOptions['attachments']) ? array() : $msgOptions['attachments']; + $msgOptions['approved'] = isset($msgOptions['approved']) ? (int) $msgOptions['approved'] : 1; + $topicOptions['id'] = empty($topicOptions['id']) ? 0 : (int) $topicOptions['id']; + $topicOptions['poll'] = isset($topicOptions['poll']) ? (int) $topicOptions['poll'] : null; + $topicOptions['lock_mode'] = isset($topicOptions['lock_mode']) ? $topicOptions['lock_mode'] : null; + $topicOptions['sticky_mode'] = isset($topicOptions['sticky_mode']) ? $topicOptions['sticky_mode'] : null; + $posterOptions['id'] = empty($posterOptions['id']) ? 0 : (int) $posterOptions['id']; + $posterOptions['ip'] = empty($posterOptions['ip']) ? $user_info['ip'] : $posterOptions['ip']; + + // We need to know if the topic is approved. If we're told that's great - if not find out. + if (!$modSettings['postmod_active']) + $topicOptions['is_approved'] = true; + elseif (!empty($topicOptions['id']) && !isset($topicOptions['is_approved'])) + { + $request = $smcFunc['db_query']('', ' + SELECT approved + FROM {db_prefix}topics + WHERE id_topic = {int:id_topic} + LIMIT 1', + array( + 'id_topic' => $topicOptions['id'], + ) + ); + list ($topicOptions['is_approved']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // If nothing was filled in as name/e-mail address, try the member table. + if (!isset($posterOptions['name']) || $posterOptions['name'] == '' || (empty($posterOptions['email']) && !empty($posterOptions['id']))) + { + if (empty($posterOptions['id'])) + { + $posterOptions['id'] = 0; + $posterOptions['name'] = $txt['guest_title']; + $posterOptions['email'] = ''; + } + elseif ($posterOptions['id'] != $user_info['id']) + { + $request = $smcFunc['db_query']('', ' + SELECT member_name, email_address + FROM {db_prefix}members + WHERE id_member = {int:id_member} + LIMIT 1', + array( + 'id_member' => $posterOptions['id'], + ) + ); + // Couldn't find the current poster? + if ($smcFunc['db_num_rows']($request) == 0) + { + trigger_error('createPost(): Invalid member id ' . $posterOptions['id'], E_USER_NOTICE); + $posterOptions['id'] = 0; + $posterOptions['name'] = $txt['guest_title']; + $posterOptions['email'] = ''; + } + else + list ($posterOptions['name'], $posterOptions['email']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + else + { + $posterOptions['name'] = $user_info['name']; + $posterOptions['email'] = $user_info['email']; + } + } + + // It's do or die time: forget any user aborts! + $previous_ignore_user_abort = ignore_user_abort(true); + + $new_topic = empty($topicOptions['id']); + + // Insert the post. + $smcFunc['db_insert']('', + '{db_prefix}messages', + array( + 'id_board' => 'int', 'id_topic' => 'int', 'id_member' => 'int', 'subject' => 'string-255', 'body' => (!empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] > 65534 ? 'string-' . $modSettings['max_messageLength'] : 'string-65534'), + 'poster_name' => 'string-255', 'poster_email' => 'string-255', 'poster_time' => 'int', 'poster_ip' => 'string-255', + 'smileys_enabled' => 'int', 'modified_name' => 'string', 'icon' => 'string-16', 'approved' => 'int', + ), + array( + $topicOptions['board'], $topicOptions['id'], $posterOptions['id'], $msgOptions['subject'], $msgOptions['body'], + $posterOptions['name'], $posterOptions['email'], time(), $posterOptions['ip'], + $msgOptions['smileys_enabled'] ? 1 : 0, '', $msgOptions['icon'], $msgOptions['approved'], + ), + array('id_msg') + ); + $msgOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}messages', 'id_msg'); + + // Something went wrong creating the message... + if (empty($msgOptions['id'])) + return false; + + // Fix the attachments. + if (!empty($msgOptions['attachments'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET id_msg = {int:id_msg} + WHERE id_attach IN ({array_int:attachment_list})', + array( + 'attachment_list' => $msgOptions['attachments'], + 'id_msg' => $msgOptions['id'], + ) + ); + + // Insert a new topic (if the topicID was left empty.) + if ($new_topic) + { + $smcFunc['db_insert']('', + '{db_prefix}topics', + array( + 'id_board' => 'int', 'id_member_started' => 'int', 'id_member_updated' => 'int', 'id_first_msg' => 'int', + 'id_last_msg' => 'int', 'locked' => 'int', 'is_sticky' => 'int', 'num_views' => 'int', + 'id_poll' => 'int', 'unapproved_posts' => 'int', 'approved' => 'int', + ), + array( + $topicOptions['board'], $posterOptions['id'], $posterOptions['id'], $msgOptions['id'], + $msgOptions['id'], $topicOptions['lock_mode'] === null ? 0 : $topicOptions['lock_mode'], $topicOptions['sticky_mode'] === null ? 0 : $topicOptions['sticky_mode'], 0, + $topicOptions['poll'] === null ? 0 : $topicOptions['poll'], $msgOptions['approved'] ? 0 : 1, $msgOptions['approved'], + ), + array('id_topic') + ); + $topicOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}topics', 'id_topic'); + + // The topic couldn't be created for some reason. + if (empty($topicOptions['id'])) + { + // We should delete the post that did work, though... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}messages + WHERE id_msg = {int:id_msg}', + array( + 'id_msg' => $msgOptions['id'], + ) + ); + + return false; + } + + // Fix the message with the topic. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET id_topic = {int:id_topic} + WHERE id_msg = {int:id_msg}', + array( + 'id_topic' => $topicOptions['id'], + 'id_msg' => $msgOptions['id'], + ) + ); + + // There's been a new topic AND a new post today. + trackStats(array('topics' => '+', 'posts' => '+')); + + updateStats('topic', true); + updateStats('subject', $topicOptions['id'], $msgOptions['subject']); + + // What if we want to export new topics out to a CMS? + call_integration_hook('integrate_create_topic', array($msgOptions, $topicOptions, $posterOptions)); + } + // The topic already exists, it only needs a little updating. + else + { + $countChange = $msgOptions['approved'] ? 'num_replies = num_replies + 1' : 'unapproved_posts = unapproved_posts + 1'; + + // Update the number of replies and the lock/sticky status. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET + ' . ($msgOptions['approved'] ? 'id_member_updated = {int:poster_id}, id_last_msg = {int:id_msg},' : '') . ' + ' . $countChange . ($topicOptions['lock_mode'] === null ? '' : ', + locked = {int:locked}') . ($topicOptions['sticky_mode'] === null ? '' : ', + is_sticky = {int:is_sticky}') . ' + WHERE id_topic = {int:id_topic}', + array( + 'poster_id' => $posterOptions['id'], + 'id_msg' => $msgOptions['id'], + 'locked' => $topicOptions['lock_mode'], + 'is_sticky' => $topicOptions['sticky_mode'], + 'id_topic' => $topicOptions['id'], + ) + ); + + // One new post has been added today. + trackStats(array('posts' => '+')); + } + + // Creating is modifying...in a way. + //!!! Why not set id_msg_modified on the insert? + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET id_msg_modified = {int:id_msg} + WHERE id_msg = {int:id_msg}', + array( + 'id_msg' => $msgOptions['id'], + ) + ); + + // Increase the number of posts and topics on the board. + if ($msgOptions['approved']) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET num_posts = num_posts + 1' . ($new_topic ? ', num_topics = num_topics + 1' : '') . ' + WHERE id_board = {int:id_board}', + array( + 'id_board' => $topicOptions['board'], + ) + ); + else + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET unapproved_posts = unapproved_posts + 1' . ($new_topic ? ', unapproved_topics = unapproved_topics + 1' : '') . ' + WHERE id_board = {int:id_board}', + array( + 'id_board' => $topicOptions['board'], + ) + ); + + // Add to the approval queue too. + $smcFunc['db_insert']('', + '{db_prefix}approval_queue', + array( + 'id_msg' => 'int', + ), + array( + $msgOptions['id'], + ), + array() + ); + } + + // Mark inserted topic as read (only for the user calling this function). + if (!empty($topicOptions['mark_as_read']) && !$user_info['is_guest']) + { + // Since it's likely they *read* it before replying, let's try an UPDATE first. + if (!$new_topic) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_topics + SET id_msg = {int:id_msg} + WHERE id_member = {int:current_member} + AND id_topic = {int:id_topic}', + array( + 'current_member' => $posterOptions['id'], + 'id_msg' => $msgOptions['id'], + 'id_topic' => $topicOptions['id'], + ) + ); + + $flag = $smcFunc['db_affected_rows']() != 0; + } + + if (empty($flag)) + { + $smcFunc['db_insert']('ignore', + '{db_prefix}log_topics', + array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), + array($topicOptions['id'], $posterOptions['id'], $msgOptions['id']), + array('id_topic', 'id_member') + ); + } + } + + // If there's a custom search index, it needs updating... + if (!empty($modSettings['search_custom_index_config'])) + { + $customIndexSettings = unserialize($modSettings['search_custom_index_config']); + + $inserts = array(); + foreach (text2words($msgOptions['body'], $customIndexSettings['bytes_per_word'], true) as $word) + $inserts[] = array($word, $msgOptions['id']); + + if (!empty($inserts)) + $smcFunc['db_insert']('ignore', + '{db_prefix}log_search_words', + array('id_word' => 'int', 'id_msg' => 'int'), + $inserts, + array('id_word', 'id_msg') + ); + } + + // Increase the post counter for the user that created the post. + if (!empty($posterOptions['update_post_count']) && !empty($posterOptions['id']) && $msgOptions['approved']) + { + // Are you the one that happened to create this post? + if ($user_info['id'] == $posterOptions['id']) + $user_info['posts']++; + updateMemberData($posterOptions['id'], array('posts' => '+')); + } + + // They've posted, so they can make the view count go up one if they really want. (this is to keep views >= replies...) + $_SESSION['last_read_topic'] = 0; + + // Better safe than sorry. + if (isset($_SESSION['topicseen_cache'][$topicOptions['board']])) + $_SESSION['topicseen_cache'][$topicOptions['board']]--; + + // Update all the stats so everyone knows about this new topic and message. + updateStats('message', true, $msgOptions['id']); + + // Update the last message on the board assuming it's approved AND the topic is. + if ($msgOptions['approved']) + updateLastMessages($topicOptions['board'], $new_topic || !empty($topicOptions['is_approved']) ? $msgOptions['id'] : 0); + + // Alright, done now... we can abort now, I guess... at least this much is done. + ignore_user_abort($previous_ignore_user_abort); + + // Success. + return true; +} + +// !!! +function createAttachment(&$attachmentOptions) +{ + global $modSettings, $sourcedir, $smcFunc, $context; + + require_once($sourcedir . '/Subs-Graphics.php'); + + // We need to know where this thing is going. + if (!empty($modSettings['currentAttachmentUploadDir'])) + { + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + + // Just use the current path for temp files. + $attach_dir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']]; + $id_folder = $modSettings['currentAttachmentUploadDir']; + } + else + { + $attach_dir = $modSettings['attachmentUploadDir']; + $id_folder = 1; + } + + $attachmentOptions['errors'] = array(); + if (!isset($attachmentOptions['post'])) + $attachmentOptions['post'] = 0; + if (!isset($attachmentOptions['approved'])) + $attachmentOptions['approved'] = 1; + + $already_uploaded = preg_match('~^post_tmp_' . $attachmentOptions['poster'] . '_\d+$~', $attachmentOptions['tmp_name']) != 0; + $file_restricted = @ini_get('open_basedir') != '' && !$already_uploaded; + + if ($already_uploaded) + $attachmentOptions['tmp_name'] = $attach_dir . '/' . $attachmentOptions['tmp_name']; + + // Make sure the file actually exists... sometimes it doesn't. + if ((!$file_restricted && !file_exists($attachmentOptions['tmp_name'])) || (!$already_uploaded && !is_uploaded_file($attachmentOptions['tmp_name']))) + { + $attachmentOptions['errors'] = array('could_not_upload'); + return false; + } + + // These are the only valid image types for SMF. + $validImageTypes = array( + 1 => 'gif', + 2 => 'jpeg', + 3 => 'png', + 5 => 'psd', + 6 => 'bmp', + 7 => 'tiff', + 8 => 'tiff', + 9 => 'jpeg', + 14 => 'iff' + ); + + if (!$file_restricted || $already_uploaded) + { + $size = @getimagesize($attachmentOptions['tmp_name']); + list ($attachmentOptions['width'], $attachmentOptions['height']) = $size; + + // If it's an image get the mime type right. + if (empty($attachmentOptions['mime_type']) && $attachmentOptions['width']) + { + // Got a proper mime type? + if (!empty($size['mime'])) + $attachmentOptions['mime_type'] = $size['mime']; + // Otherwise a valid one? + elseif (isset($validImageTypes[$size[2]])) + $attachmentOptions['mime_type'] = 'image/' . $validImageTypes[$size[2]]; + } + } + + // Get the hash if no hash has been given yet. + if (empty($attachmentOptions['file_hash'])) + $attachmentOptions['file_hash'] = getAttachmentFilename($attachmentOptions['name'], false, null, true); + + // Is the file too big? + if (!empty($modSettings['attachmentSizeLimit']) && $attachmentOptions['size'] > $modSettings['attachmentSizeLimit'] * 1024) + $attachmentOptions['errors'][] = 'too_large'; + + if (!empty($modSettings['attachmentCheckExtensions'])) + { + $allowed = explode(',', strtolower($modSettings['attachmentExtensions'])); + foreach ($allowed as $k => $dummy) + $allowed[$k] = trim($dummy); + + if (!in_array(strtolower(substr(strrchr($attachmentOptions['name'], '.'), 1)), $allowed)) + $attachmentOptions['errors'][] = 'bad_extension'; + } + + if (!empty($modSettings['attachmentDirSizeLimit'])) + { + // Make sure the directory isn't full. + $dirSize = 0; + $dir = @opendir($attach_dir) or fatal_lang_error('cant_access_upload_path', 'critical'); + while ($file = readdir($dir)) + { + if ($file == '.' || $file == '..') + continue; + + if (preg_match('~^post_tmp_\d+_\d+$~', $file) != 0) + { + // Temp file is more than 5 hours old! + if (filemtime($attach_dir . '/' . $file) < time() - 18000) + @unlink($attach_dir . '/' . $file); + continue; + } + + $dirSize += filesize($attach_dir . '/' . $file); + } + closedir($dir); + + // Too big! Maybe you could zip it or something... + if ($attachmentOptions['size'] + $dirSize > $modSettings['attachmentDirSizeLimit'] * 1024) + $attachmentOptions['errors'][] = 'directory_full'; + // Soon to be too big - warn the admins... + elseif (!isset($modSettings['attachment_full_notified']) && $modSettings['attachmentDirSizeLimit'] > 4000 && $attachmentOptions['size'] + $dirSize > ($modSettings['attachmentDirSizeLimit'] - 2000) * 1024) + { + require_once($sourcedir . '/Subs-Admin.php'); + emailAdmins('admin_attachments_full'); + updateSettings(array('attachment_full_notified' => 1)); + } + } + + // Check if the file already exists.... (for those who do not encrypt their filenames...) + if (empty($modSettings['attachmentEncryptFilenames'])) + { + // Make sure they aren't trying to upload a nasty file. + $disabledFiles = array('con', 'com1', 'com2', 'com3', 'com4', 'prn', 'aux', 'lpt1', '.htaccess', 'index.php'); + if (in_array(strtolower(basename($attachmentOptions['name'])), $disabledFiles)) + $attachmentOptions['errors'][] = 'bad_filename'; + + // Check if there's another file with that name... + $request = $smcFunc['db_query']('', ' + SELECT id_attach + FROM {db_prefix}attachments + WHERE filename = {string:filename} + LIMIT 1', + array( + 'filename' => strtolower($attachmentOptions['name']), + ) + ); + if ($smcFunc['db_num_rows']($request) > 0) + $attachmentOptions['errors'][] = 'taken_filename'; + $smcFunc['db_free_result']($request); + } + + if (!empty($attachmentOptions['errors'])) + return false; + + if (!is_writable($attach_dir)) + fatal_lang_error('attachments_no_write', 'critical'); + + // Assuming no-one set the extension let's take a look at it. + if (empty($attachmentOptions['fileext'])) + { + $attachmentOptions['fileext'] = strtolower(strrpos($attachmentOptions['name'], '.') !== false ? substr($attachmentOptions['name'], strrpos($attachmentOptions['name'], '.') + 1) : ''); + if (strlen($attachmentOptions['fileext']) > 8 || '.' . $attachmentOptions['fileext'] == $attachmentOptions['name']) + $attachmentOptions['fileext'] = ''; + } + + $smcFunc['db_insert']('', + '{db_prefix}attachments', + array( + 'id_folder' => 'int', 'id_msg' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-40', 'fileext' => 'string-8', + 'size' => 'int', 'width' => 'int', 'height' => 'int', + 'mime_type' => 'string-20', 'approved' => 'int', + ), + array( + $id_folder, (int) $attachmentOptions['post'], $attachmentOptions['name'], $attachmentOptions['file_hash'], $attachmentOptions['fileext'], + (int) $attachmentOptions['size'], (empty($attachmentOptions['width']) ? 0 : (int) $attachmentOptions['width']), (empty($attachmentOptions['height']) ? '0' : (int) $attachmentOptions['height']), + (!empty($attachmentOptions['mime_type']) ? $attachmentOptions['mime_type'] : ''), (int) $attachmentOptions['approved'], + ), + array('id_attach') + ); + $attachmentOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach'); + + if (empty($attachmentOptions['id'])) + return false; + + // If it's not approved add to the approval queue. + if (!$attachmentOptions['approved']) + $smcFunc['db_insert']('', + '{db_prefix}approval_queue', + array( + 'id_attach' => 'int', 'id_msg' => 'int', + ), + array( + $attachmentOptions['id'], (int) $attachmentOptions['post'], + ), + array() + ); + + $attachmentOptions['destination'] = getAttachmentFilename(basename($attachmentOptions['name']), $attachmentOptions['id'], $id_folder, false, $attachmentOptions['file_hash']); + + if ($already_uploaded) + rename($attachmentOptions['tmp_name'], $attachmentOptions['destination']); + elseif (!move_uploaded_file($attachmentOptions['tmp_name'], $attachmentOptions['destination'])) + fatal_lang_error('attach_timeout', 'critical'); + + // Attempt to chmod it. + @chmod($attachmentOptions['destination'], 0644); + + $size = @getimagesize($attachmentOptions['destination']); + list ($attachmentOptions['width'], $attachmentOptions['height']) = empty($size) ? array(null, null, null) : $size; + + // We couldn't access the file before... + if ($file_restricted) + { + // Have a go at getting the right mime type. + if (empty($attachmentOptions['mime_type']) && $attachmentOptions['width']) + { + if (!empty($size['mime'])) + $attachmentOptions['mime_type'] = $size['mime']; + elseif (isset($validImageTypes[$size[2]])) + $attachmentOptions['mime_type'] = 'image/' . $validImageTypes[$size[2]]; + } + + if (!empty($attachmentOptions['width']) && !empty($attachmentOptions['height'])) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET + width = {int:width}, + height = {int:height}, + mime_type = {string:mime_type} + WHERE id_attach = {int:id_attach}', + array( + 'width' => (int) $attachmentOptions['width'], + 'height' => (int) $attachmentOptions['height'], + 'id_attach' => $attachmentOptions['id'], + 'mime_type' => empty($attachmentOptions['mime_type']) ? '' : $attachmentOptions['mime_type'], + ) + ); + } + + // Security checks for images + // Do we have an image? If yes, we need to check it out! + if (isset($validImageTypes[$size[2]])) + { + if (!checkImageContents($attachmentOptions['destination'], !empty($modSettings['attachment_image_paranoid']))) + { + // It's bad. Last chance, maybe we can re-encode it? + if (empty($modSettings['attachment_image_reencode']) || (!reencodeImage($attachmentOptions['destination'], $size[2]))) + { + // Nothing to do: not allowed or not successful re-encoding it. + require_once($sourcedir . '/ManageAttachments.php'); + removeAttachments(array( + 'id_attach' => $attachmentOptions['id'] + )); + $attachmentOptions['id'] = null; + $attachmentOptions['errors'][] = 'bad_attachment'; + + return false; + } + // Success! However, successes usually come for a price: + // we might get a new format for our image... + $old_format = $size[2]; + $size = @getimagesize($attachmentOptions['destination']); + if (!(empty($size)) && ($size[2] != $old_format)) + { + // Let's update the image information + // !!! This is becoming a mess: we keep coming back and update the database, + // instead of getting it right the first time. + if (isset($validImageTypes[$size[2]])) + { + $attachmentOptions['mime_type'] = 'image/' . $validImageTypes[$size[2]]; + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET + mime_type = {string:mime_type} + WHERE id_attach = {int:id_attach}', + array( + 'id_attach' => $attachmentOptions['id'], + 'mime_type' => $attachmentOptions['mime_type'], + ) + ); + } + } + } + } + + if (!empty($attachmentOptions['skip_thumbnail']) || (empty($attachmentOptions['width']) && empty($attachmentOptions['height']))) + return true; + + // Like thumbnails, do we? + if (!empty($modSettings['attachmentThumbnails']) && !empty($modSettings['attachmentThumbWidth']) && !empty($modSettings['attachmentThumbHeight']) && ($attachmentOptions['width'] > $modSettings['attachmentThumbWidth'] || $attachmentOptions['height'] > $modSettings['attachmentThumbHeight'])) + { + if (createThumbnail($attachmentOptions['destination'], $modSettings['attachmentThumbWidth'], $modSettings['attachmentThumbHeight'])) + { + // Figure out how big we actually made it. + $size = @getimagesize($attachmentOptions['destination'] . '_thumb'); + list ($thumb_width, $thumb_height) = $size; + + if (!empty($size['mime'])) + $thumb_mime = $size['mime']; + elseif (isset($validImageTypes[$size[2]])) + $thumb_mime = 'image/' . $validImageTypes[$size[2]]; + // Lord only knows how this happened... + else + $thumb_mime = ''; + + $thumb_filename = $attachmentOptions['name'] . '_thumb'; + $thumb_size = filesize($attachmentOptions['destination'] . '_thumb'); + $thumb_file_hash = getAttachmentFilename($thumb_filename, false, null, true); + + // To the database we go! + $smcFunc['db_insert']('', + '{db_prefix}attachments', + array( + 'id_folder' => 'int', 'id_msg' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-40', 'fileext' => 'string-8', + 'size' => 'int', 'width' => 'int', 'height' => 'int', 'mime_type' => 'string-20', 'approved' => 'int', + ), + array( + $id_folder, (int) $attachmentOptions['post'], 3, $thumb_filename, $thumb_file_hash, $attachmentOptions['fileext'], + $thumb_size, $thumb_width, $thumb_height, $thumb_mime, (int) $attachmentOptions['approved'], + ), + array('id_attach') + ); + $attachmentOptions['thumb'] = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach'); + + if (!empty($attachmentOptions['thumb'])) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}attachments + SET id_thumb = {int:id_thumb} + WHERE id_attach = {int:id_attach}', + array( + 'id_thumb' => $attachmentOptions['thumb'], + 'id_attach' => $attachmentOptions['id'], + ) + ); + + rename($attachmentOptions['destination'] . '_thumb', getAttachmentFilename($thumb_filename, $attachmentOptions['thumb'], $id_folder, false, $thumb_file_hash)); + } + } + } + + return true; +} + +// !!! +function modifyPost(&$msgOptions, &$topicOptions, &$posterOptions) +{ + global $user_info, $modSettings, $smcFunc, $context; + + $topicOptions['poll'] = isset($topicOptions['poll']) ? (int) $topicOptions['poll'] : null; + $topicOptions['lock_mode'] = isset($topicOptions['lock_mode']) ? $topicOptions['lock_mode'] : null; + $topicOptions['sticky_mode'] = isset($topicOptions['sticky_mode']) ? $topicOptions['sticky_mode'] : null; + + // This is longer than it has to be, but makes it so we only set/change what we have to. + $messages_columns = array(); + if (isset($posterOptions['name'])) + $messages_columns['poster_name'] = $posterOptions['name']; + if (isset($posterOptions['email'])) + $messages_columns['poster_email'] = $posterOptions['email']; + if (isset($msgOptions['icon'])) + $messages_columns['icon'] = $msgOptions['icon']; + if (isset($msgOptions['subject'])) + $messages_columns['subject'] = $msgOptions['subject']; + if (isset($msgOptions['body'])) + { + $messages_columns['body'] = $msgOptions['body']; + + if (!empty($modSettings['search_custom_index_config'])) + { + $request = $smcFunc['db_query']('', ' + SELECT body + FROM {db_prefix}messages + WHERE id_msg = {int:id_msg}', + array( + 'id_msg' => $msgOptions['id'], + ) + ); + list ($old_body) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + } + if (!empty($msgOptions['modify_time'])) + { + $messages_columns['modified_time'] = $msgOptions['modify_time']; + $messages_columns['modified_name'] = $msgOptions['modify_name']; + $messages_columns['id_msg_modified'] = $modSettings['maxMsgID']; + } + if (isset($msgOptions['smileys_enabled'])) + $messages_columns['smileys_enabled'] = empty($msgOptions['smileys_enabled']) ? 0 : 1; + + // Which columns need to be ints? + $messageInts = array('modified_time', 'id_msg_modified', 'smileys_enabled'); + $update_parameters = array( + 'id_msg' => $msgOptions['id'], + ); + + foreach ($messages_columns as $var => $val) + { + $messages_columns[$var] = $var . ' = {' . (in_array($var, $messageInts) ? 'int' : 'string') . ':var_' . $var . '}'; + $update_parameters['var_' . $var] = $val; + } + + // Nothing to do? + if (empty($messages_columns)) + return true; + + // Change the post. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET ' . implode(', ', $messages_columns) . ' + WHERE id_msg = {int:id_msg}', + $update_parameters + ); + + // Lock and or sticky the post. + if ($topicOptions['sticky_mode'] !== null || $topicOptions['lock_mode'] !== null || $topicOptions['poll'] !== null) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET + is_sticky = {raw:is_sticky}, + locked = {raw:locked}, + id_poll = {raw:id_poll} + WHERE id_topic = {int:id_topic}', + array( + 'is_sticky' => $topicOptions['sticky_mode'] === null ? 'is_sticky' : (int) $topicOptions['sticky_mode'], + 'locked' => $topicOptions['lock_mode'] === null ? 'locked' : (int) $topicOptions['lock_mode'], + 'id_poll' => $topicOptions['poll'] === null ? 'id_poll' : (int) $topicOptions['poll'], + 'id_topic' => $topicOptions['id'], + ) + ); + } + + // Mark the edited post as read. + if (!empty($topicOptions['mark_as_read']) && !$user_info['is_guest']) + { + // Since it's likely they *read* it before editing, let's try an UPDATE first. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_topics + SET id_msg = {int:id_msg} + WHERE id_member = {int:current_member} + AND id_topic = {int:id_topic}', + array( + 'current_member' => $user_info['id'], + 'id_msg' => $modSettings['maxMsgID'], + 'id_topic' => $topicOptions['id'], + ) + ); + + $flag = $smcFunc['db_affected_rows']() != 0; + + if (empty($flag)) + { + $smcFunc['db_insert']('ignore', + '{db_prefix}log_topics', + array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), + array($topicOptions['id'], $user_info['id'], $modSettings['maxMsgID']), + array('id_topic', 'id_member') + ); + } + } + + // If there's a custom search index, it needs to be modified... + if (isset($msgOptions['body']) && !empty($modSettings['search_custom_index_config'])) + { + $customIndexSettings = unserialize($modSettings['search_custom_index_config']); + + $stopwords = empty($modSettings['search_stopwords']) ? array() : explode(',', $modSettings['search_stopwords']); + $old_index = text2words($old_body, $customIndexSettings['bytes_per_word'], true); + $new_index = text2words($msgOptions['body'], $customIndexSettings['bytes_per_word'], true); + + // Calculate the words to be added and removed from the index. + $removed_words = array_diff(array_diff($old_index, $new_index), $stopwords); + $inserted_words = array_diff(array_diff($new_index, $old_index), $stopwords); + // Delete the removed words AND the added ones to avoid key constraints. + if (!empty($removed_words)) + { + $removed_words = array_merge($removed_words, $inserted_words); + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_search_words + WHERE id_msg = {int:id_msg} + AND id_word IN ({array_int:removed_words})', + array( + 'removed_words' => $removed_words, + 'id_msg' => $msgOptions['id'], + ) + ); + } + + // Add the new words to be indexed. + if (!empty($inserted_words)) + { + $inserts = array(); + foreach ($inserted_words as $word) + $inserts[] = array($word, $msgOptions['id']); + $smcFunc['db_insert']('insert', + '{db_prefix}log_search_words', + array('id_word' => 'string', 'id_msg' => 'int'), + $inserts, + array('id_word', 'id_msg') + ); + } + } + + if (isset($msgOptions['subject'])) + { + // Only update the subject if this was the first message in the topic. + $request = $smcFunc['db_query']('', ' + SELECT id_topic + FROM {db_prefix}topics + WHERE id_first_msg = {int:id_first_msg} + LIMIT 1', + array( + 'id_first_msg' => $msgOptions['id'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 1) + updateStats('subject', $topicOptions['id'], $msgOptions['subject']); + $smcFunc['db_free_result']($request); + } + + // Finally, if we are setting the approved state we need to do much more work :( + if ($modSettings['postmod_active'] && isset($msgOptions['approved'])) + approvePosts($msgOptions['id'], $msgOptions['approved']); + + return true; +} + +// Approve (or not) some posts... without permission checks... +function approvePosts($msgs, $approve = true) +{ + global $sourcedir, $smcFunc; + + if (!is_array($msgs)) + $msgs = array($msgs); + + if (empty($msgs)) + return false; + + // May as well start at the beginning, working out *what* we need to change. + $request = $smcFunc['db_query']('', ' + SELECT m.id_msg, m.approved, m.id_topic, m.id_board, t.id_first_msg, t.id_last_msg, + m.body, m.subject, IFNULL(mem.real_name, m.poster_name) AS poster_name, m.id_member, + t.approved AS topic_approved, b.count_posts + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) + WHERE m.id_msg IN ({array_int:message_list}) + AND m.approved = {int:approved_state}', + array( + 'message_list' => $msgs, + 'approved_state' => $approve ? 0 : 1, + ) + ); + $msgs = array(); + $topics = array(); + $topic_changes = array(); + $board_changes = array(); + $notification_topics = array(); + $notification_posts = array(); + $member_post_changes = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Easy... + $msgs[] = $row['id_msg']; + $topics[] = $row['id_topic']; + + // Ensure our change array exists already. + if (!isset($topic_changes[$row['id_topic']])) + $topic_changes[$row['id_topic']] = array( + 'id_last_msg' => $row['id_last_msg'], + 'approved' => $row['topic_approved'], + 'replies' => 0, + 'unapproved_posts' => 0, + ); + if (!isset($board_changes[$row['id_board']])) + $board_changes[$row['id_board']] = array( + 'posts' => 0, + 'topics' => 0, + 'unapproved_posts' => 0, + 'unapproved_topics' => 0, + ); + + // If it's the first message then the topic state changes! + if ($row['id_msg'] == $row['id_first_msg']) + { + $topic_changes[$row['id_topic']]['approved'] = $approve ? 1 : 0; + + $board_changes[$row['id_board']]['unapproved_topics'] += $approve ? -1 : 1; + $board_changes[$row['id_board']]['topics'] += $approve ? 1 : -1; + + // Note we need to ensure we announce this topic! + $notification_topics[] = array( + 'body' => $row['body'], + 'subject' => $row['subject'], + 'name' => $row['poster_name'], + 'board' => $row['id_board'], + 'topic' => $row['id_topic'], + 'msg' => $row['id_first_msg'], + 'poster' => $row['id_member'], + ); + } + else + { + $topic_changes[$row['id_topic']]['replies'] += $approve ? 1 : -1; + + // This will be a post... but don't notify unless it's not followed by approved ones. + if ($row['id_msg'] > $row['id_last_msg']) + $notification_posts[$row['id_topic']][] = array( + 'id' => $row['id_msg'], + 'body' => $row['body'], + 'subject' => $row['subject'], + 'name' => $row['poster_name'], + 'topic' => $row['id_topic'], + ); + } + + // If this is being approved and id_msg is higher than the current id_last_msg then it changes. + if ($approve && $row['id_msg'] > $topic_changes[$row['id_topic']]['id_last_msg']) + $topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_msg']; + // If this is being unapproved, and it's equal to the id_last_msg we need to find a new one! + elseif (!$approve) + // Default to the first message and then we'll override in a bit ;) + $topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_first_msg']; + + $topic_changes[$row['id_topic']]['unapproved_posts'] += $approve ? -1 : 1; + $board_changes[$row['id_board']]['unapproved_posts'] += $approve ? -1 : 1; + $board_changes[$row['id_board']]['posts'] += $approve ? 1 : -1; + + // Post count for the user? + if ($row['id_member'] && empty($row['count_posts'])) + $member_post_changes[$row['id_member']] = isset($member_post_changes[$row['id_member']]) ? $member_post_changes[$row['id_member']] + 1 : 1; + } + $smcFunc['db_free_result']($request); + + if (empty($msgs)) + return; + + // Now we have the differences make the changes, first the easy one. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}messages + SET approved = {int:approved_state} + WHERE id_msg IN ({array_int:message_list})', + array( + 'message_list' => $msgs, + 'approved_state' => $approve ? 1 : 0, + ) + ); + + // If we were unapproving find the last msg in the topics... + if (!$approve) + { + $request = $smcFunc['db_query']('', ' + SELECT id_topic, MAX(id_msg) AS id_last_msg + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topic_list}) + AND approved = {int:approved} + GROUP BY id_topic', + array( + 'topic_list' => $topics, + 'approved' => 1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_last_msg']; + $smcFunc['db_free_result']($request); + } + + // ... next the topics... + foreach ($topic_changes as $id => $changes) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}topics + SET approved = {int:approved}, unapproved_posts = unapproved_posts + {int:unapproved_posts}, + num_replies = num_replies + {int:num_replies}, id_last_msg = {int:id_last_msg} + WHERE id_topic = {int:id_topic}', + array( + 'approved' => $changes['approved'], + 'unapproved_posts' => $changes['unapproved_posts'], + 'num_replies' => $changes['replies'], + 'id_last_msg' => $changes['id_last_msg'], + 'id_topic' => $id, + ) + ); + + // ... finally the boards... + foreach ($board_changes as $id => $changes) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET num_posts = num_posts + {int:num_posts}, unapproved_posts = unapproved_posts + {int:unapproved_posts}, + num_topics = num_topics + {int:num_topics}, unapproved_topics = unapproved_topics + {int:unapproved_topics} + WHERE id_board = {int:id_board}', + array( + 'num_posts' => $changes['posts'], + 'unapproved_posts' => $changes['unapproved_posts'], + 'num_topics' => $changes['topics'], + 'unapproved_topics' => $changes['unapproved_topics'], + 'id_board' => $id, + ) + ); + + // Finally, least importantly, notifications! + if ($approve) + { + if (!empty($notification_topics)) + { + require_once($sourcedir . '/Post.php'); + notifyMembersBoard($notification_topics); + } + if (!empty($notification_posts)) + sendApprovalNotifications($notification_posts); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}approval_queue + WHERE id_msg IN ({array_int:message_list}) + AND id_attach = {int:id_attach}', + array( + 'message_list' => $msgs, + 'id_attach' => 0, + ) + ); + } + // If unapproving add to the approval queue! + else + { + $msgInserts = array(); + foreach ($msgs as $msg) + $msgInserts[] = array($msg); + + $smcFunc['db_insert']('ignore', + '{db_prefix}approval_queue', + array('id_msg' => 'int'), + $msgInserts, + array('id_msg') + ); + } + + // Update the last messages on the boards... + updateLastMessages(array_keys($board_changes)); + + // Post count for the members? + if (!empty($member_post_changes)) + foreach ($member_post_changes as $id_member => $count_change) + updateMemberData($id_member, array('posts' => 'posts ' . ($approve ? '+' : '-') . ' ' . $count_change)); + + return true; +} + +// Approve topics? +function approveTopics($topics, $approve = true) +{ + global $smcFunc; + + if (!is_array($topics)) + $topics = array($topics); + + if (empty($topics)) + return false; + + $approve_type = $approve ? 0 : 1; + + // Just get the messages to be approved and pass through... + $request = $smcFunc['db_query']('', ' + SELECT id_msg + FROM {db_prefix}messages + WHERE id_topic IN ({array_int:topic_list}) + AND approved = {int:approve_type}', + array( + 'topic_list' => $topics, + 'approve_type' => $approve_type, + ) + ); + $msgs = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $msgs[] = $row['id_msg']; + $smcFunc['db_free_result']($request); + + return approvePosts($msgs, $approve); +} + +// A special function for handling the hell which is sending approval notifications. +function sendApprovalNotifications(&$topicData) +{ + global $txt, $scripturl, $language, $user_info; + global $modSettings, $sourcedir, $context, $smcFunc; + + // Clean up the data... + if (!is_array($topicData) || empty($topicData)) + return; + + $topics = array(); + $digest_insert = array(); + foreach ($topicData as $topic => $msgs) + foreach ($msgs as $msgKey => $msg) + { + censorText($topicData[$topic][$msgKey]['subject']); + censorText($topicData[$topic][$msgKey]['body']); + $topicData[$topic][$msgKey]['subject'] = un_htmlspecialchars($topicData[$topic][$msgKey]['subject']); + $topicData[$topic][$msgKey]['body'] = trim(un_htmlspecialchars(strip_tags(strtr(parse_bbc($topicData[$topic][$msgKey]['body'], false), array('
          ' => "\n", '' => "\n", '' => "\n", '[' => '[', ']' => ']'))))); + + $topics[] = $msg['id']; + $digest_insert[] = array($msg['topic'], $msg['id'], 'reply', $user_info['id']); + } + + // These need to go into the digest too... + $smcFunc['db_insert']('', + '{db_prefix}log_digest', + array( + 'id_topic' => 'int', 'id_msg' => 'int', 'note_type' => 'string', 'exclude' => 'int', + ), + $digest_insert, + array() + ); + + // Find everyone who needs to know about this. + $members = $smcFunc['db_query']('', ' + SELECT + mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.lngfile, + ln.sent, mem.id_group, mem.additional_groups, b.member_groups, mem.id_post_group, t.id_member_started, + ln.id_topic + FROM {db_prefix}log_notify AS ln + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member) + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic) + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + WHERE ln.id_topic IN ({array_int:topic_list}) + AND mem.is_activated = {int:is_activated} + AND mem.notify_types < {int:notify_types} + AND mem.notify_regularity < {int:notify_regularity} + GROUP BY mem.id_member, ln.id_topic, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.lngfile, ln.sent, mem.id_group, mem.additional_groups, b.member_groups, mem.id_post_group, t.id_member_started + ORDER BY mem.lngfile', + array( + 'topic_list' => $topics, + 'is_activated' => 1, + 'notify_types' => 4, + 'notify_regularity' => 2, + ) + ); + $sent = 0; + while ($row = $smcFunc['db_fetch_assoc']($members)) + { + if ($row['id_group'] != 1) + { + $allowed = explode(',', $row['member_groups']); + $row['additional_groups'] = explode(',', $row['additional_groups']); + $row['additional_groups'][] = $row['id_group']; + $row['additional_groups'][] = $row['id_post_group']; + + if (count(array_intersect($allowed, $row['additional_groups'])) == 0) + continue; + } + + $needed_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']; + if (empty($current_language) || $current_language != $needed_language) + $current_language = loadLanguage('Post', $needed_language, false); + + $sent_this_time = false; + // Now loop through all the messages to send. + foreach ($topicData[$row['id_topic']] as $msg) + { + $replacements = array( + 'TOPICSUBJECT' => $topicData[$row['id_topic']]['subject'], + 'POSTERNAME' => un_htmlspecialchars($topicData[$row['id_topic']]['name']), + 'TOPICLINK' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new', + 'UNSUBSCRIBELINK' => $scripturl . '?action=notify;topic=' . $row['id_topic'] . '.0', + ); + + $message_type = 'notification_reply'; + // Do they want the body of the message sent too? + if (!empty($row['notify_send_body']) && empty($modSettings['disallow_sendBody'])) + { + $message_type .= '_body'; + $replacements['BODY'] = $topicData[$row['id_topic']]['body']; + } + if (!empty($row['notify_regularity'])) + $message_type .= '_once'; + + // Send only if once is off or it's on and it hasn't been sent. + if (empty($row['notify_regularity']) || (empty($row['sent']) && !$sent_this_time)) + { + $emaildata = loadEmailTemplate($message_type, $replacements, $needed_language); + sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, 'm' . $topicData[$row['id_topic']]['last_id']); + $sent++; + } + + $sent_this_time = true; + } + } + $smcFunc['db_free_result']($members); + + if (isset($current_language) && $current_language != $user_info['language']) + loadLanguage('Post'); + + // Sent! + if (!empty($sent)) + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_notify + SET sent = {int:is_sent} + WHERE id_topic IN ({array_int:topic_list}) + AND id_member != {int:current_member}', + array( + 'current_member' => $user_info['id'], + 'topic_list' => $topics, + 'is_sent' => 1, + ) + ); +} + +// Update the last message in a board, and its parents. +function updateLastMessages($setboards, $id_msg = 0) +{ + global $board_info, $board, $modSettings, $smcFunc; + + // Please - let's be sane. + if (empty($setboards)) + return false; + + if (!is_array($setboards)) + $setboards = array($setboards); + + // If we don't know the id_msg we need to find it. + if (!$id_msg) + { + // Find the latest message on this board (highest id_msg.) + $request = $smcFunc['db_query']('', ' + SELECT id_board, MAX(id_last_msg) AS id_msg + FROM {db_prefix}topics + WHERE id_board IN ({array_int:board_list}) + AND approved = {int:approved} + GROUP BY id_board', + array( + 'board_list' => $setboards, + 'approved' => 1, + ) + ); + $lastMsg = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $lastMsg[$row['id_board']] = $row['id_msg']; + $smcFunc['db_free_result']($request); + } + else + { + // Just to note - there should only be one board passed if we are doing this. + foreach ($setboards as $id_board) + $lastMsg[$id_board] = $id_msg; + } + + $parent_boards = array(); + // Keep track of last modified dates. + $lastModified = $lastMsg; + // Get all the child boards for the parents, if they have some... + foreach ($setboards as $id_board) + { + if (!isset($lastMsg[$id_board])) + { + $lastMsg[$id_board] = 0; + $lastModified[$id_board] = 0; + } + + if (!empty($board) && $id_board == $board) + $parents = $board_info['parent_boards']; + else + $parents = getBoardParents($id_board); + + // Ignore any parents on the top child level. + //!!! Why? + foreach ($parents as $id => $parent) + { + if ($parent['level'] != 0) + { + // If we're already doing this one as a board, is this a higher last modified? + if (isset($lastModified[$id]) && $lastModified[$id_board] > $lastModified[$id]) + $lastModified[$id] = $lastModified[$id_board]; + elseif (!isset($lastModified[$id]) && (!isset($parent_boards[$id]) || $parent_boards[$id] < $lastModified[$id_board])) + $parent_boards[$id] = $lastModified[$id_board]; + } + } + } + + // Note to help understand what is happening here. For parents we update the timestamp of the last message for determining + // whether there are child boards which have not been read. For the boards themselves we update both this and id_last_msg. + + $board_updates = array(); + $parent_updates = array(); + // Finally, to save on queries make the changes... + foreach ($parent_boards as $id => $msg) + { + if (!isset($parent_updates[$msg])) + $parent_updates[$msg] = array($id); + else + $parent_updates[$msg][] = $id; + } + + foreach ($lastMsg as $id => $msg) + { + if (!isset($board_updates[$msg . '-' . $lastModified[$id]])) + $board_updates[$msg . '-' . $lastModified[$id]] = array( + 'id' => $msg, + 'updated' => $lastModified[$id], + 'boards' => array($id) + ); + + else + $board_updates[$msg . '-' . $lastModified[$id]]['boards'][] = $id; + } + + // Now commit the changes! + foreach ($parent_updates as $id_msg => $boards) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET id_msg_updated = {int:id_msg_updated} + WHERE id_board IN ({array_int:board_list}) + AND id_msg_updated < {int:id_msg_updated}', + array( + 'board_list' => $boards, + 'id_msg_updated' => $id_msg, + ) + ); + } + foreach ($board_updates as $board_data) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET id_last_msg = {int:id_last_msg}, id_msg_updated = {int:id_msg_updated} + WHERE id_board IN ({array_int:board_list})', + array( + 'board_list' => $board_data['boards'], + 'id_last_msg' => $board_data['id'], + 'id_msg_updated' => $board_data['updated'], + ) + ); + } +} + +// This simple function gets a list of all administrators and sends them an email to let them know a new member has joined. +function adminNotify($type, $memberID, $member_name = null) +{ + global $txt, $modSettings, $language, $scripturl, $user_info, $context, $smcFunc; + + // If the setting isn't enabled then just exit. + if (empty($modSettings['notify_new_registration'])) + return; + + if ($member_name == null) + { + // Get the new user's name.... + $request = $smcFunc['db_query']('', ' + SELECT real_name + FROM {db_prefix}members + WHERE id_member = {int:id_member} + LIMIT 1', + array( + 'id_member' => $memberID, + ) + ); + list ($member_name) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + $toNotify = array(); + $groups = array(); + + // All membergroups who can approve members. + $request = $smcFunc['db_query']('', ' + SELECT id_group + FROM {db_prefix}permissions + WHERE permission = {string:moderate_forum} + AND add_deny = {int:add_deny} + AND id_group != {int:id_group}', + array( + 'add_deny' => 1, + 'id_group' => 0, + 'moderate_forum' => 'moderate_forum', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $groups[] = $row['id_group']; + $smcFunc['db_free_result']($request); + + // Add administrators too... + $groups[] = 1; + $groups = array_unique($groups); + + // Get a list of all members who have ability to approve accounts - these are the people who we inform. + $request = $smcFunc['db_query']('', ' + SELECT id_member, lngfile, email_address + FROM {db_prefix}members + WHERE (id_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:group_array_implode}, additional_groups) != 0) + AND notify_types != {int:notify_types} + ORDER BY lngfile', + array( + 'group_list' => $groups, + 'notify_types' => 4, + 'group_array_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $groups), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $replacements = array( + 'USERNAME' => $member_name, + 'PROFILELINK' => $scripturl . '?action=profile;u=' . $memberID + ); + $emailtype = 'admin_notify'; + + // If they need to be approved add more info... + if ($type == 'approval') + { + $replacements['APPROVALLINK'] = $scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve'; + $emailtype .= '_approval'; + } + + $emaildata = loadEmailTemplate($emailtype, $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); + + // And do the actual sending... + sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); + } + $smcFunc['db_free_result']($request); + + if (isset($current_language) && $current_language != $user_info['language']) + loadLanguage('Login'); +} + +function loadEmailTemplate($template, $replacements = array(), $lang = '', $loadLang = true) +{ + global $txt, $mbname, $scripturl, $settings, $user_info; + + // First things first, load up the email templates language file, if we need to. + if ($loadLang) + loadLanguage('EmailTemplates', $lang); + + if (!isset($txt['emails'][$template])) + fatal_lang_error('email_no_template', 'template', array($template)); + + $ret = array( + 'subject' => $txt['emails'][$template]['subject'], + 'body' => $txt['emails'][$template]['body'], + ); + + // Add in the default replacements. + $replacements += array( + 'FORUMNAME' => $mbname, + 'SCRIPTURL' => $scripturl, + 'THEMEURL' => $settings['theme_url'], + 'IMAGESURL' => $settings['images_url'], + 'DEFAULT_THEMEURL' => $settings['default_theme_url'], + 'REGARDS' => $txt['regards_team'], + ); + + // Split the replacements up into two arrays, for use with str_replace + $find = array(); + $replace = array(); + + foreach ($replacements as $f => $r) + { + $find[] = '{' . $f . '}'; + $replace[] = $r; + } + + // Do the variable replacements. + $ret['subject'] = str_replace($find, $replace, $ret['subject']); + $ret['body'] = str_replace($find, $replace, $ret['body']); + + // Now deal with the {USER.variable} items. + $ret['subject'] = preg_replace_callback('~{USER.([^}]+)}~', 'user_info_callback', $ret['subject']); + $ret['body'] = preg_replace_callback('~{USER.([^}]+)}~', 'user_info_callback', $ret['body']); + + // Finally return the email to the caller so they can send it out. + return $ret; +} + +function user_info_callback($matches) +{ + global $user_info; + if (empty($matches[1])) + return ''; + + $use_ref = true; + $ref = &$user_info; + + foreach (explode('.', $matches[1]) as $index) + { + if ($use_ref && isset($ref[$index])) + $ref = &$ref[$index]; + else + { + $use_ref = false; + break; + } + } + + return $use_ref ? $ref : $matches[0]; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Recent.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Recent.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,107 @@ += {int:likely_max_msg}' . + (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND b.id_board != {int:recycle_board}' : '') . ' + AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved} + AND m.approved = {int:is_approved}' : '') . ' + ORDER BY m.id_msg DESC + LIMIT ' . $latestPostOptions['number_posts'], + array( + 'likely_max_msg' => max(0, $modSettings['maxMsgID'] - 50 * $latestPostOptions['number_posts']), + 'recycle_board' => $modSettings['recycle_board'], + 'is_approved' => 1, + ) + ); + $posts = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Censor the subject and post for the preview ;). + censorText($row['subject']); + censorText($row['body']); + + $row['body'] = strip_tags(strtr(parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']), array('
          ' => ' '))); + if ($smcFunc['strlen']($row['body']) > 128) + $row['body'] = $smcFunc['substr']($row['body'], 0, 128) . '...'; + + // Build the array. + $posts[] = array( + 'board' => array( + 'id' => $row['id_board'], + 'name' => $row['board_name'], + 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', + 'link' => '' . $row['board_name'] . '' + ), + 'topic' => $row['id_topic'], + 'poster' => array( + 'id' => $row['id_member'], + 'name' => $row['poster_name'], + 'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'], + 'link' => empty($row['id_member']) ? $row['poster_name'] : '' . $row['poster_name'] . '' + ), + 'subject' => $row['subject'], + 'short_subject' => shorten_subject($row['subject'], 24), + 'preview' => $row['body'], + 'time' => timeformat($row['poster_time']), + 'timestamp' => forum_time(true, $row['poster_time']), + 'raw_timestamp' => $row['poster_time'], + 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . ';topicseen#msg' . $row['id_msg'], + 'link' => '' . $row['subject'] . '' + ); + } + $smcFunc['db_free_result']($request); + + return $posts; +} + +// Callback-function for the cache for getLastPosts(). +function cache_getLastPosts($latestPostOptions) +{ + return array( + 'data' => getLastPosts($latestPostOptions), + 'expires' => time() + 60, + 'post_retri_eval' => ' + foreach ($cache_block[\'data\'] as $k => $post) + { + $cache_block[\'data\'][$k][\'time\'] = timeformat($post[\'raw_timestamp\']); + $cache_block[\'data\'][$k][\'timestamp\'] = forum_time(true, $post[\'raw_timestamp\']); + }', + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs-Sound.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs-Sound.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,114 @@ + 2 || ($ip2 = cache_get_data('wave_file/' . $user_info['ip2'], 20)) > 2) + die(header('HTTP/1.1 400 Bad Request')); + cache_put_data('wave_file/' . $user_info['ip'], $ip ? $ip + 1 : 1, 20); + cache_put_data('wave_file/' . $user_info['ip2'], $ip2 ? $ip2 + 1 : 1, 20); + + // Fixate randomization for this word. + mt_srand(end(unpack('n', md5($word . session_id())))); + + // Try to see if there's a sound font in the user's language. + if (file_exists($settings['default_theme_dir'] . '/fonts/sound/a.' . $user_info['language'] . '.wav')) + $sound_language = $user_info['language']; + + // English should be there. + elseif (file_exists($settings['default_theme_dir'] . '/fonts/sound/a.english.wav')) + $sound_language = 'english'; + + // Guess not... + else + return false; + + // File names are in lower case so lets make sure that we are only using a lower case string + $word = strtolower($word); + + // Loop through all letters of the word $word. + $sound_word = ''; + for ($i = 0; $i < strlen($word); $i++) + { + $sound_letter = implode('', file($settings['default_theme_dir'] . '/fonts/sound/' . $word{$i} . '.' . $sound_language . '.wav')); + if (strpos($sound_letter, 'data') === false) + return false; + + $sound_letter = substr($sound_letter, strpos($sound_letter, 'data') + 8); + switch ($word{$i} === 's' ? 0 : mt_rand(0, 2)) + { + case 0: + for ($j = 0, $n = strlen($sound_letter); $j < $n; $j++) + for ($k = 0, $m = round(mt_rand(15, 25) / 10); $k < $m; $k++) + $sound_word .= $word{$i} === 's' ? $sound_letter{$j} : chr(mt_rand(max(ord($sound_letter{$j}) - 1, 0x00), min(ord($sound_letter{$j}) + 1, 0xFF))); + break; + + case 1: + for ($j = 0, $n = strlen($sound_letter) - 1; $j < $n; $j += 2) + $sound_word .= (mt_rand(0, 3) == 0 ? '' : $sound_letter{$j}) . (mt_rand(0, 3) === 0 ? $sound_letter{$j + 1} : $sound_letter{$j}) . (mt_rand(0, 3) === 0 ? $sound_letter{$j} : $sound_letter{$j + 1}) . $sound_letter{$j + 1} . (mt_rand(0, 3) == 0 ? $sound_letter{$j + 1} : ''); + $sound_word .= str_repeat($sound_letter{$n}, 2); + break; + + case 2: + $shift = 0; + for ($j = 0, $n = strlen($sound_letter); $j < $n; $j++) + { + if (mt_rand(0, 10) === 0) + $shift += mt_rand(-3, 3); + for ($k = 0, $m = round(mt_rand(15, 25) / 10); $k < $m; $k++) + $sound_word .= chr(min(max(ord($sound_letter{$j}) + $shift, 0x00), 0xFF)); + } + break; + + } + + $sound_word .= str_repeat(chr(0x80), mt_rand(10000, 10500)); + } + + $data_size = strlen($sound_word); + $file_size = $data_size + 0x24; + $sample_rate = 16000; + + // Disable compression. + ob_end_clean(); + header('Content-Encoding: none'); + + // Output the wav. + header('Content-type: audio/x-wav'); + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT'); + header('Content-Length: ' . ($file_size + 0x08)); + + echo pack('nnVnnnnnnnnVVnnnnV', 0x5249, 0x4646, $file_size, 0x5741, 0x5645, 0x666D, 0x7420, 0x1000, 0x0000, 0x0100, 0x0100, $sample_rate, $sample_rate, 0x0100, 0x0800, 0x6461, 0x7461, $data_size), $sound_word; + + // Noting more to add. + die(); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subs.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subs.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,4275 @@ + time(), + ); + + // #1 latest member ID, #2 the real name for a new registration. + if (is_numeric($parameter1)) + { + $changes['latestMember'] = $parameter1; + $changes['latestRealName'] = $parameter2; + + updateSettings(array('totalMembers' => true), true); + } + + // We need to calculate the totals. + else + { + // Update the latest activated member (highest id_member) and count. + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*), MAX(id_member) + FROM {db_prefix}members + WHERE is_activated = {int:is_activated}', + array( + 'is_activated' => 1, + ) + ); + list ($changes['totalMembers'], $changes['latestMember']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Get the latest activated member's display name. + $result = $smcFunc['db_query']('', ' + SELECT real_name + FROM {db_prefix}members + WHERE id_member = {int:id_member} + LIMIT 1', + array( + 'id_member' => (int) $changes['latestMember'], + ) + ); + list ($changes['latestRealName']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // Are we using registration approval? + if ((!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($modSettings['approveAccountDeletion'])) + { + // Update the amount of members awaiting approval - ignoring COPPA accounts, as you can't approve them until you get permission. + $result = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}members + WHERE is_activated IN ({array_int:activation_status})', + array( + 'activation_status' => array(3, 4), + ) + ); + list ($changes['unapprovedMembers']) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + } + } + + updateSettings($changes); + break; + + case 'message': + if ($parameter1 === true && $parameter2 !== null) + updateSettings(array('totalMessages' => true, 'maxMsgID' => $parameter2), true); + else + { + // SUM and MAX on a smaller table is better for InnoDB tables. + $result = $smcFunc['db_query']('', ' + SELECT SUM(num_posts + unapproved_posts) AS total_messages, MAX(id_last_msg) AS max_msg_id + FROM {db_prefix}boards + WHERE redirect = {string:blank_redirect}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + AND id_board != {int:recycle_board}' : ''), + array( + 'recycle_board' => isset($modSettings['recycle_board']) ? $modSettings['recycle_board'] : 0, + 'blank_redirect' => '', + ) + ); + $row = $smcFunc['db_fetch_assoc']($result); + $smcFunc['db_free_result']($result); + + updateSettings(array( + 'totalMessages' => $row['total_messages'] === null ? 0 : $row['total_messages'], + 'maxMsgID' => $row['max_msg_id'] === null ? 0 : $row['max_msg_id'] + )); + } + break; + + case 'subject': + // Remove the previous subject (if any). + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_search_subjects + WHERE id_topic = {int:id_topic}', + array( + 'id_topic' => (int) $parameter1, + ) + ); + + // Insert the new subject. + if ($parameter2 !== null) + { + $parameter1 = (int) $parameter1; + $parameter2 = text2words($parameter2); + + $inserts = array(); + foreach ($parameter2 as $word) + $inserts[] = array($word, $parameter1); + + if (!empty($inserts)) + $smcFunc['db_insert']('ignore', + '{db_prefix}log_search_subjects', + array('word' => 'string', 'id_topic' => 'int'), + $inserts, + array('word', 'id_topic') + ); + } + break; + + case 'topic': + if ($parameter1 === true) + updateSettings(array('totalTopics' => true), true); + else + { + // Get the number of topics - a SUM is better for InnoDB tables. + // We also ignore the recycle bin here because there will probably be a bunch of one-post topics there. + $result = $smcFunc['db_query']('', ' + SELECT SUM(num_topics + unapproved_topics) AS total_topics + FROM {db_prefix}boards' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' + WHERE id_board != {int:recycle_board}' : ''), + array( + 'recycle_board' => !empty($modSettings['recycle_board']) ? $modSettings['recycle_board'] : 0, + ) + ); + $row = $smcFunc['db_fetch_assoc']($result); + $smcFunc['db_free_result']($result); + + updateSettings(array('totalTopics' => $row['total_topics'] === null ? 0 : $row['total_topics'])); + } + break; + + case 'postgroups': + // Parameter two is the updated columns: we should check to see if we base groups off any of these. + if ($parameter2 !== null && !in_array('posts', $parameter2)) + return; + + if (($postgroups = cache_get_data('updateStats:postgroups', 360)) == null) + { + // Fetch the postgroups! + $request = $smcFunc['db_query']('', ' + SELECT id_group, min_posts + FROM {db_prefix}membergroups + WHERE min_posts != {int:min_posts}', + array( + 'min_posts' => -1, + ) + ); + $postgroups = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $postgroups[$row['id_group']] = $row['min_posts']; + $smcFunc['db_free_result']($request); + + // Sort them this way because if it's done with MySQL it causes a filesort :(. + arsort($postgroups); + + cache_put_data('updateStats:postgroups', $postgroups, 360); + } + + // Oh great, they've screwed their post groups. + if (empty($postgroups)) + return; + + // Set all membergroups from most posts to least posts. + $conditions = ''; + foreach ($postgroups as $id => $min_posts) + { + $conditions .= ' + WHEN posts >= ' . $min_posts . (!empty($lastMin) ? ' AND posts <= ' . $lastMin : '') . ' THEN ' . $id; + $lastMin = $min_posts; + } + + // A big fat CASE WHEN... END is faster than a zillion UPDATE's ;). + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_post_group = CASE ' . $conditions . ' + ELSE 0 + END' . ($parameter1 != null ? ' + WHERE ' . (is_array($parameter1) ? 'id_member IN ({array_int:members})' : 'id_member = {int:members}') : ''), + array( + 'members' => $parameter1, + ) + ); + break; + + default: + trigger_error('updateStats(): Invalid statistic type \'' . $type . '\'', E_USER_NOTICE); + } +} + +// Assumes the data has been htmlspecialchar'd. +function updateMemberData($members, $data) +{ + global $modSettings, $user_info, $smcFunc; + + $parameters = array(); + if (is_array($members)) + { + $condition = 'id_member IN ({array_int:members})'; + $parameters['members'] = $members; + } + elseif ($members === null) + $condition = '1=1'; + else + { + $condition = 'id_member = {int:member}'; + $parameters['member'] = $members; + } + + if (!empty($modSettings['integrate_change_member_data'])) + { + // Only a few member variables are really interesting for integration. + $integration_vars = array( + 'member_name', + 'real_name', + 'email_address', + 'id_group', + 'gender', + 'birthdate', + 'website_title', + 'website_url', + 'location', + 'hide_email', + 'time_format', + 'time_offset', + 'avatar', + 'lngfile', + ); + $vars_to_integrate = array_intersect($integration_vars, array_keys($data)); + + // Only proceed if there are any variables left to call the integration function. + if (count($vars_to_integrate) != 0) + { + // Fetch a list of member_names if necessary + if ((!is_array($members) && $members === $user_info['id']) || (is_array($members) && count($members) == 1 && in_array($user_info['id'], $members))) + $member_names = array($user_info['username']); + else + { + $member_names = array(); + $request = $smcFunc['db_query']('', ' + SELECT member_name + FROM {db_prefix}members + WHERE ' . $condition, + $parameters + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $member_names[] = $row['member_name']; + $smcFunc['db_free_result']($request); + } + + if (!empty($member_names)) + foreach ($vars_to_integrate as $var) + call_integration_hook('integrate_change_member_data', array($member_names, $var, $data[$var])); + } + } + + // Everything is assumed to be a string unless it's in the below. + $knownInts = array( + 'date_registered', 'posts', 'id_group', 'last_login', 'instant_messages', 'unread_messages', + 'new_pm', 'pm_prefs', 'gender', 'hide_email', 'show_online', 'pm_email_notify', 'pm_receive_from', 'karma_good', 'karma_bad', + 'notify_announcements', 'notify_send_body', 'notify_regularity', 'notify_types', + 'id_theme', 'is_activated', 'id_msg_last_visit', 'id_post_group', 'total_time_logged_in', 'warning', + ); + $knownFloats = array( + 'time_offset', + ); + + $setString = ''; + foreach ($data as $var => $val) + { + $type = 'string'; + if (in_array($var, $knownInts)) + $type = 'int'; + elseif (in_array($var, $knownFloats)) + $type = 'float'; + elseif ($var == 'birthdate') + $type = 'date'; + + // Doing an increment? + if ($type == 'int' && ($val === '+' || $val === '-')) + { + $val = $var . ' ' . $val . ' 1'; + $type = 'raw'; + } + + // Ensure posts, instant_messages, and unread_messages don't overflow or underflow. + if (in_array($var, array('posts', 'instant_messages', 'unread_messages'))) + { + if (preg_match('~^' . $var . ' (\+ |- |\+ -)([\d]+)~', $val, $match)) + { + if ($match[1] != '+ ') + $val = 'CASE WHEN ' . $var . ' <= ' . abs($match[2]) . ' THEN 0 ELSE ' . $val . ' END'; + $type = 'raw'; + } + } + + $setString .= ' ' . $var . ' = {' . $type . ':p_' . $var . '},'; + $parameters['p_' . $var] = $val; + } + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET' . substr($setString, 0, -1) . ' + WHERE ' . $condition, + $parameters + ); + + updateStats('postgroups', $members, array_keys($data)); + + // Clear any caching? + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2 && !empty($members)) + { + if (!is_array($members)) + $members = array($members); + + foreach ($members as $member) + { + if ($modSettings['cache_enable'] >= 3) + { + cache_put_data('member_data-profile-' . $member, null, 120); + cache_put_data('member_data-normal-' . $member, null, 120); + cache_put_data('member_data-minimal-' . $member, null, 120); + } + cache_put_data('user_settings-' . $member, null, 60); + } + } +} + +// Updates the settings table as well as $modSettings... only does one at a time if $update is true. +function updateSettings($changeArray, $update = false, $debug = false) +{ + global $modSettings, $smcFunc; + + if (empty($changeArray) || !is_array($changeArray)) + return; + + // In some cases, this may be better and faster, but for large sets we don't want so many UPDATEs. + if ($update) + { + foreach ($changeArray as $variable => $value) + { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}settings + SET value = {' . ($value === false || $value === true ? 'raw' : 'string') . ':value} + WHERE variable = {string:variable}', + array( + 'value' => $value === true ? 'value + 1' : ($value === false ? 'value - 1' : $value), + 'variable' => $variable, + ) + ); + $modSettings[$variable] = $value === true ? $modSettings[$variable] + 1 : ($value === false ? $modSettings[$variable] - 1 : $value); + } + + // Clean out the cache and make sure the cobwebs are gone too. + cache_put_data('modSettings', null, 90); + + return; + } + + $replaceArray = array(); + foreach ($changeArray as $variable => $value) + { + // Don't bother if it's already like that ;). + if (isset($modSettings[$variable]) && $modSettings[$variable] == $value) + continue; + // If the variable isn't set, but would only be set to nothing'ness, then don't bother setting it. + elseif (!isset($modSettings[$variable]) && empty($value)) + continue; + + $replaceArray[] = array($variable, $value); + + $modSettings[$variable] = $value; + } + + if (empty($replaceArray)) + return; + + $smcFunc['db_insert']('replace', + '{db_prefix}settings', + array('variable' => 'string-255', 'value' => 'string-65534'), + $replaceArray, + array('variable') + ); + + // Kill the cache - it needs redoing now, but we won't bother ourselves with that here. + cache_put_data('modSettings', null, 90); +} + +// Constructs a page list. +// $pageindex = constructPageIndex($scripturl . '?board=' . $board, $_REQUEST['start'], $num_messages, $maxindex, true); +function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flexible_start = false) +{ + global $modSettings; + + // Save whether $start was less than 0 or not. + $start = (int) $start; + $start_invalid = $start < 0; + + // Make sure $start is a proper variable - not less than 0. + if ($start_invalid) + $start = 0; + // Not greater than the upper bound. + elseif ($start >= $max_value) + $start = max(0, (int) $max_value - (((int) $max_value % (int) $num_per_page) == 0 ? $num_per_page : ((int) $max_value % (int) $num_per_page))); + // And it has to be a multiple of $num_per_page! + else + $start = max(0, (int) $start - ((int) $start % (int) $num_per_page)); + + // Wireless will need the protocol on the URL somewhere. + if (WIRELESS) + $base_url .= ';' . WIRELESS_PROTOCOL; + + $base_link = '%2$s '; + + // Compact pages is off or on? + if (empty($modSettings['compactTopicPagesEnable'])) + { + // Show the left arrow. + $pageindex = $start == 0 ? ' ' : sprintf($base_link, $start - $num_per_page, '«'); + + // Show all the pages. + $display_page = 1; + for ($counter = 0; $counter < $max_value; $counter += $num_per_page) + $pageindex .= $start == $counter && !$start_invalid ? '' . $display_page++ . ' ' : sprintf($base_link, $counter, $display_page++); + + // Show the right arrow. + $display_page = ($start + $num_per_page) > $max_value ? $max_value : ($start + $num_per_page); + if ($start != $counter - $max_value && !$start_invalid) + $pageindex .= $display_page > $counter - $num_per_page ? ' ' : sprintf($base_link, $display_page, '»'); + } + else + { + // If they didn't enter an odd value, pretend they did. + $PageContiguous = (int) ($modSettings['compactTopicPagesContiguous'] - ($modSettings['compactTopicPagesContiguous'] % 2)) / 2; + + // Show the first page. (>1< ... 6 7 [8] 9 10 ... 15) + if ($start > $num_per_page * $PageContiguous) + $pageindex = sprintf($base_link, 0, '1'); + else + $pageindex = ''; + + // Show the ... after the first page. (1 >...< 6 7 [8] 9 10 ... 15) + if ($start > $num_per_page * ($PageContiguous + 1)) + $pageindex .= ' ... '; + + // Show the pages before the current one. (1 ... >6 7< [8] 9 10 ... 15) + for ($nCont = $PageContiguous; $nCont >= 1; $nCont--) + if ($start >= $num_per_page * $nCont) + { + $tmpStart = $start - $num_per_page * $nCont; + $pageindex.= sprintf($base_link, $tmpStart, $tmpStart / $num_per_page + 1); + } + + // Show the current page. (1 ... 6 7 >[8]< 9 10 ... 15) + if (!$start_invalid) + $pageindex .= '[' . ($start / $num_per_page + 1) . '] '; + else + $pageindex .= sprintf($base_link, $start, $start / $num_per_page + 1); + + // Show the pages after the current one... (1 ... 6 7 [8] >9 10< ... 15) + $tmpMaxPages = (int) (($max_value - 1) / $num_per_page) * $num_per_page; + for ($nCont = 1; $nCont <= $PageContiguous; $nCont++) + if ($start + $num_per_page * $nCont <= $tmpMaxPages) + { + $tmpStart = $start + $num_per_page * $nCont; + $pageindex .= sprintf($base_link, $tmpStart, $tmpStart / $num_per_page + 1); + } + + // Show the '...' part near the end. (1 ... 6 7 [8] 9 10 >...< 15) + if ($start + $num_per_page * ($PageContiguous + 1) < $tmpMaxPages) + $pageindex .= ' ... '; + + // Show the last number in the list. (1 ... 6 7 [8] 9 10 ... >15<) + if ($start + $num_per_page * $PageContiguous < $tmpMaxPages) + $pageindex .= sprintf($base_link, $tmpMaxPages, $tmpMaxPages / $num_per_page + 1); + } + + return $pageindex; +} + +// Formats a number to display in the style of the admin's choosing. +function comma_format($number, $override_decimal_count = false) +{ + global $txt; + static $thousands_separator = null, $decimal_separator = null, $decimal_count = null; + + // !!! Should, perhaps, this just be handled in the language files, and not a mod setting? + // (French uses 1 234,00 for example... what about a multilingual forum?) + + // Cache these values... + if ($decimal_separator === null) + { + // Not set for whatever reason? + if (empty($txt['number_format']) || preg_match('~^1([^\d]*)?234([^\d]*)(0*?)$~', $txt['number_format'], $matches) != 1) + return $number; + + // Cache these each load... + $thousands_separator = $matches[1]; + $decimal_separator = $matches[2]; + $decimal_count = strlen($matches[3]); + } + + // Format the string with our friend, number_format. + return number_format($number, is_float($number) ? ($override_decimal_count === false ? $decimal_count : $override_decimal_count) : 0, $decimal_separator, $thousands_separator); +} + +// Format a time to make it look purdy. +function timeformat($log_time, $show_today = true, $offset_type = false) +{ + global $context, $user_info, $txt, $modSettings, $smcFunc; + static $non_twelve_hour; + + // Offset the time. + if (!$offset_type) + $time = $log_time + ($user_info['time_offset'] + $modSettings['time_offset']) * 3600; + // Just the forum offset? + elseif ($offset_type == 'forum') + $time = $log_time + $modSettings['time_offset'] * 3600; + else + $time = $log_time; + + // We can't have a negative date (on Windows, at least.) + if ($log_time < 0) + $log_time = 0; + + // Today and Yesterday? + if ($modSettings['todayMod'] >= 1 && $show_today === true) + { + // Get the current time. + $nowtime = forum_time(); + + $then = @getdate($time); + $now = @getdate($nowtime); + + // Try to make something of a time format string... + $s = strpos($user_info['time_format'], '%S') === false ? '' : ':%S'; + if (strpos($user_info['time_format'], '%H') === false && strpos($user_info['time_format'], '%T') === false) + { + $h = strpos($user_info['time_format'], '%l') === false ? '%I' : '%l'; + $today_fmt = $h . ':%M' . $s . ' %p'; + } + else + $today_fmt = '%H:%M' . $s; + + // Same day of the year, same year.... Today! + if ($then['yday'] == $now['yday'] && $then['year'] == $now['year']) + return $txt['today'] . timeformat($log_time, $today_fmt, $offset_type); + + // Day-of-year is one less and same year, or it's the first of the year and that's the last of the year... + if ($modSettings['todayMod'] == '2' && (($then['yday'] == $now['yday'] - 1 && $then['year'] == $now['year']) || ($now['yday'] == 0 && $then['year'] == $now['year'] - 1) && $then['mon'] == 12 && $then['mday'] == 31)) + return $txt['yesterday'] . timeformat($log_time, $today_fmt, $offset_type); + } + + $str = !is_bool($show_today) ? $show_today : $user_info['time_format']; + + if (setlocale(LC_TIME, $txt['lang_locale'])) + { + if (!isset($non_twelve_hour)) + $non_twelve_hour = trim(strftime('%p')) === ''; + if ($non_twelve_hour && strpos($str, '%p') !== false) + $str = str_replace('%p', (strftime('%H', $time) < 12 ? $txt['time_am'] : $txt['time_pm']), $str); + + foreach (array('%a', '%A', '%b', '%B') as $token) + if (strpos($str, $token) !== false) + $str = str_replace($token, !empty($txt['lang_capitalize_dates']) ? $smcFunc['ucwords'](strftime($token, $time)) : strftime($token, $time), $str); + } + else + { + // Do-it-yourself time localization. Fun. + foreach (array('%a' => 'days_short', '%A' => 'days', '%b' => 'months_short', '%B' => 'months') as $token => $text_label) + if (strpos($str, $token) !== false) + $str = str_replace($token, $txt[$text_label][(int) strftime($token === '%a' || $token === '%A' ? '%w' : '%m', $time)], $str); + + if (strpos($str, '%p') !== false) + $str = str_replace('%p', (strftime('%H', $time) < 12 ? $txt['time_am'] : $txt['time_pm']), $str); + } + + // Windows doesn't support %e; on some versions, strftime fails altogether if used, so let's prevent that. + if ($context['server']['is_windows'] && strpos($str, '%e') !== false) + $str = str_replace('%e', ltrim(strftime('%d', $time), '0'), $str); + + // Format any other characters.. + return strftime($str, $time); +} + +// Removes special entities from strings. Compatibility... +function un_htmlspecialchars($string) +{ + static $translation; + + if (!isset($translation)) + $translation = array_flip(get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES)) + array(''' => '\'', ' ' => ' '); + + return strtr($string, $translation); +} + +// Shorten a subject + internationalization concerns. +function shorten_subject($subject, $len) +{ + global $smcFunc; + + // It was already short enough! + if ($smcFunc['strlen']($subject) <= $len) + return $subject; + + // Shorten it by the length it was too long, and strip off junk from the end. + return $smcFunc['substr']($subject, 0, $len) . '...'; +} + +// The current time with offset. +function forum_time($use_user_offset = true, $timestamp = null) +{ + global $user_info, $modSettings; + + if ($timestamp === null) + $timestamp = time(); + elseif ($timestamp == 0) + return 0; + + return $timestamp + ($modSettings['time_offset'] + ($use_user_offset ? $user_info['time_offset'] : 0)) * 3600; +} + +// This gets all possible permutations of an array. +function permute($array) +{ + $orders = array($array); + + $n = count($array); + $p = range(0, $n); + for ($i = 1; $i < $n; null) + { + $p[$i]--; + $j = $i % 2 != 0 ? $p[$i] : 0; + + $temp = $array[$i]; + $array[$i] = $array[$j]; + $array[$j] = $temp; + + for ($i = 1; $p[$i] == 0; $i++) + $p[$i] = 1; + + $orders[] = $array; + } + + return $orders; +} + +// Parse bulletin board code in a string, as well as smileys optionally. +function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = array()) +{ + global $txt, $scripturl, $context, $modSettings, $user_info, $smcFunc; + static $bbc_codes = array(), $itemcodes = array(), $no_autolink_tags = array(); + static $disabled; + + // Don't waste cycles + if ($message === '') + return ''; + + // Never show smileys for wireless clients. More bytes, can't see it anyway :P. + if (WIRELESS) + $smileys = false; + elseif ($smileys !== null && ($smileys == '1' || $smileys == '0')) + $smileys = (bool) $smileys; + + if (empty($modSettings['enableBBC']) && $message !== false) + { + if ($smileys === true) + parsesmileys($message); + + return $message; + } + + // Just in case it wasn't determined yet whether UTF-8 is enabled. + if (!isset($context['utf8'])) + $context['utf8'] = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8'; + + // If we are not doing every tag then we don't cache this run. + if (!empty($parse_tags) && !empty($bbc_codes)) + { + $temp_bbc = $bbc_codes; + $bbc_codes = array(); + } + + // Sift out the bbc for a performance improvement. + if (empty($bbc_codes) || $message === false || !empty($parse_tags)) + { + if (!empty($modSettings['disabledBBC'])) + { + $temp = explode(',', strtolower($modSettings['disabledBBC'])); + + foreach ($temp as $tag) + $disabled[trim($tag)] = true; + } + + if (empty($modSettings['enableEmbeddedFlash'])) + $disabled['flash'] = true; + + /* The following bbc are formatted as an array, with keys as follows: + + tag: the tag's name - should be lowercase! + + type: one of... + - (missing): [tag]parsed content[/tag] + - unparsed_equals: [tag=xyz]parsed content[/tag] + - parsed_equals: [tag=parsed data]parsed content[/tag] + - unparsed_content: [tag]unparsed content[/tag] + - closed: [tag], [tag/], [tag /] + - unparsed_commas: [tag=1,2,3]parsed content[/tag] + - unparsed_commas_content: [tag=1,2,3]unparsed content[/tag] + - unparsed_equals_content: [tag=...]unparsed content[/tag] + + parameters: an optional array of parameters, for the form + [tag abc=123]content[/tag]. The array is an associative array + where the keys are the parameter names, and the values are an + array which may contain the following: + - match: a regular expression to validate and match the value. + - quoted: true if the value should be quoted. + - validate: callback to evaluate on the data, which is $data. + - value: a string in which to replace $1 with the data. + either it or validate may be used, not both. + - optional: true if the parameter is optional. + + test: a regular expression to test immediately after the tag's + '=', ' ' or ']'. Typically, should have a \] at the end. + Optional. + + content: only available for unparsed_content, closed, + unparsed_commas_content, and unparsed_equals_content. + $1 is replaced with the content of the tag. Parameters + are replaced in the form {param}. For unparsed_commas_content, + $2, $3, ..., $n are replaced. + + before: only when content is not used, to go before any + content. For unparsed_equals, $1 is replaced with the value. + For unparsed_commas, $1, $2, ..., $n are replaced. + + after: similar to before in every way, except that it is used + when the tag is closed. + + disabled_content: used in place of content when the tag is + disabled. For closed, default is '', otherwise it is '$1' if + block_level is false, '
          $1
          ' elsewise. + + disabled_before: used in place of before when disabled. Defaults + to '
          ' if block_level, '' if not. + + disabled_after: used in place of after when disabled. Defaults + to '
          ' if block_level, '' if not. + + block_level: set to true the tag is a "block level" tag, similar + to HTML. Block level tags cannot be nested inside tags that are + not block level, and will not be implicitly closed as easily. + One break following a block level tag may also be removed. + + trim: if set, and 'inside' whitespace after the begin tag will be + removed. If set to 'outside', whitespace after the end tag will + meet the same fate. + + validate: except when type is missing or 'closed', a callback to + validate the data as $data. Depending on the tag's type, $data + may be a string or an array of strings (corresponding to the + replacement.) + + quoted: when type is 'unparsed_equals' or 'parsed_equals' only, + may be not set, 'optional', or 'required' corresponding to if + the content may be quoted. This allows the parser to read + [tag="abc]def[esdf]"] properly. + + require_parents: an array of tag names, or not set. If set, the + enclosing tag *must* be one of the listed tags, or parsing won't + occur. + + require_children: similar to require_parents, if set children + won't be parsed if they are not in the list. + + disallow_children: similar to, but very different from, + require_children, if it is set the listed tags will not be + parsed inside the tag. + + parsed_tags_allowed: an array restricting what BBC can be in the + parsed_equals parameter, if desired. + */ + + $codes = array( + array( + 'tag' => 'abbr', + 'type' => 'unparsed_equals', + 'before' => '', + 'after' => '', + 'quoted' => 'optional', + 'disabled_after' => ' ($1)', + ), + array( + 'tag' => 'acronym', + 'type' => 'unparsed_equals', + 'before' => '', + 'after' => '', + 'quoted' => 'optional', + 'disabled_after' => ' ($1)', + ), + array( + 'tag' => 'anchor', + 'type' => 'unparsed_equals', + 'test' => '[#]?([A-Za-z][A-Za-z0-9_\-]*)\]', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'b', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'bdo', + 'type' => 'unparsed_equals', + 'before' => '', + 'after' => '', + 'test' => '(rtl|ltr)\]', + 'block_level' => true, + ), + array( + 'tag' => 'black', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'blue', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'br', + 'type' => 'closed', + 'content' => '
          ', + ), + array( + 'tag' => 'center', + 'before' => '
          ', + 'after' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 'code', + 'type' => 'unparsed_content', + 'content' => '
          ' . $txt['code'] . ': ' . $txt['code_select'] . '
          ' . ($context['browser']['is_gecko'] || $context['browser']['is_opera'] ? '
          ' : '') . '$1' . ($context['browser']['is_gecko'] || $context['browser']['is_opera'] ? '
          ' : ''), + // !!! Maybe this can be simplified? + 'validate' => isset($disabled['code']) ? null : create_function('&$tag, &$data, $disabled', ' + global $context; + + if (!isset($disabled[\'code\'])) + { + $php_parts = preg_split(\'~(<\?php|\?>)~\', $data, -1, PREG_SPLIT_DELIM_CAPTURE); + + for ($php_i = 0, $php_n = count($php_parts); $php_i < $php_n; $php_i++) + { + // Do PHP code coloring? + if ($php_parts[$php_i] != \'<?php\') + continue; + + $php_string = \'\'; + while ($php_i + 1 < count($php_parts) && $php_parts[$php_i] != \'?>\') + { + $php_string .= $php_parts[$php_i]; + $php_parts[$php_i++] = \'\'; + } + $php_parts[$php_i] = highlight_php_code($php_string . $php_parts[$php_i]); + } + + // Fix the PHP code stuff... + $data = str_replace("
          \t
          ", "\t", implode(\'\', $php_parts)); + + // Older browsers are annoying, aren\'t they? + if ($context[\'browser\'][\'is_ie4\'] || $context[\'browser\'][\'is_ie5\'] || $context[\'browser\'][\'is_ie5.5\']) + $data = str_replace("\t", "
          \t
          ", $data); + else + $data = str_replace("\t", "\t", $data); + + // Recent Opera bug requiring temporary fix. &nsbp; is needed before to avoid broken selection. + if ($context[\'browser\'][\'is_opera\']) + $data .= \' \'; + }'), + 'block_level' => true, + ), + array( + 'tag' => 'code', + 'type' => 'unparsed_equals_content', + 'content' => '
          ' . $txt['code'] . ': ($2) ' . $txt['code_select'] . '
          ' . ($context['browser']['is_gecko'] || $context['browser']['is_opera'] ? '
          ' : '') . '$1' . ($context['browser']['is_gecko'] || $context['browser']['is_opera'] ? '
          ' : ''), + // !!! Maybe this can be simplified? + 'validate' => isset($disabled['code']) ? null : create_function('&$tag, &$data, $disabled', ' + global $context; + + if (!isset($disabled[\'code\'])) + { + $php_parts = preg_split(\'~(<\?php|\?>)~\', $data[0], -1, PREG_SPLIT_DELIM_CAPTURE); + + for ($php_i = 0, $php_n = count($php_parts); $php_i < $php_n; $php_i++) + { + // Do PHP code coloring? + if ($php_parts[$php_i] != \'<?php\') + continue; + + $php_string = \'\'; + while ($php_i + 1 < count($php_parts) && $php_parts[$php_i] != \'?>\') + { + $php_string .= $php_parts[$php_i]; + $php_parts[$php_i++] = \'\'; + } + $php_parts[$php_i] = highlight_php_code($php_string . $php_parts[$php_i]); + } + + // Fix the PHP code stuff... + $data[0] = str_replace("
          \t
          ", "\t", implode(\'\', $php_parts)); + + // Older browsers are annoying, aren\'t they? + if ($context[\'browser\'][\'is_ie4\'] || $context[\'browser\'][\'is_ie5\'] || $context[\'browser\'][\'is_ie5.5\']) + $data[0] = str_replace("\t", "
          \t
          ", $data[0]); + else + $data[0] = str_replace("\t", "\t", $data[0]); + + // Recent Opera bug requiring temporary fix. &nsbp; is needed before to avoid broken selection. + if ($context[\'browser\'][\'is_opera\']) + $data[0] .= \' \'; + }'), + 'block_level' => true, + ), + array( + 'tag' => 'color', + 'type' => 'unparsed_equals', + 'test' => '(#[\da-fA-F]{3}|#[\da-fA-F]{6}|[A-Za-z]{1,20}|rgb\(\d{1,3}, ?\d{1,3}, ?\d{1,3}\))\]', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'email', + 'type' => 'unparsed_content', + 'content' => '$1', + // !!! Should this respect guest_hideContacts? + 'validate' => create_function('&$tag, &$data, $disabled', '$data = strtr($data, array(\'
          \' => \'\'));'), + ), + array( + 'tag' => 'email', + 'type' => 'unparsed_equals', + 'before' => '', + 'after' => '', + // !!! Should this respect guest_hideContacts? + 'disallow_children' => array('email', 'ftp', 'url', 'iurl'), + 'disabled_after' => ' ($1)', + ), + array( + 'tag' => 'flash', + 'type' => 'unparsed_commas_content', + 'test' => '\d+,\d+\]', + 'content' => ($context['browser']['is_ie'] && !$context['browser']['is_mac_ie'] ? '<a href="$1" target="_blank" class="new_win">$1</a>' : '<a href="$1" target="_blank" class="new_win">$1</a>'), + 'validate' => create_function('&$tag, &$data, $disabled', ' + if (isset($disabled[\'url\'])) + $tag[\'content\'] = \'$1\'; + elseif (strpos($data[0], \'http://\') !== 0 && strpos($data[0], \'https://\') !== 0) + $data[0] = \'http://\' . $data[0]; + '), + 'disabled_content' => '$1', + ), + array( + 'tag' => 'font', + 'type' => 'unparsed_equals', + 'test' => '[A-Za-z0-9_,\-\s]+?\]', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'ftp', + 'type' => 'unparsed_content', + 'content' => '$1', + 'validate' => create_function('&$tag, &$data, $disabled', ' + $data = strtr($data, array(\'
          \' => \'\')); + if (strpos($data, \'ftp://\') !== 0 && strpos($data, \'ftps://\') !== 0) + $data = \'ftp://\' . $data; + '), + ), + array( + 'tag' => 'ftp', + 'type' => 'unparsed_equals', + 'before' => '', + 'after' => '', + 'validate' => create_function('&$tag, &$data, $disabled', ' + if (strpos($data, \'ftp://\') !== 0 && strpos($data, \'ftps://\') !== 0) + $data = \'ftp://\' . $data; + '), + 'disallow_children' => array('email', 'ftp', 'url', 'iurl'), + 'disabled_after' => ' ($1)', + ), + array( + 'tag' => 'glow', + 'type' => 'unparsed_commas', + 'test' => '[#0-9a-zA-Z\-]{3,12},([012]\d{1,2}|\d{1,2})(,[^]]+)?\]', + 'before' => $context['browser']['is_ie'] ? '
          ' : '', + 'after' => $context['browser']['is_ie'] ? '
          ' : '', + ), + array( + 'tag' => 'green', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'html', + 'type' => 'unparsed_content', + 'content' => '$1', + 'block_level' => true, + 'disabled_content' => '$1', + ), + array( + 'tag' => 'hr', + 'type' => 'closed', + 'content' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 'i', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'img', + 'type' => 'unparsed_content', + 'parameters' => array( + 'alt' => array('optional' => true), + 'width' => array('optional' => true, 'value' => ' width="$1"', 'match' => '(\d+)'), + 'height' => array('optional' => true, 'value' => ' height="$1"', 'match' => '(\d+)'), + ), + 'content' => '{alt}', + 'validate' => create_function('&$tag, &$data, $disabled', ' + $data = strtr($data, array(\'
          \' => \'\')); + if (strpos($data, \'http://\') !== 0 && strpos($data, \'https://\') !== 0) + $data = \'http://\' . $data; + '), + 'disabled_content' => '($1)', + ), + array( + 'tag' => 'img', + 'type' => 'unparsed_content', + 'content' => '', + 'validate' => create_function('&$tag, &$data, $disabled', ' + $data = strtr($data, array(\'
          \' => \'\')); + if (strpos($data, \'http://\') !== 0 && strpos($data, \'https://\') !== 0) + $data = \'http://\' . $data; + '), + 'disabled_content' => '($1)', + ), + array( + 'tag' => 'iurl', + 'type' => 'unparsed_content', + 'content' => '$1', + 'validate' => create_function('&$tag, &$data, $disabled', ' + $data = strtr($data, array(\'
          \' => \'\')); + if (strpos($data, \'http://\') !== 0 && strpos($data, \'https://\') !== 0) + $data = \'http://\' . $data; + '), + ), + array( + 'tag' => 'iurl', + 'type' => 'unparsed_equals', + 'before' => '', + 'after' => '', + 'validate' => create_function('&$tag, &$data, $disabled', ' + if (substr($data, 0, 1) == \'#\') + $data = \'#post_\' . substr($data, 1); + elseif (strpos($data, \'http://\') !== 0 && strpos($data, \'https://\') !== 0) + $data = \'http://\' . $data; + '), + 'disallow_children' => array('email', 'ftp', 'url', 'iurl'), + 'disabled_after' => ' ($1)', + ), + array( + 'tag' => 'left', + 'before' => '
          ', + 'after' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 'li', + 'before' => '
        • ', + 'after' => '
        • ', + 'trim' => 'outside', + 'require_parents' => array('list'), + 'block_level' => true, + 'disabled_before' => '', + 'disabled_after' => '
          ', + ), + array( + 'tag' => 'list', + 'before' => '
            ', + 'after' => '
          ', + 'trim' => 'inside', + 'require_children' => array('li', 'list'), + 'block_level' => true, + ), + array( + 'tag' => 'list', + 'parameters' => array( + 'type' => array('match' => '(none|disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-alpha|upper-alpha|lower-greek|lower-latin|upper-latin|hebrew|armenian|georgian|cjk-ideographic|hiragana|katakana|hiragana-iroha|katakana-iroha)'), + ), + 'before' => '
            ', + 'after' => '
          ', + 'trim' => 'inside', + 'require_children' => array('li'), + 'block_level' => true, + ), + array( + 'tag' => 'ltr', + 'before' => '
          ', + 'after' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 'me', + 'type' => 'unparsed_equals', + 'before' => '
          * $1 ', + 'after' => '
          ', + 'quoted' => 'optional', + 'block_level' => true, + 'disabled_before' => '/me ', + 'disabled_after' => '
          ', + ), + array( + 'tag' => 'move', + 'before' => '', + 'after' => '', + 'block_level' => true, + 'disallow_children' => array('move'), + ), + array( + 'tag' => 'nobbc', + 'type' => 'unparsed_content', + 'content' => '$1', + ), + array( + 'tag' => 'php', + 'type' => 'unparsed_content', + 'content' => '$1', + 'validate' => isset($disabled['php']) ? null : create_function('&$tag, &$data, $disabled', ' + if (!isset($disabled[\'php\'])) + { + $add_begin = substr(trim($data), 0, 5) != \'<?\'; + $data = highlight_php_code($add_begin ? \'<?php \' . $data . \'?>\' : $data); + if ($add_begin) + $data = preg_replace(array(\'~^(.+?)<\?.{0,40}?php(?: |\s)~\', \'~\?>((?:)*)$~\'), \'$1\', $data, 2); + }'), + 'block_level' => false, + 'disabled_content' => '$1', + ), + array( + 'tag' => 'pre', + 'before' => '
          ',
          +				'after' => '
          ', + ), + array( + 'tag' => 'quote', + 'before' => '
          ' . $txt['quote'] . '
          ', + 'after' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 'quote', + 'parameters' => array( + 'author' => array('match' => '(.{1,192}?)', 'quoted' => true), + ), + 'before' => '
          ' . $txt['quote_from'] . ': {author}
          ', + 'after' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 'quote', + 'type' => 'parsed_equals', + 'before' => '
          ' . $txt['quote_from'] . ': $1
          ', + 'after' => '
          ', + 'quoted' => 'optional', + // Don't allow everything to be embedded with the author name. + 'parsed_tags_allowed' => array('url', 'iurl', 'ftp'), + 'block_level' => true, + ), + array( + 'tag' => 'quote', + 'parameters' => array( + 'author' => array('match' => '([^<>]{1,192}?)'), + 'link' => array('match' => '(?:board=\d+;)?((?:topic|threadid)=[\dmsg#\./]{1,40}(?:;start=[\dmsg#\./]{1,40})?|action=profile;u=\d+)'), + 'date' => array('match' => '(\d+)', 'validate' => 'timeformat'), + ), + 'before' => '
          ', + 'after' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 'quote', + 'parameters' => array( + 'author' => array('match' => '(.{1,192}?)'), + ), + 'before' => '
          ' . $txt['quote_from'] . ': {author}
          ', + 'after' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 'red', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'right', + 'before' => '
          ', + 'after' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 'rtl', + 'before' => '
          ', + 'after' => '
          ', + 'block_level' => true, + ), + array( + 'tag' => 's', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'shadow', + 'type' => 'unparsed_commas', + 'test' => '[#0-9a-zA-Z\-]{3,12},(left|right|top|bottom|[0123]\d{0,2})\]', + 'before' => $context['browser']['is_ie'] ? '' : '', + 'after' => '', + 'validate' => $context['browser']['is_ie'] ? create_function('&$tag, &$data, $disabled', ' + if ($data[1] == \'left\') + $data[1] = 270; + elseif ($data[1] == \'right\') + $data[1] = 90; + elseif ($data[1] == \'top\') + $data[1] = 0; + elseif ($data[1] == \'bottom\') + $data[1] = 180; + else + $data[1] = (int) $data[1];') : create_function('&$tag, &$data, $disabled', ' + if ($data[1] == \'top\' || (is_numeric($data[1]) && $data[1] < 50)) + $data[1] = \'0 -2px 1px\'; + elseif ($data[1] == \'right\' || (is_numeric($data[1]) && $data[1] < 100)) + $data[1] = \'2px 0 1px\'; + elseif ($data[1] == \'bottom\' || (is_numeric($data[1]) && $data[1] < 190)) + $data[1] = \'0 2px 1px\'; + elseif ($data[1] == \'left\' || (is_numeric($data[1]) && $data[1] < 280)) + $data[1] = \'-2px 0 1px\'; + else + $data[1] = \'1px 1px 1px\';'), + ), + array( + 'tag' => 'size', + 'type' => 'unparsed_equals', + 'test' => '([1-9][\d]?p[xt]|small(?:er)?|large[r]?|x[x]?-(?:small|large)|medium|(0\.[1-9]|[1-9](\.[\d][\d]?)?)?em)\]', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'size', + 'type' => 'unparsed_equals', + 'test' => '[1-7]\]', + 'before' => '', + 'after' => '', + 'validate' => create_function('&$tag, &$data, $disabled', ' + $sizes = array(1 => 0.7, 2 => 1.0, 3 => 1.35, 4 => 1.45, 5 => 2.0, 6 => 2.65, 7 => 3.95); + $data = $sizes[$data] . \'em\';' + ), + ), + array( + 'tag' => 'sub', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'sup', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'table', + 'before' => '', + 'after' => '
          ', + 'trim' => 'inside', + 'require_children' => array('tr'), + 'block_level' => true, + ), + array( + 'tag' => 'td', + 'before' => '', + 'after' => '', + 'require_parents' => array('tr'), + 'trim' => 'outside', + 'block_level' => true, + 'disabled_before' => '', + 'disabled_after' => '', + ), + array( + 'tag' => 'time', + 'type' => 'unparsed_content', + 'content' => '$1', + 'validate' => create_function('&$tag, &$data, $disabled', ' + if (is_numeric($data)) + $data = timeformat($data); + else + $tag[\'content\'] = \'[time]$1[/time]\';'), + ), + array( + 'tag' => 'tr', + 'before' => '', + 'after' => '', + 'require_parents' => array('table'), + 'require_children' => array('td'), + 'trim' => 'both', + 'block_level' => true, + 'disabled_before' => '', + 'disabled_after' => '', + ), + array( + 'tag' => 'tt', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'u', + 'before' => '', + 'after' => '', + ), + array( + 'tag' => 'url', + 'type' => 'unparsed_content', + 'content' => '$1', + 'validate' => create_function('&$tag, &$data, $disabled', ' + $data = strtr($data, array(\'
          \' => \'\')); + if (strpos($data, \'http://\') !== 0 && strpos($data, \'https://\') !== 0) + $data = \'http://\' . $data; + '), + ), + array( + 'tag' => 'url', + 'type' => 'unparsed_equals', + 'before' => '', + 'after' => '', + 'validate' => create_function('&$tag, &$data, $disabled', ' + if (strpos($data, \'http://\') !== 0 && strpos($data, \'https://\') !== 0) + $data = \'http://\' . $data; + '), + 'disallow_children' => array('email', 'ftp', 'url', 'iurl'), + 'disabled_after' => ' ($1)', + ), + array( + 'tag' => 'white', + 'before' => '', + 'after' => '', + ), + ); + + // Let mods add new BBC without hassle. + call_integration_hook('integrate_bbc_codes', array(&$codes)); + + // This is mainly for the bbc manager, so it's easy to add tags above. Custom BBC should be added above this line. + if ($message === false) + { + if (isset($temp_bbc)) + $bbc_codes = $temp_bbc; + return $codes; + } + + // So the parser won't skip them. + $itemcodes = array( + '*' => 'disc', + '@' => 'disc', + '+' => 'square', + 'x' => 'square', + '#' => 'square', + 'o' => 'circle', + 'O' => 'circle', + '0' => 'circle', + ); + if (!isset($disabled['li']) && !isset($disabled['list'])) + { + foreach ($itemcodes as $c => $dummy) + $bbc_codes[$c] = array(); + } + + // Inside these tags autolink is not recommendable. + $no_autolink_tags = array( + 'url', + 'iurl', + 'ftp', + 'email', + ); + + // Shhhh! + if (!isset($disabled['color'])) + { + $codes[] = array( + 'tag' => 'chrissy', + 'before' => '', + 'after' => ' :-*', + ); + $codes[] = array( + 'tag' => 'kissy', + 'before' => '', + 'after' => ' :-*', + ); + } + + foreach ($codes as $code) + { + // If we are not doing every tag only do ones we are interested in. + if (empty($parse_tags) || in_array($code['tag'], $parse_tags)) + $bbc_codes[substr($code['tag'], 0, 1)][] = $code; + } + $codes = null; + } + + // Shall we take the time to cache this? + if ($cache_id != '' && !empty($modSettings['cache_enable']) && (($modSettings['cache_enable'] >= 2 && strlen($message) > 1000) || strlen($message) > 2400) && empty($parse_tags)) + { + // It's likely this will change if the message is modified. + $cache_key = 'parse:' . $cache_id . '-' . md5(md5($message) . '-' . $smileys . (empty($disabled) ? '' : implode(',', array_keys($disabled))) . serialize($context['browser']) . $txt['lang_locale'] . $user_info['time_offset'] . $user_info['time_format']); + + if (($temp = cache_get_data($cache_key, 240)) != null) + return $temp; + + $cache_t = microtime(); + } + + if ($smileys === 'print') + { + // [glow], [shadow], and [move] can't really be printed. + $disabled['glow'] = true; + $disabled['shadow'] = true; + $disabled['move'] = true; + + // Colors can't well be displayed... supposed to be black and white. + $disabled['color'] = true; + $disabled['black'] = true; + $disabled['blue'] = true; + $disabled['white'] = true; + $disabled['red'] = true; + $disabled['green'] = true; + $disabled['me'] = true; + + // Color coding doesn't make sense. + $disabled['php'] = true; + + // Links are useless on paper... just show the link. + $disabled['ftp'] = true; + $disabled['url'] = true; + $disabled['iurl'] = true; + $disabled['email'] = true; + $disabled['flash'] = true; + + // !!! Change maybe? + if (!isset($_GET['images'])) + $disabled['img'] = true; + + // !!! Interface/setting to add more? + } + + $open_tags = array(); + $message = strtr($message, array("\n" => '
          ')); + + // The non-breaking-space looks a bit different each time. + $non_breaking_space = $context['utf8'] ? ($context['server']['complex_preg_chars'] ? '\x{A0}' : "\xC2\xA0") : '\xA0'; + + // This saves time by doing our break long words checks here. + if (!empty($modSettings['fixLongWords']) && $modSettings['fixLongWords'] > 5) + { + if ($context['browser']['is_gecko'] || $context['browser']['is_konqueror']) + $breaker = ' '; + // Opera... + elseif ($context['browser']['is_opera']) + $breaker = ' '; + // Internet Explorer... + else + $breaker = ' '; + + // PCRE will not be happy if we don't give it a short. + $modSettings['fixLongWords'] = (int) min(65535, $modSettings['fixLongWords']); + } + + $pos = -1; + while ($pos !== false) + { + $last_pos = isset($last_pos) ? max($pos, $last_pos) : $pos; + $pos = strpos($message, '[', $pos + 1); + + // Failsafe. + if ($pos === false || $last_pos > $pos) + $pos = strlen($message) + 1; + + // Can't have a one letter smiley, URL, or email! (sorry.) + if ($last_pos < $pos - 1) + { + // Make sure the $last_pos is not negative. + $last_pos = max($last_pos, 0); + + // Pick a block of data to do some raw fixing on. + $data = substr($message, $last_pos, $pos - $last_pos); + + // Take care of some HTML! + if (!empty($modSettings['enablePostHTML']) && strpos($data, '<') !== false) + { + $data = preg_replace('~<a\s+href=((?:")?)((?:https?://|ftps?://|mailto:)\S+?)\\1>~i', '[url=$2]', $data); + $data = preg_replace('~</a>~i', '[/url]', $data); + + //
          should be empty. + $empty_tags = array('br', 'hr'); + foreach ($empty_tags as $tag) + $data = str_replace(array('<' . $tag . '>', '<' . $tag . '/>', '<' . $tag . ' />'), '[' . $tag . ' /]', $data); + + // b, u, i, s, pre... basic tags. + $closable_tags = array('b', 'u', 'i', 's', 'em', 'ins', 'del', 'pre', 'blockquote'); + foreach ($closable_tags as $tag) + { + $diff = substr_count($data, '<' . $tag . '>') - substr_count($data, '</' . $tag . '>'); + $data = strtr($data, array('<' . $tag . '>' => '<' . $tag . '>', '</' . $tag . '>' => '')); + + if ($diff > 0) + $data = substr($data, 0, -1) . str_repeat('', $diff) . substr($data, -1); + } + + // Do - with security... action= -> action-. + preg_match_all('~<img\s+src=((?:")?)((?:https?://|ftps?://)\S+?)\\1(?:\s+alt=(".*?"|\S*?))?(?:\s?/)?>~i', $data, $matches, PREG_PATTERN_ORDER); + if (!empty($matches[0])) + { + $replaces = array(); + foreach ($matches[2] as $match => $imgtag) + { + $alt = empty($matches[3][$match]) ? '' : ' alt=' . preg_replace('~^"|"$~', '', $matches[3][$match]); + + // Remove action= from the URL - no funny business, now. + if (preg_match('~action(=|%3d)(?!dlattach)~i', $imgtag) != 0) + $imgtag = preg_replace('~action(?:=|%3d)(?!dlattach)~i', 'action-', $imgtag); + + // Check if the image is larger than allowed. + if (!empty($modSettings['max_image_width']) && !empty($modSettings['max_image_height'])) + { + list ($width, $height) = url_image_size($imgtag); + + if (!empty($modSettings['max_image_width']) && $width > $modSettings['max_image_width']) + { + $height = (int) (($modSettings['max_image_width'] * $height) / $width); + $width = $modSettings['max_image_width']; + } + + if (!empty($modSettings['max_image_height']) && $height > $modSettings['max_image_height']) + { + $width = (int) (($modSettings['max_image_height'] * $width) / $height); + $height = $modSettings['max_image_height']; + } + + // Set the new image tag. + $replaces[$matches[0][$match]] = '[img width=' . $width . ' height=' . $height . $alt . ']' . $imgtag . '[/img]'; + } + else + $replaces[$matches[0][$match]] = '[img' . $alt . ']' . $imgtag . '[/img]'; + } + + $data = strtr($data, $replaces); + } + } + + if (!empty($modSettings['autoLinkUrls'])) + { + // Are we inside tags that should be auto linked? + $no_autolink_area = false; + if (!empty($open_tags)) + { + foreach ($open_tags as $open_tag) + if (in_array($open_tag['tag'], $no_autolink_tags)) + $no_autolink_area = true; + } + + // Don't go backwards. + //!!! Don't think is the real solution.... + $lastAutoPos = isset($lastAutoPos) ? $lastAutoPos : 0; + if ($pos < $lastAutoPos) + $no_autolink_area = true; + $lastAutoPos = $pos; + + if (!$no_autolink_area) + { + // Parse any URLs.... have to get rid of the @ problems some things cause... stupid email addresses. + if (!isset($disabled['url']) && (strpos($data, '://') !== false || strpos($data, 'www.') !== false) && strpos($data, '[url') === false) + { + // Switch out quotes really quick because they can cause problems. + $data = strtr($data, array(''' => '\'', ' ' => $context['utf8'] ? "\xC2\xA0" : "\xA0", '"' => '>">', '"' => '<"<', '<' => '\.(;\'"]|^)((?:http|https)://[\w\-_%@:|]+(?:\.[\w\-_%]+)*(?::\d+)?(?:/[\w\-_\~%\.@!,\?&;=#(){}+:\'\\\\]*)*[/\w\-_\~%@\?;=#}\\\\])~i', + '~(?<=[\s>\.(;\'"]|^)((?:ftp|ftps)://[\w\-_%@:|]+(?:\.[\w\-_%]+)*(?::\d+)?(?:/[\w\-_\~%\.@,\?&;=#(){}+:\'\\\\]*)*[/\w\-_\~%@\?;=#}\\\\])~i', + '~(?<=[\s>(\'<]|^)(www(?:\.[\w\-_]+)+(?::\d+)?(?:/[\w\-_\~%\.@!,\?&;=#(){}+:\'\\\\]*)*[/\w\-_\~%@\?;=#}\\\\])~i' + ), array( + '[url]$1[/url]', + '[ftp]$1[/ftp]', + '[url=http://$1]$1[/url]' + ), $data))) + $data = $result; + + $data = strtr($data, array('\'' => ''', $context['utf8'] ? "\xC2\xA0" : "\xA0" => ' ', '>">' => '"', '<"<' => '"', ' '<')); + } + + // Next, emails... + if (!isset($disabled['email']) && strpos($data, '@') !== false && strpos($data, '[email') === false) + { + $data = preg_replace('~(?<=[\?\s' . $non_breaking_space . '\[\]()*\\\;>]|^)([\w\-\.]{1,80}@[\w\-]+\.[\w\-\.]+[\w\-])(?=[?,\s' . $non_breaking_space . '\[\]()*\\\]|$|
          | |>|<|"|'|\.(?:\.|;| |\s|$|
          ))~' . ($context['utf8'] ? 'u' : ''), '[email]$1[/email]', $data); + $data = preg_replace('~(?<=
          )([\w\-\.]{1,80}@[\w\-]+\.[\w\-\.]+[\w\-])(?=[?\.,;\s' . $non_breaking_space . '\[\]()*\\\]|$|
          | |>|<|"|')~' . ($context['utf8'] ? 'u' : ''), '[email]$1[/email]', $data); + } + } + } + + $data = strtr($data, array("\t" => '   ')); + + if (!empty($modSettings['fixLongWords']) && $modSettings['fixLongWords'] > 5) + { + // The idea is, find words xx long, and then replace them with xx + space + more. + if ($smcFunc['strlen']($data) > $modSettings['fixLongWords']) + { + // This is done in a roundabout way because $breaker has "long words" :P. + $data = strtr($data, array($breaker => '< >', ' ' => $context['utf8'] ? "\xC2\xA0" : "\xA0")); + $data = preg_replace( + '~(?<=[>;:!? ' . $non_breaking_space . '\]()]|^)([\w' . ($context['utf8'] ? '\pL' : '') . '\.]{' . $modSettings['fixLongWords'] . ',})~e' . ($context['utf8'] ? 'u' : ''), + 'preg_replace(\'/(.{' . ($modSettings['fixLongWords'] - 1) . '})/' . ($context['utf8'] ? 'u' : '') . '\', \'\\$1< >\', \'$1\')', + $data); + $data = strtr($data, array('< >' => $breaker, $context['utf8'] ? "\xC2\xA0" : "\xA0" => ' ')); + } + } + + // If it wasn't changed, no copying or other boring stuff has to happen! + if ($data != substr($message, $last_pos, $pos - $last_pos)) + { + $message = substr($message, 0, $last_pos) . $data . substr($message, $pos); + + // Since we changed it, look again in case we added or removed a tag. But we don't want to skip any. + $old_pos = strlen($data) + $last_pos; + $pos = strpos($message, '[', $last_pos); + $pos = $pos === false ? $old_pos : min($pos, $old_pos); + } + } + + // Are we there yet? Are we there yet? + if ($pos >= strlen($message) - 1) + break; + + $tags = strtolower(substr($message, $pos + 1, 1)); + + if ($tags == '/' && !empty($open_tags)) + { + $pos2 = strpos($message, ']', $pos + 1); + if ($pos2 == $pos + 2) + continue; + $look_for = strtolower(substr($message, $pos + 2, $pos2 - $pos - 2)); + + $to_close = array(); + $block_level = null; + do + { + $tag = array_pop($open_tags); + if (!$tag) + break; + + if (!empty($tag['block_level'])) + { + // Only find out if we need to. + if ($block_level === false) + { + array_push($open_tags, $tag); + break; + } + + // The idea is, if we are LOOKING for a block level tag, we can close them on the way. + if (strlen($look_for) > 0 && isset($bbc_codes[$look_for[0]])) + { + foreach ($bbc_codes[$look_for[0]] as $temp) + if ($temp['tag'] == $look_for) + { + $block_level = !empty($temp['block_level']); + break; + } + } + + if ($block_level !== true) + { + $block_level = false; + array_push($open_tags, $tag); + break; + } + } + + $to_close[] = $tag; + } + while ($tag['tag'] != $look_for); + + // Did we just eat through everything and not find it? + if ((empty($open_tags) && (empty($tag) || $tag['tag'] != $look_for))) + { + $open_tags = $to_close; + continue; + } + elseif (!empty($to_close) && $tag['tag'] != $look_for) + { + if ($block_level === null && isset($look_for[0], $bbc_codes[$look_for[0]])) + { + foreach ($bbc_codes[$look_for[0]] as $temp) + if ($temp['tag'] == $look_for) + { + $block_level = !empty($temp['block_level']); + break; + } + } + + // We're not looking for a block level tag (or maybe even a tag that exists...) + if (!$block_level) + { + foreach ($to_close as $tag) + array_push($open_tags, $tag); + continue; + } + } + + foreach ($to_close as $tag) + { + $message = substr($message, 0, $pos) . "\n" . $tag['after'] . "\n" . substr($message, $pos2 + 1); + $pos += strlen($tag['after']) + 2; + $pos2 = $pos - 1; + + // See the comment at the end of the big loop - just eating whitespace ;). + if (!empty($tag['block_level']) && substr($message, $pos, 6) == '
          ') + $message = substr($message, 0, $pos) . substr($message, $pos + 6); + if (!empty($tag['trim']) && $tag['trim'] != 'inside' && preg_match('~(
          | |\s)*~', substr($message, $pos), $matches) != 0) + $message = substr($message, 0, $pos) . substr($message, $pos + strlen($matches[0])); + } + + if (!empty($to_close)) + { + $to_close = array(); + $pos--; + } + + continue; + } + + // No tags for this character, so just keep going (fastest possible course.) + if (!isset($bbc_codes[$tags])) + continue; + + $inside = empty($open_tags) ? null : $open_tags[count($open_tags) - 1]; + $tag = null; + foreach ($bbc_codes[$tags] as $possible) + { + // Not a match? + if (strtolower(substr($message, $pos + 1, strlen($possible['tag']))) != $possible['tag']) + continue; + + $next_c = substr($message, $pos + 1 + strlen($possible['tag']), 1); + + // A test validation? + if (isset($possible['test']) && preg_match('~^' . $possible['test'] . '~', substr($message, $pos + 1 + strlen($possible['tag']) + 1)) == 0) + continue; + // Do we want parameters? + elseif (!empty($possible['parameters'])) + { + if ($next_c != ' ') + continue; + } + elseif (isset($possible['type'])) + { + // Do we need an equal sign? + if (in_array($possible['type'], array('unparsed_equals', 'unparsed_commas', 'unparsed_commas_content', 'unparsed_equals_content', 'parsed_equals')) && $next_c != '=') + continue; + // Maybe we just want a /... + if ($possible['type'] == 'closed' && $next_c != ']' && substr($message, $pos + 1 + strlen($possible['tag']), 2) != '/]' && substr($message, $pos + 1 + strlen($possible['tag']), 3) != ' /]') + continue; + // An immediate ]? + if ($possible['type'] == 'unparsed_content' && $next_c != ']') + continue; + } + // No type means 'parsed_content', which demands an immediate ] without parameters! + elseif ($next_c != ']') + continue; + + // Check allowed tree? + if (isset($possible['require_parents']) && ($inside === null || !in_array($inside['tag'], $possible['require_parents']))) + continue; + elseif (isset($inside['require_children']) && !in_array($possible['tag'], $inside['require_children'])) + continue; + // If this is in the list of disallowed child tags, don't parse it. + elseif (isset($inside['disallow_children']) && in_array($possible['tag'], $inside['disallow_children'])) + continue; + + $pos1 = $pos + 1 + strlen($possible['tag']) + 1; + + // Quotes can have alternate styling, we do this php-side due to all the permutations of quotes. + if ($possible['tag'] == 'quote') + { + // Start with standard + $quote_alt = false; + foreach ($open_tags as $open_quote) + { + // Every parent quote this quote has flips the styling + if ($open_quote['tag'] == 'quote') + $quote_alt = !$quote_alt; + } + // Add a class to the quote to style alternating blockquotes + $possible['before'] = strtr($possible['before'], array('
          ' => '
          ')); + } + + // This is long, but it makes things much easier and cleaner. + if (!empty($possible['parameters'])) + { + $preg = array(); + foreach ($possible['parameters'] as $p => $info) + $preg[] = '(\s+' . $p . '=' . (empty($info['quoted']) ? '' : '"') . (isset($info['match']) ? $info['match'] : '(.+?)') . (empty($info['quoted']) ? '' : '"') . ')' . (empty($info['optional']) ? '' : '?'); + + // Okay, this may look ugly and it is, but it's not going to happen much and it is the best way of allowing any order of parameters but still parsing them right. + $match = false; + $orders = permute($preg); + foreach ($orders as $p) + if (preg_match('~^' . implode('', $p) . '\]~i', substr($message, $pos1 - 1), $matches) != 0) + { + $match = true; + break; + } + + // Didn't match our parameter list, try the next possible. + if (!$match) + continue; + + $params = array(); + for ($i = 1, $n = count($matches); $i < $n; $i += 2) + { + $key = strtok(ltrim($matches[$i]), '='); + if (isset($possible['parameters'][$key]['value'])) + $params['{' . $key . '}'] = strtr($possible['parameters'][$key]['value'], array('$1' => $matches[$i + 1])); + elseif (isset($possible['parameters'][$key]['validate'])) + $params['{' . $key . '}'] = $possible['parameters'][$key]['validate']($matches[$i + 1]); + else + $params['{' . $key . '}'] = $matches[$i + 1]; + + // Just to make sure: replace any $ or { so they can't interpolate wrongly. + $params['{' . $key . '}'] = strtr($params['{' . $key . '}'], array('$' => '$', '{' => '{')); + } + + foreach ($possible['parameters'] as $p => $info) + { + if (!isset($params['{' . $p . '}'])) + $params['{' . $p . '}'] = ''; + } + + $tag = $possible; + + // Put the parameters into the string. + if (isset($tag['before'])) + $tag['before'] = strtr($tag['before'], $params); + if (isset($tag['after'])) + $tag['after'] = strtr($tag['after'], $params); + if (isset($tag['content'])) + $tag['content'] = strtr($tag['content'], $params); + + $pos1 += strlen($matches[0]) - 1; + } + else + $tag = $possible; + break; + } + + // Item codes are complicated buggers... they are implicit [li]s and can make [list]s! + if ($smileys !== false && $tag === null && isset($itemcodes[substr($message, $pos + 1, 1)]) && substr($message, $pos + 2, 1) == ']' && !isset($disabled['list']) && !isset($disabled['li'])) + { + if (substr($message, $pos + 1, 1) == '0' && !in_array(substr($message, $pos - 1, 1), array(';', ' ', "\t", '>'))) + continue; + $tag = $itemcodes[substr($message, $pos + 1, 1)]; + + // First let's set up the tree: it needs to be in a list, or after an li. + if ($inside === null || ($inside['tag'] != 'list' && $inside['tag'] != 'li')) + { + $open_tags[] = array( + 'tag' => 'list', + 'after' => '
        ', + 'block_level' => true, + 'require_children' => array('li'), + 'disallow_children' => isset($inside['disallow_children']) ? $inside['disallow_children'] : null, + ); + $code = '
          '; + } + // We're in a list item already: another itemcode? Close it first. + elseif ($inside['tag'] == 'li') + { + array_pop($open_tags); + $code = ''; + } + else + $code = ''; + + // Now we open a new tag. + $open_tags[] = array( + 'tag' => 'li', + 'after' => '', + 'trim' => 'outside', + 'block_level' => true, + 'disallow_children' => isset($inside['disallow_children']) ? $inside['disallow_children'] : null, + ); + + // First, open the tag... + $code .= ''; + $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos + 3); + $pos += strlen($code) - 1 + 2; + + // Next, find the next break (if any.) If there's more itemcode after it, keep it going - otherwise close! + $pos2 = strpos($message, '
          ', $pos); + $pos3 = strpos($message, '[/', $pos); + if ($pos2 !== false && ($pos2 <= $pos3 || $pos3 === false)) + { + preg_match('~^(
          | |\s|\[)+~', substr($message, $pos2 + 6), $matches); + $message = substr($message, 0, $pos2) . "\n" . (!empty($matches[0]) && substr($matches[0], -1) == '[' ? '[/li]' : '[/li][/list]') . "\n" . substr($message, $pos2); + + $open_tags[count($open_tags) - 2]['after'] = '
        '; + } + // Tell the [list] that it needs to close specially. + else + { + // Move the li over, because we're not sure what we'll hit. + $open_tags[count($open_tags) - 1]['after'] = ''; + $open_tags[count($open_tags) - 2]['after'] = '
    '; + } + + continue; + } + + // Implicitly close lists and tables if something other than what's required is in them. This is needed for itemcode. + if ($tag === null && $inside !== null && !empty($inside['require_children'])) + { + array_pop($open_tags); + + $message = substr($message, 0, $pos) . "\n" . $inside['after'] . "\n" . substr($message, $pos); + $pos += strlen($inside['after']) - 1 + 2; + } + + // No tag? Keep looking, then. Silly people using brackets without actual tags. + if ($tag === null) + continue; + + // Propagate the list to the child (so wrapping the disallowed tag won't work either.) + if (isset($inside['disallow_children'])) + $tag['disallow_children'] = isset($tag['disallow_children']) ? array_unique(array_merge($tag['disallow_children'], $inside['disallow_children'])) : $inside['disallow_children']; + + // Is this tag disabled? + if (isset($disabled[$tag['tag']])) + { + if (!isset($tag['disabled_before']) && !isset($tag['disabled_after']) && !isset($tag['disabled_content'])) + { + $tag['before'] = !empty($tag['block_level']) ? '
    ' : ''; + $tag['after'] = !empty($tag['block_level']) ? '
    ' : ''; + $tag['content'] = isset($tag['type']) && $tag['type'] == 'closed' ? '' : (!empty($tag['block_level']) ? '
    $1
    ' : '$1'); + } + elseif (isset($tag['disabled_before']) || isset($tag['disabled_after'])) + { + $tag['before'] = isset($tag['disabled_before']) ? $tag['disabled_before'] : (!empty($tag['block_level']) ? '
    ' : ''); + $tag['after'] = isset($tag['disabled_after']) ? $tag['disabled_after'] : (!empty($tag['block_level']) ? '
    ' : ''); + } + else + $tag['content'] = $tag['disabled_content']; + } + + // The only special case is 'html', which doesn't need to close things. + if (!empty($tag['block_level']) && $tag['tag'] != 'html' && empty($inside['block_level'])) + { + $n = count($open_tags) - 1; + while (empty($open_tags[$n]['block_level']) && $n >= 0) + $n--; + + // Close all the non block level tags so this tag isn't surrounded by them. + for ($i = count($open_tags) - 1; $i > $n; $i--) + { + $message = substr($message, 0, $pos) . "\n" . $open_tags[$i]['after'] . "\n" . substr($message, $pos); + $pos += strlen($open_tags[$i]['after']) + 2; + $pos1 += strlen($open_tags[$i]['after']) + 2; + + // Trim or eat trailing stuff... see comment at the end of the big loop. + if (!empty($open_tags[$i]['block_level']) && substr($message, $pos, 6) == '
    ') + $message = substr($message, 0, $pos) . substr($message, $pos + 6); + if (!empty($open_tags[$i]['trim']) && $tag['trim'] != 'inside' && preg_match('~(
    | |\s)*~', substr($message, $pos), $matches) != 0) + $message = substr($message, 0, $pos) . substr($message, $pos + strlen($matches[0])); + + array_pop($open_tags); + } + } + + // No type means 'parsed_content'. + if (!isset($tag['type'])) + { + // !!! Check for end tag first, so people can say "I like that [i] tag"? + $open_tags[] = $tag; + $message = substr($message, 0, $pos) . "\n" . $tag['before'] . "\n" . substr($message, $pos1); + $pos += strlen($tag['before']) - 1 + 2; + } + // Don't parse the content, just skip it. + elseif ($tag['type'] == 'unparsed_content') + { + $pos2 = stripos($message, '[/' . substr($message, $pos + 1, strlen($tag['tag'])) . ']', $pos1); + if ($pos2 === false) + continue; + + $data = substr($message, $pos1, $pos2 - $pos1); + + if (!empty($tag['block_level']) && substr($data, 0, 6) == '
    ') + $data = substr($data, 6); + + if (isset($tag['validate'])) + $tag['validate']($tag, $data, $disabled); + + $code = strtr($tag['content'], array('$1' => $data)); + $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos2 + 3 + strlen($tag['tag'])); + + $pos += strlen($code) - 1 + 2; + $last_pos = $pos + 1; + + } + // Don't parse the content, just skip it. + elseif ($tag['type'] == 'unparsed_equals_content') + { + // The value may be quoted for some tags - check. + if (isset($tag['quoted'])) + { + $quoted = substr($message, $pos1, 6) == '"'; + if ($tag['quoted'] != 'optional' && !$quoted) + continue; + + if ($quoted) + $pos1 += 6; + } + else + $quoted = false; + + $pos2 = strpos($message, $quoted == false ? ']' : '"]', $pos1); + if ($pos2 === false) + continue; + $pos3 = stripos($message, '[/' . substr($message, $pos + 1, strlen($tag['tag'])) . ']', $pos2); + if ($pos3 === false) + continue; + + $data = array( + substr($message, $pos2 + ($quoted == false ? 1 : 7), $pos3 - ($pos2 + ($quoted == false ? 1 : 7))), + substr($message, $pos1, $pos2 - $pos1) + ); + + if (!empty($tag['block_level']) && substr($data[0], 0, 6) == '
    ') + $data[0] = substr($data[0], 6); + + // Validation for my parking, please! + if (isset($tag['validate'])) + $tag['validate']($tag, $data, $disabled); + + $code = strtr($tag['content'], array('$1' => $data[0], '$2' => $data[1])); + $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos3 + 3 + strlen($tag['tag'])); + $pos += strlen($code) - 1 + 2; + } + // A closed tag, with no content or value. + elseif ($tag['type'] == 'closed') + { + $pos2 = strpos($message, ']', $pos); + $message = substr($message, 0, $pos) . "\n" . $tag['content'] . "\n" . substr($message, $pos2 + 1); + $pos += strlen($tag['content']) - 1 + 2; + } + // This one is sorta ugly... :/. Unfortunately, it's needed for flash. + elseif ($tag['type'] == 'unparsed_commas_content') + { + $pos2 = strpos($message, ']', $pos1); + if ($pos2 === false) + continue; + $pos3 = stripos($message, '[/' . substr($message, $pos + 1, strlen($tag['tag'])) . ']', $pos2); + if ($pos3 === false) + continue; + + // We want $1 to be the content, and the rest to be csv. + $data = explode(',', ',' . substr($message, $pos1, $pos2 - $pos1)); + $data[0] = substr($message, $pos2 + 1, $pos3 - $pos2 - 1); + + if (isset($tag['validate'])) + $tag['validate']($tag, $data, $disabled); + + $code = $tag['content']; + foreach ($data as $k => $d) + $code = strtr($code, array('$' . ($k + 1) => trim($d))); + $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos3 + 3 + strlen($tag['tag'])); + $pos += strlen($code) - 1 + 2; + } + // This has parsed content, and a csv value which is unparsed. + elseif ($tag['type'] == 'unparsed_commas') + { + $pos2 = strpos($message, ']', $pos1); + if ($pos2 === false) + continue; + + $data = explode(',', substr($message, $pos1, $pos2 - $pos1)); + + if (isset($tag['validate'])) + $tag['validate']($tag, $data, $disabled); + + // Fix after, for disabled code mainly. + foreach ($data as $k => $d) + $tag['after'] = strtr($tag['after'], array('$' . ($k + 1) => trim($d))); + + $open_tags[] = $tag; + + // Replace them out, $1, $2, $3, $4, etc. + $code = $tag['before']; + foreach ($data as $k => $d) + $code = strtr($code, array('$' . ($k + 1) => trim($d))); + $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos2 + 1); + $pos += strlen($code) - 1 + 2; + } + // A tag set to a value, parsed or not. + elseif ($tag['type'] == 'unparsed_equals' || $tag['type'] == 'parsed_equals') + { + // The value may be quoted for some tags - check. + if (isset($tag['quoted'])) + { + $quoted = substr($message, $pos1, 6) == '"'; + if ($tag['quoted'] != 'optional' && !$quoted) + continue; + + if ($quoted) + $pos1 += 6; + } + else + $quoted = false; + + $pos2 = strpos($message, $quoted == false ? ']' : '"]', $pos1); + if ($pos2 === false) + continue; + + $data = substr($message, $pos1, $pos2 - $pos1); + + // Validation for my parking, please! + if (isset($tag['validate'])) + $tag['validate']($tag, $data, $disabled); + + // For parsed content, we must recurse to avoid security problems. + if ($tag['type'] != 'unparsed_equals') + $data = parse_bbc($data, !empty($tag['parsed_tags_allowed']) ? false : true, '', !empty($tag['parsed_tags_allowed']) ? $tag['parsed_tags_allowed'] : array()); + + $tag['after'] = strtr($tag['after'], array('$1' => $data)); + + $open_tags[] = $tag; + + $code = strtr($tag['before'], array('$1' => $data)); + $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos2 + ($quoted == false ? 1 : 7)); + $pos += strlen($code) - 1 + 2; + } + + // If this is block level, eat any breaks after it. + if (!empty($tag['block_level']) && substr($message, $pos + 1, 6) == '
    ') + $message = substr($message, 0, $pos + 1) . substr($message, $pos + 7); + + // Are we trimming outside this tag? + if (!empty($tag['trim']) && $tag['trim'] != 'outside' && preg_match('~(
    | |\s)*~', substr($message, $pos + 1), $matches) != 0) + $message = substr($message, 0, $pos + 1) . substr($message, $pos + 1 + strlen($matches[0])); + } + + // Close any remaining tags. + while ($tag = array_pop($open_tags)) + $message .= "\n" . $tag['after'] . "\n"; + + // Parse the smileys within the parts where it can be done safely. + if ($smileys === true) + { + $message_parts = explode("\n", $message); + for ($i = 0, $n = count($message_parts); $i < $n; $i += 2) + parsesmileys($message_parts[$i]); + + $message = implode('', $message_parts); + } + + // No smileys, just get rid of the markers. + else + $message = strtr($message, array("\n" => '')); + + if (substr($message, 0, 1) == ' ') + $message = ' ' . substr($message, 1); + + // Cleanup whitespace. + $message = strtr($message, array(' ' => '  ', "\r" => '', "\n" => '
    ', '
    ' => '
     ', ' ' => "\n")); + + // Cache the output if it took some time... + if (isset($cache_key, $cache_t) && array_sum(explode(' ', microtime())) - array_sum(explode(' ', $cache_t)) > 0.05) + cache_put_data($cache_key, $message, 240); + + // If this was a force parse revert if needed. + if (!empty($parse_tags)) + { + if (empty($temp_bbc)) + $bbc_codes = array(); + else + { + $bbc_codes = $temp_bbc; + unset($temp_bbc); + } + } + + return $message; +} + +// Parse smileys in the passed message. +function parsesmileys(&$message) +{ + global $modSettings, $txt, $user_info, $context, $smcFunc; + static $smileyPregSearch = array(), $smileyPregReplacements = array(); + + // No smiley set at all?! + if ($user_info['smiley_set'] == 'none') + return; + + // If the smiley array hasn't been set, do it now. + if (empty($smileyPregSearch)) + { + // Use the default smileys if it is disabled. (better for "portability" of smileys.) + if (empty($modSettings['smiley_enable'])) + { + $smileysfrom = array('>:D', ':D', '::)', '>:(', ':))', ':)', ';)', ';D', ':(', ':o', '8)', ':P', '???', ':-[', ':-X', ':-*', ':\'(', ':-\\', '^-^', 'O0', 'C:-)', '0:)'); + $smileysto = array('evil.gif', 'cheesy.gif', 'rolleyes.gif', 'angry.gif', 'laugh.gif', 'smiley.gif', 'wink.gif', 'grin.gif', 'sad.gif', 'shocked.gif', 'cool.gif', 'tongue.gif', 'huh.gif', 'embarrassed.gif', 'lipsrsealed.gif', 'kiss.gif', 'cry.gif', 'undecided.gif', 'azn.gif', 'afro.gif', 'police.gif', 'angel.gif'); + $smileysdescs = array('', $txt['icon_cheesy'], $txt['icon_rolleyes'], $txt['icon_angry'], '', $txt['icon_smiley'], $txt['icon_wink'], $txt['icon_grin'], $txt['icon_sad'], $txt['icon_shocked'], $txt['icon_cool'], $txt['icon_tongue'], $txt['icon_huh'], $txt['icon_embarrassed'], $txt['icon_lips'], $txt['icon_kiss'], $txt['icon_cry'], $txt['icon_undecided'], '', '', '', ''); + } + else + { + // Load the smileys in reverse order by length so they don't get parsed wrong. + if (($temp = cache_get_data('parsing_smileys', 480)) == null) + { + $result = $smcFunc['db_query']('', ' + SELECT code, filename, description + FROM {db_prefix}smileys', + array( + ) + ); + $smileysfrom = array(); + $smileysto = array(); + $smileysdescs = array(); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + $smileysfrom[] = $row['code']; + $smileysto[] = $row['filename']; + $smileysdescs[] = $row['description']; + } + $smcFunc['db_free_result']($result); + + cache_put_data('parsing_smileys', array($smileysfrom, $smileysto, $smileysdescs), 480); + } + else + list ($smileysfrom, $smileysto, $smileysdescs) = $temp; + } + + // The non-breaking-space is a complex thing... + $non_breaking_space = $context['utf8'] ? ($context['server']['complex_preg_chars'] ? '\x{A0}' : "\xC2\xA0") : '\xA0'; + + // This smiley regex makes sure it doesn't parse smileys within code tags (so [url=mailto:David@bla.com] doesn't parse the :D smiley) + $smileyPregReplacements = array(); + $searchParts = array(); + for ($i = 0, $n = count($smileysfrom); $i < $n; $i++) + { + $smileyCode = '' . strtr(htmlspecialchars($smileysfrom[$i], ENT_QUOTES), array(':' => ':', '(' => '(', ')' => ')', '$' => '$', '[' => '[')). ''; + + $smileyPregReplacements[$smileysfrom[$i]] = $smileyCode; + $smileyPregReplacements[htmlspecialchars($smileysfrom[$i], ENT_QUOTES)] = $smileyCode; + $searchParts[] = preg_quote($smileysfrom[$i], '~'); + $searchParts[] = preg_quote(htmlspecialchars($smileysfrom[$i], ENT_QUOTES), '~'); + } + + $smileyPregSearch = '~(?<=[>:\?\.\s' . $non_breaking_space . '[\]()*\\\;]|^)(' . implode('|', $searchParts) . ')(?=[^[:alpha:]0-9]|$)~e' . ($context['utf8'] ? 'u' : ''); + } + + // Replace away! + $message = preg_replace($smileyPregSearch, 'isset($smileyPregReplacements[\'$1\']) ? $smileyPregReplacements[\'$1\'] : \'\'', $message); +} + +// Highlight any code... +function highlight_php_code($code) +{ + global $context; + + // Remove special characters. + $code = un_htmlspecialchars(strtr($code, array('
    ' => "\n", "\t" => 'SMF_TAB();', '[' => '['))); + + $oldlevel = error_reporting(0); + + // It's easier in 4.2.x+. + if (@version_compare(PHP_VERSION, '4.2.0') == -1) + { + ob_start(); + @highlight_string($code); + $buffer = str_replace(array("\n", "\r"), '', ob_get_contents()); + ob_end_clean(); + } + else + $buffer = str_replace(array("\n", "\r"), '', @highlight_string($code, true)); + + error_reporting($oldlevel); + + // Yes, I know this is kludging it, but this is the best way to preserve tabs from PHP :P. + $buffer = preg_replace('~SMF_TAB(?:<(?:font color|span style)="[^"]*?">)?\\(\\);~', '
    ' . "\t" . '
    ', $buffer); + + return strtr($buffer, array('\'' => ''', '' => '', '' => '')); +} + +// Put this user in the online log. +function writeLog($force = false) +{ + global $user_info, $user_settings, $context, $modSettings, $settings, $topic, $board, $smcFunc, $sourcedir; + + // If we are showing who is viewing a topic, let's see if we are, and force an update if so - to make it accurate. + if (!empty($settings['display_who_viewing']) && ($topic || $board)) + { + // Take the opposite approach! + $force = true; + // Don't update for every page - this isn't wholly accurate but who cares. + if ($topic) + { + if (isset($_SESSION['last_topic_id']) && $_SESSION['last_topic_id'] == $topic) + $force = false; + $_SESSION['last_topic_id'] = $topic; + } + } + + // Are they a spider we should be tracking? Mode = 1 gets tracked on its spider check... + if (!empty($user_info['possibly_robot']) && !empty($modSettings['spider_mode']) && $modSettings['spider_mode'] > 1) + { + require_once($sourcedir . '/ManageSearchEngines.php'); + logSpider(); + } + + // Don't mark them as online more than every so often. + if (!empty($_SESSION['log_time']) && $_SESSION['log_time'] >= (time() - 8) && !$force) + return; + + if (!empty($modSettings['who_enabled'])) + { + $serialized = $_GET + array('USER_AGENT' => $_SERVER['HTTP_USER_AGENT']); + + // In the case of a dlattach action, session_var may not be set. + if (!isset($context['session_var'])) + $context['session_var'] = $_SESSION['session_var']; + + unset($serialized['sesc'], $serialized[$context['session_var']]); + $serialized = serialize($serialized); + } + else + $serialized = ''; + + // Guests use 0, members use their session ID. + $session_id = $user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id(); + + // Grab the last all-of-SMF-specific log_online deletion time. + $do_delete = cache_get_data('log_online-update', 30) < time() - 30; + + // If the last click wasn't a long time ago, and there was a last click... + if (!empty($_SESSION['log_time']) && $_SESSION['log_time'] >= time() - $modSettings['lastActive'] * 20) + { + if ($do_delete) + { + $smcFunc['db_query']('delete_log_online_interval', ' + DELETE FROM {db_prefix}log_online + WHERE log_time < {int:log_time} + AND session != {string:session}', + array( + 'log_time' => time() - $modSettings['lastActive'] * 60, + 'session' => $session_id, + ) + ); + + // Cache when we did it last. + cache_put_data('log_online-update', time(), 30); + } + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_online + SET log_time = {int:log_time}, ip = IFNULL(INET_ATON({string:ip}), 0), url = {string:url} + WHERE session = {string:session}', + array( + 'log_time' => time(), + 'ip' => $user_info['ip'], + 'url' => $serialized, + 'session' => $session_id, + ) + ); + + // Guess it got deleted. + if ($smcFunc['db_affected_rows']() == 0) + $_SESSION['log_time'] = 0; + } + else + $_SESSION['log_time'] = 0; + + // Otherwise, we have to delete and insert. + if (empty($_SESSION['log_time'])) + { + if ($do_delete || !empty($user_info['id'])) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_online + WHERE ' . ($do_delete ? 'log_time < {int:log_time}' : '') . ($do_delete && !empty($user_info['id']) ? ' OR ' : '') . (empty($user_info['id']) ? '' : 'id_member = {int:current_member}'), + array( + 'current_member' => $user_info['id'], + 'log_time' => time() - $modSettings['lastActive'] * 60, + ) + ); + + $smcFunc['db_insert']($do_delete ? 'ignore' : 'replace', + '{db_prefix}log_online', + array('session' => 'string', 'id_member' => 'int', 'id_spider' => 'int', 'log_time' => 'int', 'ip' => 'raw', 'url' => 'string'), + array($session_id, $user_info['id'], empty($_SESSION['id_robot']) ? 0 : $_SESSION['id_robot'], time(), 'IFNULL(INET_ATON(\'' . $user_info['ip'] . '\'), 0)', $serialized), + array('session') + ); + } + + // Mark your session as being logged. + $_SESSION['log_time'] = time(); + + // Well, they are online now. + if (empty($_SESSION['timeOnlineUpdated'])) + $_SESSION['timeOnlineUpdated'] = time(); + + // Set their login time, if not already done within the last minute. + if (SMF != 'SSI' && !empty($user_info['last_login']) && $user_info['last_login'] < time() - 60) + { + // Don't count longer than 15 minutes. + if (time() - $_SESSION['timeOnlineUpdated'] > 60 * 15) + $_SESSION['timeOnlineUpdated'] = time(); + + $user_settings['total_time_logged_in'] += time() - $_SESSION['timeOnlineUpdated']; + updateMemberData($user_info['id'], array('last_login' => time(), 'member_ip' => $user_info['ip'], 'member_ip2' => $_SERVER['BAN_CHECK_IP'], 'total_time_logged_in' => $user_settings['total_time_logged_in'])); + + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2) + cache_put_data('user_settings-' . $user_info['id'], $user_settings, 60); + + $user_info['total_time_logged_in'] += time() - $_SESSION['timeOnlineUpdated']; + $_SESSION['timeOnlineUpdated'] = time(); + } +} + +// Make sure the browser doesn't come back and repost the form data. Should be used whenever anything is posted. +function redirectexit($setLocation = '', $refresh = false) +{ + global $scripturl, $context, $modSettings, $db_show_debug, $db_cache; + + // In case we have mail to send, better do that - as obExit doesn't always quite make it... + if (!empty($context['flush_mail'])) + AddMailQueue(true); + + $add = preg_match('~^(ftp|http)[s]?://~', $setLocation) == 0 && substr($setLocation, 0, 6) != 'about:'; + + if (WIRELESS) + { + // Add the scripturl on if needed. + if ($add) + $setLocation = $scripturl . '?' . $setLocation; + + $char = strpos($setLocation, '?') === false ? '?' : ';'; + + if (strpos($setLocation, '#') !== false) + $setLocation = strtr($setLocation, array('#' => $char . WIRELESS_PROTOCOL . '#')); + else + $setLocation .= $char . WIRELESS_PROTOCOL; + } + elseif ($add) + $setLocation = $scripturl . ($setLocation != '' ? '?' . $setLocation : ''); + + // Put the session ID in. + if (defined('SID') && SID != '') + $setLocation = preg_replace('/^' . preg_quote($scripturl, '/') . '(?!\?' . preg_quote(SID, '/') . ')\\??/', $scripturl . '?' . SID . ';', $setLocation); + // Keep that debug in their for template debugging! + elseif (isset($_GET['debug'])) + $setLocation = preg_replace('/^' . preg_quote($scripturl, '/') . '\\??/', $scripturl . '?debug;', $setLocation); + + if (!empty($modSettings['queryless_urls']) && (empty($context['server']['is_cgi']) || @ini_get('cgi.fix_pathinfo') == 1 || @get_cfg_var('cgi.fix_pathinfo') == 1) && (!empty($context['server']['is_apache']) || !empty($context['server']['is_lighttpd']))) + { + if (defined('SID') && SID != '') + $setLocation = preg_replace('/^' . preg_quote($scripturl, '/') . '\?(?:' . SID . '(?:;|&|&))((?:board|topic)=[^#]+?)(#[^"]*?)?$/e', "\$scripturl . '/' . strtr('\$1', '&;=', '//,') . '.html\$2?' . SID", $setLocation); + else + $setLocation = preg_replace('/^' . preg_quote($scripturl, '/') . '\?((?:board|topic)=[^#"]+?)(#[^"]*?)?$/e', "\$scripturl . '/' . strtr('\$1', '&;=', '//,') . '.html\$2'", $setLocation); + } + + // Maybe integrations want to change where we are heading? + call_integration_hook('integrate_redirect', array(&$setLocation, &$refresh)); + + // We send a Refresh header only in special cases because Location looks better. (and is quicker...) + if ($refresh && !WIRELESS) + header('Refresh: 0; URL=' . strtr($setLocation, array(' ' => '%20'))); + else + header('Location: ' . str_replace(' ', '%20', $setLocation)); + + // Debugging. + if (isset($db_show_debug) && $db_show_debug === true) + $_SESSION['debug_redirect'] = $db_cache; + + obExit(false); +} + +// Ends execution. Takes care of template loading and remembering the previous URL. +function obExit($header = null, $do_footer = null, $from_index = false, $from_fatal_error = false) +{ + global $context, $settings, $modSettings, $txt, $smcFunc; + static $header_done = false, $footer_done = false, $level = 0, $has_fatal_error = false; + + // Attempt to prevent a recursive loop. + ++$level; + if ($level > 1 && !$from_fatal_error && !$has_fatal_error) + exit; + if ($from_fatal_error) + $has_fatal_error = true; + + // Clear out the stat cache. + trackStats(); + + // If we have mail to send, send it. + if (!empty($context['flush_mail'])) + AddMailQueue(true); + + $do_header = $header === null ? !$header_done : $header; + if ($do_footer === null) + $do_footer = $do_header; + + // Has the template/header been done yet? + if ($do_header) + { + // Was the page title set last minute? Also update the HTML safe one. + if (!empty($context['page_title']) && empty($context['page_title_html_safe'])) + $context['page_title_html_safe'] = $smcFunc['htmlspecialchars'](un_htmlspecialchars($context['page_title'])); + + // Start up the session URL fixer. + ob_start('ob_sessrewrite'); + + if (!empty($settings['output_buffers']) && is_string($settings['output_buffers'])) + $buffers = explode(',', $settings['output_buffers']); + elseif (!empty($settings['output_buffers'])) + $buffers = $settings['output_buffers']; + else + $buffers = array(); + + if (isset($modSettings['integrate_buffer'])) + $buffers = array_merge(explode(',', $modSettings['integrate_buffer']), $buffers); + + if (!empty($buffers)) + foreach ($buffers as $function) + { + $function = trim($function); + $call = strpos($function, '::') !== false ? explode('::', $function) : $function; + + // Is it valid? + if (is_callable($call)) + ob_start($call); + } + + // Display the screen in the logical order. + template_header(); + $header_done = true; + } + if ($do_footer) + { + if (WIRELESS && !isset($context['sub_template'])) + fatal_lang_error('wireless_error_notyet', false); + + // Just show the footer, then. + loadSubTemplate(isset($context['sub_template']) ? $context['sub_template'] : 'main'); + + // Anything special to put out? + if (!empty($context['insert_after_template']) && !isset($_REQUEST['xml'])) + echo $context['insert_after_template']; + + // Just so we don't get caught in an endless loop of errors from the footer... + if (!$footer_done) + { + $footer_done = true; + template_footer(); + + // (since this is just debugging... it's okay that it's after .) + if (!isset($_REQUEST['xml'])) + db_debug_junk(); + } + } + + // Remember this URL in case someone doesn't like sending HTTP_REFERER. + if (strpos($_SERVER['REQUEST_URL'], 'action=dlattach') === false && strpos($_SERVER['REQUEST_URL'], 'action=viewsmfile') === false) + $_SESSION['old_url'] = $_SERVER['REQUEST_URL']; + + // For session check verfication.... don't switch browsers... + $_SESSION['USER_AGENT'] = $_SERVER['HTTP_USER_AGENT']; + + if (!empty($settings['strict_doctype'])) + { + // The theme author wants to use the STRICT doctype (only God knows why). + $temp = ob_get_contents(); + if (function_exists('ob_clean')) + ob_clean(); + else + { + ob_end_clean(); + ob_start('ob_sessrewrite'); + } + + echo strtr($temp, array( + 'var smf_iso_case_folding' => 'var target_blank = \'_blank\'; var smf_iso_case_folding', + 'target="_blank"' => 'onclick="this.target=target_blank"')); + } + + // Hand off the output to the portal, etc. we're integrated with. + call_integration_hook('integrate_exit', array($do_footer && !WIRELESS)); + + // Don't exit if we're coming from index.php; that will pass through normally. + if (!$from_index || WIRELESS) + exit; +} + +// Usage: logAction('remove', array('starter' => $id_member_started)); +function logAction($action, $extra = array(), $log_type = 'moderate') +{ + global $modSettings, $user_info, $smcFunc, $sourcedir; + + $log_types = array( + 'moderate' => 1, + 'user' => 2, + 'admin' => 3, + ); + + if (!is_array($extra)) + trigger_error('logAction(): data is not an array with action \'' . $action . '\'', E_USER_NOTICE); + + // Pull out the parts we want to store separately, but also make sure that the data is proper + if (isset($extra['topic'])) + { + if (!is_numeric($extra['topic'])) + trigger_error('logAction(): data\'s topic is not a number', E_USER_NOTICE); + $topic_id = empty($extra['topic']) ? '0' : (int)$extra['topic']; + unset($extra['topic']); + } + else + $topic_id = '0'; + + if (isset($extra['message'])) + { + if (!is_numeric($extra['message'])) + trigger_error('logAction(): data\'s message is not a number', E_USER_NOTICE); + $msg_id = empty($extra['message']) ? '0' : (int)$extra['message']; + unset($extra['message']); + } + else + $msg_id = '0'; + + // Is there an associated report on this? + if (in_array($action, array('move', 'remove', 'split', 'merge'))) + { + $request = $smcFunc['db_query']('', ' + SELECT id_report + FROM {db_prefix}log_reported + WHERE {raw:column_name} = {int:reported} + LIMIT 1', + array( + 'column_name' => !empty($msg_id) ? 'id_msg' : 'id_topic', + 'reported' => !empty($msg_id) ? $msg_id : $topic_id, + )); + + // Alright, if we get any result back, update open reports. + if ($smcFunc['db_num_rows']($request) > 0) + { + require_once($sourcedir . '/ModerationCenter.php'); + updateSettings(array('last_mod_report_action' => time())); + recountOpenReports(); + } + $smcFunc['db_free_result']($request); + } + + // No point in doing anything else, if the log isn't even enabled. + if (empty($modSettings['modlog_enabled']) || !isset($log_types[$log_type])) + return false; + + if (isset($extra['member']) && !is_numeric($extra['member'])) + trigger_error('logAction(): data\'s member is not a number', E_USER_NOTICE); + + if (isset($extra['board'])) + { + if (!is_numeric($extra['board'])) + trigger_error('logAction(): data\'s board is not a number', E_USER_NOTICE); + $board_id = empty($extra['board']) ? '0' : (int)$extra['board']; + unset($extra['board']); + } + else + $board_id = '0'; + + if (isset($extra['board_to'])) + { + if (!is_numeric($extra['board_to'])) + trigger_error('logAction(): data\'s board_to is not a number', E_USER_NOTICE); + if (empty($board_id)) + { + $board_id = empty($extra['board_to']) ? '0' : (int)$extra['board_to']; + unset($extra['board_to']); + } + } + + $smcFunc['db_insert']('', + '{db_prefix}log_actions', + array( + 'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', + 'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534', + ), + array( + time(), $log_types[$log_type], $user_info['id'], $user_info['ip'], $action, + $board_id, $topic_id, $msg_id, serialize($extra), + ), + array('id_action') + ); + + return $smcFunc['db_insert_id']('{db_prefix}log_actions', 'id_action'); +} + +// Track Statistics. +function trackStats($stats = array()) +{ + global $modSettings, $smcFunc; + static $cache_stats = array(); + + if (empty($modSettings['trackStats'])) + return false; + if (!empty($stats)) + return $cache_stats = array_merge($cache_stats, $stats); + elseif (empty($cache_stats)) + return false; + + $setStringUpdate = ''; + $insert_keys = array(); + $date = strftime('%Y-%m-%d', forum_time(false)); + $update_parameters = array( + 'current_date' => $date, + ); + foreach ($cache_stats as $field => $change) + { + $setStringUpdate .= ' + ' . $field . ' = ' . ($change === '+' ? $field . ' + 1' : '{int:' . $field . '}') . ','; + + if ($change === '+') + $cache_stats[$field] = 1; + else + $update_parameters[$field] = $change; + $insert_keys[$field] = 'int'; + } + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_activity + SET' . substr($setStringUpdate, 0, -1) . ' + WHERE date = {date:current_date}', + $update_parameters + ); + if ($smcFunc['db_affected_rows']() == 0) + { + $smcFunc['db_insert']('ignore', + '{db_prefix}log_activity', + array_merge($insert_keys, array('date' => 'date')), + array_merge($cache_stats, array($date)), + array('date') + ); + } + + // Don't do this again. + $cache_stats = array(); + + return true; +} + +// Make sure the user isn't posting over and over again. +function spamProtection($error_type) +{ + global $modSettings, $txt, $user_info, $smcFunc; + + // Certain types take less/more time. + $timeOverrides = array( + 'login' => 2, + 'register' => 2, + 'sendtopc' => $modSettings['spamWaitTime'] * 4, + 'sendmail' => $modSettings['spamWaitTime'] * 5, + 'reporttm' => $modSettings['spamWaitTime'] * 4, + 'search' => !empty($modSettings['search_floodcontrol_time']) ? $modSettings['search_floodcontrol_time'] : 1, + ); + + // Moderators are free... + if (!allowedTo('moderate_board')) + $timeLimit = isset($timeOverrides[$error_type]) ? $timeOverrides[$error_type] : $modSettings['spamWaitTime']; + else + $timeLimit = 2; + + // Delete old entries... + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}log_floodcontrol + WHERE log_time < {int:log_time} + AND log_type = {string:log_type}', + array( + 'log_time' => time() - $timeLimit, + 'log_type' => $error_type, + ) + ); + + // Add a new entry, deleting the old if necessary. + $smcFunc['db_insert']('replace', + '{db_prefix}log_floodcontrol', + array('ip' => 'string-16', 'log_time' => 'int', 'log_type' => 'string'), + array($user_info['ip'], time(), $error_type), + array('ip', 'log_type') + ); + + // If affected is 0 or 2, it was there already. + if ($smcFunc['db_affected_rows']() != 1) + { + // Spammer! You only have to wait a *few* seconds! + fatal_lang_error($error_type . 'WaitTime_broken', false, array($timeLimit)); + return true; + } + + // They haven't posted within the limit. + return false; +} + +// Get the size of a specified image with better error handling. +function url_image_size($url) +{ + global $sourcedir; + + // Make sure it is a proper URL. + $url = str_replace(' ', '%20', $url); + + // Can we pull this from the cache... please please? + if (($temp = cache_get_data('url_image_size-' . md5($url), 240)) !== null) + return $temp; + $t = microtime(); + + // Get the host to pester... + preg_match('~^\w+://(.+?)/(.*)$~', $url, $match); + + // Can't figure it out, just try the image size. + if ($url == '' || $url == 'http://' || $url == 'https://') + { + return false; + } + elseif (!isset($match[1])) + { + $size = @getimagesize($url); + } + else + { + // Try to connect to the server... give it half a second. + $temp = 0; + $fp = @fsockopen($match[1], 80, $temp, $temp, 0.5); + + // Successful? Continue... + if ($fp != false) + { + // Send the HEAD request (since we don't have to worry about chunked, HTTP/1.1 is fine here.) + fwrite($fp, 'HEAD /' . $match[2] . ' HTTP/1.1' . "\r\n" . 'Host: ' . $match[1] . "\r\n" . 'User-Agent: PHP/SMF' . "\r\n" . 'Connection: close' . "\r\n\r\n"); + + // Read in the HTTP/1.1 or whatever. + $test = substr(fgets($fp, 11), -1); + fclose($fp); + + // See if it returned a 404/403 or something. + if ($test < 4) + { + $size = @getimagesize($url); + + // This probably means allow_url_fopen is off, let's try GD. + if ($size === false && function_exists('imagecreatefromstring')) + { + include_once($sourcedir . '/Subs-Package.php'); + + // It's going to hate us for doing this, but another request... + $image = @imagecreatefromstring(fetch_web_data($url)); + if ($image !== false) + { + $size = array(imagesx($image), imagesy($image)); + imagedestroy($image); + } + } + } + } + } + + // If we didn't get it, we failed. + if (!isset($size)) + $size = false; + + // If this took a long time, we may never have to do it again, but then again we might... + if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $t)) > 0.8) + cache_put_data('url_image_size-' . md5($url), $size, 240); + + // Didn't work. + return $size; +} + +function determineTopicClass(&$topic_context) +{ + // Set topic class depending on locked status and number of replies. + if ($topic_context['is_very_hot']) + $topic_context['class'] = 'veryhot'; + elseif ($topic_context['is_hot']) + $topic_context['class'] = 'hot'; + else + $topic_context['class'] = 'normal'; + + $topic_context['class'] .= $topic_context['is_poll'] ? '_poll' : '_post'; + + if ($topic_context['is_locked']) + $topic_context['class'] .= '_locked'; + + if ($topic_context['is_sticky']) + $topic_context['class'] .= '_sticky'; + + // This is so old themes will still work. + $topic_context['extended_class'] = &$topic_context['class']; +} + +// Sets up the basic theme context stuff. +function setupThemeContext($forceload = false) +{ + global $modSettings, $user_info, $scripturl, $context, $settings, $options, $txt, $maintenance; + global $user_settings, $smcFunc; + static $loaded = false; + + // Under SSI this function can be called more then once. That can cause some problems. + // So only run the function once unless we are forced to run it again. + if ($loaded && !$forceload) + return; + + $loaded = true; + + $context['in_maintenance'] = !empty($maintenance); + $context['current_time'] = timeformat(time(), false); + $context['current_action'] = isset($_GET['action']) ? $_GET['action'] : ''; + $context['show_quick_login'] = !empty($modSettings['enableVBStyleLogin']) && $user_info['is_guest']; + + // Get some news... + $context['news_lines'] = explode("\n", str_replace("\r", '', trim(addslashes($modSettings['news'])))); + $context['fader_news_lines'] = array(); + for ($i = 0, $n = count($context['news_lines']); $i < $n; $i++) + { + if (trim($context['news_lines'][$i]) == '') + continue; + + // Clean it up for presentation ;). + $context['news_lines'][$i] = parse_bbc(stripslashes(trim($context['news_lines'][$i])), true, 'news' . $i); + + // Gotta be special for the javascript. + $context['fader_news_lines'][$i] = strtr(addslashes($context['news_lines'][$i]), array('/' => '\/', ' (isset($_SESSION['unread_messages']) ? $_SESSION['unread_messages'] : 0)) + $context['user']['popup_messages'] = true; + else + $context['user']['popup_messages'] = false; + $_SESSION['unread_messages'] = $user_info['unread_messages']; + + if (allowedTo('moderate_forum')) + $context['unapproved_members'] = (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($modSettings['approveAccountDeletion']) ? $modSettings['unapprovedMembers'] : 0; + $context['show_open_reports'] = empty($user_settings['mod_prefs']) || $user_settings['mod_prefs'][0] == 1; + + $context['user']['avatar'] = array(); + + // Figure out the avatar... uploaded? + if ($user_info['avatar']['url'] == '' && !empty($user_info['avatar']['id_attach'])) + $context['user']['avatar']['href'] = $user_info['avatar']['custom_dir'] ? $modSettings['custom_avatar_url'] . '/' . $user_info['avatar']['filename'] : $scripturl . '?action=dlattach;attach=' . $user_info['avatar']['id_attach'] . ';type=avatar'; + // Full URL? + elseif (substr($user_info['avatar']['url'], 0, 7) == 'http://') + { + $context['user']['avatar']['href'] = $user_info['avatar']['url']; + + if ($modSettings['avatar_action_too_large'] == 'option_html_resize' || $modSettings['avatar_action_too_large'] == 'option_js_resize') + { + if (!empty($modSettings['avatar_max_width_external'])) + $context['user']['avatar']['width'] = $modSettings['avatar_max_width_external']; + if (!empty($modSettings['avatar_max_height_external'])) + $context['user']['avatar']['height'] = $modSettings['avatar_max_height_external']; + } + } + // Otherwise we assume it's server stored? + elseif ($user_info['avatar']['url'] != '') + $context['user']['avatar']['href'] = $modSettings['avatar_url'] . '/' . htmlspecialchars($user_info['avatar']['url']); + + if (!empty($context['user']['avatar'])) + $context['user']['avatar']['image'] = ''; + + // Figure out how long they've been logged in. + $context['user']['total_time_logged_in'] = array( + 'days' => floor($user_info['total_time_logged_in'] / 86400), + 'hours' => floor(($user_info['total_time_logged_in'] % 86400) / 3600), + 'minutes' => floor(($user_info['total_time_logged_in'] % 3600) / 60) + ); + } + else + { + $context['user']['messages'] = 0; + $context['user']['unread_messages'] = 0; + $context['user']['avatar'] = array(); + $context['user']['total_time_logged_in'] = array('days' => 0, 'hours' => 0, 'minutes' => 0); + $context['user']['popup_messages'] = false; + + if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1) + $txt['welcome_guest'] .= $txt['welcome_guest_activate']; + + // If we've upgraded recently, go easy on the passwords. + if (!empty($modSettings['disableHashTime']) && ($modSettings['disableHashTime'] == 1 || time() < $modSettings['disableHashTime'])) + $context['disable_login_hashing'] = true; + elseif ($context['browser']['is_ie5'] || $context['browser']['is_ie5.5']) + $context['disable_login_hashing'] = true; + } + + // Setup the main menu items. + setupMenuContext(); + + if (empty($settings['theme_version'])) + $context['show_vBlogin'] = $context['show_quick_login']; + + // This is here because old index templates might still use it. + $context['show_news'] = !empty($settings['enable_news']); + + // This is done to allow theme authors to customize it as they want. + $context['show_pm_popup'] = $context['user']['popup_messages'] && !empty($options['popup_messages']) && (!isset($_REQUEST['action']) || $_REQUEST['action'] != 'pm'); + + // Resize avatars the fancy, but non-GD requiring way. + if ($modSettings['avatar_action_too_large'] == 'option_js_resize' && (!empty($modSettings['avatar_max_width_external']) || !empty($modSettings['avatar_max_height_external']))) + { + $context['html_headers'] .= ' + '; + } + + // This looks weird, but it's because BoardIndex.php references the variable. + $context['common_stats']['latest_member'] = array( + 'id' => $modSettings['latestMember'], + 'name' => $modSettings['latestRealName'], + 'href' => $scripturl . '?action=profile;u=' . $modSettings['latestMember'], + 'link' => '' . $modSettings['latestRealName'] . '', + ); + $context['common_stats'] = array( + 'total_posts' => comma_format($modSettings['totalMessages']), + 'total_topics' => comma_format($modSettings['totalTopics']), + 'total_members' => comma_format($modSettings['totalMembers']), + 'latest_member' => $context['common_stats']['latest_member'], + ); + + if (empty($settings['theme_version'])) + $context['html_headers'] .= ' + '; + + if (!isset($context['page_title'])) + $context['page_title'] = ''; + + // Set some specific vars. + $context['page_title_html_safe'] = $smcFunc['htmlspecialchars'](un_htmlspecialchars($context['page_title'])); + $context['meta_keywords'] = !empty($modSettings['meta_keywords']) ? $smcFunc['htmlspecialchars']($modSettings['meta_keywords']) : ''; +} + +// This is the only template included in the sources... +function template_rawdata() +{ + global $context; + + echo $context['raw_data']; +} + +function template_header() +{ + global $txt, $modSettings, $context, $settings, $user_info, $boarddir, $cachedir; + + setupThemeContext(); + + // Print stuff to prevent caching of pages (except on attachment errors, etc.) + if (empty($context['no_last_modified'])) + { + header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + + // Are we debugging the template/html content? + if (!isset($_REQUEST['xml']) && isset($_GET['debug']) && !$context['browser']['is_ie'] && !WIRELESS) + header('Content-Type: application/xhtml+xml'); + elseif (!isset($_REQUEST['xml']) && !WIRELESS) + header('Content-Type: text/html; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); + } + + header('Content-Type: text/' . (isset($_REQUEST['xml']) ? 'xml' : 'html') . '; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); + + $checked_securityFiles = false; + $showed_banned = false; + foreach ($context['template_layers'] as $layer) + { + loadSubTemplate($layer . '_above', true); + + // May seem contrived, but this is done in case the body and main layer aren't there... + if (in_array($layer, array('body', 'main')) && allowedTo('admin_forum') && !$user_info['is_guest'] && !$checked_securityFiles) + { + $checked_securityFiles = true; + $securityFiles = array('install.php', 'webinstall.php', 'upgrade.php', 'convert.php', 'repair_paths.php', 'repair_settings.php', 'Settings.php~', 'Settings_bak.php~'); + foreach ($securityFiles as $i => $securityFile) + { + if (!file_exists($boarddir . '/' . $securityFile)) + unset($securityFiles[$i]); + } + + if (!empty($securityFiles) || (!empty($modSettings['cache_enable']) && !is_writable($cachedir))) + { + echo ' +
    +

    !!

    +

    ', empty($securityFiles) ? $txt['cache_writable_head'] : $txt['security_risk'], '

    +

    '; + + foreach ($securityFiles as $securityFile) + { + echo ' + ', $txt['not_removed'], '', $securityFile, '!
    '; + + if ($securityFile == 'Settings.php~' || $securityFile == 'Settings_bak.php~') + echo ' + ', sprintf($txt['not_removed_extra'], $securityFile, substr($securityFile, 0, -1)), '
    '; + } + + if (!empty($modSettings['cache_enable']) && !is_writable($cachedir)) + echo ' + ', $txt['cache_writable'], '
    '; + + echo ' +

    +
    '; + } + } + // If the user is banned from posting inform them of it. + elseif (in_array($layer, array('main', 'body')) && isset($_SESSION['ban']['cannot_post']) && !$showed_banned) + { + $showed_banned = true; + echo ' +
    + ', sprintf($txt['you_are_post_banned'], $user_info['is_guest'] ? $txt['guest_title'] : $user_info['name']); + + if (!empty($_SESSION['ban']['cannot_post']['reason'])) + echo ' +
    ', $_SESSION['ban']['cannot_post']['reason'], '
    '; + + if (!empty($_SESSION['ban']['expire_time'])) + echo ' +
    ', sprintf($txt['your_ban_expires'], timeformat($_SESSION['ban']['expire_time'], false)), '
    '; + else + echo ' +
    ', $txt['your_ban_expires_never'], '
    '; + + echo ' +
    '; + } + } + + if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'defaults' && isset($settings['default_template'])) + { + $settings['theme_url'] = $settings['default_theme_url']; + $settings['images_url'] = $settings['default_images_url']; + $settings['theme_dir'] = $settings['default_theme_dir']; + } +} + +// Show the copyright... +function theme_copyright($get_it = false) +{ + global $forum_copyright, $context, $boardurl, $forum_version, $txt, $modSettings; + + // Don't display copyright for things like SSI. + if (!isset($forum_version)) + return; + + // Put in the version... + $forum_copyright = sprintf($forum_copyright, $forum_version); + + echo ' + ' . $forum_copyright . ' + '; +} + +function template_footer() +{ + global $context, $settings, $modSettings, $time_start, $db_count; + + // Show the load time? (only makes sense for the footer.) + $context['show_load_time'] = !empty($modSettings['timeLoadPageEnable']); + $context['load_time'] = round(array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)), 3); + $context['load_queries'] = $db_count; + + if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'defaults' && isset($settings['default_template'])) + { + $settings['theme_url'] = $settings['actual_theme_url']; + $settings['images_url'] = $settings['actual_images_url']; + $settings['theme_dir'] = $settings['actual_theme_dir']; + } + + foreach (array_reverse($context['template_layers']) as $layer) + loadSubTemplate($layer . '_below', true); + +} + +// Debugging. +function db_debug_junk() +{ + global $context, $scripturl, $boarddir, $modSettings, $boarddir; + global $db_cache, $db_count, $db_show_debug, $cache_count, $cache_hits, $txt; + + // Add to Settings.php if you want to show the debugging information. + if (!isset($db_show_debug) || $db_show_debug !== true || (isset($_GET['action']) && $_GET['action'] == 'viewquery') || WIRELESS) + return; + + if (empty($_SESSION['view_queries'])) + $_SESSION['view_queries'] = 0; + if (empty($context['debug']['language_files'])) + $context['debug']['language_files'] = array(); + if (empty($context['debug']['sheets'])) + $context['debug']['sheets'] = array(); + + $files = get_included_files(); + $total_size = 0; + for ($i = 0, $n = count($files); $i < $n; $i++) + { + if (file_exists($files[$i])) + $total_size += filesize($files[$i]); + $files[$i] = strtr($files[$i], array($boarddir => '.')); + } + + $warnings = 0; + if (!empty($db_cache)) + { + foreach ($db_cache as $q => $qq) + { + if (!empty($qq['w'])) + $warnings += count($qq['w']); + } + + $_SESSION['debug'] = &$db_cache; + } + + // Gotta have valid HTML ;). + $temp = ob_get_contents(); + if (function_exists('ob_clean')) + ob_clean(); + else + { + ob_end_clean(); + ob_start('ob_sessrewrite'); + } + + echo preg_replace('~\s*~', '', $temp), ' +
    + ', $txt['debug_templates'], count($context['debug']['templates']), ': ', implode(', ', $context['debug']['templates']), '.
    + ', $txt['debug_subtemplates'], count($context['debug']['sub_templates']), ': ', implode(', ', $context['debug']['sub_templates']), '.
    + ', $txt['debug_language_files'], count($context['debug']['language_files']), ': ', implode(', ', $context['debug']['language_files']), '.
    + ', $txt['debug_stylesheets'], count($context['debug']['sheets']), ': ', implode(', ', $context['debug']['sheets']), '.
    + ', $txt['debug_files_included'], count($files), ' - ', round($total_size / 1024), $txt['debug_kb'], ' (', $txt['debug_show'], ')
    '; + + if (!empty($modSettings['cache_enable']) && !empty($cache_hits)) + { + $entries = array(); + $total_t = 0; + $total_s = 0; + foreach ($cache_hits as $cache_hit) + { + $entries[] = $cache_hit['d'] . ' ' . $cache_hit['k'] . ': ' . sprintf($txt['debug_cache_seconds_bytes'], comma_format($cache_hit['t'], 5), $cache_hit['s']); + $total_t += $cache_hit['t']; + $total_s += $cache_hit['s']; + } + + echo ' + ', $txt['debug_cache_hits'], $cache_count, ': ', sprintf($txt['debug_cache_seconds_bytes_total'], comma_format($total_t, 5), comma_format($total_s)), ' (', $txt['debug_show'], ')
    '; + } + + echo ' + ', $warnings == 0 ? sprintf($txt['debug_queries_used'], (int) $db_count) : sprintf($txt['debug_queries_used_and_warnings'], (int) $db_count, $warnings), '
    +
    '; + + if ($_SESSION['view_queries'] == 1 && !empty($db_cache)) + foreach ($db_cache as $q => $qq) + { + $is_select = substr(trim($qq['q']), 0, 6) == 'SELECT' || preg_match('~^INSERT(?: IGNORE)? INTO \w+(?:\s+\([^)]+\))?\s+SELECT .+$~s', trim($qq['q'])) != 0; + // Temporary tables created in earlier queries are not explainable. + if ($is_select) + { + foreach (array('log_topics_unread', 'topics_posted_in', 'tmp_log_search_topics', 'tmp_log_search_messages') as $tmp) + if (strpos(trim($qq['q']), $tmp) !== false) + { + $is_select = false; + break; + } + } + // But actual creation of the temporary tables are. + elseif (preg_match('~^CREATE TEMPORARY TABLE .+?SELECT .+$~s', trim($qq['q'])) != 0) + $is_select = true; + + // Make the filenames look a bit better. + if (isset($qq['f'])) + $qq['f'] = preg_replace('~^' . preg_quote($boarddir, '~') . '~', '...', $qq['f']); + + echo ' + ', $is_select ? '' : '', nl2br(str_replace("\t", '   ', htmlspecialchars(ltrim($qq['q'], "\n\r")))) . ($is_select ? '' : '
    ') . '
    +    '; + if (!empty($qq['f']) && !empty($qq['l'])) + echo sprintf($txt['debug_query_in_line'], $qq['f'], $qq['l']); + + if (isset($qq['s'], $qq['t']) && isset($txt['debug_query_which_took_at'])) + echo sprintf($txt['debug_query_which_took_at'], round($qq['t'], 8), round($qq['s'], 8)) . '
    '; + elseif (isset($qq['t'])) + echo sprintf($txt['debug_query_which_took'], round($qq['t'], 8)) . '
    '; + echo ' +
    '; + } + + echo ' + ', $txt['debug_' . (empty($_SESSION['view_queries']) ? 'show' : 'hide') . '_queries'], ' +
    '; +} + +// Get an attachment's encrypted filename. If $new is true, won't check for file existence. +function getAttachmentFilename($filename, $attachment_id, $dir = null, $new = false, $file_hash = '') +{ + global $modSettings, $smcFunc; + + // Just make up a nice hash... + if ($new) + return sha1(md5($filename . time()) . mt_rand()); + + // Grab the file hash if it wasn't added. + if ($file_hash === '') + { + $request = $smcFunc['db_query']('', ' + SELECT file_hash + FROM {db_prefix}attachments + WHERE id_attach = {int:id_attach}', + array( + 'id_attach' => $attachment_id, + )); + + if ($smcFunc['db_num_rows']($request) === 0) + return false; + + list ($file_hash) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // In case of files from the old system, do a legacy call. + if (empty($file_hash)) + return getLegacyAttachmentFilename($filename, $attachment_id, $dir, $new); + + // Are we using multiple directories? + if (!empty($modSettings['currentAttachmentUploadDir'])) + { + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + $path = $modSettings['attachmentUploadDir'][$dir]; + } + else + $path = $modSettings['attachmentUploadDir']; + + return $path . '/' . $attachment_id . '_' . $file_hash; +} + +// Older attachments may still use this function. +function getLegacyAttachmentFilename($filename, $attachment_id, $dir = null, $new = false) +{ + global $modSettings, $db_character_set; + + $clean_name = $filename; + // Remove international characters (windows-1252) + // These lines should never be needed again. Still, behave. + if (empty($db_character_set) || $db_character_set != 'utf8') + { + $clean_name = strtr($filename, + "\x8a\x8e\x9a\x9e\x9f\xc0\xc1\xc2\xc3\xc4\xc5\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xe0\xe1\xe2\xe3\xe4\xe5\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xff", + 'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'); + $clean_name = strtr($clean_name, array("\xde" => 'TH', "\xfe" => + 'th', "\xd0" => 'DH', "\xf0" => 'dh', "\xdf" => 'ss', "\x8c" => 'OE', + "\x9c" => 'oe', "\c6" => 'AE', "\xe6" => 'ae', "\xb5" => 'u')); + } + // Sorry, no spaces, dots, or anything else but letters allowed. + $clean_name = preg_replace(array('/\s/', '/[^\w_\.\-]/'), array('_', ''), $clean_name); + + $enc_name = $attachment_id . '_' . strtr($clean_name, '.', '_') . md5($clean_name); + $clean_name = preg_replace('~\.[\.]+~', '.', $clean_name); + + if ($attachment_id == false || ($new && empty($modSettings['attachmentEncryptFilenames']))) + return $clean_name; + elseif ($new) + return $enc_name; + + // Are we using multiple directories? + if (!empty($modSettings['currentAttachmentUploadDir'])) + { + if (!is_array($modSettings['attachmentUploadDir'])) + $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); + $path = $modSettings['attachmentUploadDir'][$dir]; + } + else + $path = $modSettings['attachmentUploadDir']; + + if (file_exists($path . '/' . $enc_name)) + $filename = $path . '/' . $enc_name; + else + $filename = $path . '/' . $clean_name; + + return $filename; +} + +// Convert a single IP to a ranged IP. +function ip2range($fullip) +{ + // Pretend that 'unknown' is 255.255.255.255. (since that can't be an IP anyway.) + if ($fullip == 'unknown') + $fullip = '255.255.255.255'; + + $ip_parts = explode('.', $fullip); + $ip_array = array(); + + if (count($ip_parts) != 4) + return array(); + + for ($i = 0; $i < 4; $i++) + { + if ($ip_parts[$i] == '*') + $ip_array[$i] = array('low' => '0', 'high' => '255'); + elseif (preg_match('/^(\d{1,3})\-(\d{1,3})$/', $ip_parts[$i], $range) == 1) + $ip_array[$i] = array('low' => $range[1], 'high' => $range[2]); + elseif (is_numeric($ip_parts[$i])) + $ip_array[$i] = array('low' => $ip_parts[$i], 'high' => $ip_parts[$i]); + } + + return $ip_array; +} + +// Lookup an IP; try shell_exec first because we can do a timeout on it. +function host_from_ip($ip) +{ + global $modSettings; + + if (($host = cache_get_data('hostlookup-' . $ip, 600)) !== null) + return $host; + $t = microtime(); + + // If we can't access nslookup/host, PHP 4.1.x might just crash. + if (@version_compare(PHP_VERSION, '4.2.0') == -1) + $host = false; + + // Try the Linux host command, perhaps? + if (!isset($host) && (strpos(strtolower(PHP_OS), 'win') === false || strpos(strtolower(PHP_OS), 'darwin') !== false) && mt_rand(0, 1) == 1) + { + if (!isset($modSettings['host_to_dis'])) + $test = @shell_exec('host -W 1 ' . @escapeshellarg($ip)); + else + $test = @shell_exec('host ' . @escapeshellarg($ip)); + + // Did host say it didn't find anything? + if (strpos($test, 'not found') !== false) + $host = ''; + // Invalid server option? + elseif ((strpos($test, 'invalid option') || strpos($test, 'Invalid query name 1')) && !isset($modSettings['host_to_dis'])) + updateSettings(array('host_to_dis' => 1)); + // Maybe it found something, after all? + elseif (preg_match('~\s([^\s]+?)\.\s~', $test, $match) == 1) + $host = $match[1]; + } + + // This is nslookup; usually only Windows, but possibly some Unix? + if (!isset($host) && strpos(strtolower(PHP_OS), 'win') !== false && strpos(strtolower(PHP_OS), 'darwin') === false && mt_rand(0, 1) == 1) + { + $test = @shell_exec('nslookup -timeout=1 ' . @escapeshellarg($ip)); + if (strpos($test, 'Non-existent domain') !== false) + $host = ''; + elseif (preg_match('~Name:\s+([^\s]+)~', $test, $match) == 1) + $host = $match[1]; + } + + // This is the last try :/. + if (!isset($host) || $host === false) + $host = @gethostbyaddr($ip); + + // It took a long time, so let's cache it! + if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $t)) > 0.5) + cache_put_data('hostlookup-' . $ip, $host, 600); + + return $host; +} + +// Chops a string into words and prepares them to be inserted into (or searched from) the database. +function text2words($text, $max_chars = 20, $encrypt = false) +{ + global $smcFunc, $context; + + // Step 1: Remove entities/things we don't consider words: + $words = preg_replace('~(?:[\x0B\0' . ($context['utf8'] ? ($context['server']['complex_preg_chars'] ? '\x{A0}' : "\xC2\xA0") : '\xA0') . '\t\r\s\n(){}\\[\\]<>!@$%^*.,:+=`\~\?/\\\\]+|&(?:amp|lt|gt|quot);)+~' . ($context['utf8'] ? 'u' : ''), ' ', strtr($text, array('
    ' => ' '))); + + // Step 2: Entities we left to letters, where applicable, lowercase. + $words = un_htmlspecialchars($smcFunc['strtolower']($words)); + + // Step 3: Ready to split apart and index! + $words = explode(' ', $words); + + if ($encrypt) + { + $possible_chars = array_flip(array_merge(range(46, 57), range(65, 90), range(97, 122))); + $returned_ints = array(); + foreach ($words as $word) + { + if (($word = trim($word, '-_\'')) !== '') + { + $encrypted = substr(crypt($word, 'uk'), 2, $max_chars); + $total = 0; + for ($i = 0; $i < $max_chars; $i++) + $total += $possible_chars[ord($encrypted{$i})] * pow(63, $i); + $returned_ints[] = $max_chars == 4 ? min($total, 16777215) : $total; + } + } + return array_unique($returned_ints); + } + else + { + // Trim characters before and after and add slashes for database insertion. + $returned_words = array(); + foreach ($words as $word) + if (($word = trim($word, '-_\'')) !== '') + $returned_words[] = $max_chars === null ? $word : substr($word, 0, $max_chars); + + // Filter out all words that occur more than once. + return array_unique($returned_words); + } +} + +// Creates an image/text button +function create_button($name, $alt, $label = '', $custom = '', $force_use = false) +{ + global $settings, $txt, $context; + + // Does the current loaded theme have this and we are not forcing the usage of this function? + if (function_exists('template_create_button') && !$force_use) + return template_create_button($name, $alt, $label = '', $custom = ''); + + if (!$settings['use_image_buttons']) + return $txt[$alt]; + elseif (!empty($settings['use_buttons'])) + return '' . $txt[$alt] . '' . ($label != '' ? '' . $txt[$label] . '' : ''); + else + return '' . $txt[$alt] . ''; +} + +// Empty out the cache folder. +function clean_cache($type = '') +{ + global $cachedir, $sourcedir; + + // No directory = no game. + if (!is_dir($cachedir)) + return; + + // Remove the files in SMF's own disk cache, if any + $dh = opendir($cachedir); + while ($file = readdir($dh)) + { + if ($file != '.' && $file != '..' && $file != 'index.php' && $file != '.htaccess' && (!$type || substr($file, 0, strlen($type)) == $type)) + @unlink($cachedir . '/' . $file); + } + closedir($dh); + + // Invalidate cache, to be sure! + // ... as long as Load.php can be modified, anyway. + @touch($sourcedir . '/' . 'Load.php'); + clearstatcache(); +} + +// Load classes that are both (E_STRICT) PHP 4 and PHP 5 compatible. +function loadClassFile($filename) +{ + global $sourcedir; + static $files_included = array(); + + if (!file_exists($sourcedir . '/' . $filename)) + fatal_lang_error('error_bad_file', 'general', array($sourcedir . '/' . $filename)); + + // Using a version below PHP 5.0? Do a compatibility conversion. + if (@version_compare(PHP_VERSION, '5.0.0') != 1) + { + // Check if it was included before. + if (in_array($filename, $files_included)) + return; + + // Make sure we don't include it again. + $files_included[] = $filename; + + // Do some replacements to make it PHP 4 compatible. + eval('?' . '>' . preg_replace(array( + '~class\s+([\w-_]+)([^}]+)function\s+__construct\s*\(~', + '~([\s\t]+)public\s+\$~', + '~([\s\t]+)private\s+\$~', + '~([\s\t]+)protected\s+\$~', + '~([\s\t]+)public\s+function\s+~', + '~([\s\t]+)private\s+function\s+~', + '~([\s\t]+)protected\s+function\s+~', + ), array( + 'class $1$2function $1(', + '$1var $', + '$1var $', + '$1var $', + '$1function ', + '$1function ', + '$1function ', + ), rtrim(file_get_contents($sourcedir . '/' . $filename)))); + } + else + require_once($sourcedir . '/' . $filename); +} + +function setupMenuContext() +{ + global $context, $modSettings, $user_info, $txt, $scripturl; + + // Set up the menu privileges. + $context['allow_search'] = allowedTo('search_posts'); + $context['allow_admin'] = allowedTo(array('admin_forum', 'manage_boards', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'manage_attachments', 'manage_smileys')); + $context['allow_edit_profile'] = !$user_info['is_guest'] && allowedTo(array('profile_view_own', 'profile_view_any', 'profile_identity_own', 'profile_identity_any', 'profile_extra_own', 'profile_extra_any', 'profile_remove_own', 'profile_remove_any', 'moderate_forum', 'manage_membergroups', 'profile_title_own', 'profile_title_any')); + $context['allow_memberlist'] = allowedTo('view_mlist'); + $context['allow_calendar'] = allowedTo('calendar_view') && !empty($modSettings['cal_enabled']); + $context['allow_moderation_center'] = $context['user']['can_mod']; + $context['allow_pm'] = allowedTo('pm_read'); + + $cacheTime = $modSettings['lastActive'] * 60; + + // All the buttons we can possible want and then some, try pulling the final list of buttons from cache first. + if (($menu_buttons = cache_get_data('menu_buttons-' . implode('_', $user_info['groups']) . '-' . $user_info['language'], $cacheTime)) === null || time() - $cacheTime <= $modSettings['settings_updated']) + { + $buttons = array( + 'home' => array( + 'title' => $txt['home'], + 'href' => $scripturl, + 'show' => true, + 'sub_buttons' => array( + ), + 'is_last' => $context['right_to_left'], + ), + 'help' => array( + 'title' => $txt['help'], + 'href' => $scripturl . '?action=help', + 'show' => true, + 'sub_buttons' => array( + ), + ), + 'search' => array( + 'title' => $txt['search'], + 'href' => $scripturl . '?action=search', + 'show' => $context['allow_search'], + 'sub_buttons' => array( + ), + ), + 'admin' => array( + 'title' => $txt['admin'], + 'href' => $scripturl . '?action=admin', + 'show' => $context['allow_admin'], + 'sub_buttons' => array( + 'featuresettings' => array( + 'title' => $txt['modSettings_title'], + 'href' => $scripturl . '?action=admin;area=featuresettings', + 'show' => allowedTo('admin_forum'), + ), + 'packages' => array( + 'title' => $txt['package'], + 'href' => $scripturl . '?action=admin;area=packages', + 'show' => allowedTo('admin_forum'), + ), + 'errorlog' => array( + 'title' => $txt['errlog'], + 'href' => $scripturl . '?action=admin;area=logs;sa=errorlog;desc', + 'show' => allowedTo('admin_forum') && !empty($modSettings['enableErrorLogging']), + ), + 'permissions' => array( + 'title' => $txt['edit_permissions'], + 'href' => $scripturl . '?action=admin;area=permissions', + 'show' => allowedTo('manage_permissions'), + 'is_last' => true, + ), + ), + ), + 'moderate' => array( + 'title' => $txt['moderate'], + 'href' => $scripturl . '?action=moderate', + 'show' => $context['allow_moderation_center'], + 'sub_buttons' => array( + 'modlog' => array( + 'title' => $txt['modlog_view'], + 'href' => $scripturl . '?action=moderate;area=modlog', + 'show' => !empty($modSettings['modlog_enabled']) && !empty($user_info['mod_cache']) && $user_info['mod_cache']['bq'] != '0=1', + ), + 'poststopics' => array( + 'title' => $txt['mc_unapproved_poststopics'], + 'href' => $scripturl . '?action=moderate;area=postmod;sa=posts', + 'show' => $modSettings['postmod_active'] && !empty($user_info['mod_cache']['ap']), + ), + 'attachments' => array( + 'title' => $txt['mc_unapproved_attachments'], + 'href' => $scripturl . '?action=moderate;area=attachmod;sa=attachments', + 'show' => $modSettings['postmod_active'] && !empty($user_info['mod_cache']['ap']), + ), + 'reports' => array( + 'title' => $txt['mc_reported_posts'], + 'href' => $scripturl . '?action=moderate;area=reports', + 'show' => !empty($user_info['mod_cache']) && $user_info['mod_cache']['bq'] != '0=1', + 'is_last' => true, + ), + ), + ), + 'profile' => array( + 'title' => $txt['profile'], + 'href' => $scripturl . '?action=profile', + 'show' => $context['allow_edit_profile'], + 'sub_buttons' => array( + 'summary' => array( + 'title' => $txt['summary'], + 'href' => $scripturl . '?action=profile', + 'show' => true, + ), + 'account' => array( + 'title' => $txt['account'], + 'href' => $scripturl . '?action=profile;area=account', + 'show' => allowedTo(array('profile_identity_any', 'profile_identity_own', 'manage_membergroups')), + ), + 'profile' => array( + 'title' => $txt['forumprofile'], + 'href' => $scripturl . '?action=profile;area=forumprofile', + 'show' => allowedTo(array('profile_extra_any', 'profile_extra_own')), + 'is_last' => true, + ), + ), + ), + 'pm' => array( + 'title' => $txt['pm_short'], + 'href' => $scripturl . '?action=pm', + 'show' => $context['allow_pm'], + 'sub_buttons' => array( + 'pm_read' => array( + 'title' => $txt['pm_menu_read'], + 'href' => $scripturl . '?action=pm', + 'show' => allowedTo('pm_read'), + ), + 'pm_send' => array( + 'title' => $txt['pm_menu_send'], + 'href' => $scripturl . '?action=pm;sa=send', + 'show' => allowedTo('pm_send'), + 'is_last' => true, + ), + ), + ), + 'calendar' => array( + 'title' => $txt['calendar'], + 'href' => $scripturl . '?action=calendar', + 'show' => $context['allow_calendar'], + 'sub_buttons' => array( + 'view' => array( + 'title' => $txt['calendar_menu'], + 'href' => $scripturl . '?action=calendar', + 'show' => allowedTo('calendar_post'), + ), + 'post' => array( + 'title' => $txt['calendar_post_event'], + 'href' => $scripturl . '?action=calendar;sa=post', + 'show' => allowedTo('calendar_post'), + 'is_last' => true, + ), + ), + ), + 'mlist' => array( + 'title' => $txt['members_title'], + 'href' => $scripturl . '?action=mlist', + 'show' => $context['allow_memberlist'], + 'sub_buttons' => array( + 'mlist_view' => array( + 'title' => $txt['mlist_menu_view'], + 'href' => $scripturl . '?action=mlist', + 'show' => true, + ), + 'mlist_search' => array( + 'title' => $txt['mlist_search'], + 'href' => $scripturl . '?action=mlist;sa=search', + 'show' => true, + 'is_last' => true, + ), + ), + ), + 'login' => array( + 'title' => $txt['login'], + 'href' => $scripturl . '?action=login', + 'show' => $user_info['is_guest'], + 'sub_buttons' => array( + ), + ), + 'register' => array( + 'title' => $txt['register'], + 'href' => $scripturl . '?action=register', + 'show' => $user_info['is_guest'], + 'sub_buttons' => array( + ), + 'is_last' => !$context['right_to_left'], + ), + 'logout' => array( + 'title' => $txt['logout'], + 'href' => $scripturl . '?action=logout;%1$s=%2$s', + 'show' => !$user_info['is_guest'], + 'sub_buttons' => array( + ), + 'is_last' => !$context['right_to_left'], + ), + ); + + // Allow editing menu buttons easily. + call_integration_hook('integrate_menu_buttons', array(&$buttons)); + + // Now we put the buttons in the context so the theme can use them. + $menu_buttons = array(); + foreach ($buttons as $act => $button) + if (!empty($button['show'])) + { + $button['active_button'] = false; + + // Make sure the last button truely is the last button. + if (!empty($button['is_last'])) + { + if (isset($last_button)) + unset($menu_buttons[$last_button]['is_last']); + $last_button = $act; + } + + // Go through the sub buttons if there are any. + if (!empty($button['sub_buttons'])) + foreach ($button['sub_buttons'] as $key => $subbutton) + { + if (empty($subbutton['show'])) + unset($button['sub_buttons'][$key]); + + // 2nd level sub buttons next... + if (!empty($subbutton['sub_buttons'])) + { + foreach ($subbutton['sub_buttons'] as $key2 => $sub_button2) + { + if (empty($sub_button2['show'])) + unset($button['sub_buttons'][$key]['sub_buttons'][$key2]); + } + } + } + + $menu_buttons[$act] = $button; + } + + if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2) + cache_put_data('menu_buttons-' . implode('_', $user_info['groups']) . '-' . $user_info['language'], $menu_buttons, $cacheTime); + } + + $context['menu_buttons'] = $menu_buttons; + + // Logging out requires the session id in the url. + if (isset($context['menu_buttons']['logout'])) + $context['menu_buttons']['logout']['href'] = sprintf($context['menu_buttons']['logout']['href'], $context['session_var'], $context['session_id']); + + // Figure out which action we are doing so we can set the active tab. + // Default to home. + $current_action = 'home'; + + if (isset($context['menu_buttons'][$context['current_action']])) + $current_action = $context['current_action']; + elseif ($context['current_action'] == 'search2') + $current_action = 'search'; + elseif ($context['current_action'] == 'theme') + $current_action = isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'pick' ? 'profile' : 'admin'; + elseif ($context['current_action'] == 'register2') + $current_action = 'register'; + elseif ($context['current_action'] == 'login2' || ($user_info['is_guest'] && $context['current_action'] == 'reminder')) + $current_action = 'login'; + elseif ($context['current_action'] == 'groups' && $context['allow_moderation_center']) + $current_action = 'moderate'; + + $context['menu_buttons'][$current_action]['active_button'] = true; + + if (!$user_info['is_guest'] && $context['user']['unread_messages'] > 0 && isset($context['menu_buttons']['pm'])) + { + $context['menu_buttons']['pm']['alttitle'] = $context['menu_buttons']['pm']['title'] . ' [' . $context['user']['unread_messages'] . ']'; + $context['menu_buttons']['pm']['title'] .= ' [' . $context['user']['unread_messages'] . ']'; + } +} + +// Generate a random seed and ensure it's stored in settings. +function smf_seed_generator() +{ + global $modSettings; + + // Never existed? + if (empty($modSettings['rand_seed'])) + { + $modSettings['rand_seed'] = microtime() * 1000000; + updateSettings(array('rand_seed' => $modSettings['rand_seed'])); + } + + if (@version_compare(PHP_VERSION, '4.2.0') == -1) + { + $seed = ($modSettings['rand_seed'] + ((double) microtime() * 1000003)) & 0x7fffffff; + mt_srand($seed); + } + + // Change the seed. + updateSettings(array('rand_seed' => mt_rand())); +} + +// Process functions of an integration hook. +function call_integration_hook($hook, $parameters = array()) +{ + global $modSettings; + + $results = array(); + if (empty($modSettings[$hook])) + return $results; + + $functions = explode(',', $modSettings[$hook]); + + // Loop through each function. + foreach ($functions as $function) + { + $function = trim($function); + $call = strpos($function, '::') !== false ? explode('::', $function) : $function; + + // Is it valid? + if (is_callable($call)) + $results[$function] = call_user_func_array($call, $parameters); + } + + return $results; +} + +// Add a function for integration hook. +function add_integration_function($hook, $function, $permanent = true) +{ + global $smcFunc, $modSettings; + + // Is it going to be permanent? + if ($permanent) + { + $request = $smcFunc['db_query']('', ' + SELECT value + FROM {db_prefix}settings + WHERE variable = {string:variable}', + array( + 'variable' => $hook, + ) + ); + list($current_functions) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (!empty($current_functions)) + { + $current_functions = explode(',', $current_functions); + if (in_array($function, $current_functions)) + return; + + $permanent_functions = array_merge($current_functions, array($function)); + } + else + $permanent_functions = array($function); + + updateSettings(array($hook => implode(',', $permanent_functions))); + } + + // Make current function list usable. + $functions = empty($modSettings[$hook]) ? array() : explode(',', $modSettings[$hook]); + + // Do nothing, if it's already there. + if (in_array($function, $functions)) + return; + + $functions[] = $function; + $modSettings[$hook] = implode(',', $functions); +} + +// Remove an integration hook function. +function remove_integration_function($hook, $function) +{ + global $smcFunc, $modSettings; + + // Get the permanent functions. + $request = $smcFunc['db_query']('', ' + SELECT value + FROM {db_prefix}settings + WHERE variable = {string:variable}', + array( + 'variable' => $hook, + ) + ); + list($current_functions) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (!empty($current_functions)) + { + $current_functions = explode(',', $current_functions); + + if (in_array($function, $current_functions)) + updateSettings(array($hook => implode(',', array_diff($current_functions, array($function))))); + } + + // Turn the function list into something usable. + $functions = empty($modSettings[$hook]) ? array() : explode(',', $modSettings[$hook]); + + // You can only remove it if it's available. + if (!in_array($function, $functions)) + return; + + $functions = array_diff($functions, array($function)); + $modSettings[$hook] = implode(',', $functions); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Subscriptions-PayPal.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Subscriptions-PayPal.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,332 @@ + $txt['paypal_email_desc']), + ); + + return $setting_data; + } + + // Is this enabled for new payments? + public function gatewayEnabled() + { + global $modSettings; + + return !empty($modSettings['paypal_email']); + } + + // What do we want? + public function fetchGatewayFields($unique_id, $sub_data, $value, $period, $return_url) + { + global $modSettings, $txt, $boardurl; + + $return_data = array( + 'form' => 'https://www.' . (!empty($modSettings['paidsubs_test']) ? 'sandbox.' : '') . 'paypal.com/cgi-bin/webscr', + 'id' => 'paypal', + 'hidden' => array(), + 'title' => $txt['paypal'], + 'desc' => $txt['paid_confirm_paypal'], + 'submit' => $txt['paid_paypal_order'], + 'javascript' => '', + ); + + // All the standard bits. + $return_data['hidden']['business'] = $modSettings['paypal_email']; + $return_data['hidden']['item_name'] = $sub_data['name'] . ' ' . $txt['subscription']; + $return_data['hidden']['item_number'] = $unique_id; + $return_data['hidden']['currency_code'] = strtoupper($modSettings['paid_currency_code']); + $return_data['hidden']['no_shipping'] = 1; + $return_data['hidden']['no_note'] = 1; + $return_data['hidden']['amount'] = $value; + $return_data['hidden']['cmd'] = !$sub_data['repeatable'] ? '_xclick' : '_xclick-subscriptions'; + $return_data['hidden']['return'] = $return_url; + $return_data['hidden']['a3'] = $value; + $return_data['hidden']['src'] = 1; + $return_data['hidden']['notify_url'] = $boardurl . '/subscriptions.php'; + + // Now stuff dependant on what we're doing. + if ($sub_data['flexible']) + { + $return_data['hidden']['p3'] = 1; + $return_data['hidden']['t3'] = strtoupper(substr($period, 0, 1)); + } + else + { + preg_match('~(\d*)(\w)~', $sub_data['real_length'], $match); + $unit = $match[1]; + $period = $match[2]; + + $return_data['hidden']['p3'] = $unit; + $return_data['hidden']['t3'] = $period; + } + + // If it's repeatable do soem javascript to respect this idea. + if (!empty($sub_data['repeatable'])) + $return_data['javascript'] = ' + document.write(\'
    \'); + + function switchPaypalRecur() + { + document.getElementById("paypal_cmd").value = document.getElementById("do_paypal_recur").checked ? "_xclick-subscriptions" : "_xclick"; + }'; + + return $return_data; + } +} + +class paypal_payment +{ + private $return_data; + + // This function returns true/false for whether this gateway thinks the data is intended for it. + public function isValid() + { + global $modSettings; + + // Has the user set up an email address? + if (empty($modSettings['paypal_email'])) + return false; + // Check the correct transaction types are even here. + if ((!isset($_POST['txn_type']) && !isset($_POST['payment_status'])) || (!isset($_POST['business']) && !isset($_POST['receiver_email']))) + return false; + // Correct email address? + if (!isset($_POST['business'])) + $_POST['business'] = $_POST['receiver_email']; + if ($modSettings['paypal_email'] != $_POST['business'] && (empty($modSettings['paypal_additional_emails']) || !in_array($_POST['business'], explode(',', $modSettings['paypal_additional_emails'])))) + return false; + return true; + } + + // Validate all the data was valid. + public function precheck() + { + global $modSettings, $txt; + + // Put this to some default value. + if (!isset($_POST['txn_type'])) + $_POST['txn_type'] = ''; + + // Build the request string - starting with the minimum requirement. + $requestString = 'cmd=_notify-validate'; + + // Now my dear, add all the posted bits. + foreach ($_POST as $k => $v) + $requestString .= '&' . $k . '=' . urlencode($v); + + // Can we use curl? + if (function_exists('curl_init') && $curl = curl_init((!empty($modSettings['paidsubs_test']) ? 'https://www.sandbox.' : 'http://www.') . 'paypal.com/cgi-bin/webscr')) + { + // Set the post data. + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDSIZE, 0); + curl_setopt($curl, CURLOPT_POSTFIELDS, $requestString); + + curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($curl, CURLOPT_FORBID_REUSE, 1); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Host: www.' . (!empty($modSettings['paidsubs_test']) ? 'sandbox.' : '') . 'paypal.com', + 'Connection: close' + )); + + // Fetch the data returned as a string. + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + + // Fetch the data. + $this->return_data = curl_exec($curl); + + // Close the session. + curl_close($curl); + } + // Otherwise good old HTTP. + else + { + // Setup the headers. + $header = 'POST /cgi-bin/webscr HTTP/1.1' . "\r\n"; + $header .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n"; + $header .= 'Host: www.' . (!empty($modSettings['paidsubs_test']) ? 'sandbox.' : '') . 'paypal.com' . "\r\n"; + $header .= 'Content-Length: ' . strlen ($requestString) . "\r\n"; + $header .= 'Connection: close' . "\r\n\r\n"; + + // Open the connection. + if (!empty($modSettings['paidsubs_test'])) + $fp = fsockopen('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30); + else + $fp = fsockopen('www.paypal.com', 80, $errno, $errstr, 30); + + // Did it work? + if (!$fp) + generateSubscriptionError($txt['paypal_could_not_connect']); + + // Put the data to the port. + fputs($fp, $header . $requestString); + + // Get the data back... + while (!feof($fp)) + { + $this->return_data = fgets($fp, 1024); + if (strcmp(trim($this->return_data), 'VERIFIED') == 0) + break; + } + + // Clean up. + fclose($fp); + } + + // If this isn't verified then give up... + // !! This contained a comment "send an email", but we don't appear to send any? + if (strcmp(trim($this->return_data), 'VERIFIED') != 0) + exit; + + // Check that this is intended for us. + if ($modSettings['paypal_email'] != $_POST['business'] && (empty($modSettings['paypal_additional_emails']) || !in_array($_POST['business'], explode(',', $modSettings['paypal_additional_emails'])))) + exit; + + // Is this a subscription - and if so it's it a secondary payment that we need to process? + if ($this->isSubscription() && (empty($_POST['item_number']) || strpos($_POST['item_number'], '+') === false)) + // Calculate the subscription it relates to! + $this->_findSubscription(); + + // Verify the currency! + if (strtolower($_POST['mc_currency']) != $modSettings['paid_currency_code']) + exit; + + // Can't exist if it doesn't contain anything. + if (empty($_POST['item_number'])) + exit; + + // Return the id_sub and id_member + return explode('+', $_POST['item_number']); + } + + // Is this a refund? + public function isRefund() + { + if ($_POST['payment_status'] == 'Refunded' || $_POST['payment_status'] == 'Reversed' || $_POST['txn_type'] == 'Refunded' || ($_POST['txn_type'] == 'reversal' && $_POST['payment_status'] == 'Completed')) + return true; + else + return false; + } + + // Is this a subscription? + public function isSubscription() + { + if (substr($_POST['txn_type'], 0, 14) == 'subscr_payment' && $_POST['payment_status'] == 'Completed') + return true; + else + return false; + } + + // Is this a normal payment? + public function isPayment() + { + if ($_POST['payment_status'] == 'Completed' && $_POST['txn_type'] == 'web_accept') + return true; + else + return false; + } + + // How much was paid? + public function getCost() + { + return (isset($_POST['tax']) ? $_POST['tax'] : 0) + $_POST['mc_gross']; + } + + // exit. + public function close() + { + global $smcFunc, $subscription_id; + + // If it's a subscription record the reference. + if ($_POST['txn_type'] == 'subscr_payment' && !empty($_POST['subscr_id'])) + { + $_POST['subscr_id'] = $_POST['subscr_id']; + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET vendor_ref = {string:vendor_ref} + WHERE id_sublog = {int:current_subscription}', + array( + 'current_subscription' => $subscription_id, + 'vendor_ref' => $_POST['subscr_id'], + ) + ); + } + + exit(); + } + + // A private function to find out the subscription details. + private function _findSubscription() + { + global $smcFunc; + + // Assume we have this? + if (empty($_POST['subscr_id'])) + return false; + + // Do we have this in the database? + $request = $smcFunc['db_query']('', ' + SELECT id_member, id_subscribe + FROM {db_prefix}log_subscribed + WHERE vendor_ref = {string:vendor_ref} + LIMIT 1', + array( + 'vendor_ref' => $_POST['subscr_id'], + ) + ); + // No joy? + if ($smcFunc['db_num_rows']($request) == 0) + { + // Can we identify them by email? + if (!empty($_POST['payer_email'])) + { + $smcFunc['db_free_result']($request); + $request = $smcFunc['db_query']('', ' + SELECT ls.id_member, ls.id_subscribe + FROM {db_prefix}log_subscribed AS ls + INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ls.id_member) + WHERE mem.email_address = {string:payer_email} + LIMIT 1', + array( + 'payer_email' => $_POST['payer_email'], + ) + ); + if ($smcFunc['db_num_rows']($request) == 0) + return false; + } + else + return false; + } + list ($member_id, $subscription_id) = $smcFunc['db_fetch_row']($request); + $_POST['item_number'] = $member_id . '+' . $subscription_id; + $smcFunc['db_free_result']($request); + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Themes.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Themes.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2163 @@ + value).) + - tar and gzip the directory - and you're done! + - please include any special license in a license.txt file. + // !!! Thumbnail? +*/ + +// Subaction handler. +function ThemesMain() +{ + global $txt, $context, $scripturl; + + // Load the important language files... + loadLanguage('Themes'); + loadLanguage('Settings'); + + // No funny business - guests only. + is_not_guest(); + + // Default the page title to Theme Administration by default. + $context['page_title'] = $txt['themeadmin_title']; + + // Theme administration, removal, choice, or installation... + $subActions = array( + 'admin' => 'ThemeAdmin', + 'list' => 'ThemeList', + 'reset' => 'SetThemeOptions', + 'settings' => 'SetThemeSettings', + 'options' => 'SetThemeOptions', + 'install' => 'ThemeInstall', + 'remove' => 'RemoveTheme', + 'pick' => 'PickTheme', + 'edit' => 'EditTheme', + 'copy' => 'CopyTemplate', + ); + + // !!! Layout Settings? + if (!empty($context['admin_menu_name'])) + { + $context[$context['admin_menu_name']]['tab_data'] = array( + 'title' => $txt['themeadmin_title'], + 'help' => 'themes', + 'description' => $txt['themeadmin_description'], + 'tabs' => array( + 'admin' => array( + 'description' => $txt['themeadmin_admin_desc'], + ), + 'list' => array( + 'description' => $txt['themeadmin_list_desc'], + ), + 'reset' => array( + 'description' => $txt['themeadmin_reset_desc'], + ), + 'edit' => array( + 'description' => $txt['themeadmin_edit_desc'], + ), + ), + ); + } + + // Follow the sa or just go to administration. + if (isset($_GET['sa']) && !empty($subActions[$_GET['sa']])) + $subActions[$_GET['sa']](); + else + $subActions['admin'](); +} + +function ThemeAdmin() +{ + global $context, $boarddir, $modSettings, $smcFunc; + + loadLanguage('Admin'); + isAllowedTo('admin_forum'); + + // If we aren't submitting - that is, if we are about to... + if (!isset($_POST['submit'])) + { + loadTemplate('Themes'); + + // Make our known themes a little easier to work with. + $knownThemes = !empty($modSettings['knownThemes']) ? explode(',',$modSettings['knownThemes']) : array(); + + // Load up all the themes. + $request = $smcFunc['db_query']('', ' + SELECT id_theme, value AS name + FROM {db_prefix}themes + WHERE variable = {string:name} + AND id_member = {int:no_member} + ORDER BY id_theme', + array( + 'no_member' => 0, + 'name' => 'name', + ) + ); + $context['themes'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['themes'][] = array( + 'id' => $row['id_theme'], + 'name' => $row['name'], + 'known' => in_array($row['id_theme'], $knownThemes), + ); + $smcFunc['db_free_result']($request); + + // Can we create a new theme? + $context['can_create_new'] = is_writable($boarddir . '/Themes'); + $context['new_theme_dir'] = substr(realpath($boarddir . '/Themes/default'), 0, -7); + + // Look for a non existent theme directory. (ie theme87.) + $theme_dir = $boarddir . '/Themes/theme'; + $i = 1; + while (file_exists($theme_dir . $i)) + $i++; + $context['new_theme_name'] = 'theme' . $i; + } + else + { + checkSession(); + + if (isset($_POST['options']['known_themes'])) + foreach ($_POST['options']['known_themes'] as $key => $id) + $_POST['options']['known_themes'][$key] = (int) $id; + else + fatal_lang_error('themes_none_selectable', false); + + if (!in_array($_POST['options']['theme_guests'], $_POST['options']['known_themes'])) + fatal_lang_error('themes_default_selectable', false); + + // Commit the new settings. + updateSettings(array( + 'theme_allow' => $_POST['options']['theme_allow'], + 'theme_guests' => $_POST['options']['theme_guests'], + 'knownThemes' => implode(',', $_POST['options']['known_themes']), + )); + if ((int) $_POST['theme_reset'] == 0 || in_array($_POST['theme_reset'], $_POST['options']['known_themes'])) + updateMemberData(null, array('id_theme' => (int) $_POST['theme_reset'])); + + redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=admin'); + } +} + +function ThemeList() +{ + global $context, $boarddir, $boardurl, $smcFunc; + + loadLanguage('Admin'); + isAllowedTo('admin_forum'); + + if (isset($_POST['submit'])) + { + checkSession(); + + $request = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE variable IN ({string:theme_dir}, {string:theme_url}, {string:images_url}, {string:base_theme_dir}, {string:base_theme_url}, {string:base_images_url}) + AND id_member = {int:no_member}', + array( + 'no_member' => 0, + 'theme_dir' => 'theme_dir', + 'theme_url' => 'theme_url', + 'images_url' => 'images_url', + 'base_theme_dir' => 'base_theme_dir', + 'base_theme_url' => 'base_theme_url', + 'base_images_url' => 'base_images_url', + ) + ); + $themes = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $themes[$row['id_theme']][$row['variable']] = $row['value']; + $smcFunc['db_free_result']($request); + + $setValues = array(); + foreach ($themes as $id => $theme) + { + if (file_exists($_POST['reset_dir'] . '/' . basename($theme['theme_dir']))) + { + $setValues[] = array($id, 0, 'theme_dir', realpath($_POST['reset_dir'] . '/' . basename($theme['theme_dir']))); + $setValues[] = array($id, 0, 'theme_url', $_POST['reset_url'] . '/' . basename($theme['theme_dir'])); + $setValues[] = array($id, 0, 'images_url', $_POST['reset_url'] . '/' . basename($theme['theme_dir']) . '/' . basename($theme['images_url'])); + } + + if (isset($theme['base_theme_dir']) && file_exists($_POST['reset_dir'] . '/' . basename($theme['base_theme_dir']))) + { + $setValues[] = array($id, 0, 'base_theme_dir', realpath($_POST['reset_dir'] . '/' . basename($theme['base_theme_dir']))); + $setValues[] = array($id, 0, 'base_theme_url', $_POST['reset_url'] . '/' . basename($theme['base_theme_dir'])); + $setValues[] = array($id, 0, 'base_images_url', $_POST['reset_url'] . '/' . basename($theme['base_theme_dir']) . '/' . basename($theme['base_images_url'])); + } + + cache_put_data('theme_settings-' . $id, null, 90); + } + + if (!empty($setValues)) + { + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + $setValues, + array('id_theme', 'variable', 'id_member') + ); + } + + redirectexit('action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id']); + } + + loadTemplate('Themes'); + + $request = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE variable IN ({string:name}, {string:theme_dir}, {string:theme_url}, {string:images_url}) + AND id_member = {int:no_member}', + array( + 'no_member' => 0, + 'name' => 'name', + 'theme_dir' => 'theme_dir', + 'theme_url' => 'theme_url', + 'images_url' => 'images_url', + ) + ); + $context['themes'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($context['themes'][$row['id_theme']])) + $context['themes'][$row['id_theme']] = array( + 'id' => $row['id_theme'], + ); + $context['themes'][$row['id_theme']][$row['variable']] = $row['value']; + } + $smcFunc['db_free_result']($request); + + foreach ($context['themes'] as $i => $theme) + { + $context['themes'][$i]['theme_dir'] = realpath($context['themes'][$i]['theme_dir']); + + if (file_exists($context['themes'][$i]['theme_dir'] . '/index.template.php')) + { + // Fetch the header... a good 256 bytes should be more than enough. + $fp = fopen($context['themes'][$i]['theme_dir'] . '/index.template.php', 'rb'); + $header = fread($fp, 256); + fclose($fp); + + // Can we find a version comment, at all? + if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) + $context['themes'][$i]['version'] = $match[1]; + } + + $context['themes'][$i]['valid_path'] = file_exists($context['themes'][$i]['theme_dir']) && is_dir($context['themes'][$i]['theme_dir']); + } + + $context['reset_dir'] = realpath($boarddir . '/Themes'); + $context['reset_url'] = $boardurl . '/Themes'; + + $context['sub_template'] = 'list_themes'; +} + +// Administrative global settings. +function SetThemeOptions() +{ + global $txt, $context, $settings, $modSettings, $smcFunc; + + $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (isset($_GET['id']) ? (int) $_GET['id'] : 0); + + isAllowedTo('admin_forum'); + + if (empty($_GET['th']) && empty($_GET['id'])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE variable IN ({string:name}, {string:theme_dir}) + AND id_member = {int:no_member}', + array( + 'no_member' => 0, + 'name' => 'name', + 'theme_dir' => 'theme_dir', + ) + ); + $context['themes'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($context['themes'][$row['id_theme']])) + $context['themes'][$row['id_theme']] = array( + 'id' => $row['id_theme'], + 'num_default_options' => 0, + 'num_members' => 0, + ); + $context['themes'][$row['id_theme']][$row['variable']] = $row['value']; + } + $smcFunc['db_free_result']($request); + + $request = $smcFunc['db_query']('', ' + SELECT id_theme, COUNT(*) AS value + FROM {db_prefix}themes + WHERE id_member = {int:guest_member} + GROUP BY id_theme', + array( + 'guest_member' => -1, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['themes'][$row['id_theme']]['num_default_options'] = $row['value']; + $smcFunc['db_free_result']($request); + + // Need to make sure we don't do custom fields. + $request = $smcFunc['db_query']('', ' + SELECT col_name + FROM {db_prefix}custom_fields', + array( + ) + ); + $customFields = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $customFields[] = $row['col_name']; + $smcFunc['db_free_result']($request); + $customFieldsQuery = empty($customFields) ? '' : ('AND variable NOT IN ({array_string:custom_fields})'); + + $request = $smcFunc['db_query']('themes_count', ' + SELECT COUNT(DISTINCT id_member) AS value, id_theme + FROM {db_prefix}themes + WHERE id_member > {int:no_member} + ' . $customFieldsQuery . ' + GROUP BY id_theme', + array( + 'no_member' => 0, + 'custom_fields' => empty($customFields) ? array() : $customFields, + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['themes'][$row['id_theme']]['num_members'] = $row['value']; + $smcFunc['db_free_result']($request); + + // There has to be a Settings template! + foreach ($context['themes'] as $k => $v) + if (empty($v['theme_dir']) || (!file_exists($v['theme_dir'] . '/Settings.template.php') && empty($v['num_members']))) + unset($context['themes'][$k]); + + loadTemplate('Themes'); + $context['sub_template'] = 'reset_list'; + + return; + } + + // Submit? + if (isset($_POST['submit']) && empty($_POST['who'])) + { + checkSession(); + + if (empty($_POST['options'])) + $_POST['options'] = array(); + if (empty($_POST['default_options'])) + $_POST['default_options'] = array(); + + // Set up the sql query. + $setValues = array(); + + foreach ($_POST['options'] as $opt => $val) + $setValues[] = array(-1, $_GET['th'], $opt, is_array($val) ? implode(',', $val) : $val); + + $old_settings = array(); + foreach ($_POST['default_options'] as $opt => $val) + { + $old_settings[] = $opt; + + $setValues[] = array(-1, 1, $opt, is_array($val) ? implode(',', $val) : $val); + } + + // If we're actually inserting something.. + if (!empty($setValues)) + { + // Are there options in non-default themes set that should be cleared? + if (!empty($old_settings)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE id_theme != {int:default_theme} + AND id_member = {int:guest_member} + AND variable IN ({array_string:old_settings})', + array( + 'default_theme' => 1, + 'guest_member' => -1, + 'old_settings' => $old_settings, + ) + ); + + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + $setValues, + array('id_theme', 'variable', 'id_member') + ); + } + + cache_put_data('theme_settings-' . $_GET['th'], null, 90); + cache_put_data('theme_settings-1', null, 90); + + redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=reset'); + } + elseif (isset($_POST['submit']) && $_POST['who'] == 1) + { + checkSession(); + + $_POST['options'] = empty($_POST['options']) ? array() : $_POST['options']; + $_POST['options_master'] = empty($_POST['options_master']) ? array() : $_POST['options_master']; + $_POST['default_options'] = empty($_POST['default_options']) ? array() : $_POST['default_options']; + $_POST['default_options_master'] = empty($_POST['default_options_master']) ? array() : $_POST['default_options_master']; + + $old_settings = array(); + foreach ($_POST['default_options'] as $opt => $val) + { + if ($_POST['default_options_master'][$opt] == 0) + continue; + elseif ($_POST['default_options_master'][$opt] == 1) + { + // Delete then insert for ease of database compatibility! + $smcFunc['db_query']('substring', ' + DELETE FROM {db_prefix}themes + WHERE id_theme = {int:default_theme} + AND id_member != {int:no_member} + AND variable = SUBSTRING({string:option}, 1, 255)', + array( + 'default_theme' => 1, + 'no_member' => 0, + 'option' => $opt, + ) + ); + $smcFunc['db_query']('substring', ' + INSERT INTO {db_prefix}themes + (id_member, id_theme, variable, value) + SELECT id_member, 1, SUBSTRING({string:option}, 1, 255), SUBSTRING({string:value}, 1, 65534) + FROM {db_prefix}members', + array( + 'option' => $opt, + 'value' => (is_array($val) ? implode(',', $val) : $val), + ) + ); + + $old_settings[] = $opt; + } + elseif ($_POST['default_options_master'][$opt] == 2) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE variable = {string:option_name} + AND id_member > {int:no_member}', + array( + 'no_member' => 0, + 'option_name' => $opt, + ) + ); + } + } + + // Delete options from other themes. + if (!empty($old_settings)) + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE id_theme != {int:default_theme} + AND id_member > {int:no_member} + AND variable IN ({array_string:old_settings})', + array( + 'default_theme' => 1, + 'no_member' => 0, + 'old_settings' => $old_settings, + ) + ); + + foreach ($_POST['options'] as $opt => $val) + { + if ($_POST['options_master'][$opt] == 0) + continue; + elseif ($_POST['options_master'][$opt] == 1) + { + // Delete then insert for ease of database compatibility - again! + $smcFunc['db_query']('substring', ' + DELETE FROM {db_prefix}themes + WHERE id_theme = {int:current_theme} + AND id_member != {int:no_member} + AND variable = SUBSTRING({string:option}, 1, 255)', + array( + 'current_theme' => $_GET['th'], + 'no_member' => 0, + 'option' => $opt, + ) + ); + $smcFunc['db_query']('substring', ' + INSERT INTO {db_prefix}themes + (id_member, id_theme, variable, value) + SELECT id_member, {int:current_theme}, SUBSTRING({string:option}, 1, 255), SUBSTRING({string:value}, 1, 65534) + FROM {db_prefix}members', + array( + 'current_theme' => $_GET['th'], + 'option' => $opt, + 'value' => (is_array($val) ? implode(',', $val) : $val), + ) + ); + } + elseif ($_POST['options_master'][$opt] == 2) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE variable = {string:option} + AND id_member > {int:no_member} + AND id_theme = {int:current_theme}', + array( + 'no_member' => 0, + 'current_theme' => $_GET['th'], + 'option' => $opt, + ) + ); + } + } + + redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=reset'); + } + elseif (!empty($_GET['who']) && $_GET['who'] == 2) + { + checkSession('get'); + + // Don't delete custom fields!! + if ($_GET['th'] == 1) + { + $request = $smcFunc['db_query']('', ' + SELECT col_name + FROM {db_prefix}custom_fields', + array( + ) + ); + $customFields = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $customFields[] = $row['col_name']; + $smcFunc['db_free_result']($request); + } + $customFieldsQuery = empty($customFields) ? '' : ('AND variable NOT IN ({array_string:custom_fields})'); + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE id_member > {int:no_member} + AND id_theme = {int:current_theme} + ' . $customFieldsQuery, + array( + 'no_member' => 0, + 'current_theme' => $_GET['th'], + 'custom_fields' => empty($customFields) ? array() : $customFields, + ) + ); + + redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=reset'); + } + + $old_id = $settings['theme_id']; + $old_settings = $settings; + + loadTheme($_GET['th'], false); + + loadLanguage('Profile'); + //!!! Should we just move these options so they are no longer theme dependant? + loadLanguage('PersonalMessage'); + + // Let the theme take care of the settings. + loadTemplate('Settings'); + loadSubTemplate('options'); + + $context['sub_template'] = 'set_options'; + $context['page_title'] = $txt['theme_settings']; + + $context['options'] = $context['theme_options']; + $context['theme_settings'] = $settings; + + if (empty($_REQUEST['who'])) + { + $request = $smcFunc['db_query']('', ' + SELECT variable, value + FROM {db_prefix}themes + WHERE id_theme IN (1, {int:current_theme}) + AND id_member = {int:guest_member}', + array( + 'current_theme' => $_GET['th'], + 'guest_member' => -1, + ) + ); + $context['theme_options'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $context['theme_options'][$row['variable']] = $row['value']; + $smcFunc['db_free_result']($request); + + $context['theme_options_reset'] = false; + } + else + { + $context['theme_options'] = array(); + $context['theme_options_reset'] = true; + } + + foreach ($context['options'] as $i => $setting) + { + // Is this disabled? + if ($setting['id'] == 'calendar_start_day' && empty($modSettings['cal_enabled'])) + { + unset($context['options'][$i]); + continue; + } + elseif (($setting['id'] == 'topics_per_page' || $setting['id'] == 'messages_per_page') && !empty($modSettings['disableCustomPerPage'])) + { + unset($context['options'][$i]); + continue; + } + + if (!isset($setting['type']) || $setting['type'] == 'bool') + $context['options'][$i]['type'] = 'checkbox'; + elseif ($setting['type'] == 'int' || $setting['type'] == 'integer') + $context['options'][$i]['type'] = 'number'; + elseif ($setting['type'] == 'string') + $context['options'][$i]['type'] = 'text'; + + if (isset($setting['options'])) + $context['options'][$i]['type'] = 'list'; + + $context['options'][$i]['value'] = !isset($context['theme_options'][$setting['id']]) ? '' : $context['theme_options'][$setting['id']]; + } + + // Restore the existing theme. + loadTheme($old_id, false); + $settings = $old_settings; + + loadTemplate('Themes'); +} + +// Administrative global settings. +function SetThemeSettings() +{ + global $txt, $context, $settings, $modSettings, $sourcedir, $smcFunc; + + if (empty($_GET['th']) && empty($_GET['id'])) + return ThemeAdmin(); + $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id']; + + // Select the best fitting tab. + $context[$context['admin_menu_name']]['current_subsection'] = 'list'; + + loadLanguage('Admin'); + isAllowedTo('admin_forum'); + + // Validate inputs/user. + if (empty($_GET['th'])) + fatal_lang_error('no_theme', false); + + // Fetch the smiley sets... + $sets = explode(',', 'none,' . $modSettings['smiley_sets_known']); + $set_names = explode("\n", $txt['smileys_none'] . "\n" . $modSettings['smiley_sets_names']); + $context['smiley_sets'] = array( + '' => $txt['smileys_no_default'] + ); + foreach ($sets as $i => $set) + $context['smiley_sets'][$set] = htmlspecialchars($set_names[$i]); + + $old_id = $settings['theme_id']; + $old_settings = $settings; + + loadTheme($_GET['th'], false); + + // Sadly we really do need to init the template. + loadSubTemplate('init', 'ignore'); + + // Also load the actual themes language file - in case of special settings. + loadLanguage('Settings', '', true, true); + + // And the custom language strings... + loadLanguage('ThemeStrings', '', false, true); + + // Let the theme take care of the settings. + loadTemplate('Settings'); + loadSubTemplate('settings'); + + // Load the variants separately... + $settings['theme_variants'] = array(); + if (file_exists($settings['theme_dir'] . '/index.template.php')) + { + $file_contents = implode('', file($settings['theme_dir'] . '/index.template.php')); + if (preg_match('~\$settings\[\'theme_variants\'\]\s*=(.+?);~', $file_contents, $matches)) + eval('global $settings;' . $matches[0]); + } + + // Submitting! + if (isset($_POST['submit'])) + { + checkSession(); + + if (empty($_POST['options'])) + $_POST['options'] = array(); + if (empty($_POST['default_options'])) + $_POST['default_options'] = array(); + + // Make sure items are cast correctly. + foreach ($context['theme_settings'] as $item) + { + // Disregard this item if this is just a separator. + if (!is_array($item)) + continue; + + foreach (array('options', 'default_options') as $option) + { + if (!isset($_POST[$option][$item['id']])) + continue; + // Checkbox. + elseif (empty($item['type'])) + $_POST[$option][$item['id']] = $_POST[$option][$item['id']] ? 1 : 0; + // Number + elseif ($item['type'] == 'number') + $_POST[$option][$item['id']] = (int) $_POST[$option][$item['id']]; + } + } + + // Set up the sql query. + $inserts = array(); + foreach ($_POST['options'] as $opt => $val) + $inserts[] = array(0, $_GET['th'], $opt, is_array($val) ? implode(',', $val) : $val); + foreach ($_POST['default_options'] as $opt => $val) + $inserts[] = array(0, 1, $opt, is_array($val) ? implode(',', $val) : $val); + // If we're actually inserting something.. + if (!empty($inserts)) + { + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + $inserts, + array('id_member', 'id_theme', 'variable') + ); + } + + cache_put_data('theme_settings-' . $_GET['th'], null, 90); + cache_put_data('theme_settings-1', null, 90); + + // Invalidate the cache. + updateSettings(array('settings_updated' => time())); + + redirectexit('action=admin;area=theme;sa=settings;th=' . $_GET['th'] . ';' . $context['session_var'] . '=' . $context['session_id']); + } + + $context['sub_template'] = 'set_settings'; + $context['page_title'] = $txt['theme_settings']; + + foreach ($settings as $setting => $dummy) + { + if (!in_array($setting, array('theme_url', 'theme_dir', 'images_url', 'template_dirs'))) + $settings[$setting] = htmlspecialchars__recursive($settings[$setting]); + } + + $context['settings'] = $context['theme_settings']; + $context['theme_settings'] = $settings; + + foreach ($context['settings'] as $i => $setting) + { + // Separators are dummies, so leave them alone. + if (!is_array($setting)) + continue; + + if (!isset($setting['type']) || $setting['type'] == 'bool') + $context['settings'][$i]['type'] = 'checkbox'; + elseif ($setting['type'] == 'int' || $setting['type'] == 'integer') + $context['settings'][$i]['type'] = 'number'; + elseif ($setting['type'] == 'string') + $context['settings'][$i]['type'] = 'text'; + + if (isset($setting['options'])) + $context['settings'][$i]['type'] = 'list'; + + $context['settings'][$i]['value'] = !isset($settings[$setting['id']]) ? '' : $settings[$setting['id']]; + } + + // Do we support variants? + if (!empty($settings['theme_variants'])) + { + $context['theme_variants'] = array(); + foreach ($settings['theme_variants'] as $variant) + { + // Have any text, old chap? + $context['theme_variants'][$variant] = array( + 'label' => isset($txt['variant_' . $variant]) ? $txt['variant_' . $variant] : $variant, + 'thumbnail' => !file_exists($settings['theme_dir'] . '/images/thumbnail.gif') || file_exists($settings['theme_dir'] . '/images/thumbnail_' . $variant . '.gif') ? $settings['images_url'] . '/thumbnail_' . $variant . '.gif' : ($settings['images_url'] . '/thumbnail.gif'), + ); + } + $context['default_variant'] = !empty($settings['default_variant']) && isset($context['theme_variants'][$settings['default_variant']]) ? $settings['default_variant'] : $settings['theme_variants'][0]; + } + + // Restore the current theme. + loadTheme($old_id, false); + + // Reinit just incase. + loadSubTemplate('init', 'ignore'); + + $settings = $old_settings; + + loadTemplate('Themes'); +} + +// Remove a theme from the database. +function RemoveTheme() +{ + global $modSettings, $context, $smcFunc; + + checkSession('get'); + + isAllowedTo('admin_forum'); + + // The theme's ID must be an integer. + $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id']; + + // You can't delete the default theme! + if ($_GET['th'] == 1) + fatal_lang_error('no_access', false); + + $known = explode(',', $modSettings['knownThemes']); + for ($i = 0, $n = count($known); $i < $n; $i++) + { + if ($known[$i] == $_GET['th']) + unset($known[$i]); + } + + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE id_theme = {int:current_theme}', + array( + 'current_theme' => $_GET['th'], + ) + ); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}members + SET id_theme = {int:default_theme} + WHERE id_theme = {int:current_theme}', + array( + 'default_theme' => 0, + 'current_theme' => $_GET['th'], + ) + ); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}boards + SET id_theme = {int:default_theme} + WHERE id_theme = {int:current_theme}', + array( + 'default_theme' => 0, + 'current_theme' => $_GET['th'], + ) + ); + + $known = strtr(implode(',', $known), array(',,' => ',')); + + // Fix it if the theme was the overall default theme. + if ($modSettings['theme_guests'] == $_GET['th']) + updateSettings(array('theme_guests' => '1', 'knownThemes' => $known)); + else + updateSettings(array('knownThemes' => $known)); + + redirectexit('action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id']); +} + +// Choose a theme from a list. +function PickTheme() +{ + global $txt, $context, $modSettings, $user_info, $language, $smcFunc, $settings, $scripturl; + + loadLanguage('Profile'); + loadTemplate('Themes'); + + // Build the link tree. + $context['linktree'][] = array( + 'url' => $scripturl . '?action=theme;sa=pick;u=' . (!empty($_REQUEST['u']) ? (int) $_REQUEST['u'] : 0), + 'name' => $txt['theme_pick'], + ); + + $_SESSION['id_theme'] = 0; + + if (isset($_GET['id'])) + $_GET['th'] = $_GET['id']; + + // Saving a variant cause JS doesn't work - pretend it did ;) + if (isset($_POST['save'])) + { + // Which theme? + foreach ($_POST['save'] as $k => $v) + $_GET['th'] = (int) $k; + + if (isset($_POST['vrt'][$k])) + $_GET['vrt'] = $_POST['vrt'][$k]; + } + + // Have we made a desicion, or are we just browsing? + if (isset($_GET['th'])) + { + checkSession('get'); + + $_GET['th'] = (int) $_GET['th']; + + // Save for this user. + if (!isset($_REQUEST['u']) || !allowedTo('admin_forum')) + { + updateMemberData($user_info['id'], array('id_theme' => (int) $_GET['th'])); + + // A variants to save for the user? + if (!empty($_GET['vrt'])) + { + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + array($_GET['th'], $user_info['id'], 'theme_variant', $_GET['vrt']), + array('id_theme', 'id_member', 'variable') + ); + cache_put_data('theme_settings-' . $_GET['th'] . ':' . $user_info['id'], null, 90); + + $_SESSION['id_variant'] = 0; + } + + redirectexit('action=profile;area=theme'); + } + + // If changing members or guests - and there's a variant - assume changing default variant. + if (!empty($_GET['vrt']) && ($_REQUEST['u'] == '0' || $_REQUEST['u'] == '-1')) + { + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + array($_GET['th'], 0, 'default_variant', $_GET['vrt']), + array('id_theme', 'id_member', 'variable') + ); + + // Make it obvious that it's changed + cache_put_data('theme_settings-' . $_GET['th'], null, 90); + } + + // For everyone. + if ($_REQUEST['u'] == '0') + { + updateMemberData(null, array('id_theme' => (int) $_GET['th'])); + + // Remove any custom variants. + if (!empty($_GET['vrt'])) + { + $smcFunc['db_query']('', ' + DELETE FROM {db_prefix}themes + WHERE id_theme = {int:current_theme} + AND variable = {string:theme_variant}', + array( + 'current_theme' => (int) $_GET['th'], + 'theme_variant' => 'theme_variant', + ) + ); + } + + redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']); + } + // Change the default/guest theme. + elseif ($_REQUEST['u'] == '-1') + { + updateSettings(array('theme_guests' => (int) $_GET['th'])); + + redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']); + } + // Change a specific member's theme. + else + { + updateMemberData((int) $_REQUEST['u'], array('id_theme' => (int) $_GET['th'])); + + if (!empty($_GET['vrt'])) + { + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + array($_GET['th'], (int) $_REQUEST['u'], 'theme_variant', $_GET['vrt']), + array('id_theme', 'id_member', 'variable') + ); + cache_put_data('theme_settings-' . $_GET['th'] . ':' . (int) $_REQUEST['u'], null, 90); + + if ($user_info['id'] == $_REQUEST['u']) + $_SESSION['id_variant'] = 0; + } + + redirectexit('action=profile;u=' . (int) $_REQUEST['u'] . ';area=theme'); + } + } + + // Figure out who the member of the minute is, and what theme they've chosen. + if (!isset($_REQUEST['u']) || !allowedTo('admin_forum')) + { + $context['current_member'] = $user_info['id']; + $context['current_theme'] = $user_info['theme']; + } + // Everyone can't chose just one. + elseif ($_REQUEST['u'] == '0') + { + $context['current_member'] = 0; + $context['current_theme'] = 0; + } + // Guests and such... + elseif ($_REQUEST['u'] == '-1') + { + $context['current_member'] = -1; + $context['current_theme'] = $modSettings['theme_guests']; + } + // Someones else :P. + else + { + $context['current_member'] = (int) $_REQUEST['u']; + + $request = $smcFunc['db_query']('', ' + SELECT id_theme + FROM {db_prefix}members + WHERE id_member = {int:current_member} + LIMIT 1', + array( + 'current_member' => $context['current_member'], + ) + ); + list ($context['current_theme']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + } + + // Get the theme name and descriptions. + $context['available_themes'] = array(); + if (!empty($modSettings['knownThemes'])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE variable IN ({string:name}, {string:theme_url}, {string:theme_dir}, {string:images_url}, {string:disable_user_variant})' . (!allowedTo('admin_forum') ? ' + AND id_theme IN ({array_string:known_themes})' : '') . ' + AND id_theme != {int:default_theme} + AND id_member = {int:no_member}', + array( + 'default_theme' => 0, + 'name' => 'name', + 'no_member' => 0, + 'theme_url' => 'theme_url', + 'theme_dir' => 'theme_dir', + 'images_url' => 'images_url', + 'disable_user_variant' => 'disable_user_variant', + 'known_themes' => explode(',', $modSettings['knownThemes']), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($context['available_themes'][$row['id_theme']])) + $context['available_themes'][$row['id_theme']] = array( + 'id' => $row['id_theme'], + 'selected' => $context['current_theme'] == $row['id_theme'], + 'num_users' => 0 + ); + $context['available_themes'][$row['id_theme']][$row['variable']] = $row['value']; + } + $smcFunc['db_free_result']($request); + } + + // Okay, this is a complicated problem: the default theme is 1, but they aren't allowed to access 1! + if (!isset($context['available_themes'][$modSettings['theme_guests']])) + { + $context['available_themes'][0] = array( + 'num_users' => 0 + ); + $guest_theme = 0; + } + else + $guest_theme = $modSettings['theme_guests']; + + $request = $smcFunc['db_query']('', ' + SELECT id_theme, COUNT(*) AS the_count + FROM {db_prefix}members + GROUP BY id_theme + ORDER BY id_theme DESC', + array( + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + // Figure out which theme it is they are REALLY using. + if (!empty($modSettings['knownThemes']) && !in_array($row['id_theme'], explode(',',$modSettings['knownThemes']))) + $row['id_theme'] = $guest_theme; + elseif (empty($modSettings['theme_allow'])) + $row['id_theme'] = $guest_theme; + + if (isset($context['available_themes'][$row['id_theme']])) + $context['available_themes'][$row['id_theme']]['num_users'] += $row['the_count']; + else + $context['available_themes'][$guest_theme]['num_users'] += $row['the_count']; + } + $smcFunc['db_free_result']($request); + + // Get any member variant preferences. + $variant_preferences = array(); + if ($context['current_member'] > 0) + { + $request = $smcFunc['db_query']('', ' + SELECT id_theme, value + FROM {db_prefix}themes + WHERE variable = {string:theme_variant}', + array( + 'theme_variant' => 'theme_variant', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + $variant_preferences[$row['id_theme']] = $row['value']; + $smcFunc['db_free_result']($request); + } + + // Save the setting first. + $current_images_url = $settings['images_url']; + $current_theme_variants = !empty($settings['theme_variants']) ? $settings['theme_variants'] : array(); + + foreach ($context['available_themes'] as $id_theme => $theme_data) + { + // Don't try to load the forum or board default theme's data... it doesn't have any! + if ($id_theme == 0) + continue; + + // The thumbnail needs the correct path. + $settings['images_url'] = &$theme_data['images_url']; + + if (file_exists($theme_data['theme_dir'] . '/languages/Settings.' . $user_info['language'] . '.php')) + include($theme_data['theme_dir'] . '/languages/Settings.' . $user_info['language'] . '.php'); + elseif (file_exists($theme_data['theme_dir'] . '/languages/Settings.' . $language . '.php')) + include($theme_data['theme_dir'] . '/languages/Settings.' . $language . '.php'); + else + { + $txt['theme_thumbnail_href'] = $theme_data['images_url'] . '/thumbnail.gif'; + $txt['theme_description'] = ''; + } + + $context['available_themes'][$id_theme]['thumbnail_href'] = $txt['theme_thumbnail_href']; + $context['available_themes'][$id_theme]['description'] = $txt['theme_description']; + + // Are there any variants? + if (file_exists($theme_data['theme_dir'] . '/index.template.php') && empty($theme_data['disable_user_variant'])) + { + $file_contents = implode('', file($theme_data['theme_dir'] . '/index.template.php')); + if (preg_match('~\$settings\[\'theme_variants\'\]\s*=(.+?);~', $file_contents, $matches)) + { + $settings['theme_variants'] = array(); + + // Fill settings up. + eval('global $settings;' . $matches[0]); + + if (!empty($settings['theme_variants'])) + { + loadLanguage('Settings'); + + $context['available_themes'][$id_theme]['variants'] = array(); + foreach ($settings['theme_variants'] as $variant) + $context['available_themes'][$id_theme]['variants'][$variant] = array( + 'label' => isset($txt['variant_' . $variant]) ? $txt['variant_' . $variant] : $variant, + 'thumbnail' => !file_exists($theme_data['theme_dir'] . '/images/thumbnail.gif') || file_exists($theme_data['theme_dir'] . '/images/thumbnail_' . $variant . '.gif') ? $theme_data['images_url'] . '/thumbnail_' . $variant . '.gif' : ($theme_data['images_url'] . '/thumbnail.gif'), + ); + + $context['available_themes'][$id_theme]['selected_variant'] = isset($_GET['vrt']) ? $_GET['vrt'] : (!empty($variant_preferences[$id_theme]) ? $variant_preferences[$id_theme] : (!empty($settings['default_variant']) ? $settings['default_variant'] : $settings['theme_variants'][0])); + if (!isset($context['available_themes'][$id_theme]['variants'][$context['available_themes'][$id_theme]['selected_variant']]['thumbnail'])) + $context['available_themes'][$id_theme]['selected_variant'] = $settings['theme_variants'][0]; + + $context['available_themes'][$id_theme]['thumbnail_href'] = $context['available_themes'][$id_theme]['variants'][$context['available_themes'][$id_theme]['selected_variant']]['thumbnail']; + // Allow themes to override the text. + $context['available_themes'][$id_theme]['pick_label'] = isset($txt['variant_pick']) ? $txt['variant_pick'] : $txt['theme_pick_variant']; + } + } + } + } + // Then return it. + $settings['images_url'] = $current_images_url; + $settings['theme_variants'] = $current_theme_variants; + + // As long as we're not doing the default theme... + if (!isset($_REQUEST['u']) || $_REQUEST['u'] >= 0) + { + if ($guest_theme != 0) + $context['available_themes'][0] = $context['available_themes'][$guest_theme]; + + $context['available_themes'][0]['id'] = 0; + $context['available_themes'][0]['name'] = $txt['theme_forum_default']; + $context['available_themes'][0]['selected'] = $context['current_theme'] == 0; + $context['available_themes'][0]['description'] = $txt['theme_global_description']; + } + + ksort($context['available_themes']); + + $context['page_title'] = $txt['theme_pick']; + $context['sub_template'] = 'pick'; +} + +function ThemeInstall() +{ + global $sourcedir, $boarddir, $boardurl, $txt, $context, $settings, $modSettings, $smcFunc; + + checkSession('request'); + + isAllowedTo('admin_forum'); + checkSession('request'); + + require_once($sourcedir . '/Subs-Package.php'); + + loadTemplate('Themes'); + + if (isset($_GET['theme_id'])) + { + $result = $smcFunc['db_query']('', ' + SELECT value + FROM {db_prefix}themes + WHERE id_theme = {int:current_theme} + AND id_member = {int:no_member} + AND variable = {string:name} + LIMIT 1', + array( + 'current_theme' => (int) $_GET['theme_id'], + 'no_member' => 0, + 'name' => 'name', + ) + ); + list ($theme_name) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + $context['sub_template'] = 'installed'; + $context['page_title'] = $txt['theme_installed']; + $context['installed_theme'] = array( + 'id' => (int) $_GET['theme_id'], + 'name' => $theme_name, + ); + + return; + } + + if ((!empty($_FILES['theme_gz']) && (!isset($_FILES['theme_gz']['error']) || $_FILES['theme_gz']['error'] != 4)) || !empty($_REQUEST['theme_gz'])) + $method = 'upload'; + elseif (isset($_REQUEST['theme_dir']) && rtrim(realpath($_REQUEST['theme_dir']), '/\\') != realpath($boarddir . '/Themes') && file_exists($_REQUEST['theme_dir'])) + $method = 'path'; + else + $method = 'copy'; + + if (!empty($_REQUEST['copy']) && $method == 'copy') + { + // Hopefully the themes directory is writable, or we might have a problem. + if (!is_writable($boarddir . '/Themes')) + fatal_lang_error('theme_install_write_error', 'critical'); + + $theme_dir = $boarddir . '/Themes/' . preg_replace('~[^A-Za-z0-9_\- ]~', '', $_REQUEST['copy']); + + umask(0); + mkdir($theme_dir, 0777); + + @set_time_limit(600); + if (function_exists('apache_reset_timeout')) + @apache_reset_timeout(); + + // Create subdirectories for css and javascript files. + mkdir($theme_dir . '/css', 0777); + mkdir($theme_dir . '/scripts', 0777); + + // Copy over the default non-theme files. + $to_copy = array('/index.php', '/index.template.php', '/css/index.css', '/css/rtl.css', '/scripts/theme.js'); + foreach ($to_copy as $file) + { + copy($settings['default_theme_dir'] . $file, $theme_dir . $file); + @chmod($theme_dir . $file, 0777); + } + + // And now the entire images directory! + copytree($settings['default_theme_dir'] . '/images', $theme_dir . '/images'); + package_flush_cache(); + + $theme_name = $_REQUEST['copy']; + $images_url = $boardurl . '/Themes/' . basename($theme_dir) . '/images'; + $theme_dir = realpath($theme_dir); + + // Lets get some data for the new theme. + $request = $smcFunc['db_query']('', ' + SELECT variable, value + FROM {db_prefix}themes + WHERE variable IN ({string:theme_templates}, {string:theme_layers}) + AND id_member = {int:no_member} + AND id_theme = {int:default_theme}', + array( + 'no_member' => 0, + 'default_theme' => 1, + 'theme_templates' => 'theme_templates', + 'theme_layers' => 'theme_layers', + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if ($row['variable'] == 'theme_templates') + $theme_templates = $row['value']; + elseif ($row['variable'] == 'theme_layers') + $theme_layers = $row['value']; + else + continue; + } + $smcFunc['db_free_result']($request); + + // Lets add a theme_info.xml to this theme. + $xml_info = '<' . '?xml version="1.0"?' . '> + + + smf:' . $smcFunc['strtolower'](str_replace(array(' '), '_', $_REQUEST['copy'])) . ' + ' . $modSettings['smfVersion'] . ' + + ' . $_REQUEST['copy'] . ' + + info@simplemachines.org + + http://www.simplemachines.org/ + + ' . (empty($theme_layers) ? 'html,body' : $theme_layers) . ' + + ' . (empty($theme_templates) ? 'index' : $theme_templates) . ' + + +'; + + // Now write it. + $fp = @fopen($theme_dir . '/theme_info.xml', 'w+'); + if ($fp) + { + fwrite($fp, $xml_info); + fclose($fp); + } + } + elseif (isset($_REQUEST['theme_dir']) && $method == 'path') + { + if (!is_dir($_REQUEST['theme_dir']) || !file_exists($_REQUEST['theme_dir'] . '/theme_info.xml')) + fatal_lang_error('theme_install_error', false); + + $theme_name = basename($_REQUEST['theme_dir']); + $theme_dir = $_REQUEST['theme_dir']; + } + elseif ($method = 'upload') + { + // Hopefully the themes directory is writable, or we might have a problem. + if (!is_writable($boarddir . '/Themes')) + fatal_lang_error('theme_install_write_error', 'critical'); + + require_once($sourcedir . '/Subs-Package.php'); + + // Set the default settings... + $theme_name = strtok(basename(isset($_FILES['theme_gz']) ? $_FILES['theme_gz']['name'] : $_REQUEST['theme_gz']), '.'); + $theme_name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $theme_name); + $theme_dir = $boarddir . '/Themes/' . $theme_name; + + if (isset($_FILES['theme_gz']) && is_uploaded_file($_FILES['theme_gz']['tmp_name']) && (@ini_get('open_basedir') != '' || file_exists($_FILES['theme_gz']['tmp_name']))) + $extracted = read_tgz_file($_FILES['theme_gz']['tmp_name'], $boarddir . '/Themes/' . $theme_name, false, true); + elseif (isset($_REQUEST['theme_gz'])) + { + // Check that the theme is from simplemachines.org, for now... maybe add mirroring later. + if (preg_match('~^http://[\w_\-]+\.simplemachines\.org/~', $_REQUEST['theme_gz']) == 0 || strpos($_REQUEST['theme_gz'], 'dlattach') !== false) + fatal_lang_error('not_on_simplemachines'); + + $extracted = read_tgz_file($_REQUEST['theme_gz'], $boarddir . '/Themes/' . $theme_name, false, true); + } + else + redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']); + } + + // Something go wrong? + if ($theme_dir != '' && basename($theme_dir) != 'Themes') + { + // Defaults. + $install_info = array( + 'theme_url' => $boardurl . '/Themes/' . basename($theme_dir), + 'images_url' => isset($images_url) ? $images_url : $boardurl . '/Themes/' . basename($theme_dir) . '/images', + 'theme_dir' => $theme_dir, + 'name' => $theme_name + ); + + if (file_exists($theme_dir . '/theme_info.xml')) + { + $theme_info = file_get_contents($theme_dir . '/theme_info.xml'); + + $xml_elements = array( + 'name' => 'name', + 'theme_layers' => 'layers', + 'theme_templates' => 'templates', + 'based_on' => 'based-on', + ); + foreach ($xml_elements as $var => $name) + { + if (preg_match('~<' . $name . '>(?:)?~', $theme_info, $match) == 1) + $install_info[$var] = $match[1]; + } + + if (preg_match('~(?:)?~', $theme_info, $match) == 1) + { + $install_info['images_url'] = $install_info['theme_url'] . '/' . $match[1]; + $explicit_images = true; + } + if (preg_match('~(?:)?~', $theme_info, $match) == 1) + $install_info += unserialize($match[1]); + } + + if (isset($install_info['based_on'])) + { + if ($install_info['based_on'] == 'default') + { + $install_info['theme_url'] = $settings['default_theme_url']; + $install_info['images_url'] = $settings['default_images_url']; + } + elseif ($install_info['based_on'] != '') + { + $install_info['based_on'] = preg_replace('~[^A-Za-z0-9\-_ ]~', '', $install_info['based_on']); + + $request = $smcFunc['db_query']('', ' + SELECT th.value AS base_theme_dir, th2.value AS base_theme_url' . (!empty($explicit_images) ? '' : ', th3.value AS images_url') . ' + FROM {db_prefix}themes AS th + INNER JOIN {db_prefix}themes AS th2 ON (th2.id_theme = th.id_theme + AND th2.id_member = {int:no_member} + AND th2.variable = {string:theme_url})' . (!empty($explicit_images) ? '' : ' + INNER JOIN {db_prefix}themes AS th3 ON (th3.id_theme = th.id_theme + AND th3.id_member = {int:no_member} + AND th3.variable = {string:images_url})') . ' + WHERE th.id_member = {int:no_member} + AND (th.value LIKE {string:based_on} OR th.value LIKE {string:based_on_path}) + AND th.variable = {string:theme_dir} + LIMIT 1', + array( + 'no_member' => 0, + 'theme_url' => 'theme_url', + 'images_url' => 'images_url', + 'theme_dir' => 'theme_dir', + 'based_on' => '%/' . $install_info['based_on'], + 'based_on_path' => '%' . "\\" . $install_info['based_on'], + ) + ); + $temp = $smcFunc['db_fetch_assoc']($request); + $smcFunc['db_free_result']($request); + + // !!! An error otherwise? + if (is_array($temp)) + { + $install_info = $temp + $install_info; + + if (empty($explicit_images) && !empty($install_info['base_theme_url'])) + $install_info['theme_url'] = $install_info['base_theme_url']; + } + } + + unset($install_info['based_on']); + } + + // Find the newest id_theme. + $result = $smcFunc['db_query']('', ' + SELECT MAX(id_theme) + FROM {db_prefix}themes', + array( + ) + ); + list ($id_theme) = $smcFunc['db_fetch_row']($result); + $smcFunc['db_free_result']($result); + + // This will be theme number... + $id_theme++; + + $inserts = array(); + foreach ($install_info as $var => $val) + $inserts[] = array($id_theme, $var, $val); + + if (!empty($inserts)) + $smcFunc['db_insert']('insert', + '{db_prefix}themes', + array('id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + $inserts, + array('id_theme', 'variable') + ); + + updateSettings(array('knownThemes' => strtr($modSettings['knownThemes'] . ',' . $id_theme, array(',,' => ',')))); + } + + redirectexit('action=admin;area=theme;sa=install;theme_id=' . $id_theme . ';' . $context['session_var'] . '=' . $context['session_id']); +} + +// Possibly the simplest and best example of how to ues the template system. +function WrapAction() +{ + global $context, $settings, $sourcedir; + + // Load any necessary template(s)? + if (isset($settings['catch_action']['template'])) + { + // Load both the template and language file. (but don't fret if the language file isn't there...) + loadTemplate($settings['catch_action']['template']); + loadLanguage($settings['catch_action']['template'], '', false); + } + + // Any special layers? + if (isset($settings['catch_action']['layers'])) + $context['template_layers'] = $settings['catch_action']['layers']; + + // Just call a function? + if (isset($settings['catch_action']['function'])) + { + if (isset($settings['catch_action']['filename'])) + template_include($sourcedir . '/' . $settings['catch_action']['filename'], true); + + $settings['catch_action']['function'](); + } + // And finally, the main sub template ;). + elseif (isset($settings['catch_action']['sub_template'])) + $context['sub_template'] = $settings['catch_action']['sub_template']; +} + +// Set an option via javascript. +function SetJavaScript() +{ + global $settings, $user_info, $smcFunc, $options; + + // Check the session id. + checkSession('get'); + + // This good-for-nothing pixel is being used to keep the session alive. + if (empty($_GET['var']) || !isset($_GET['val'])) + redirectexit($settings['images_url'] . '/blank.gif'); + + // Sorry, guests can't go any further than this.. + if ($user_info['is_guest'] || $user_info['id'] == 0) + obExit(false); + + $reservedVars = array( + 'actual_theme_url', + 'actual_images_url', + 'base_theme_dir', + 'base_theme_url', + 'default_images_url', + 'default_theme_dir', + 'default_theme_url', + 'default_template', + 'images_url', + 'number_recent_posts', + 'smiley_sets_default', + 'theme_dir', + 'theme_id', + 'theme_layers', + 'theme_templates', + 'theme_url', + 'name', + ); + + // Can't change reserved vars. + if (in_array(strtolower($_GET['var']), $reservedVars)) + redirectexit($settings['images_url'] . '/blank.gif'); + + // Use a specific theme? + if (isset($_GET['th']) || isset($_GET['id'])) + { + // Invalidate the current themes cache too. + cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 60); + + $settings['theme_id'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id']; + } + + // If this is the admin preferences the passed value will just be an element of it. + if ($_GET['var'] == 'admin_preferences') + { + $options['admin_preferences'] = !empty($options['admin_preferences']) ? unserialize($options['admin_preferences']) : array(); + // New thingy... + if (isset($_GET['admin_key']) && strlen($_GET['admin_key']) < 5) + $options['admin_preferences'][$_GET['admin_key']] = $_GET['val']; + + // Change the value to be something nice, + $_GET['val'] = serialize($options['admin_preferences']); + } + + // Update the option. + $smcFunc['db_insert']('replace', + '{db_prefix}themes', + array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), + array($settings['theme_id'], $user_info['id'], $_GET['var'], is_array($_GET['val']) ? implode(',', $_GET['val']) : $_GET['val']), + array('id_theme', 'id_member', 'variable') + ); + + cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 60); + + // Don't output anything... + redirectexit($settings['images_url'] . '/blank.gif'); +} + +function EditTheme() +{ + global $context, $settings, $scripturl, $boarddir, $smcFunc; + + if (isset($_REQUEST['preview'])) + { + // !!! Should this be removed? + die; + } + + isAllowedTo('admin_forum'); + loadTemplate('Themes'); + + $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) @$_GET['id']; + + if (empty($_GET['th'])) + { + $request = $smcFunc['db_query']('', ' + SELECT id_theme, variable, value + FROM {db_prefix}themes + WHERE variable IN ({string:name}, {string:theme_dir}, {string:theme_templates}, {string:theme_layers}) + AND id_member = {int:no_member}', + array( + 'name' => 'name', + 'theme_dir' => 'theme_dir', + 'theme_templates' => 'theme_templates', + 'theme_layers' => 'theme_layers', + 'no_member' => 0, + ) + ); + $context['themes'] = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + if (!isset($context['themes'][$row['id_theme']])) + $context['themes'][$row['id_theme']] = array( + 'id' => $row['id_theme'], + 'num_default_options' => 0, + 'num_members' => 0, + ); + $context['themes'][$row['id_theme']][$row['variable']] = $row['value']; + } + $smcFunc['db_free_result']($request); + + foreach ($context['themes'] as $key => $theme) + { + // There has to be a Settings template! + if (!file_exists($theme['theme_dir'] . '/index.template.php') && !file_exists($theme['theme_dir'] . '/css/index.css')) + unset($context['themes'][$key]); + else + { + if (!isset($theme['theme_templates'])) + $templates = array('index'); + else + $templates = explode(',', $theme['theme_templates']); + + foreach ($templates as $template) + if (file_exists($theme['theme_dir'] . '/' . $template . '.template.php')) + { + // Fetch the header... a good 256 bytes should be more than enough. + $fp = fopen($theme['theme_dir'] . '/' . $template . '.template.php', 'rb'); + $header = fread($fp, 256); + fclose($fp); + + // Can we find a version comment, at all? + if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) + { + $ver = $match[1]; + if (!isset($context['themes'][$key]['version']) || $context['themes'][$key]['version'] > $ver) + $context['themes'][$key]['version'] = $ver; + } + } + + $context['themes'][$key]['can_edit_style'] = file_exists($theme['theme_dir'] . '/css/index.css'); + } + } + + $context['sub_template'] = 'edit_list'; + + return 'no_themes'; + } + + $context['session_error'] = false; + + // Get the directory of the theme we are editing. + $request = $smcFunc['db_query']('', ' + SELECT value, id_theme + FROM {db_prefix}themes + WHERE variable = {string:theme_dir} + AND id_theme = {int:current_theme} + LIMIT 1', + array( + 'current_theme' => $_GET['th'], + 'theme_dir' => 'theme_dir', + ) + ); + list ($theme_dir, $context['theme_id']) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (!file_exists($theme_dir . '/index.template.php') && !file_exists($theme_dir . '/css/index.css')) + fatal_lang_error('theme_edit_missing', false); + + if (!isset($_REQUEST['filename'])) + { + if (isset($_GET['directory'])) + { + if (substr($_GET['directory'], 0, 1) == '.') + $_GET['directory'] = ''; + else + { + $_GET['directory'] = preg_replace(array('~^[\./\\:\0\n\r]+~', '~[\\\\]~', '~/[\./]+~'), array('', '/', '/'), $_GET['directory']); + + $temp = realpath($theme_dir . '/' . $_GET['directory']); + if (empty($temp) || substr($temp, 0, strlen(realpath($theme_dir))) != realpath($theme_dir)) + $_GET['directory'] = ''; + } + } + + if (isset($_GET['directory']) && $_GET['directory'] != '') + { + $context['theme_files'] = get_file_listing($theme_dir . '/' . $_GET['directory'], $_GET['directory'] . '/'); + + $temp = dirname($_GET['directory']); + array_unshift($context['theme_files'], array( + 'filename' => $temp == '.' || $temp == '' ? '/ (..)' : $temp . ' (..)', + 'is_writable' => is_writable($theme_dir . '/' . $temp), + 'is_directory' => true, + 'is_template' => false, + 'is_image' => false, + 'is_editable' => false, + 'href' => $scripturl . '?action=admin;area=theme;th=' . $_GET['th'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=edit;directory=' . $temp, + 'size' => '', + )); + } + else + $context['theme_files'] = get_file_listing($theme_dir, ''); + + $context['sub_template'] = 'edit_browse'; + + return; + } + else + { + if (substr($_REQUEST['filename'], 0, 1) == '.') + $_REQUEST['filename'] = ''; + else + { + $_REQUEST['filename'] = preg_replace(array('~^[\./\\:\0\n\r]+~', '~[\\\\]~', '~/[\./]+~'), array('', '/', '/'), $_REQUEST['filename']); + + $temp = realpath($theme_dir . '/' . $_REQUEST['filename']); + if (empty($temp) || substr($temp, 0, strlen(realpath($theme_dir))) != realpath($theme_dir)) + $_REQUEST['filename'] = ''; + } + + if (empty($_REQUEST['filename'])) + fatal_lang_error('theme_edit_missing', false); + } + + if (isset($_POST['submit'])) + { + if (checkSession('post', '', false) == '') + { + if (is_array($_POST['entire_file'])) + $_POST['entire_file'] = implode("\n", $_POST['entire_file']); + $_POST['entire_file'] = rtrim(strtr($_POST['entire_file'], array("\r" => '', ' ' => "\t"))); + + // Check for a parse error! + if (substr($_REQUEST['filename'], -13) == '.template.php' && is_writable($theme_dir) && @ini_get('display_errors')) + { + $request = $smcFunc['db_query']('', ' + SELECT value + FROM {db_prefix}themes + WHERE variable = {string:theme_url} + AND id_theme = {int:current_theme} + LIMIT 1', + array( + 'current_theme' => $_GET['th'], + 'theme_url' => 'theme_url', + ) + ); + list ($theme_url) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + $fp = fopen($theme_dir . '/tmp_' . session_id() . '.php', 'w'); + fwrite($fp, $_POST['entire_file']); + fclose($fp); + + // !!! Use fetch_web_data()? + $error = @file_get_contents($theme_url . '/tmp_' . session_id() . '.php'); + if (preg_match('~ (\d+)$~i', $error) != 0) + $error_file = $theme_dir . '/tmp_' . session_id() . '.php'; + else + unlink($theme_dir . '/tmp_' . session_id() . '.php'); + } + + if (!isset($error_file)) + { + $fp = fopen($theme_dir . '/' . $_REQUEST['filename'], 'w'); + fwrite($fp, $_POST['entire_file']); + fclose($fp); + + redirectexit('action=admin;area=theme;th=' . $_GET['th'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=edit;directory=' . dirname($_REQUEST['filename'])); + } + } + // Session timed out. + else + { + loadLanguage('Errors'); + + $context['session_error'] = true; + $context['sub_template'] = 'edit_file'; + + // Recycle the submitted data. + $context['entire_file'] = htmlspecialchars($_POST['entire_file']); + + // You were able to submit it, so it's reasonable to assume you are allowed to save. + $context['allow_save'] = true; + + return; + } + } + + $context['allow_save'] = is_writable($theme_dir . '/' . $_REQUEST['filename']); + $context['allow_save_filename'] = strtr($theme_dir . '/' . $_REQUEST['filename'], array($boarddir => '...')); + $context['edit_filename'] = htmlspecialchars($_REQUEST['filename']); + + if (substr($_REQUEST['filename'], -4) == '.css') + { + $context['sub_template'] = 'edit_style'; + + $context['entire_file'] = htmlspecialchars(strtr(file_get_contents($theme_dir . '/' . $_REQUEST['filename']), array("\t" => ' '))); + } + elseif (substr($_REQUEST['filename'], -13) == '.template.php') + { + $context['sub_template'] = 'edit_template'; + + if (!isset($error_file)) + $file_data = file($theme_dir . '/' . $_REQUEST['filename']); + else + { + if (preg_match('~(.+?:.+?).+?(.+?\d+)$~i', $error, $match) != 0) + $context['parse_error'] = $match[1] . $_REQUEST['filename'] . $match[2]; + $file_data = file($error_file); + unlink($error_file); + } + + $j = 0; + $context['file_parts'] = array(array('lines' => 0, 'line' => 1, 'data' => '')); + for ($i = 0, $n = count($file_data); $i < $n; $i++) + { + if (isset($file_data[$i + 1]) && substr($file_data[$i + 1], 0, 9) == 'function ') + { + // Try to format the functions a little nicer... + $context['file_parts'][$j]['data'] = trim($context['file_parts'][$j]['data']) . "\n"; + + if (empty($context['file_parts'][$j]['lines'])) + unset($context['file_parts'][$j]); + $context['file_parts'][++$j] = array('lines' => 0, 'line' => $i + 1, 'data' => ''); + } + + $context['file_parts'][$j]['lines']++; + $context['file_parts'][$j]['data'] .= htmlspecialchars(strtr($file_data[$i], array("\t" => ' '))); + } + + $context['entire_file'] = htmlspecialchars(strtr(implode('', $file_data), array("\t" => ' '))); + } + else + { + $context['sub_template'] = 'edit_file'; + + $context['entire_file'] = htmlspecialchars(strtr(file_get_contents($theme_dir . '/' . $_REQUEST['filename']), array("\t" => ' '))); + } +} + +function get_file_listing($path, $relative) +{ + global $scripturl, $txt, $context; + + // Is it even a directory? + if (!is_dir($path)) + fatal_lang_error('error_invalid_dir', 'critical'); + + $dir = dir($path); + $entries = array(); + while ($entry = $dir->read()) + $entries[] = $entry; + $dir->close(); + + natcasesort($entries); + + $listing1 = array(); + $listing2 = array(); + + foreach ($entries as $entry) + { + // Skip all dot files, including .htaccess. + if (substr($entry, 0, 1) == '.' || $entry == 'CVS') + continue; + + if (is_dir($path . '/' . $entry)) + $listing1[] = array( + 'filename' => $entry, + 'is_writable' => is_writable($path . '/' . $entry), + 'is_directory' => true, + 'is_template' => false, + 'is_image' => false, + 'is_editable' => false, + 'href' => $scripturl . '?action=admin;area=theme;th=' . $_GET['th'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=edit;directory=' . $relative . $entry, + 'size' => '', + ); + else + { + $size = filesize($path . '/' . $entry); + if ($size > 2048 || $size == 1024) + $size = comma_format($size / 1024) . ' ' . $txt['themeadmin_edit_kilobytes']; + else + $size = comma_format($size) . ' ' . $txt['themeadmin_edit_bytes']; + + $listing2[] = array( + 'filename' => $entry, + 'is_writable' => is_writable($path . '/' . $entry), + 'is_directory' => false, + 'is_template' => preg_match('~\.template\.php$~', $entry) != 0, + 'is_image' => preg_match('~\.(jpg|jpeg|gif|bmp|png)$~', $entry) != 0, + 'is_editable' => is_writable($path . '/' . $entry) && preg_match('~\.(php|pl|css|js|vbs|xml|xslt|txt|xsl|html|htm|shtm|shtml|asp|aspx|cgi|py)$~', $entry) != 0, + 'href' => $scripturl . '?action=admin;area=theme;th=' . $_GET['th'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=edit;filename=' . $relative . $entry, + 'size' => $size, + 'last_modified' => timeformat(filemtime($path . '/' . $entry)), + ); + } + } + + return array_merge($listing1, $listing2); +} + +function CopyTemplate() +{ + global $context, $settings, $smcFunc; + + isAllowedTo('admin_forum'); + loadTemplate('Themes'); + + $context[$context['admin_menu_name']]['current_subsection'] = 'edit'; + + $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id']; + + $request = $smcFunc['db_query']('', ' + SELECT th1.value, th1.id_theme, th2.value + FROM {db_prefix}themes AS th1 + LEFT JOIN {db_prefix}themes AS th2 ON (th2.variable = {string:base_theme_dir} AND th2.id_theme = {int:current_theme}) + WHERE th1.variable = {string:theme_dir} + AND th1.id_theme = {int:current_theme} + LIMIT 1', + array( + 'current_theme' => $_GET['th'], + 'base_theme_dir' => 'base_theme_dir', + 'theme_dir' => 'theme_dir', + ) + ); + list ($theme_dir, $context['theme_id'], $base_theme_dir) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (isset($_REQUEST['template']) && preg_match('~[\./\\\\:\0]~', $_REQUEST['template']) == 0) + { + if (!empty($base_theme_dir) && file_exists($base_theme_dir . '/' . $_REQUEST['template'] . '.template.php')) + $filename = $base_theme_dir . '/' . $_REQUEST['template'] . '.template.php'; + elseif (file_exists($settings['default_theme_dir'] . '/' . $_REQUEST['template'] . '.template.php')) + $filename = $settings['default_theme_dir'] . '/' . $_REQUEST['template'] . '.template.php'; + else + fatal_lang_error('no_access', false); + + $fp = fopen($theme_dir . '/' . $_REQUEST['template'] . '.template.php', 'w'); + fwrite($fp, file_get_contents($filename)); + fclose($fp); + + redirectexit('action=admin;area=theme;th=' . $context['theme_id'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=copy'); + } + elseif (isset($_REQUEST['lang_file']) && preg_match('~^[^\./\\\\:\0]\.[^\./\\\\:\0]$~', $_REQUEST['lang_file']) != 0) + { + if (!empty($base_theme_dir) && file_exists($base_theme_dir . '/languages/' . $_REQUEST['lang_file'] . '.php')) + $filename = $base_theme_dir . '/languages/' . $_REQUEST['template'] . '.php'; + elseif (file_exists($settings['default_theme_dir'] . '/languages/' . $_REQUEST['template'] . '.php')) + $filename = $settings['default_theme_dir'] . '/languages/' . $_REQUEST['template'] . '.php'; + else + fatal_lang_error('no_access', false); + + $fp = fopen($theme_dir . '/languages/' . $_REQUEST['lang_file'] . '.php', 'w'); + fwrite($fp, file_get_contents($filename)); + fclose($fp); + + redirectexit('action=admin;area=theme;th=' . $context['theme_id'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=copy'); + } + + $templates = array(); + $lang_files = array(); + + $dir = dir($settings['default_theme_dir']); + while ($entry = $dir->read()) + { + if (substr($entry, -13) == '.template.php') + $templates[] = substr($entry, 0, -13); + } + $dir->close(); + + $dir = dir($settings['default_theme_dir'] . '/languages'); + while ($entry = $dir->read()) + { + if (preg_match('~^([^\.]+\.[^\.]+)\.php$~', $entry, $matches)) + $lang_files[] = $matches[1]; + } + $dir->close(); + + if (!empty($base_theme_dir)) + { + $dir = dir($base_theme_dir); + while ($entry = $dir->read()) + { + if (substr($entry, -13) == '.template.php' && !in_array(substr($entry, 0, -13), $templates)) + $templates[] = substr($entry, 0, -13); + } + $dir->close(); + + if (file_exists($base_theme_dir . '/languages')) + { + $dir = dir($base_theme_dir . '/languages'); + while ($entry = $dir->read()) + { + if (preg_match('~^([^\.]+\.[^\.]+)\.php$~', $entry, $matches) && !in_array($matches[1], $lang_files)) + $lang_files[] = $matches[1]; + } + $dir->close(); + } + } + + natcasesort($templates); + natcasesort($lang_files); + + $context['available_templates'] = array(); + foreach ($templates as $template) + $context['available_templates'][$template] = array( + 'filename' => $template . '.template.php', + 'value' => $template, + 'already_exists' => false, + 'can_copy' => is_writable($theme_dir), + ); + $context['available_language_files'] = array(); + foreach ($lang_files as $file) + $context['available_language_files'][$file] = array( + 'filename' => $file . '.php', + 'value' => $file, + 'already_exists' => false, + 'can_copy' => file_exists($theme_dir . '/languages') ? is_writable($theme_dir . '/languages') : is_writable($theme_dir), + ); + + $dir = dir($theme_dir); + while ($entry = $dir->read()) + { + if (substr($entry, -13) == '.template.php' && isset($context['available_templates'][substr($entry, 0, -13)])) + { + $context['available_templates'][substr($entry, 0, -13)]['already_exists'] = true; + $context['available_templates'][substr($entry, 0, -13)]['can_copy'] = is_writable($theme_dir . '/' . $entry); + } + } + $dir->close(); + + if (file_exists($theme_dir . '/languages')) + { + $dir = dir($theme_dir . '/languages'); + while ($entry = $dir->read()) + { + if (preg_match('~^([^\.]+\.[^\.]+)\.php$~', $entry, $matches) && isset($context['available_language_files'][$matches[1]])) + { + $context['available_language_files'][$matches[1]]['already_exists'] = true; + $context['available_language_files'][$matches[1]]['can_copy'] = is_writable($theme_dir . '/languages/' . $entry); + } + } + $dir->close(); + } + + $context['sub_template'] = 'copy_template'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/ViewQuery.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/ViewQuery.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,191 @@ + + + + ', $context['forum_name_html_safe'], ' + + + + +
    '; + + foreach ($_SESSION['debug'] as $q => $query_data) + { + // Fix the indentation.... + $query_data['q'] = ltrim(str_replace("\r", '', $query_data['q']), "\n"); + $query = explode("\n", $query_data['q']); + $min_indent = 0; + foreach ($query as $line) + { + preg_match('/^(\t*)/', $line, $temp); + if (strlen($temp[0]) < $min_indent || $min_indent == 0) + $min_indent = strlen($temp[0]); + } + foreach ($query as $l => $dummy) + $query[$l] = substr($dummy, $min_indent); + $query_data['q'] = implode("\n", $query); + + // Make the filenames look a bit better. + if (isset($query_data['f'])) + $query_data['f'] = preg_replace('~^' . preg_quote($boarddir, '~') . '~', '...', $query_data['f']); + + $is_select_query = substr(trim($query_data['q']), 0, 6) == 'SELECT'; + if ($is_select_query) + $select = $query_data['q']; + elseif (preg_match('~^INSERT(?: IGNORE)? INTO \w+(?:\s+\([^)]+\))?\s+(SELECT .+)$~s', trim($query_data['q']), $matches) != 0) + { + $is_select_query = true; + $select = $matches[1]; + } + elseif (preg_match('~^CREATE TEMPORARY TABLE .+?(SELECT .+)$~s', trim($query_data['q']), $matches) != 0) + { + $is_select_query = true; + $select = $matches[1]; + } + // Temporary tables created in earlier queries are not explainable. + if ($is_select_query) + { + foreach (array('log_topics_unread', 'topics_posted_in', 'tmp_log_search_topics', 'tmp_log_search_messages') as $tmp) + if (strpos($select, $tmp) !== false) + { + $is_select_query = false; + break; + } + } + + echo ' +
    + + ', nl2br(str_replace("\t", '   ', htmlspecialchars($query_data['q']))), ' +
    '; + + if (!empty($query_data['f']) && !empty($query_data['l'])) + echo sprintf($txt['debug_query_in_line'], $query_data['f'], $query_data['l']); + + if (isset($query_data['s'], $query_data['t']) && isset($txt['debug_query_which_took_at'])) + echo sprintf($txt['debug_query_which_took_at'], round($query_data['t'], 8), round($query_data['s'], 8)); + else + echo sprintf($txt['debug_query_which_took'], round($query_data['t'], 8)); + + echo ' +
    '; + + // Explain the query. + if ($query_id == $q && $is_select_query) + { + $result = $smcFunc['db_query']('', ' + EXPLAIN ' . $select, + array( + ) + ); + if ($result === false) + { + echo ' + + +
    ', $smcFunc['db_error']($db_connection), '
    '; + continue; + } + + echo ' + '; + + $row = $smcFunc['db_fetch_assoc']($result); + + echo ' + + + + '; + + $smcFunc['db_data_seek']($result, 0); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + echo ' + + + + '; + } + $smcFunc['db_free_result']($result); + + echo ' +
    ' . implode('', array_keys($row)) . '
    ' . implode('', $row) . '
    '; + } + } + + echo ' +
    + +'; + + obExit(false); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Who.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Who.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,743 @@ + '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']]; + } + // Did we set a preferred sort order earlier in the session? + elseif (isset($_SESSION['who_online_sort_by'])) + { + $context['sort_by'] = $_SESSION['who_online_sort_by']; + $sort_method = $sort_methods[$_SESSION['who_online_sort_by']]; + } + // Default to last time online. + 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']]; + } + // Perhaps we saved a filter earlier in the session? + 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'; + + // Get the total amount of members online. + $request = $smcFunc['db_query']('', ' + SELECT COUNT(*) + FROM {db_prefix}log_online AS lo + LEFT JOIN {db_prefix}members AS mem ON (lo.id_member = mem.id_member)' . (!empty($conditions) ? ' + WHERE ' . implode(' AND ', $conditions) : ''), + array( + ) + ); + list ($totalMembers) = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + // 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']; + + // Look for people online, provided they don't mind if you see they are. + $request = $smcFunc['db_query']('', ' + SELECT + lo.log_time, lo.id_member, lo.url, INET_NTOA(lo.ip) AS ip, mem.real_name, + lo.session, mg.online_color, IFNULL(mem.show_online, 1) AS show_online, + lo.id_spider + FROM {db_prefix}log_online AS lo + LEFT JOIN {db_prefix}members AS mem ON (lo.id_member = mem.id_member) + LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:regular_member} THEN mem.id_post_group ELSE mem.id_group END)' . (!empty($conditions) ? ' + WHERE ' . implode(' AND ', $conditions) : '') . ' + ORDER BY {raw:sort_method} {raw:sort_direction} + LIMIT {int:offset}, {int:limit}', + array( + 'regular_member' => 0, + 'sort_method' => $sort_method, + 'sort_direction' => $context['sort_direction'] == 'up' ? 'ASC' : 'DESC', + 'offset' => $context['start'], + 'limit' => $modSettings['defaultMaxMembers'], + ) + ); + $context['members'] = array(); + $member_ids = array(); + $url_data = array(); + while ($row = $smcFunc['db_fetch_assoc']($request)) + { + $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'] : '', + // It is *going* to be today or yesterday, so why keep that information in there? + 'time' => strtr(timeformat($row['log_time']), array($txt['today'] => '', $txt['yesterday'] => '')), + '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']; + } + $smcFunc['db_free_result']($request); + + // 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 + ); + } + + $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'); + + // any profile fields disabled? + $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array(); + +} + +function determineActions($urls, $preferred_prefix = false) +{ + global $txt, $user_info, $modSettings, $smcFunc, $context; + + if (!allowedTo('who_view')) + return array(); + loadLanguage('Who'); + + // Actions that require a specific permission level. + $allowedActions = array( + 'admin' => array('moderate_forum', 'manage_membergroups', 'manage_bans', 'admin_forum', 'manage_permissions', 'send_mail', 'manage_attachments', 'manage_smileys', 'manage_boards', 'edit_news'), + 'ban' => array('manage_bans'), + 'boardrecount' => array('admin_forum'), + 'calendar' => array('calendar_view'), + 'editnews' => array('edit_news'), + 'mailing' => array('send_mail'), + 'maintain' => array('admin_forum'), + 'manageattachments' => array('manage_attachments'), + 'manageboards' => array('manage_boards'), + 'mlist' => array('view_mlist'), + 'moderate' => array('access_mod_center', 'moderate_forum', 'manage_membergroups'), + 'optimizetables' => array('admin_forum'), + 'repairboards' => array('admin_forum'), + 'search' => array('search_posts'), + 'search2' => array('search_posts'), + 'setcensor' => array('moderate_forum'), + 'setreserve' => array('moderate_forum'), + 'stats' => array('view_stats'), + 'viewErrorLog' => array('admin_forum'), + 'viewmembers' => array('moderate_forum'), + ); + + if (!is_array($urls)) + $url_list = array(array($urls, $user_info['id'])); + else + $url_list = $urls; + + // These are done to later query these in large chunks. (instead of one by one.) + $topic_ids = array(); + $profile_ids = array(); + $board_ids = array(); + + $data = array(); + foreach ($url_list as $k => $url) + { + // Get the request parameters.. + $actions = @unserialize($url[0]); + if ($actions === false) + continue; + + // If it's the admin or moderation center, and there is an area set, use that instead. + if (isset($actions['action']) && ($actions['action'] == 'admin' || $actions['action'] == 'moderate') && isset($actions['area'])) + $actions['action'] = $actions['area']; + + // Check if there was no action or the action is display. + if (!isset($actions['action']) || $actions['action'] == 'display') + { + // It's a topic! Must be! + if (isset($actions['topic'])) + { + // Assume they can't view it, and queue it up for later. + $data[$k] = $txt['who_hidden']; + $topic_ids[(int) $actions['topic']][$k] = $txt['who_topic']; + } + // It's a board! + elseif (isset($actions['board'])) + { + // Hide first, show later. + $data[$k] = $txt['who_hidden']; + $board_ids[$actions['board']][$k] = $txt['who_board']; + } + // It's the board index!! It must be! + else + $data[$k] = $txt['who_index']; + } + // Probably an error or some goon? + elseif ($actions['action'] == '') + $data[$k] = $txt['who_index']; + // Some other normal action...? + else + { + // Viewing/editing a profile. + if ($actions['action'] == 'profile') + { + // Whose? Their own? + if (empty($actions['u'])) + $actions['u'] = $url[1]; + + $data[$k] = $txt['who_hidden']; + $profile_ids[(int) $actions['u']][$k] = $actions['action'] == 'profile' ? $txt['who_viewprofile'] : $txt['who_profile']; + } + elseif (($actions['action'] == 'post' || $actions['action'] == 'post2') && empty($actions['topic']) && isset($actions['board'])) + { + $data[$k] = $txt['who_hidden']; + $board_ids[(int) $actions['board']][$k] = isset($actions['poll']) ? $txt['who_poll'] : $txt['who_post']; + } + // A subaction anyone can view... if the language string is there, show it. + elseif (isset($actions['sa']) && isset($txt['whoall_' . $actions['action'] . '_' . $actions['sa']])) + $data[$k] = $preferred_prefix && isset($txt[$preferred_prefix . $actions['action'] . '_' . $actions['sa']]) ? $txt[$preferred_prefix . $actions['action'] . '_' . $actions['sa']] : $txt['whoall_' . $actions['action'] . '_' . $actions['sa']]; + // An action any old fellow can look at. (if ['whoall_' . $action] exists, we know everyone can see it.) + elseif (isset($txt['whoall_' . $actions['action']])) + $data[$k] = $preferred_prefix && isset($txt[$preferred_prefix . $actions['action']]) ? $txt[$preferred_prefix . $actions['action']] : $txt['whoall_' . $actions['action']]; + // Viewable if and only if they can see the board... + elseif (isset($txt['whotopic_' . $actions['action']])) + { + // Find out what topic they are accessing. + $topic = (int) (isset($actions['topic']) ? $actions['topic'] : (isset($actions['from']) ? $actions['from'] : 0)); + + $data[$k] = $txt['who_hidden']; + $topic_ids[$topic][$k] = $txt['whotopic_' . $actions['action']]; + } + elseif (isset($txt['whopost_' . $actions['action']])) + { + // Find out what message they are accessing. + $msgid = (int) (isset($actions['msg']) ? $actions['msg'] : (isset($actions['quote']) ? $actions['quote'] : 0)); + + $result = $smcFunc['db_query']('', ' + SELECT m.id_topic, m.subject + FROM {db_prefix}messages AS m + INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) + INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ') + WHERE m.id_msg = {int:id_msg} + AND {query_see_board}' . ($modSettings['postmod_active'] ? ' + AND m.approved = {int:is_approved}' : '') . ' + LIMIT 1', + array( + 'is_approved' => 1, + 'id_msg' => $msgid, + ) + ); + list ($id_topic, $subject) = $smcFunc['db_fetch_row']($result); + $data[$k] = sprintf($txt['whopost_' . $actions['action']], $id_topic, $subject); + $smcFunc['db_free_result']($result); + + if (empty($id_topic)) + $data[$k] = $txt['who_hidden']; + } + // Viewable only by administrators.. (if it starts with whoadmin, it's admin only!) + elseif (allowedTo('moderate_forum') && isset($txt['whoadmin_' . $actions['action']])) + $data[$k] = $txt['whoadmin_' . $actions['action']]; + // Viewable by permission level. + elseif (isset($allowedActions[$actions['action']])) + { + if (allowedTo($allowedActions[$actions['action']])) + $data[$k] = $txt['whoallow_' . $actions['action']]; + else + $data[$k] = $txt['who_hidden']; + } + // Unlisted or unknown action. + else + $data[$k] = $txt['who_unknown']; + } + + // Maybe the action is integrated into another system? + if (count($integrate_actions = call_integration_hook('integrate_whos_online', array($actions))) > 0) + { + foreach ($integrate_actions as $integrate_action) + { + if (!empty($integrate_action)) + { + $data[$k] = $integrate_action; + break; + } + } + } + } + + // Load topic names. + if (!empty($topic_ids)) + { + $result = $smcFunc['db_query']('', ' + SELECT t.id_topic, m.subject + FROM {db_prefix}topics AS t + INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) + INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) + WHERE {query_see_board} + AND t.id_topic IN ({array_int:topic_list})' . ($modSettings['postmod_active'] ? ' + AND t.approved = {int:is_approved}' : '') . ' + LIMIT {int:limit}', + array( + 'topic_list' => array_keys($topic_ids), + 'is_approved' => 1, + 'limit' => count($topic_ids), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Show the topic's subject for each of the actions. + foreach ($topic_ids[$row['id_topic']] as $k => $session_text) + $data[$k] = sprintf($session_text, $row['id_topic'], censorText($row['subject'])); + } + $smcFunc['db_free_result']($result); + } + + // Load board names. + if (!empty($board_ids)) + { + $result = $smcFunc['db_query']('', ' + SELECT b.id_board, b.name + FROM {db_prefix}boards AS b + WHERE {query_see_board} + AND b.id_board IN ({array_int:board_list}) + LIMIT ' . count($board_ids), + array( + 'board_list' => array_keys($board_ids), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // Put the board name into the string for each member... + foreach ($board_ids[$row['id_board']] as $k => $session_text) + $data[$k] = sprintf($session_text, $row['id_board'], $row['name']); + } + $smcFunc['db_free_result']($result); + } + + // Load member names for the profile. + if (!empty($profile_ids) && (allowedTo('profile_view_any') || allowedTo('profile_view_own'))) + { + $result = $smcFunc['db_query']('', ' + SELECT id_member, real_name + FROM {db_prefix}members + WHERE id_member IN ({array_int:member_list}) + LIMIT ' . count($profile_ids), + array( + 'member_list' => array_keys($profile_ids), + ) + ); + while ($row = $smcFunc['db_fetch_assoc']($result)) + { + // If they aren't allowed to view this person's profile, skip it. + if (!allowedTo('profile_view_any') && $user_info['id'] != $row['id_member']) + continue; + + // Set their action on each - session/text to sprintf. + foreach ($profile_ids[$row['id_member']] as $k => $session_text) + $data[$k] = sprintf($session_text, $row['id_member'], $row['real_name']); + } + $smcFunc['db_free_result']($result); + } + + if (!is_array($urls)) + return isset($data[0]) ? $data[0] : false; + else + return $data; +} + +function Credits($in_admin = false) +{ + global $context, $modSettings, $forum_copyright, $forum_version, $boardurl, $txt, $user_info; + + // Don't blink. Don't even blink. Blink and you're dead. + loadLanguage('Who'); + + $context['credits'] = array( + array( + 'pretext' => $txt['credits_intro'], + 'title' => $txt['credits_team'], + 'groups' => array( + array( + 'title' => $txt['credits_groups_ps'], + 'members' => array( + 'Michael "Oldiesmann" Eshom', + 'Amacythe', + 'Jeremy "SleePy" Darwood', + 'Justin "metallica48423" O\'Leary', + ), + ), + array( + 'title' => $txt['credits_groups_dev'], + 'members' => array( + 'Norv', + 'Aaron van Geffen', + 'Antechinus', + 'Bjoern "Bloc" Kristiansen', + 'Hendrik Jan "Compuart" Visser', + 'Juan "JayBachatero" Hernandez', + 'Karl "RegularExpression" Benson', + $user_info['is_admin'] ? 'Matt "Grudge" Wolf': 'Grudge', + 'Michael "Thantos" Miller', + 'Selman "[SiNaN]" Eser', + 'Theodore "Orstio" Hildebrandt', + 'Thorsten "TE" Eurich', + 'winrules', + ), + ), + array( + 'title' => $txt['credits_groups_support'], + 'members' => array( + 'JimM', + 'Adish "(F.L.A.M.E.R)" Patel', + 'Aleksi "Lex" Kilpinen', + 'Ben Scott', + 'Bigguy', + 'CapadY', + 'Chas Large', + 'Duncan85', + 'Eliana Tamerin', + 'Fiery', + 'gbsothere', + 'Harro', + 'Huw', + 'Jan-Olof "Owdy" Eriksson', + 'Jeremy "jerm" Strike', + 'Jessica "Miss All Sunday" Gonzales', + 'K@', + 'Kevin "greyknight17" Hou', + 'KGIII', + 'Kill Em All', + 'Mattitude', + 'Mashby', + 'Mick G.', + 'Michele "Illori" Davis', + 'MrPhil', + 'Nick "Fizzy" Dyer', + 'Nick "Ha²"', + 'Paul_Pauline', + 'Piro "Sarge" Dhima', + 'Rumbaar', + 'Pitti', + 'RedOne', + 'S-Ace', + 'Wade "sησω" Poulsen', + 'xenovanis', + ), + ), + array( + 'title' => $txt['credits_groups_customize'], + 'members' => array( + 'Brad "IchBin™" Grow', + 'ディン1031', + 'Brannon "B" Hall', + 'Bryan "Runic" Deakin', + 'Bulakbol', + 'Colin "Shadow82x" Blaber', + 'Daniel15', + 'Eren Yasarkurt', + 'Gary M. Gadsdon', + 'Jason "JBlaze" Clemons', + 'Jerry', + 'Jonathan "vbgamer45" Valentin', + 'Kays', + 'Killer Possum', + 'Kirby', + 'Matt "SlammedDime" Zuba', + 'Matthew "Labradoodle-360" Kerle', + 'Nibogo', + 'Niko', + 'Peter "Arantor" Spicer', + 'snork13', + 'Spuds', + 'Steven "Fustrate" Hoffman', + 'Joey "Tyrsson" Smith', + ), + ), + array( + 'title' => $txt['credits_groups_docs'], + 'members' => array( + 'Joshua "groundup" Dickerson', + 'AngellinaBelle', + 'Daniel Diehl', + 'Dannii Willis', + 'emanuele', + 'Graeme Spence', + 'Jack "akabugeyes" Thorsen', + 'Jade Elizabeth Trainor', + 'Peter Duggan', + ), + ), + array( + 'title' => $txt['credits_groups_marketing'], + 'members' => array( + 'Kindred', + 'Marcus "cσσкιє мσηѕтєя" Forsberg', + 'Ralph "[n3rve]" Otowo', + 'rickC', + 'Tony Reid', + ), + ), + array( + 'title' => $txt['credits_groups_internationalizers'], + 'members' => array( + 'Relyana', + 'Akyhne', + 'GravuTrad', + ), + ), + array( + 'title' => $txt['credits_groups_servers'], + 'members' => array( + 'Derek Schwab', + 'Liroy "CoreISP" van Hoewijk', + ), + ), + ), + ), + ); + + // Give the translators some credit for their hard work. + if (!empty($txt['translation_credits'])) + $context['credits'][] = array( + 'title' => $txt['credits_groups_translation'], + 'groups' => array( + array( + 'title' => $txt['credits_groups_translation'], + 'members' => $txt['translation_credits'], + ), + ), + ); + + $context['credits'][] = array( + 'title' => $txt['credits_special'], + 'posttext' => $txt['credits_anyone'], + 'groups' => array( + array( + 'title' => $txt['credits_groups_consultants'], + 'members' => array( + 'Brett Flannigan', + 'Mark Rose', + 'René-Gilles "Nao 尚" Deberdt', + ), + ), + array( + 'title' => $txt['credits_groups_beta'], + 'members' => array( + $txt['credits_beta_message'], + ), + ), + array( + 'title' => $txt['credits_groups_translators'], + 'members' => array( + $txt['credits_translators_message'], + ), + ), + array( + 'title' => $txt['credits_groups_founder'], + 'members' => array( + 'Unknown W. "[Unknown]" Brackets', + ), + ), + array( + 'title' => $txt['credits_groups_orignal_pm'], + 'members' => array( + 'Jeff Lewis', + 'Joseph Fung', + 'David Recordon', + ), + ), + ), + ); + + $context['copyrights'] = array( + 'smf' => sprintf($forum_copyright, $forum_version), + + /* Modification Authors: You may add a copyright statement to this array for your mods. + Copyright statements should be in the form of a value only without a array key. I.E.: + 'Some Mod by Thantos © 2010', + $txt['some_mod_copyright'], + */ + 'mods' => array( + ), + ); + + if (!$in_admin) + { + loadTemplate('Who'); + $context['sub_template'] = 'credits'; + $context['robot_no_index'] = true; + $context['page_title'] = $txt['credits']; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Xml.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Xml.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,75 @@ + array( + 'function' => 'GetJumpTo', + ), + 'messageicons' => array( + 'function' => 'ListMessageIcons', + ), + ); + if (!isset($_REQUEST['sa'], $sub_actions[$_REQUEST['sa']])) + fatal_lang_error('no_access', false); + + $sub_actions[$_REQUEST['sa']]['function'](); +} + +// Get a list of boards and categories used for the jumpto dropdown. +function GetJumpTo() +{ + global $user_info, $context, $smcFunc, $sourcedir; + + // Find the boards/cateogories they can see. + require_once($sourcedir . '/Subs-MessageIndex.php'); + $boardListOptions = array( + 'use_permissions' => true, + 'selected_board' => isset($context['current_board']) ? $context['current_board'] : 0, + ); + $context['jump_to'] = getBoardList($boardListOptions); + + // Make the board safe for display. + foreach ($context['jump_to'] as $id_cat => $cat) + { + $context['jump_to'][$id_cat]['name'] = un_htmlspecialchars(strip_tags($cat['name'])); + foreach ($cat['boards'] as $id_board => $board) + $context['jump_to'][$id_cat]['boards'][$id_board]['name'] = un_htmlspecialchars(strip_tags($board['name'])); + } + + $context['sub_template'] = 'jump_to'; +} + +function ListMessageIcons() +{ + global $context, $sourcedir, $board; + + require_once($sourcedir . '/Subs-Editor.php'); + $context['icons'] = getMessageIcons($board); + + $context['sub_template'] = 'message_icons'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,16 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Admin.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Admin.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2112 @@ + +
    +

    '; + + if ($context['user']['is_admin']) + echo ' + +
    + + + + +
    +
    '; + + echo $txt['admin_center'], ' +

    +
    + +
    +
    + ', $txt['hello_guest'], ' ', $context['user']['name'], '! + ', sprintf($txt['admin_main_welcome'], $txt['admin_center'], $txt['help'], $txt['help']), ' +
    +
    + '; + + // Is there an update available? + echo ' +
    '; + + echo ' +
    '; + + // Display the "live news" from simplemachines.org. + echo ' +
    +
    +

    + ', $txt['help'], ' ', $txt['live'], ' +

    +
    +
    + +
    +
    ', $txt['lfyi'], '
    +
    + +
    +
    '; + + // Show the user version information from their server. + echo ' +
    + +
    + +
    +
    + ', $txt['support_versions'], ':
    + ', $txt['support_versions_forum'], ': + ', $context['forum_version'], '
    + ', $txt['support_versions_current'], ': + ??
    + ', $context['can_admin'] ? '' . $txt['version_check_more'] . '' : '', '
    '; + + // Display all the members who can administrate the forum. + echo ' +
    + ', $txt['administrators'], ': + ', implode(', ', $context['administrators']); + // If we have lots of admins... don't show them all. + if (!empty($context['more_admins_link'])) + echo ' + (', $context['more_admins_link'], ')'; + + echo ' +
    +
    + +
    +
    +
    '; + + echo ' +
    + +
    +
      '; + + foreach ($context['quick_admin_tasks'] as $task) + echo ' +
    • + ', !empty($task['icon']) ? '' : '', ' +
      ', $task['link'], '
      + ', $task['description'],' +
    • '; + + echo ' +
    +
    + +
    + +
    '; + + // The below functions include all the scripts needed from the simplemachines.org site. The language and format are passed for internationalization. + if (empty($modSettings['disable_smf_js'])) + echo ' + + '; + + // This sets the announcements and current versions themselves ;). + echo ' + + '; +} + +// Show some support information and credits to those who helped make this. +function template_credits() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Show the user version information from their server. + echo ' + +
    +
    +

    + ', $txt['support_title'], ' +

    +
    +
    + +
    + ', $txt['support_versions'], ':
    + ', $txt['support_versions_forum'], ': + ', $context['forum_version'], '', $context['can_admin'] ? ' ' . $txt['version_check_more'] . '' : '', '
    + ', $txt['support_versions_current'], ': + ??
    '; + + // Display all the variables we have server information for. + foreach ($context['current_versions'] as $version) + echo ' + ', $version['title'], ': + ', $version['version'], '
    '; + + echo ' +
    + +
    + '; + + // Point the admin to common support resources. + echo ' +
    +

    + ', $txt['support_resources'], ' +

    +
    +
    + +
    +

    ', $txt['support_resources_p1'], '

    +

    ', $txt['support_resources_p2'], '

    +
    + +
    '; + + // Display latest support questions from simplemachines.org. + echo ' +
    +

    + ', $txt['help'], ' ', $txt['support_latest'], ' +

    +
    +
    + +
    +
    ', $txt['support_latest_fetch'], '
    +
    + +
    '; + + // The most important part - the credits :P. + echo ' +
    +

    + ', $txt['admin_credits'], ' +

    +
    +
    + +
    '; + + foreach ($context['credits'] as $section) + { + if (isset($section['pretext'])) + echo ' +

    ', $section['pretext'], '

    '; + + echo ' +
    '; + + foreach ($section['groups'] as $group) + { + if (isset($group['title'])) + echo ' +
    + ', $group['title'], ': +
    '; + + echo ' +
    ', implode(', ', $group['members']), '
    '; + } + + echo ' +
    '; + + if (isset($section['posttext'])) + echo ' +

    ', $section['posttext'], '

    '; + } + + echo ' +
    + +
    +
    +
    '; + + // This makes all the support information available to the support script... + echo ' + + + + '; + + // This sets the latest support stuff. + echo ' + '; +} + +// Displays information about file versions installed, and compares them to current version. +function template_view_versions() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    + ', $txt['admin_version_check'], ' +

    +
    +
    ', $txt['version_check_desc'], '
    + + + + + + + + + '; + + // The current version of the core SMF package. + echo ' + + + + + '; + + // Now list all the source file versions, starting with the overall version (if all match!). + echo ' + + + + + + +
    + ', $txt['admin_smffile'], ' + + ', $txt['dvc_your'], ' + + ', $txt['dvc_current'], ' +
    + ', $txt['admin_smfpackage'], ' + + ', $context['forum_version'], ' + + ?? +
    + ', $txt['dvc_sources'], ' + + ?? + + ?? +
    + + + '; + + // Loop through every source file displaying its version - using javascript. + foreach ($context['file_versions'] as $filename => $version) + echo ' + + + + + '; + + // Default template files. + echo ' + +
    + ', $filename, ' + + ', $version, ' + + ?? +
    + + + + + + + + + +
    + ', $txt['dvc_default'], ' + + ?? + + ?? +
    + + + '; + + foreach ($context['default_template_versions'] as $filename => $version) + echo ' + + + + + '; + + // Now the language files... + echo ' + +
    + ', $filename, ' + + ', $version, ' + + ?? +
    + + + + + + + + + +
    + ', $txt['dvc_languages'], ' + + ?? + + ?? +
    + + + '; + + foreach ($context['default_language_versions'] as $language => $files) + { + foreach ($files as $filename => $version) + echo ' + + + + + '; + } + + echo ' + +
    + ', $filename, '.', $language, '.php + + ', $version, ' + + ?? +
    '; + + // Finally, display the version information for the currently selected theme - if it is not the default one. + if (!empty($context['template_versions'])) + { + echo ' + + + + + + + + +
    + ', $txt['dvc_templates'], ' + + ?? + + ?? +
    + + + '; + + foreach ($context['template_versions'] as $filename => $version) + echo ' + + + + + '; + + echo ' + +
    + ', $filename, ' + + ', $version, ' + + ?? +
    '; + } + + echo ' +
    +
    '; + + /* Below is the hefty javascript for this. Upon opening the page it checks the current file versions with ones + held at simplemachines.org and works out if they are up to date. If they aren't it colors that files number + red. It also contains the function, swapOption, that toggles showing the detailed information for each of the + file categories. (sources, languages, and templates.) */ + echo ' + + + '; + +} + +// Form for stopping people using naughty words, etc. +function template_edit_censored() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // First section is for adding/removing words from the censored list. + echo ' +
    +
    +
    +

    + ', $txt['admin_censored_words'], ' +

    +
    +
    + +
    +

    ', $txt['admin_censored_where'], '

    '; + + // Show text boxes for censoring [bad ] => [good ]. + foreach ($context['censored_words'] as $vulgar => $proper) + echo ' +
    =>
    '; + + // Now provide a way to censor more words. + echo ' + +
    + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    + +
    '; + + // This table lets you test out your filters by typing in rude words and seeing what comes out. + echo ' +
    +

    + ', $txt['censor_test'], ' +

    +
    +
    + +
    +

    + + +

    +
    + +
    + + +
    +
    +
    '; +} + +// Maintenance is a lovely thing, isn't it? +function template_not_done() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    + ', $txt['not_done_title'], ' +

    +
    +
    + +
    + ', $txt['not_done_reason']; + + if (!empty($context['continue_percent'])) + echo ' +
    +
    +
    ', $context['continue_percent'], '%
    +
     
    +
    +
    '; + + if (!empty($context['substep_enabled'])) + echo ' +
    + ', $context['substep_title'], ' +
    +
    ', $context['substep_continue_percent'], '%
    +
     
    +
    +
    '; + + echo ' +
    +
    + ', $context['continue_post_data'], ' +
    +
    + +
    +
    +
    + '; +} + +// Template for showing settings (Of any kind really!) +function template_show_settings() +{ + global $context, $txt, $settings, $scripturl; + + echo ' + '; + + if (!empty($context['settings_insert_above'])) + echo $context['settings_insert_above']; + + echo ' +
    +
    '; + + // Is there a custom title? + if (isset($context['settings_title'])) + echo ' +
    +

    + ', $context['settings_title'], ' +

    +
    '; + + // Have we got some custom code to insert? + if (!empty($context['settings_message'])) + echo ' +
    ', $context['settings_message'], '
    '; + + // Now actually loop through all the variables. + $is_open = false; + foreach ($context['config_vars'] as $config_var) + { + // Is it a title or a description? + if (is_array($config_var) && ($config_var['type'] == 'title' || $config_var['type'] == 'desc')) + { + // Not a list yet? + if ($is_open) + { + $is_open = false; + echo ' + +
    + + '; + } + + // A title? + if ($config_var['type'] == 'title') + { + echo ' +
    +

    + ', ($config_var['help'] ? '' . $txt['help'] . '' : ''), ' + ', $config_var['label'], ' +

    +
    '; + } + // A description? + else + { + echo ' +

    + ', $config_var['label'], ' +

    '; + } + + continue; + } + + // Not a list yet? + if (!$is_open) + { + $is_open = true; + echo ' +
    + +
    +
    '; + } + + // Hang about? Are you pulling my leg - a callback?! + if (is_array($config_var) && $config_var['type'] == 'callback') + { + if (function_exists('template_callback_' . $config_var['name'])) + call_user_func('template_callback_' . $config_var['name']); + + continue; + } + + if (is_array($config_var)) + { + // First off, is this a span like a message? + if (in_array($config_var['type'], array('message', 'warning'))) + { + echo ' + + ', $config_var['label'], ' + '; + } + // Otherwise it's an input box of some kind. + else + { + echo ' + '; + + // Some quick helpers... + $javascript = $config_var['javascript']; + $disabled = !empty($config_var['disabled']) ? ' disabled="disabled"' : ''; + $subtext = !empty($config_var['subtext']) ? '
    ' . $config_var['subtext'] . '' : ''; + + // Show the [?] button. + if ($config_var['help']) + echo ' + ', $txt['help'], '', $subtext, ($config_var['type'] == 'password' ? '
    ' . $txt['admin_confirm_password'] . '' : ''), ' + '; + else + echo ' + ', $subtext, ($config_var['type'] == 'password' ? '
    ' . $txt['admin_confirm_password'] . '' : ''), ' + '; + + echo ' + ', + $config_var['preinput']; + + // Show a check box. + if ($config_var['type'] == 'check') + echo ' + '; + // Escape (via htmlspecialchars.) the text box. + elseif ($config_var['type'] == 'password') + echo ' +
    + '; + // Show a selection box. + elseif ($config_var['type'] == 'select') + { + echo ' + '; + } + // Text area? + elseif ($config_var['type'] == 'large_text') + echo ' + '; + // Permission group? + elseif ($config_var['type'] == 'permissions') + theme_inline_permissions($config_var['name']); + // BBC selection? + elseif ($config_var['type'] == 'bbc') + { + echo ' +
    + ', $txt['bbcTagsToUse_select'], ' +
      '; + + foreach ($context['bbc_columns'] as $bbcColumn) + { + foreach ($bbcColumn as $bbcTag) + echo ' +
    • + ', $bbcTag['show_help'] ? ' (?)' : '', ' +
    • '; + } + echo '
    + +
    '; + } + // A simple message? + elseif ($config_var['type'] == 'var_message') + echo ' + ', $config_var['var_message'], '
    '; + // Assume it must be a text box. + else + echo ' + '; + + echo isset($config_var['postinput']) ? ' + ' . $config_var['postinput'] : '', + ''; + } + } + + else + { + // Just show a separator. + if ($config_var == '') + echo ' + +
    +
    '; + else + echo ' +
    + ' . $config_var . ' +
    '; + } + } + + if ($is_open) + echo ' +
    '; + + if (empty($context['settings_save_dont_show'])) + echo ' +
    +
    + +
    '; + + if ($is_open) + echo ' +
    + + '; + + echo ' + + + +
    '; + + if (!empty($context['settings_post_javascript'])) + echo ' + '; + + if (!empty($context['settings_insert_below'])) + echo $context['settings_insert_below']; +} + +// Template for showing custom profile fields. +function template_show_custom_profile() +{ + global $context, $txt, $settings, $scripturl; + + // Standard fields. + template_show_list('standard_profile_fields'); + + echo ' +
    '; + + // Custom fields. + template_show_list('custom_profile_fields'); +} + +// Edit a profile field? +function template_edit_profile_field() +{ + global $context, $txt, $settings, $scripturl; + + // All the javascript for this page - quite a bit! + echo ' + '; + + echo ' +
    +
    +
    +

    + ', $context['page_title'], ' +

    +
    +
    + +
    +
    + ', $txt['custom_edit_general'], ' + +
    +
    + ', $txt['custom_edit_name'], ': +
    +
    + +
    +
    + ', $txt['custom_edit_desc'], ': +
    +
    + +
    +
    + ', $txt['custom_edit_profile'], ':
    + ', $txt['custom_edit_profile_desc'], ' +
    +
    + +
    +
    + ', $txt['custom_edit_registration'], ': +
    +
    + +
    +
    + ', $txt['custom_edit_display'], ': +
    +
    + +
    + +
    + ', $txt['custom_edit_placement'], ': +
    +
    + +
    +
    + ', $txt['help'], ' + ', $txt['custom_edit_enclose'], ':
    + ', $txt['custom_edit_enclose_desc'], ' +
    +
    + +
    +
    +
    +
    + ', $txt['custom_edit_input'], ' +
    +
    + ', $txt['custom_edit_picktype'], ': +
    +
    + +
    +
    + ', $txt['custom_edit_max_length'], ':
    + ', $txt['custom_edit_max_length_desc'], ' +
    +
    + +
    +
    + ', $txt['custom_edit_dimension'], ': +
    +
    + ', $txt['custom_edit_dimension_row'], ': + ', $txt['custom_edit_dimension_col'], ': +
    +
    + ', $txt['custom_edit_bbc'], ' +
    +
    + +
    +
    + ', $txt['help'], ' + ', $txt['custom_edit_options'], ':
    + ', $txt['custom_edit_options_desc'], ' +
    +
    +
    '; + + foreach ($context['field']['options'] as $k => $option) + { + echo ' + ', $k == 0 ? '' : '
    ', ''; + } + echo ' + + [', $txt['custom_edit_options_more'], '] +
    +
    +
    + ', $txt['custom_edit_default'], ': +
    +
    + +
    +
    +
    +
    + ', $txt['custom_edit_advanced'], ' +
    +
    + ', $txt['help'], ' + ', $txt['custom_edit_mask'], ':
    + ', $txt['custom_edit_mask_desc'], ' +
    +
    + +
    + + + +
    +
    + ', $txt['custom_edit_privacy'], ': + ', $txt['custom_edit_privacy_desc'], ' +
    +
    + +
    +
    + ', $txt['custom_edit_can_search'], ':
    + ', $txt['custom_edit_can_search_desc'], ' +
    +
    + +
    +
    + ', $txt['custom_edit_active'], ':
    + ', $txt['custom_edit_active_desc'], ' +
    +
    + +
    +
    +
    +
    + '; + + if ($context['fid']) + echo ' + '; + + echo ' +
    +
    + +
    + +
    +
    +
    '; + + // Get the javascript bits right! + echo ' + '; +} + +// Results page for an admin search. +function template_admin_search_results() +{ + global $context, $txt, $settings, $options, $scripturl; + + echo ' +
    +

    + +
    + + + +
    +
    +  ', sprintf($txt['admin_search_results_desc'], $context['search_term']), ' +

    +
    +
    + +
    '; + + if (empty($context['search_results'])) + { + echo ' +

    ', $txt['admin_search_results_none'], '

    '; + } + else + { + echo ' +
      '; + foreach ($context['search_results'] as $result) + { + // Is it a result from the online manual? + if ($context['search_type'] == 'online') + { + echo ' +
    1. +

      + ', $result['messages'][0]['subject'], ' +
      ', $result['category']['name'], '  /  + ', $result['board']['name'], ' / +

      +

      + ', $result['messages'][0]['body'], ' +

      +
    2. '; + } + // Otherwise it's... not! + else + { + echo ' +
    3. + ', $result['name'], ' [', isset($txt['admin_search_section_' . $result['type']]) ? $txt['admin_search_section_' . $result['type']] : $result['type'] , ']'; + + if ($result['help']) + echo ' +

      ', $result['help'], '

      '; + + echo ' +
    4. '; + } + } + echo ' +
    '; + } + + echo ' +
    + +
    +
    '; +} + +// Turn on and off certain key features. +function template_core_features() +{ + global $context, $txt, $settings, $options, $scripturl; + + echo ' + +
    '; + if ($context['is_new_install']) + { + echo ' +
    +

    + ', $txt['core_settings_welcome_msg'], ' +

    +
    +
    + ', $txt['core_settings_welcome_msg_desc'], ' +
    '; + } + + echo ' +
    +
    +

    + ', $txt['core_settings_title'], ' +

    +
    '; + + $alternate = true; + foreach ($context['features'] as $id => $feature) + { + echo ' +
    + +
    + ', $feature['title'], ' + +

    ', ($feature['enabled'] && $feature['url'] ? '' . $feature['title'] . '' : $feature['title']), '

    +

    ', $feature['desc'], '

    +
    + + +
    +
    + +
    '; + + $alternate = !$alternate; + } + + echo ' +
    + + + +
    +
    +
    +
    '; + + // Turn on the pretty javascript if we can! + echo ' + '; +} + +// Add a new language +function template_add_language() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $txt['add_language'], ' +

    +
    +
    + +
    +
    + ', $txt['add_language_smf'], ' + + '; + + if (!empty($context['smf_error'])) + echo ' +
    ', $txt['add_language_error_' . $context['smf_error']], '
    '; + + echo ' + +
    +
    + ', $context['browser']['is_ie'] ? ' ' : '', ' + +
    +
    + +
    + '; + + // Had some results? + if (!empty($context['smf_languages'])) + { + echo ' +
    ', $txt['add_language_smf_found'], '
    + + + + + + + + + + + + '; + + foreach ($context['smf_languages'] as $language) + echo ' + + + + + + + '; + + echo ' + +
    ', $txt['name'], '', $txt['add_language_smf_desc'], '', $txt['add_language_smf_version'], '', $txt['add_language_smf_utf8'], '', $txt['add_language_smf_install'], '
    ', $language['name'], '', $language['description'], '', $language['version'], '', $language['utf8'] ? $txt['yes'] : $txt['no'], '', $txt['add_language_smf_install'], '
    '; + } + + echo ' +
    +
    +
    '; +} + +// Download a new language file? +function template_download_language() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // Actually finished? + if (!empty($context['install_complete'])) + { + echo ' +
    +
    +

    + ', $txt['languages_download_complete'], ' +

    +
    +
    + +
    + ', $context['install_complete'], ' +
    + +
    +
    +
    '; + return; + } + + // An error? + if (!empty($context['error_message'])) + echo ' +
    +

    ', $context['error_message'], '

    +
    '; + + // Provide something of an introduction... + echo ' +
    +
    +
    +

    + ', $txt['languages_download'], ' +

    +
    +
    + +
    +

    + ', $txt['languages_download_note'], ' +

    +
    + ', $txt['languages_download_info'], ' +
    +
    + +
    '; + + // Show the main files. + template_show_list('lang_main_files_list'); + + // Now, all the images and the likes, hidden via javascript 'cause there are so fecking many. + echo ' +
    +
    +

    + ', $txt['languages_download_theme_files'], ' +

    +
    + + + + + + + + + + '; + + foreach ($context['files']['images'] as $theme => $group) + { + $count = 0; + echo ' + + + '; + + $alternate = false; + foreach ($group as $file) + { + echo ' + + + + + + '; + $alternate = !$alternate; + } + } + + echo ' + +
    + ', $txt['languages_download_filename'], ' + + ', $txt['languages_download_writable'], ' + + ', $txt['languages_download_exists'], ' + + ', $txt['languages_download_copy'], ' +
    + * ', isset($context['theme_names'][$theme]) ? $context['theme_names'][$theme] : $theme, ' +
    + ', $file['name'], '
    + ', $txt['languages_download_dest'], ': ', $file['destination'], ' +
    + ', ($file['writable'] ? $txt['yes'] : $txt['no']), ' + + ', $file['exists'] ? ($file['exists'] == 'same' ? $txt['languages_download_exists_same'] : $txt['languages_download_exists_different']) : $txt['no'], ' + + +
    '; + + // Do we want some FTP baby? + if (!empty($context['still_not_writable'])) + { + if (!empty($context['package_ftp']['error'])) + echo ' +
    + ', $context['package_ftp']['error'], ' +
    '; + + echo ' +
    +

    + ', $txt['package_ftp_necessary'], ' +

    +
    +
    + +
    +

    ', $txt['package_ftp_why'],'

    +
    +
    ', $txt['package_ftp_server'], ': +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    +
    +
    + +
    '; + } + + // Install? + echo ' +
    + + +
    +
    +
    +
    '; + + // The javascript for expand and collapse of sections. + echo ' + '; +} + +// Edit some language entries? +function template_modify_language_entries() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $txt['edit_languages'], ' +

    +
    '; + + // Not writable? + if ($context['lang_file_not_writable_message']) + echo ' +
    +

    ', $context['lang_file_not_writable_message'], '

    +
    '; + + echo ' +
    + ', $txt['edit_language_entries_primary'], ' +
    +
    + +
    +
    + ', $context['primary_settings']['name'], ' +
    +
    + ', $txt['languages_character_set'], ': +
    +
    + +
    +
    + ', $txt['languages_locale'], ': +
    +
    + +
    +
    + ', $txt['languages_dictionary'], ': +
    +
    + +
    +
    + ', $txt['languages_spelling'], ': +
    +
    + +
    +
    + ', $txt['languages_rtl'], ': +
    +
    + +
    +
    +
    +
    + + '; + + // English can't be deleted. + if ($context['lang_id'] != 'english') + echo ' + '; + + echo ' +
    +
    + +
    +
    + +
    +
    +

    + ', $txt['edit_language_entries'], ' +

    +
    +
    + ', $txt['edit_language_entries_file'], ': + + + +
    '; + + // Is it not writable? + if (!empty($context['entries_not_writable_message'])) + echo ' +
    + ', $context['entries_not_writable_message'], ' +
    '; + + // Already have some? + if (!empty($context['file_entries'])) + { + echo ' +
    + +
    +
    '; + + $cached = array(); + foreach ($context['file_entries'] as $entry) + { + // Do it in two's! + if (empty($cached)) + { + $cached = $entry; + continue; + } + + echo ' +
    + ', $cached['key'], ' +
    +
    + ', $entry['key'], ' +
    +
    + + +
    +
    + + +
    '; + $cached = array(); + } + + // Odd number? + if (!empty($cached)) + echo ' + +
    + ', $cached['key'], ' +
    +
    +
    +
    + + +
    +
    +
    '; + + echo ' +
    + '; + + echo ' +
    + +
    '; + } + echo ' +
    +
    +
    '; +} + +// This little beauty shows questions and answer from the captcha type feature. +function template_callback_question_answer_list() +{ + global $txt, $context; + + echo ' +
    + ', $txt['setup_verification_question'], ' +
    +
    + ', $txt['setup_verification_answer'], ' +
    '; + + foreach ($context['question_answers'] as $data) + echo ' + +
    + +
    +
    + +
    '; + + // Some blank ones. + for ($count = 0; $count < 3; $count++) + echo ' +
    + +
    +
    + +
    '; + + echo ' +
    +
    '; + + // The javascript needs to go at the end but we'll put it in this template for looks. + $context['settings_post_javascript'] .= ' + // Create a named element dynamically - thanks to: http://www.thunderguy.com/semicolon/2005/05/23/setting-the-name-attribute-in-internet-explorer/ + function createNamedElement(type, name, customFields) + { + var element = null; + + if (!customFields) + customFields = ""; + + // Try the IE way; this fails on standards-compliant browsers + try + { + element = document.createElement("<" + type + \' name="\' + name + \'" \' + customFields + ">"); + } + catch (e) + { + } + if (!element || element.nodeName != type.toUpperCase()) + { + // Non-IE browser; use canonical method to create named element + element = document.createElement(type); + element.name = name; + } + + return element; + } + + var placeHolder = document.getElementById(\'add_more_question_placeholder\'); + + function addAnotherQuestion() + { + var newDT = document.createElement("dt"); + + var newInput = createNamedElement("input", "question[]"); + newInput.type = "text"; + newInput.className = "input_text"; + newInput.size = "50"; + newInput.setAttribute("class", "verification_question"); + newDT.appendChild(newInput); + + newDD = document.createElement("dd"); + + newInput = createNamedElement("input", "answer[]"); + newInput.type = "text"; + newInput.className = "input_text"; + newInput.size = "50"; + newInput.setAttribute("class", "verification_answer"); + newDD.appendChild(newInput); + + placeHolder.parentNode.insertBefore(newDT, placeHolder); + placeHolder.parentNode.insertBefore(newDD, placeHolder); + } + document.getElementById(\'add_more_link_div\').style.display = \'\'; + '; +} + +// Repairing boards. +function template_repair_boards() +{ + global $context, $txt, $scripturl; + + echo ' +
    +
    +

    ', + $context['error_search'] ? $txt['errors_list'] : $txt['errors_fixing'] , ' +

    +
    +
    + +
    '; + + // Are we actually fixing them, or is this just a prompt? + if ($context['error_search']) + { + if (!empty($context['to_fix'])) + { + echo ' + ', $txt['errors_found'], ': +
      '; + + foreach ($context['repair_errors'] as $error) + echo ' +
    • + ', $error, ' +
    • '; + + echo ' +
    +

    + ', $txt['errors_fix'], ' +

    +

    + ', $txt['yes'], ' - ', $txt['no'], ' +

    '; + } + else + echo ' +

    ', $txt['maintain_no_errors'], '

    +

    + ', $txt['maintain_return'], ' +

    '; + + } + else + { + if (!empty($context['redirect_to_recount'])) + { + echo ' +

    + ', $txt['errors_do_recount'], ' +

    +
    + + +
    '; + } + else + { + echo ' +

    ', $txt['errors_fixed'], '

    +

    + ', $txt['maintain_return'], ' +

    '; + } + } + + echo ' +
    + +
    +
    +
    '; + + if (!empty($context['redirect_to_recount'])) + { + echo ' + '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/BoardIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/BoardIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,508 @@ + + ', $txt['members'], ': ', $context['common_stats']['total_members'], '  •  ', $txt['posts_made'], ': ', $context['common_stats']['total_posts'], '  •  ', $txt['topics'], ': ', $context['common_stats']['total_topics'], ' + ', ($settings['show_latest_member'] ? ' ' . $txt['welcome_member'] . ' ' . $context['common_stats']['latest_member']['link'] . '' . $txt['newest_member'] : '') , ' + '; + + // Show the news fader? (assuming there are things to show...) + if ($settings['show_newsfader'] && !empty($context['fader_news_lines'])) + { + echo ' +
    +
    +

    + + ', $txt['news'], ' +

    +
    + +
    + + '; + } + + echo ' +
    + '; + + /* Each category in categories is made up of: + id, href, link, name, is_collapsed (is it collapsed?), can_collapse (is it okay if it is?), + new (is it new?), collapse_href (href to collapse/expand), collapse_image (up/down image), + and boards. (see below.) */ + foreach ($context['categories'] as $category) + { + // If theres no parent boards we can see, avoid showing an empty category (unless its collapsed) + if (empty($category['boards']) && !$category['is_collapsed']) + continue; + + echo ' + + + + + '; + + // Assuming the category hasn't been collapsed... + if (!$category['is_collapsed']) + { + + echo ' + '; + /* Each board in each category's boards has: + new (is it new?), id, name, description, moderators (see below), link_moderators (just a list.), + children (see below.), link_children (easier to use.), children_new (are they new?), + topics (# of), posts (# of), link, href, and last_post. (see below.) */ + foreach ($category['boards'] as $board) + { + echo ' + + + + + + '; + // Show the "Child Boards: ". (there's a link_children but we're going to bold the new ones...) + if (!empty($board['children'])) + { + // Sort the links into an array with new boards bold so it can be imploded. + $children = array(); + /* Each child in each board's children has: + id, name, description, new (is it new?), topics (#), posts (#), href, link, and last_post. */ + foreach ($board['children'] as $child) + { + if (!$child['is_redirect']) + $child['link'] = '' . $child['name'] . ($child['new'] ? '' : '') . ''; + else + $child['link'] = '' . $child['name'] . ''; + + // Has it posts awaiting approval? + if ($child['can_approve_posts'] && ($child['unapproved_posts'] || $child['unapproved_topics'])) + $child['link'] .= ' (!)'; + + $children[] = $child['new'] ? '' . $child['link'] . '' : $child['link']; + } + echo ' + + + '; + } + } + echo ' + '; + } + echo ' + + + + + '; + } + echo ' +
    +
    +

    '; + + // If this category even can collapse, show a link to collapse it. + if ($category['can_collapse']) + echo ' + ', $category['collapse_image'], ''; + + if (!$context['user']['is_guest'] && !empty($category['show_unread'])) + echo ' + ', $txt['view_unread_category'], ''; + + echo ' + ', $category['link'], ' +

    +
    +
    + '; + + // If the board or children is new, show an indicator. + if ($board['new'] || $board['children_new']) + echo ' + ', $txt['new_posts'], ''; + // Is it a redirection board? + elseif ($board['is_redirect']) + echo ' + *'; + // No new posts at all! The agony!! + else + echo ' + ', $txt['old_posts'], ''; + + echo ' + + + ', $board['name'], ''; + + // Has it outstanding posts for approval? + if ($board['can_approve_posts'] && ($board['unapproved_posts'] || $board['unapproved_topics'])) + echo ' + (!)'; + + echo ' + +

    ', $board['description'] , '

    '; + + // Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.) + if (!empty($board['moderators'])) + echo ' +

    ', count($board['moderators']) == 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $board['link_moderators']), '

    '; + + // Show some basic information about the number of posts, etc. + echo ' +
    +

    ', comma_format($board['posts']), ' ', $board['is_redirect'] ? $txt['redirects'] : $txt['posts'], '
    + ', $board['is_redirect'] ? '' : comma_format($board['topics']) . ' ' . $txt['board_topics'], ' +

    +
    '; + + /* The board's and children's 'last_post's have: + time, timestamp (a number that represents the time.), id (of the post), topic (topic id.), + link, href, subject, start (where they should go for the first unread post.), + and member. (which has id, name, link, href, username in it.) */ + if (!empty($board['last_post']['id'])) + echo ' +

    ', $txt['last_post'], ' ', $txt['by'], ' ', $board['last_post']['member']['link'] , '
    + ', $txt['in'], ' ', $board['last_post']['link'], '
    + ', $txt['on'], ' ', $board['last_post']['time'],' +

    '; + echo ' +
    + ', $txt['parent_boards'], ': ', implode(', ', $children), ' +
    +
    '; + + if ($context['user']['is_logged']) + { + echo ' +
    '; + + // Mark read button. + $mark_read_button = array( + 'markread' => array('text' => 'mark_as_read', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=all;' . $context['session_var'] . '=' . $context['session_id']), + ); + + echo ' +
      +
    • ', $txt['new_posts'], '
    • +
    • ', $txt['old_posts'], '
    • +
    • ', $txt['redirect_board'], '
    • +
    +
    '; + + // Show the mark all as read button? + if ($settings['show_mark_read'] && !empty($context['categories'])) + echo '
    ', template_button_strip($mark_read_button, 'right'), '
    '; + } + else + { + echo ' +
    +
      +
    • ', $txt['old_posts'], '
    • +
    • ', $txt['redirect_board'], '
    • +
    +
    '; + } + + template_info_center(); +} + +function template_info_center() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // Here's where the "Info Center" starts... + echo ' + +
    +
    +

    + + ', sprintf($txt['info_center_title'], $context['forum_name_html_safe']), ' +

    +
    + +
    + '; + + // Info center collapse object. + echo ' + '; +} +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Calendar.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Calendar.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,824 @@ + +
    + ', template_show_month_grid('prev'), ' + ', template_show_month_grid('current'), ' + ', template_show_month_grid('next'), ' +
    +
    + ', $context['view_week'] ? template_show_week_grid('main') : template_show_month_grid('main'); + + // Build the calendar button array. + $calendar_buttons = array( + 'post_event' => array('test' => 'can_post', 'text' => 'calendar_post_event', 'image' => 'calendarpe.gif', 'lang' => true, 'url' => $scripturl . '?action=calendar;sa=post;month=' . $context['current_month'] . ';year=' . $context['current_year'] . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + template_button_strip($calendar_buttons, 'right'); + + // Show some controls to allow easy calendar navigation. + echo ' +
    + + + '; + + echo ' +
    +
    +
    + '; +} + +// Template for posting a calendar event. +function template_event_post() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // Start the javascript for drop down boxes... + echo ' + + +
    '; + + if (!empty($context['event']['new'])) + echo ' + '; + + // Start the main table. + echo ' +
    +
    +

    + ', $context['page_title'], ' +

    +
    '; + + if (!empty($context['post_error']['messages'])) + { + echo ' +
    +
    +
    + ', $context['error_type'] == 'serious' ? '' . $txt['error_while_submitting'] . '' : '', ' +
    +
    + ', implode('
    ', $context['post_error']['messages']), ' +
    +
    +
    '; + } + + echo ' +
    + +
    +
    + ', $txt['calendar_event_title'], ' + +
    + ', $txt['calendar_year'], ' + + ', $txt['calendar_month'], ' + + ', $txt['calendar_day'], ' + +
    +
    '; + + if (!empty($modSettings['cal_allowspan']) || $context['event']['new']) + echo ' +
    + ', $txt['calendar_event_options'], ' +
    +
      '; + + // If events can span more than one day then allow the user to select how long it should last. + if (!empty($modSettings['cal_allowspan'])) + { + echo ' +
    • + ', $txt['calendar_numb_days'], ' + +
    • '; + } + + // If this is a new event let the user specify which board they want the linked post to be put into. + if ($context['event']['new']) + { + echo ' +
    • + ', $txt['calendar_link_event'], ' + +
    • +
    • + ', $txt['calendar_post_in'], ' + +
    • '; + } + + if (!empty($modSettings['cal_allowspan']) || $context['event']['new']) + echo ' +
    +
    +
    '; + + echo ' +
    + '; + // Delete button? + if (empty($context['event']['new'])) + echo ' + '; + + echo ' + + +
    +
    + +
    +
    +
    +
    '; +} + +// Display a monthly calendar grid. +function template_show_month_grid($grid_name) +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings, $smcFunc; + + if (!isset($context['calendar_grid_' . $grid_name])) + return false; + + $calendar_data = &$context['calendar_grid_' . $grid_name]; + $colspan = !empty($calendar_data['show_week_links']) ? 8 : 7; + + if (empty($calendar_data['disable_title'])) + { + echo ' +
    +

    '; + + if (empty($calendar_data['previous_calendar']['disabled']) && $calendar_data['show_next_prev']) + echo ' + «'; + + if (empty($calendar_data['next_calendar']['disabled']) && $calendar_data['show_next_prev']) + echo ' + »'; + + if ($calendar_data['show_next_prev']) + echo ' + ', $txt['months_titles'][$calendar_data['current_month']], ' ', $calendar_data['current_year']; + else + echo ' + ', $txt['months_titles'][$calendar_data['current_month']], ' ', $calendar_data['current_year'], ''; + + echo ' +

    +
    '; + } + + echo ' + '; + + // Show each day of the week. + if (empty($calendar_data['disable_day_titles'])) + { + echo ' + '; + + if (!empty($calendar_data['show_week_links'])) + echo ' + '; + + foreach ($calendar_data['week_days'] as $day) + { + echo ' + '; + } + echo ' + '; + } + + /* Each week in weeks contains the following: + days (a list of days), number (week # in the year.) */ + foreach ($calendar_data['weeks'] as $week) + { + echo ' + '; + + if (!empty($calendar_data['show_week_links'])) + echo ' + '; + + /* Every day has the following: + day (# in month), is_today (is this day *today*?), is_first_day (first day of the week?), + holidays, events, birthdays. (last three are lists.) */ + foreach ($week['days'] as $day) + { + // If this is today, make it a different color and show a border. + echo ' + '; + } + + echo ' + '; + } + + echo ' +
     ', !empty($calendar_data['short_day_titles']) ? ($smcFunc['substr']($txt['days'][$day], 0, 1)) : $txt['days'][$day], '
    + » + '; + + // Skip it if it should be blank - it's not a day if it has no number. + if (!empty($day['day'])) + { + // Should the day number be a link? + if (!empty($modSettings['cal_daysaslink']) && $context['can_post']) + echo ' + ', $day['day'], ''; + else + echo ' + ', $day['day']; + + // Is this the first day of the week? (and are we showing week numbers?) + if ($day['is_first_day'] && $calendar_data['size'] != 'small') + echo ' - ', $txt['calendar_week'], ' ', $week['number'], ''; + + // Are there any holidays? + if (!empty($day['holidays'])) + echo ' +
    ', $txt['calendar_prompt'], ' ', implode(', ', $day['holidays']), '
    '; + + // Show any birthdays... + if (!empty($day['birthdays'])) + { + echo ' +
    + ', $txt['birthdays'], ''; + + /* Each of the birthdays has: + id, name (person), age (if they have one set?), and is_last. (last in list?) */ + $use_js_hide = empty($context['show_all_birthdays']) && count($day['birthdays']) > 15; + $count = 0; + foreach ($day['birthdays'] as $member) + { + echo ' + ', $member['name'], isset($member['age']) ? ' (' . $member['age'] . ')' : '', '', $member['is_last'] || ($count == 10 && $use_js_hide)? '' : ', '; + + // Stop at ten? + if ($count == 10 && $use_js_hide) + echo '...
    (', sprintf($txt['calendar_click_all'], count($day['birthdays'])), ')
    '; + + echo ' +
    '; + } + + // Any special posted events? + if (!empty($day['events'])) + { + echo ' +
    + ', $txt['events'], ''; + + /* The events are made up of: + title, href, is_last, can_edit (are they allowed to?), and modify_href. */ + foreach ($day['events'] as $event) + { + // If they can edit the event, show a star they can click on.... + if ($event['can_edit']) + echo ' + *'; + + echo ' + ', $event['link'], $event['is_last'] ? '' : ', '; + } + + echo ' +
    '; + } + } + + echo ' +
    '; +} + +// Or show a weekly one? +function template_show_week_grid($grid_name) +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + if (!isset($context['calendar_grid_' . $grid_name])) + return false; + + $calendar_data = &$context['calendar_grid_' . $grid_name]; + + // Loop through each month (At least one) and print out each day. + foreach ($calendar_data['months'] as $month_data) + { + echo ' +
    +

    '; + + if (empty($calendar_data['previous_calendar']['disabled']) && $calendar_data['show_next_prev'] && empty($done_title)) + echo ' + «'; + + if (empty($calendar_data['next_calendar']['disabled']) && $calendar_data['show_next_prev'] && empty($done_title)) + echo ' + »'; + + echo ' + ', $txt['months_titles'][$month_data['current_month']], ' ', $month_data['current_year'], '', empty($done_title) && !empty($calendar_data['week_number']) ? (' - ' . $txt['calendar_week'] . ' ' . $calendar_data['week_number']) : '', ' +

    +
    '; + + $done_title = true; + + echo ' + '; + + foreach ($month_data['days'] as $day) + { + echo ' + + + + + + + '; + } + + echo ' +
    +
    +

    ', $txt['days'][$day['day_of_week']], '

    +
    +
    '; + + // Should the day number be a link? + if (!empty($modSettings['cal_daysaslink']) && $context['can_post']) + echo ' + ', $day['day'], ''; + else + echo ' + ', $day['day']; + + echo ' + '; + + // Are there any holidays? + if (!empty($day['holidays'])) + echo ' +
    ', $txt['calendar_prompt'], ' ', implode(', ', $day['holidays']), '
    '; + + // Show any birthdays... + if (!empty($day['birthdays'])) + { + echo ' +
    + ', $txt['birthdays'], ''; + + /* Each of the birthdays has: + id, name (person), age (if they have one set?), and is_last. (last in list?) */ + foreach ($day['birthdays'] as $member) + echo ' + ', $member['name'], isset($member['age']) ? ' (' . $member['age'] . ')' : '', '', $member['is_last'] ? '' : ', '; + echo ' +
    '; + } + + // Any special posted events? + if (!empty($day['events'])) + { + echo ' +
    + ', $txt['events'], ''; + + /* The events are made up of: + title, href, is_last, can_edit (are they allowed to?), and modify_href. */ + foreach ($day['events'] as $event) + { + // If they can edit the event, show a star they can click on.... + if ($event['can_edit']) + echo ' + * '; + + echo ' + ', $event['link'], $event['is_last'] ? '' : ', '; + } + + echo ' +
    '; + } + + echo ' +
    '; + } +} + +function template_bcd() +{ + global $context, $scripturl; + + echo ' + + + '; + + $alt = false; + foreach ($context['clockicons'] as $t => $v) + { + echo ' + '; + + $alt = !$alt; + } + + echo ' + +
    BCD Clock
    '; + + foreach ($v as $i) + echo ' +
    '; + + echo ' +
    +

    Are you hardcore?

    + + '; +} + +function template_hms() +{ + global $context, $scripturl; + + echo ' + + '; + $alt = false; + foreach ($context['clockicons'] as $t => $v) + { + echo ' + + '; + $alt = !$alt; + } + + echo ' + + +
    Binary Clock
    '; + foreach ($v as $i) + echo ' + '; + echo ' +
    Too tough for you?
    '; + + echo ' + '; +} + +function template_omfg() +{ + global $context, $scripturl; + + echo ' + + '; + $alt = false; + foreach ($context['clockicons'] as $t => $v) + { + echo ' + + '; + $alt = !$alt; + } + + echo ' + +
    OMFG Binary Clock
    '; + foreach ($v as $i) + echo ' + '; + echo ' +
    '; + + echo ' + '; +} + +function template_thetime() +{ + global $context, $scripturl; + + echo ' + + '; + $alt = false; + foreach ($context['clockicons'] as $t => $v) + { + echo ' + + '; + $alt = !$alt; + } + + echo ' + +
    The time you requested
    '; + foreach ($v as $i) + echo ' + '; + echo ' +
    '; + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Combat.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Combat.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,40 @@ + + a img{ + border: 0; + } + '; + +// Generate a strip of buttons, out of buttons. +function template_button_strip($button_strip, $direction = 'top', $force_reset = false, $custom_td = '') +{ + global $settings, $buttons, $context, $txt, $scripturl; + + if (empty($button_strip)) + return ''; + + // Create the buttons... + foreach ($button_strip as $key => $value) + { + if (isset($value['test']) && empty($context[$value['test']])) + { + unset($button_strip[$key]); + continue; + } + elseif (!isset($buttons[$key]) || $force_reset) + $buttons[$key] = '' . ($settings['use_image_buttons'] ? '' . $txt[$value['text']] . '' : $txt[$value['text']]) . ''; + + $button_strip[$key] = $buttons[$key]; + } + + echo ' + ', implode($context['menu_separator'], $button_strip) , ''; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Compat.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Compat.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,46 @@ + $value) + { + if (!isset($value['test']) || !empty($context[$value['test']])) + $buttons[] = ' +
  1. ' . $txt[$value['text']] . '
  2. '; + } + + // No buttons? No button strip either. + if (empty($buttons)) + return; + + // Make the last one, as easy as possible. + $buttons[count($buttons) - 1] = str_replace('', '', $buttons[count($buttons) - 1]); + + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Display.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Display.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,883 @@ + + ', $txt['report_sent'], ' + '; + } + + // Show the anchor for the top and for the first message. If the first message is new, say so. + echo ' + + ', $context['first_new_message'] ? '' : ''; + + // Is this topic also a poll? + if ($context['is_poll']) + { + echo ' +
    +
    +

    + ', $txt['poll'], ' +

    +
    +
    + +
    +

    + ', $context['poll']['question'], ' +

    '; + + // Are they not allowed to vote but allowed to view the options? + if ($context['poll']['show_results'] || !$context['allow_vote']) + { + echo ' +
    '; + + // Show each option with its corresponding percentage bar. + foreach ($context['poll']['options'] as $option) + { + echo ' +
    ', $option['option'], '
    +
    '; + + if ($context['allow_poll_view']) + echo ' + ', $option['bar_ndt'], ' + ', $option['votes'], ' (', $option['percent'], '%)'; + + echo ' +
    '; + } + + echo ' +
    '; + + if ($context['allow_poll_view']) + echo ' +

    ', $txt['poll_total_voters'], ': ', $context['poll']['total_votes'], '

    '; + } + // They are allowed to vote! Go to it! + else + { + echo ' +
    '; + + // Show a warning if they are allowed more than one option. + if ($context['poll']['allowed_warning']) + echo ' +

    ', $context['poll']['allowed_warning'], '

    '; + + echo ' +
      '; + + // Show each option with its button - a radio likely. + foreach ($context['poll']['options'] as $option) + echo ' +
    • ', $option['vote_button'], '
    • '; + + echo ' +
    +
    + + +
    +
    '; + } + + // Is the clock ticking? + if (!empty($context['poll']['expire_time'])) + echo ' +

    ', ($context['poll']['is_expired'] ? $txt['poll_expired_on'] : $txt['poll_expires_on']), ': ', $context['poll']['expire_time'], '

    '; + + echo ' +
    + +
    +
    +
    '; + + // Build the poll moderation button array. + $poll_buttons = array( + 'vote' => array('test' => 'allow_return_vote', 'text' => 'poll_return_vote', 'image' => 'poll_options.gif', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start']), + 'results' => array('test' => 'show_view_results_button', 'text' => 'poll_results', 'image' => 'poll_results.gif', '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.gif', 'lang' => true, 'url' => $scripturl . '?action=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.gif', '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.gif', '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.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['poll_remove_warn'] . '\');"', 'url' => $scripturl . '?action=removepoll;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + template_button_strip($poll_buttons); + + echo ' +
    '; + } + + // Does this topic have some events linked to it? + if (!empty($context['linked_calendar_events'])) + { + echo ' +
    +
    +

    ', $txt['calendar_linked_events'], '

    +
    +
    + +
    +
      '; + + foreach ($context['linked_calendar_events'] as $event) + echo ' +
    • + ', ($event['can_edit'] ? ' ' : ''), '', $event['title'], ': ', $event['start_date'], ($event['start_date'] != $event['end_date'] ? ' - ' . $event['end_date'] : ''), ' +
    • '; + + echo ' +
    +
    + +
    +
    '; + } + + // Build the normal button array. + $normal_buttons = array( + 'reply' => array('test' => 'can_reply', 'text' => 'reply', 'image' => 'reply.gif', 'lang' => true, 'url' => $scripturl . '?action=post;topic=' . $context['current_topic'] . '.' . $context['start'] . ';last_msg=' . $context['topic_last_message'], 'active' => true), + 'add_poll' => array('test' => 'can_add_poll', 'text' => 'add_poll', 'image' => 'add_poll.gif', 'lang' => true, 'url' => $scripturl . '?action=editpoll;add;topic=' . $context['current_topic'] . '.' . $context['start']), + 'notify' => array('test' => 'can_mark_notify', 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', 'image' => ($context['is_marked_notify'] ? 'un' : '') . 'notify.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . ($context['is_marked_notify'] ? $txt['notification_disable_topic'] : $txt['notification_enable_topic']) . '\');"', '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.gif', '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']), + 'send' => array('test' => 'can_send_topic', 'text' => 'send_topic', 'image' => 'sendtopic.gif', 'lang' => true, 'url' => $scripturl . '?action=emailuser;sa=sendtopic;topic=' . $context['current_topic'] . '.0'), + 'print' => array('text' => 'print', 'image' => 'print.gif', 'lang' => true, 'custom' => 'rel="new_win nofollow"', 'url' => $scripturl . '?action=printpage;topic=' . $context['current_topic'] . '.0'), + ); + + // Allow adding new buttons easily. + call_integration_hook('integrate_display_buttons', array(&$normal_buttons)); + + // Show the page index... "Pages: [1]". + echo ' +
    + ', template_button_strip($normal_buttons, 'right'), ' + +
    '; + + // Show the topic information - icon, subject, etc. + echo ' +
    +
    +

    + + ', $txt['author'], ' + ', $txt['topic'], ': ', $context['subject'], '  (', $txt['read'], ' ', $context['num_views'], ' ', $txt['times'], ') +

    +
    '; + + if (!empty($settings['display_who_viewing'])) + { + echo ' +

    '; + + // Show just numbers...? + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) == 1 ? $txt['who_member'] : $txt['members']; + // Or show the actual people viewing the topic? + else + echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) || $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + + // Now show how many guests are here too. + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_topic'], ' +

    '; + } + + echo ' +
    '; + + $ignoredMsgs = array(); + $removableMessageIDs = array(); + $alternate = false; + + // Get all the messages... + while ($message = $context['get_message']()) + { + $ignoring = false; + $alternate = !$alternate; + if ($message['can_remove']) + $removableMessageIDs[] = $message['id']; + + // Are we ignoring this message? + if (!empty($message['is_ignored'])) + { + $ignoring = true; + $ignoredMsgs[] = $message['id']; + } + + // Show the message anchor and a "new" anchor if this message is new. + if ($message['id'] != $context['first_message']) + echo ' + ', $message['first_new'] ? '' : ''; + + echo ' +
    + +
    '; + + // Show information about the poster of this message. + echo ' +
    +

    '; + + // Show online and offline buttons? + if (!empty($modSettings['onlineEnable']) && !$message['member']['is_guest']) + echo ' + ', $context['can_send_pm'] ? '' : '', '', $message['member']['online']['text'], '', $context['can_send_pm'] ? '' : ''; + + // Show a link to the member's profile. + echo ' + ', $message['member']['link'], ' +

    +
      '; + + // Show the member's custom title, if they have one. + if (!empty($message['member']['title'])) + echo ' +
    • ', $message['member']['title'], '
    • '; + + // Show the member's primary group (like 'Administrator') if they have one. + if (!empty($message['member']['group'])) + echo ' +
    • ', $message['member']['group'], '
    • '; + + // Don't show these things for guests. + if (!$message['member']['is_guest']) + { + // Show the post group if and only if they have no other group or the option is on, and they are in a post group. + if ((empty($settings['hide_post_group']) || $message['member']['group'] == '') && $message['member']['post_group'] != '') + echo ' +
    • ', $message['member']['post_group'], '
    • '; + echo ' +
    • ', $message['member']['group_stars'], '
    • '; + + // Show avatars, images, etc.? + if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image'])) + echo ' +
    • + + ', $message['member']['avatar']['image'], ' + +
    • '; + + // Show how many posts they have made. + if (!isset($context['disabled_fields']['posts'])) + echo ' +
    • ', $txt['member_postcount'], ': ', $message['member']['posts'], '
    • '; + + // Is karma display enabled? Total or +/-? + if ($modSettings['karmaMode'] == '1') + echo ' +
    • ', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '
    • '; + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    • ', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '
    • '; + + // Is this user allowed to modify this member's karma? + if ($message['member']['karma']['allow']) + echo ' +
    • + ', $modSettings['karmaApplaudLabel'], ' + ', $modSettings['karmaSmiteLabel'], ' +
    • '; + + // Show the member's gender icon? + if (!empty($settings['show_gender']) && $message['member']['gender']['image'] != '' && !isset($context['disabled_fields']['gender'])) + echo ' +
    • ', $txt['gender'], ': ', $message['member']['gender']['image'], '
    • '; + + // Show their personal text? + if (!empty($settings['show_blurb']) && $message['member']['blurb'] != '') + echo ' +
    • ', $message['member']['blurb'], '
    • '; + + // Any custom fields to show as icons? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 1 || empty($custom['value'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    • +
        '; + } + echo ' +
      • ', $custom['value'], '
      • '; + } + if ($shown) + echo ' +
      +
    • '; + } + + // This shows the popular messaging icons. + if ($message['member']['has_messenger'] && $message['member']['can_view_profile']) + echo ' +
    • +
        + ', !empty($message['member']['icq']['link']) ? '
      • ' . $message['member']['icq']['link'] . '
      • ' : '', ' + ', !empty($message['member']['msn']['link']) ? '
      • ' . $message['member']['msn']['link'] . '
      • ' : '', ' + ', !empty($message['member']['aim']['link']) ? '
      • ' . $message['member']['aim']['link'] . '
      • ' : '', ' + ', !empty($message['member']['yim']['link']) ? '
      • ' . $message['member']['yim']['link'] . '
      • ' : '', ' +
      +
    • '; + + // Show the profile, website, email address, and personal message buttons. + if ($settings['show_profile_buttons']) + { + echo ' +
    • + +
    • '; + } + + // Any custom fields for standard placement? + if (!empty($message['member']['custom_fields'])) + { + foreach ($message['member']['custom_fields'] as $custom) + if (empty($custom['placement']) || empty($custom['value'])) + echo ' +
    • ', $custom['title'], ': ', $custom['value'], '
    • '; + } + + // Are we showing the warning status? + if ($message['member']['can_see_warning']) + echo ' +
    • ', $context['can_issue_warning'] ? '' : '', '', $txt['user_warn_' . $message['member']['warning_status']], '', $context['can_issue_warning'] ? '' : '', '', $txt['warn_' . $message['member']['warning_status']], '
    • '; + } + // Otherwise, show the guest's email. + elseif (!empty($message['member']['email']) && in_array($message['member']['show_email'], array('yes', 'yes_permission_override', 'no_through_forum'))) + echo ' + '; + + // Done with the information about the poster... on to the post itself. + echo ' +
    +
    +
    +
    +
    +
    + +
    +
    + ', $message['subject'], ' +
    +
    « ', !empty($message['counter']) ? $txt['reply_noun'] . ' #' . $message['counter'] : '', ' ', $txt['on'], ': ', $message['time'], ' »
    +
    +
    '; + + // If this is the first post, (#0) just say when it was posted - otherwise give the reply #. + if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg']) + echo ' +
      '; + + // Maybe we can approve it, maybe we should? + if ($message['can_approve']) + echo ' +
    • ', $txt['approve'], '
    • '; + + // Can they reply? Have they turned on quick reply? + if ($context['can_quote'] && !empty($options['display_quick_reply'])) + echo ' +
    • ', $txt['quote'], '
    • '; + + // So... quick reply is off, but they *can* reply? + elseif ($context['can_quote']) + echo ' +
    • ', $txt['quote'], '
    • '; + + // Can the user modify the contents of this post? + if ($message['can_modify']) + echo ' +
    • ', $txt['modify'], '
    • '; + + // How about... even... remove it entirely?! + if ($message['can_remove']) + echo ' +
    • ', $txt['remove'], '
    • '; + + // What about splitting it off the rest of the topic? + if ($context['can_split'] && !empty($context['real_num_replies'])) + echo ' +
    • ', $txt['split'], '
    • '; + + // Can we restore topics? + if ($context['can_restore_msg']) + echo ' +
    • ', $txt['restore_message'], '
    • '; + + // Show a checkbox for quick moderation? + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $message['can_remove']) + echo ' + '; + + if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg']) + echo ' +
    '; + + echo ' +
    '; + + // Ignoring this user? Hide the post. + if ($ignoring) + echo ' +
    + ', $txt['ignoring_user'], ' + +
    '; + + // Show the post itself, finally! + echo ' +
    '; + + if (!$message['approved'] && $message['member']['id'] != 0 && $message['member']['id'] == $context['user']['id']) + echo ' +
    + ', $txt['post_awaiting_approval'], ' +
    '; + echo ' +
    ', $message['body'], '
    +
    '; + + // Can the user modify the contents of this post? Show the modify inline image. + if ($message['can_modify']) + echo ' + '; + + // Assuming there are attachments... + if (!empty($message['attachment'])) + { + echo ' + '; + } + + echo ' +
    +
    +
    '; + + // Show "� Last Edit: Time by Person �" if this post was edited. + if ($settings['show_modify'] && !empty($message['modified']['name'])) + echo ' + « ', $txt['last_edit'], ': ', $message['modified']['time'], ' ', $txt['by'], ' ', $message['modified']['name'], ' »'; + + echo ' +
    + '; + + // Are there any custom profile fields for above the signature? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 2 || empty($custom['value'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    +
      '; + } + echo ' +
    • ', $custom['value'], '
    • '; + } + if ($shown) + echo ' +
    +
    '; + } + + // Show the member's signature? + if (!empty($message['member']['signature']) && empty($options['show_no_signatures']) && $context['signature_enabled']) + echo ' +
    ', $message['member']['signature'], '
    '; + + echo ' +
    +
    + +
    +
    '; + } + + echo ' +
    +
    + '; + + // Show the page index... "Pages: [1]". + echo ' +
    + ', template_button_strip($normal_buttons, 'right'), ' + + +
    '; + + // Show the lower breadcrumbs. + theme_linktree(); + + $mod_buttons = array( + 'move' => array('test' => 'can_move', 'text' => 'move_topic', 'image' => 'admin_move.gif', 'lang' => true, 'url' => $scripturl . '?action=movetopic;topic=' . $context['current_topic'] . '.0'), + 'delete' => array('test' => 'can_delete', 'text' => 'remove_topic', 'image' => 'admin_rem.gif', '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.gif', 'lang' => true, 'url' => $scripturl . '?action=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.gif', 'lang' => true, 'url' => $scripturl . '?action=sticky;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), + 'merge' => array('test' => 'can_merge', 'text' => 'merge', 'image' => 'merge.gif', '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.gif', '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']) + $mod_buttons[] = array('text' => 'restore_topic', 'image' => '', 'lang' => true, 'url' => $scripturl . '?action=restoretopic;topics=' . $context['current_topic'] . ';' . $context['session_var'] . '=' . $context['session_id']); + + // Allow adding new mod buttons easily. + call_integration_hook('integrate_mod_buttons', array(&$mod_buttons)); + + echo ' +
    ', template_button_strip($mod_buttons, 'bottom', array('id' => 'moderationbuttons_strip')), '
    '; + + // Show the jumpto box, or actually...let Javascript do it. + echo ' +
     
    '; + + if ($context['can_reply'] && !empty($options['display_quick_reply'])) + { + echo ' + +
    + + +
    '; + } + else + echo ' +
    '; + + if ($context['show_spellchecking']) + echo ' +
    + '; + + echo ' + + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Errors.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Errors.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,203 @@ + +
    +

    + ', $context['error_title'], ' +

    +
    +
    + +
    ', $context['error_message'], '
    + +
    + '; + + // Show a back button (using javascript.) + echo ' + '; +} + +function template_error_log() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    + +
    +

    + + ', $txt['help'], ' ', $txt['errlog'], ' + +

    +
    + + + + + + + '; + + if ($context['has_filter']) + echo ' + + + '; + + if (!empty($context['errors'])) + echo ' + + + '; + + foreach ($context['errors'] as $error) + { + echo ' + + + + + + + + '; + } + + if (!empty($context['errors'])) + echo ' + + + '; + else + echo ' + + + '; + + echo ' + + + +
    +   ', $txt['apply_filter_of_type'], ':'; + + $error_types = array(); + foreach ($context['error_types'] as $type => $details) + $error_types[] = ($details['is_selected'] ? ' ' : '') . '' . $details['label'] . ''; + + echo ' + ', implode(' | ', $error_types), ' +
    +   ', $txt['pages'], ': ', $context['page_index'], ' +
    + ', $txt['applying_filter'], ': ', $context['filter']['entity'], ' ', $context['filter']['value']['html'], ' (', $txt['clear_filter'], ') +
    +
    + +
    + + + ', $txt['apply_filter'], ': ', $txt['filter_only_member'], ' + ', $error['member']['link'], '
    + ', $txt['apply_filter'], ': ', $txt['filter_only_ip'], ' + ', $error['member']['ip'], '   +
      +
    + ', $txt['reverse_direction'], ' + ', $error['time'], ' +
    '; + + if ($error['member']['session'] != '') + echo ' + ', $txt['apply_filter'], ': ', $txt['filter_only_session'], ' + ', $error['member']['session'], ' +
    '; + + echo ' + ', $txt['apply_filter'], ': ', $txt['filter_only_type'], ' + ', $txt['error_type'], ': ', $error['error_type']['name'], ' +
    +
    ', $txt['apply_filter'], ': ', $txt['filter_only_url'], '
    + +
    ', $txt['apply_filter'], ': ', $txt['filter_only_message'], '
    +
    ', $error['message']['html'], '
    '; + + if (!empty($error['file'])) + echo ' +
    ', $txt['apply_filter'], ': ', $txt['filter_only_file'], '
    +
    + ', $txt['file'], ': ', $error['file']['link'], '
    + ', $txt['line'], ': ', $error['file']['line'], ' +
    '; + + echo ' +
    +
    +   +
    ', $txt['errlog_no_entries'], '
    +   ', $txt['pages'], ': ', $context['page_index'], ' +

    '; + if ($context['sort_direction'] == 'down') + echo ' + '; + echo ' + +
    '; +} + +function template_show_file() +{ + global $context, $settings; + + echo ' + + + ', $context['file_data']['file'], ' + + + + + '; + foreach ($context['file_data']['contents'] as $index => $line) + { + $line_num = $index+$context['file_data']['min']; + $is_target = $line_num == $context['file_data']['target']; + echo ' + + + + '; + } + echo ' +
    ==>' : '>', $line_num , ':', $line, '
    + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/GenericControls.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/GenericControls.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,359 @@ + +
    +
    + +
    +
    +
    + + + '; +} + +function template_control_richedit_buttons($editor_id) +{ + global $context, $settings, $options, $txt, $modSettings, $scripturl; + + $editor_context = &$context['controls']['richedit'][$editor_id]; + + echo ' + '; + + if ($editor_context['preview_type']) + echo ' + '; + + if ($context['show_spellchecking']) + echo ' + '; +} + +// What's this, verification?! +function template_control_verification($verify_id, $display_type = 'all', $reset = false) +{ + global $context, $settings, $options, $txt, $modSettings; + + $verify_context = &$context['controls']['verification'][$verify_id]; + + // Keep track of where we are. + if (empty($verify_context['tracking']) || $reset) + $verify_context['tracking'] = 0; + + // How many items are there to display in total. + $total_items = count($verify_context['questions']) + ($verify_context['show_visual'] ? 1 : 0); + + // If we've gone too far, stop. + if ($verify_context['tracking'] > $total_items) + return false; + + // Loop through each item to show them. + for ($i = 0; $i < $total_items; $i++) + { + // If we're after a single item only show it if we're in the right place. + if ($display_type == 'single' && $verify_context['tracking'] != $i) + continue; + + if ($display_type != 'single') + echo ' +
    '; + + // Do the actual stuff - image first? + if ($i == 0 && $verify_context['show_visual']) + { + if ($context['use_graphic_library']) + echo ' + ', $txt['visual_verification_description'], ''; + else + echo ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ''; + + if (WIRELESS) + echo '
    + '; + else + echo ' +
    + ', $txt['visual_verification_sound'], ' / ', $txt['visual_verification_request_new'], '', $display_type != 'quick_reply' ? '
    ' : '', '
    + ', $txt['visual_verification_description'], ':', $display_type != 'quick_reply' ? '
    ' : '', ' + +
    '; + } + else + { + // Where in the question array is this question? + $qIndex = $verify_context['show_visual'] ? $i - 1 : $i; + + echo ' +
    + ', $verify_context['questions'][$qIndex]['q'], ':
    + +
    '; + } + + if ($display_type != 'single') + echo ' +
    '; + + // If we were displaying just one and we did it, break. + if ($display_type == 'single' && $verify_context['tracking'] == $i) + break; + } + + // Assume we found something, always, + $verify_context['tracking']++; + + // Tell something displaying piecemeal to keep going. + if ($display_type == 'single') + return true; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/GenericList.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/GenericList.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,333 @@ + +
    '; + + // Show the title of the table (if any). + if (!empty($cur_list['title'])) + echo ' +
    +

    + ', $cur_list['title'], ' +

    +
    '; + // This is for the old style menu with the arrows "> Test | Test 1" + if (empty($settings['use_tabs']) && isset($cur_list['list_menu'], $cur_list['list_menu']['show_on']) && ($cur_list['list_menu']['show_on'] == 'both' || $cur_list['list_menu']['show_on'] == 'top')) + template_create_list_menu($cur_list['list_menu'], 'top'); + + if (isset($cur_list['additional_rows']['top_of_list'])) + template_additional_rows('top_of_list', $cur_list); + + if (isset($cur_list['additional_rows']['after_title'])) + { + echo ' +
    '; + template_additional_rows('after_title', $cur_list); + echo ' +
    '; + } + + if (!empty($cur_list['items_per_page']) || isset($cur_list['additional_rows']['bottom_of_list'])) + { + echo ' +
    '; + + // Show the page index (if this list doesn't intend to show all items). + if (!empty($cur_list['items_per_page'])) + echo ' +
    +
    ', $txt['pages'], ': ', $cur_list['page_index'], '
    +
    '; + + if (isset($cur_list['additional_rows']['above_column_headers'])) + { + echo ' +
    '; + + template_additional_rows('above_column_headers', $cur_list); + + echo ' +
    '; + } + + echo ' +
    '; + } + + echo ' + '; + + // Show the column headers. + $header_count = count($cur_list['headers']); + if (!($header_count < 2 && empty($cur_list['headers'][0]['label']))) + { + echo ' + + '; + + // Loop through each column and add a table header. + $i = 0; + foreach ($cur_list['headers'] as $col_header) + { + $i ++; + if (empty($col_header['class']) && $i == 1) + $col_header['class'] = 'first_th'; + elseif (empty($col_header['class']) && $i == $header_count) + $col_header['class'] = 'last_th'; + + echo ' + '; + } + + echo ' + + + '; + } + + // Show a nice message informing there are no items in this list. + if (empty($cur_list['rows']) && !empty($cur_list['no_items_label'])) + echo ' + + + '; + + // Show the list rows. + elseif (!empty($cur_list['rows'])) + { + $alternate = false; + foreach ($cur_list['rows'] as $id => $row) + { + echo ' + '; + + foreach ($row as $row_data) + echo ' + ', $row_data['value'], ''; + + echo ' + '; + + $alternate = !$alternate; + } + } + + echo ' + +
    ', empty($col_header['href']) ? '' : '', empty($col_header['label']) ? ' ' : $col_header['label'], empty($col_header['href']) ? '' : '', empty($col_header['sort_image']) ? '' : ' ', '
    ', $cur_list['no_items_label'], '
    '; + + if (!empty($cur_list['items_per_page']) || isset($cur_list['additional_rows']['below_table_data']) || isset($cur_list['additional_rows']['bottom_of_list'])) + { + echo ' +
    '; + + // Show the page index (if this list doesn't intend to show all items). + if (!empty($cur_list['items_per_page'])) + echo ' +
    +
    ', $txt['pages'], ': ', $cur_list['page_index'], '
    +
    '; + + if (isset($cur_list['additional_rows']['below_table_data'])) + { + echo ' +
    '; + + template_additional_rows('below_table_data', $cur_list); + + echo ' +
    '; + } + + if (isset($cur_list['additional_rows']['bottom_of_list'])) + { + echo ' +
    '; + + template_additional_rows('bottom_of_list', $cur_list); + + echo ' +
    '; + } + + echo ' +
    '; + } + + if (isset($cur_list['form'])) + { + foreach ($cur_list['form']['hidden_fields'] as $name => $value) + echo ' + '; + + echo ' +
    + '; + } + + // Tabs at the bottom. Usually bottom alligned. + if (!empty($settings['use_tabs']) && isset($cur_list['list_menu'], $cur_list['list_menu']['show_on']) && ($cur_list['list_menu']['show_on'] == 'both' || $cur_list['list_menu']['show_on'] == 'bottom')) + template_create_list_menu($cur_list['list_menu'], 'bottom'); + + if (isset($cur_list['javascript'])) + echo ' + '; +} + +function template_additional_rows($row_position, $cur_list) +{ + global $context, $settings, $options; + + foreach ($cur_list['additional_rows'][$row_position] as $row) + echo ' +
    ', $row['value'], '
    '; +} + +function template_create_list_menu($list_menu, $direction = 'top') +{ + global $context, $settings; + + /** + // This is use if you want your generic lists to have tabs. + $cur_list['list_menu'] = array( + // This is the style to use. Tabs or Buttons (Text 1 | Text 2). + // By default tabs are selected if not set. + // The main difference between tabs and buttons is that tabs get highlighted if selected. + // If style is set to buttons and use tabs is diabled then we change the style to old styled tabs. + 'style' => 'tabs', + // The posisiton of the tabs/buttons. Left or Right. By default is set to left. + 'position' => 'left', + // This is used by the old styled menu. We *need* to know the total number of columns to span. + 'columns' => 0, + // This gives you the option to show tabs only at the top, bottom or both. + // By default they are just shown at the top. + 'show_on' => 'top', + // Links. This is the core of the array. It has all the info that we need. + 'links' => array( + 'name' => array( + // This will tell use were to go when they click it. + 'href' => $scripturl . '?action=theaction', + // The name that you want to appear for the link. + 'label' => $txt['name'], + // If we use tabs instead of buttons we highlight the current tab. + // Must use conditions to determine if its selected or not. + 'is_selected' => isset($_REQUEST['name']), + ), + ), + ); + */ + + // Are we using right-to-left orientation? + $first = $context['right_to_left'] ? 'last' : 'first'; + $last = $context['right_to_left'] ? 'first' : 'last'; + + // Tabs take preference over buttons in certain cases. + if (empty($settings['use_tabs']) && $list_menu['style'] == 'button') + $list_menu['style'] = 'tabs'; + + if (!isset($list_menu['style']) || isset($list_menu['style']) && $list_menu['style'] == 'tabs') + { + if (!empty($settings['use_tabs'])) + { + echo ' + + ', $list_menu['position'] == 'right' ? ' + ' : '', ' + ', $list_menu['position'] == 'left' ? ' + ' : '', ' + +
      + + + '; + + foreach ($list_menu['links'] as $link) + { + if ($link['is_selected']) + echo ' + + + '; + else + echo ' + '; + } + + echo ' + + +
       + ', $link['label'], ' +   + ', $link['label'], ' +  
    +
     
    '; + } + else + { + echo ' + + '; + + $links = array(); + foreach ($list_menu['links'] as $link) + $links[] = ($link['is_selected'] ? '> ' : '') . '' . $link['label'] . ''; + + echo ' + ', implode(' | ', $links), ' + + '; + } + } + elseif (isset($list_menu['style']) && $list_menu['style'] == 'buttons') + { + $links = array(); + foreach ($list_menu['links'] as $link) + $links[] = '' . $link['label'] . ''; + + echo ' + + ', $list_menu['position'] == 'right' ? ' + ' : '', ' + ', $list_menu['position'] == 'left' ? ' + ' : '', ' + +
      + + + + + + +
     ', implode('  |  ', $links), ' 
    +
     
    '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/GenericMenu.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/GenericMenu.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,367 @@ + +
    '; + + // What one are we rendering? + $context['cur_menu_id'] = isset($context['cur_menu_id']) ? $context['cur_menu_id'] + 1 : 1; + $menu_context = &$context['menu_data_' . $context['cur_menu_id']]; + + // For every section that appears on the sidebar... + $firstSection = true; + foreach ($menu_context['sections'] as $section) + { + // Show the section header - and pump up the line spacing for readability. + echo ' +
    +
    +

    '; + + if ($firstSection && !empty($menu_context['can_toggle_drop_down'])) + { + echo ' + + ', $section['title'],'! + '; + } + else + { + echo ' + ', $section['title']; + } + + echo ' +

    +
    +
      '; + + // For every area of this section show a link to that area (bold if it's currently selected.) + foreach ($section['areas'] as $i => $area) + { + // Not supposed to be printed? + if (empty($area['label'])) + continue; + + echo ' +
    • '; + + // Is this the current area, or just some area? + if ($i == $menu_context['current_area']) + { + echo ' + ', $area['label'], ''; + + if (empty($context['tabs'])) + $context['tabs'] = isset($area['subsections']) ? $area['subsections'] : array(); + } + else + echo ' + ', $area['label'], ''; + + echo ' +
    • '; + } + + echo ' +
    +
    '; + + $firstSection = false; + } + + // This is where the actual "main content" area for the admin section starts. + echo ' +
    +
    '; + + // If there are any "tabs" setup, this is the place to shown them. + if (!empty($context['tabs']) && empty($context['force_disable_tabs'])) + template_generic_menu_tabs($menu_context); +} + +// Part of the sidebar layer - closes off the main bit. +function template_generic_menu_sidebar_below() +{ + global $context, $settings, $options; + + echo ' +
    +
    '; +} + +// This contains the html for the side bar of the admin center, which is used for all admin pages. +function template_generic_menu_dropdown_above() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Which menu are we rendering? + $context['cur_menu_id'] = isset($context['cur_menu_id']) ? $context['cur_menu_id'] + 1 : 1; + $menu_context = &$context['menu_data_' . $context['cur_menu_id']]; + + if (!empty($menu_context['can_toggle_drop_down'])) + echo ' + *'; + + echo ' +
    +
    '; + + // This is the main table - we need it so we can keep the content to the right of it. + echo ' +
    '; + + // It's possible that some pages have their own tabs they wanna force... + if (!empty($context['tabs'])) + template_generic_menu_tabs($menu_context); +} + +// Part of the admin layer - used with admin_above to close the table started in it. +function template_generic_menu_dropdown_below() +{ + global $context, $settings, $options; + + echo ' +
    '; +} + +// Some code for showing a tabbed view. +function template_generic_menu_tabs(&$menu_context) +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Handy shortcut. + $tab_context = &$menu_context['tab_data']; + + echo ' +
    +

    '; + + // Exactly how many tabs do we have? + foreach ($context['tabs'] as $id => $tab) + { + // Can this not be accessed? + if (!empty($tab['disabled'])) + { + $tab_context['tabs'][$id]['disabled'] = true; + continue; + } + + // Did this not even exist - or do we not have a label? + if (!isset($tab_context['tabs'][$id])) + $tab_context['tabs'][$id] = array('label' => $tab['label']); + elseif (!isset($tab_context['tabs'][$id]['label'])) + $tab_context['tabs'][$id]['label'] = $tab['label']; + + // Has a custom URL defined in the main admin structure? + if (isset($tab['url']) && !isset($tab_context['tabs'][$id]['url'])) + $tab_context['tabs'][$id]['url'] = $tab['url']; + // Any additional paramaters for the url? + if (isset($tab['add_params']) && !isset($tab_context['tabs'][$id]['add_params'])) + $tab_context['tabs'][$id]['add_params'] = $tab['add_params']; + // Has it been deemed selected? + if (!empty($tab['is_selected'])) + $tab_context['tabs'][$id]['is_selected'] = true; + // Does it have its own help? + if (!empty($tab['help'])) + $tab_context['tabs'][$id]['help'] = $tab['help']; + // Is this the last one? + if (!empty($tab['is_last']) && !isset($tab_context['override_last'])) + $tab_context['tabs'][$id]['is_last'] = true; + } + + // Find the selected tab + foreach ($tab_context['tabs'] as $sa => $tab) + { + if (!empty($tab['is_selected']) || (isset($menu_context['current_subsection']) && $menu_context['current_subsection'] == $sa)) + { + $selected_tab = $tab; + $tab_context['tabs'][$sa]['is_selected'] = true; + } + } + + // Show an icon and/or a help item? + if (!empty($selected_tab['icon']) || !empty($tab_context['icon']) || !empty($selected_tab['help']) || !empty($tab_context['help'])) + { + echo ' + '; + + if (!empty($selected_tab['icon']) || !empty($tab_context['icon'])) + echo ''; + + if (!empty($selected_tab['help']) || !empty($tab_context['help'])) + echo '', $txt['help'], ''; + + echo $tab_context['title'], ' + '; + } + else + { + echo ' + ', $tab_context['title']; + } + + echo ' +

    +
    '; + + // Shall we use the tabs? + if (!empty($settings['use_tabs'])) + { + echo ' +

    + ', !empty($selected_tab['description']) ? $selected_tab['description'] : $tab_context['description'], ' +

    '; + + // The admin tabs. + echo ' +
    +
      '; + + // Print out all the items in this tab. + foreach ($tab_context['tabs'] as $sa => $tab) + { + if (!empty($tab['disabled'])) + continue; + + if (!empty($tab['is_selected'])) + { + echo ' +
    • + ', $tab['label'], ' +
    • '; + } + else + echo ' +
    • + ', $tab['label'], ' +
    • '; + } + + // the end of tabs + echo ' +
    +

    '; + } + // ...if not use the old style + else + { + echo ' +

    '; + + // Print out all the items in this tab. + foreach ($tab_context['tabs'] as $sa => $tab) + { + if (!empty($tab['disabled'])) + continue; + + if (!empty($tab['is_selected'])) + { + echo ' + * ', $tab['label'], ''; + } + else + echo ' + ', $tab['label'], ''; + + if (empty($tab['is_last'])) + echo ' | '; + } + + echo ' +

    +

    ', isset($selected_tab['description']) ? $selected_tab['description'] : $tab_context['description'], '

    '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Help.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Help.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,185 @@ + + + + + + ', $context['page_title'], ' + + + + +
    + ', $context['help_text'], '
    +
    + ', $txt['close_window'], ' +
    + +'; +} + +function template_find_members() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' + + + ', $txt['find_members'], ' + + + + + + + +
    + +
    +
    +
    +

    ', $txt['find_members'], '

    +
    +
    + ', $txt['find_username'], ':
    +
    + ', $txt['find_wildcards'], '
    '; + + // Only offer to search for buddies if we have some! + if (!empty($context['show_buddies'])) + echo ' +
    '; + + echo ' +
    + + +
    +
    +
    +
    + +
    + +
    +
    +
    +

    ', $txt['find_results'], '

    +
    '; + + if (empty($context['results'])) + echo ' +

    ', $txt['find_no_results'], '

    '; + else + { + echo ' +
      '; + + $alternate = true; + foreach ($context['results'] as $result) + { + echo ' +
    • + ', $txt['view_profile'], ' + ', $result['name'], ' +
    • '; + + $alternate = !$alternate; + } + + echo ' +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + + echo ' +
    +
    + + + + +
    '; + + if (empty($context['results'])) + echo ' + '; + + echo ' + +'; +} + +// The main help page. +function template_manual() +{ + global $context, $scripturl, $txt; + + echo ' +
    +

    ', $txt['manual_smf_user_help'], '

    +
    +
    +
    + +
    +

    ', sprintf($txt['manual_welcome'], $context['forum_name']), '

    +

    ', $txt['manual_introduction'], '

    + +

    ', sprintf($txt['manual_docs_and_credits'], $context['wiki_url'], $scripturl . '?action=credits'), '

    +
    + +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Login.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Login.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,308 @@ + + +
    +
    '; + + // Focus on the correct input - username or password. + echo ' + '; +} + +// Tell a guest to get lost or login! +function template_kick_guest() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // This isn't that much... just like normal login but with a message at the top. + echo ' + +
    + +
    '; + + // Do the focus thing... + echo ' + '; +} + +// This is for maintenance mode. +function template_maintenance() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Display the administrator's message at the top. + echo ' + +
    + +
    '; +} + +// This is for the security stuff - makes administrators login every so often. +function template_admin_login() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Since this should redirect to whatever they were doing, send all the get data. + echo ' + + +
    + + +
    '; + + // Focus on the password box. + echo ' +'; +} + +// Activate your account manually? +function template_retry_activate() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Just ask them for their code so they can try it again... + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    + +
    '; + + // You didn't even have an ID? + if (empty($context['member_id'])) + echo ' +
    +
    ', $txt['invalid_activation_username'], ':
    +
    '; + + echo ' +
    ', $txt['invalid_activation_retry'], ':
    +
    +
    +

    +
    + +
    '; +} + +// Activate your account manually? +function template_resend() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Just ask them for their code so they can try it again... + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    + +
    +
    +
    ', $txt['invalid_activation_username'], ':
    +
    +
    +

    ', $txt['invalid_activation_new'], '

    +
    +
    ', $txt['invalid_activation_new_email'], ':
    +
    +
    ', $txt['invalid_activation_password'], ':
    +
    +
    '; + + if ($context['can_activate']) + echo ' +

    ', $txt['invalid_activation_known'], '

    +
    +
    ', $txt['invalid_activation_retry'], ':
    +
    +
    '; + + echo ' +

    +
    + +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageAttachments.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageAttachments.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,215 @@ + +'; +} + +function template_browse() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + '; + + template_show_list('file_list'); + echo ' +
    '; + +} + +function template_maintenance() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['attachment_stats'], '

    +
    +
    + +
    +
    +
    ', $txt['attachment_total'], ':
    ', $context['num_attachments'], '
    +
    ', $txt['attachment_manager_total_avatars'], ':
    ', $context['num_avatars'], '
    +
    ', $txt['attachmentdir_size' . ($context['attach_multiple_dirs'] ? '_current' : '')], ':
    ', $context['attachment_total_size'], ' ', $txt['kilobyte'], '
    +
    ', $txt['attachment_space' . ($context['attach_multiple_dirs'] ? '_current' : '')], ':
    ', isset($context['attachment_space']) ? $context['attachment_space'] . ' ' . $txt['kilobyte'] : $txt['attachmentdir_size_not_set'], '
    +
    +
    + +
    +
    +

    ', $txt['attachment_integrity_check'], '

    +
    +
    + +
    +
    +

    ', $txt['attachment_integrity_check_desc'], '

    + +
    +
    + +
    +
    +

    ', $txt['attachment_pruning'], '

    +
    +
    + +
    +
    + ', $txt['attachment_remove_old'], ' ', $txt['days_word'], '
    + ', $txt['attachment_pruning_message'], ':
    + + + + +
    +
    +
    + ', $txt['attachment_remove_size'], ' ', $txt['kilobyte'], '
    + ', $txt['attachment_pruning_message'], ':
    + + + + +
    +
    +
    + ', $txt['attachment_manager_avatars_older'], ' ', $txt['days_word'], '
    + + + + +
    +
    + +
    +
    +
    '; +} + +function template_attachment_repair() +{ + global $context, $txt, $scripturl, $settings; + + // If we've completed just let them know! + if ($context['completed']) + { + echo ' +
    +
    +

    ', $txt['repair_attachments_complete'], '

    +
    +
    + +
    + ', $txt['repair_attachments_complete_desc'], ' +
    + +
    +
    +
    '; + } + + // What about if no errors were even found? + elseif (!$context['errors_found']) + { + echo ' +
    +
    +

    ', $txt['repair_attachments_complete'], '

    +
    +
    + +
    + ', $txt['repair_attachments_no_errors'], ' +
    + +
    +
    +
    '; + } + // Otherwise, I'm sad to say, we have a problem! + else + { + echo ' +
    +
    +
    +

    ', $txt['repair_attachments'], '

    +
    +
    + +
    +

    ', $txt['repair_attachments_error_desc'], '

    '; + + // Loop through each error reporting the status + foreach ($context['repair_errors'] as $error => $number) + { + if (!empty($number)) + echo ' + +
    '; + } + + echo '
    + + +
    + +
    +
    +
    +
    '; + } +} + +function template_attachment_paths() +{ + template_show_list('attach_paths'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageBans.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageBans.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,365 @@ + + +
    +

    + ', $context['ban']['is_new'] ? $txt['ban_add_new'] : $txt['ban_edit'] . ' \'' . $context['ban']['name'] . '\'', ' +

    +
    '; + + if ($context['ban']['is_new']) + echo ' +
    ', $txt['ban_add_notes'], '
    '; + + echo ' +
    + +
    +
    +
    +
    + ', $txt['ban_name'], ': +
    +
    + +
    +
    + ', $txt['ban_reason'], ':
    + ', $txt['ban_reason_desc'], ' +
    +
    + +
    +
    + ', $txt['ban_notes'], ':
    + ', $txt['ban_notes_desc'], ' +
    +
    + +
    +
    +
    + + ', $txt['ban_expiration'], ' + +
    + : ', $txt['ban_days'], '
    + +
    +
    + + ', $txt['ban_restriction'], ' + +
    +
    + (?)
    +
    +
    +
    +
    '; + + if (!empty($context['ban_suggestions'])) + { + echo ' +
    + + ', $txt['ban_triggers'], ' + +
    +
    + + +
    +
    + +
    '; + + if (empty($modSettings['disableHostnameLookup'])) + echo ' +
    + + +
    +
    + +
    '; + + echo ' +
    + + +
    +
    + +
    +
    + + : +
    +
    '; + + if (empty($context['ban_suggestions']['member']['id'])) + echo ' + '; + else + echo ' + ', $context['ban_suggestions']['member']['link'], ' + '; + echo ' +
    '; + + if (!empty($context['ban_suggestions']['message_ips'])) + { + echo ' +
    +
    ', $txt['ips_in_messages'], ':
    +
    '; + + foreach ($context['ban_suggestions']['message_ips'] as $ip) + echo ' +
    + +
    +
    + ', $ip, ' +
    '; + } + + if (!empty($context['ban_suggestions']['error_ips'])) + { + echo ' +
    +
    ', $txt['ips_in_errors'], '
    +
    '; + + foreach ($context['ban_suggestions']['error_ips'] as $ip) + echo ' +
    + +
    +
    + ', $ip, ' +
    '; + } + + echo ' +
    +
    '; + } + + echo ' +
    + + + + +
    +
    +
    + +
    '; + + if (!$context['ban']['is_new'] && empty($context['ban_suggestions'])) + { + echo ' +
    +
    + + + + + + '; + if (empty($context['ban_items'])) + echo ' + + + '; + else + { + foreach ($context['ban_items'] as $ban_item) + { + echo ' + + + + + + '; + } + } + + echo ' + +
    ', $txt['ban_banned_entity'], ' + ', $txt['ban_hits'], ' + ', $txt['ban_actions'], ' + +
    (', $txt['ban_no_triggers'], ')
    '; + if ($ban_item['type'] == 'ip') + echo ' ', $txt['ip'], ': ', $ban_item['ip']; + elseif ($ban_item['type'] == 'hostname') + echo ' ', $txt['hostname'], ': ', $ban_item['hostname']; + elseif ($ban_item['type'] == 'email') + echo ' ', $txt['email'], ': ', $ban_item['email']; + elseif ($ban_item['type'] == 'user') + echo ' ', $txt['username'], ': ', $ban_item['user']['link']; + echo ' + ', $ban_item['hits'], '', $txt['ban_edit_trigger'], '
    + +
    + + +
    '; + + } + + echo ' + +
    + + '; +} + +function template_ban_edit_trigger() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    + ', $context['ban_trigger']['is_new'] ? $txt['ban_add_trigger'] : $txt['ban_edit_trigger_title'], ' +

    +
    +
    + +
    +
    + + ', $txt['ban_triggers'], ' + +
    +
    + + ', $txt['ban_on_ip'], ' +
    +
    + +
    '; + if (empty($modSettings['disableHostnameLookup'])) + echo ' +
    + + ', $txt['ban_on_hostname'], ' +
    +
    + +
    '; + echo ' +
    + + ', $txt['ban_on_email'], ' +
    +
    + +
    +
    + + ', $txt['ban_on_username'], ' +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    + + + +
    +
    +
    + + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageBoards.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageBoards.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,633 @@ + +
    +

    ', $txt['boardsEdit'], '

    +
    '; + + if (!empty($context['move_board'])) + echo ' +
    +

    ', $context['move_title'], ' [', $txt['mboards_cancel_moving'], ']', '

    +
    '; + + // No categories so show a label. + if (empty($context['categories'])) + echo ' +
    + +
    + ', $txt['mboards_no_cats'], ' +
    + +
    '; + + // Loop through every category, listing the boards in each as we go. + foreach ($context['categories'] as $category) + { + // Link to modify the category. + echo ' + '; + + // Boards table header. + echo ' +
    +
    + +
    +
      '; + + if (!empty($category['move_link'])) + echo ' +
    • ', $category['move_link']['label'], '
    • '; + + $alternate = false; + + // List through every board in the category, printing its name and link to modify the board. + foreach ($category['boards'] as $board) + { + $alternate = !$alternate; + + echo ' + ', $board['name'], '', !empty($modSettings['recycle_board']) && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board['id'] ? ' ' . $txt['recycle_board'] . '' : '', ' + ', $context['can_manage_permissions'] ? '' . $txt['mboards_permissions'] . '' : '', ' + ', $txt['mboards_move'], ' + ', $txt['mboards_modify'], '
      + '; + + if (!empty($board['move_links'])) + { + $alternate = !$alternate; + + echo ' +
    • '; + + foreach ($board['move_links'] as $link) + echo ' + ', $link['label'], ''; + + echo ' +
    • '; + } + } + + // Button to add a new board. + echo ' +
    +
    + + +
    +
    + +
    +
    '; + } + echo ' + +
    '; +} + +// Template for editing/adding a category on the forum. +function template_modify_category() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Print table header. + echo ' +
    +
    + +
    +

    + ', isset($context['category']['is_new']) ? $txt['mboards_new_cat_name'] : $txt['catEdit'], ' +

    +
    +
    + +
    +
    '; + // If this isn't the only category, let the user choose where this category should be positioned down the board index. + if (count($context['category_order']) > 1) + { + echo ' +
    ', $txt['order'], ':
    +
    + +
    '; + } + // Allow the user to edit the category name and/or choose whether you can collapse the category. + echo ' +
    + ', $txt['full_name'], ':
    + ', $txt['name_on_display'], ' +
    +
    + +
    +
    + ' . $txt['collapse_enable'] . '
    + ' . $txt['collapse_desc'] . ' +
    +
    + +
    '; + + // Table footer. + echo ' +
    +
    '; + + if (isset($context['category']['is_new'])) + echo ' + '; + else + echo ' + + '; + echo ' + '; + + // If this category is empty we don't bother with the next confirmation screen. + if ($context['category']['is_empty']) + echo ' + '; + + echo ' +
    +
    + +
    +
    +
    +
    '; +} + +// A template to confirm if a user wishes to delete a category - and whether they want to save the boards. +function template_confirm_category_delete() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Print table header. + echo ' +
    +
    + +
    +

    ', $txt['mboards_delete_cat'], '

    +
    +
    + +
    +

    ', $txt['mboards_delete_cat_contains'], ':

    +
      '; + + foreach ($context['category']['children'] as $child) + echo ' +
    • ', $child, '
    • '; + + echo ' +
    +
    + +
    +
    +

    ', $txt['mboards_delete_what_do'], '

    +
    +
    + +
    +

    +
    + : + +

    + + + + +
    + +
    +
    +
    +
    '; +} + +// Below is the template for adding/editing an board on the forum. +function template_modify_board() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // The main table header. + echo ' +
    +
    + +
    +

    + ', isset($context['board']['is_new']) ? $txt['mboards_new_board_name'] : $txt['boardsEdit'], ' +

    +
    +
    + +
    +
    '; + + // Option for choosing the category the board lives in. + echo ' + +
    + ', $txt['mboards_category'], ': + +
    +
    + +
    '; + + // If this isn't the only board in this category let the user choose where the board is to live. + if ((isset($context['board']['is_new']) && count($context['board_order']) > 0) || count($context['board_order']) > 1) + { + echo ' +
    + ', $txt['order'], ': +
    +
    '; + + // The first select box gives the user the option to position it before, after or as a child of another board. + echo ' + '; + + // The second select box lists all the boards in the category. + echo ' + +
    '; + } + + // Options for board name and description. + echo ' +
    + ', $txt['full_name'], ':
    + ', $txt['name_on_display'], ' +
    +
    + +
    +
    + ', $txt['mboards_description'], ':
    + ', $txt['mboards_description_desc'], ' +
    +
    + +
    +
    + ', $txt['permission_profile'], ':
    + ', $context['can_manage_permissions'] ? sprintf($txt['permission_profile_desc'], $scripturl . '?action=admin;area=permissions;sa=profiles;' . $context['session_var'] . '=' . $context['session_id']) : strip_tags($txt['permission_profile_desc']), ' +
    +
    + +
    +
    + ', $txt['mboards_groups'], ':
    + ', $txt['mboards_groups_desc'], ' +
    +
    '; + + // List all the membergroups so the user can choose who may access this board. + foreach ($context['groups'] as $group) + echo ' +
    '; + echo ' + ', $txt['check_all'], '
    +
    +
    '; + + // Options to choose moderators, specifiy as announcement board and choose whether to count posts here. + echo ' +
    + ', $txt['mboards_moderators'], ':
    + ', $txt['mboards_moderators_desc'], '
    +
    +
    + +
    +
    +
    +
    '; + + if (empty($context['board']['is_recycle']) && empty($context['board']['topics'])) + echo ' +
    +
    + ', $txt['mboards_redirect'], ':
    + ', $txt['mboards_redirect_desc'], '
    +
    +
    + +
    +
    '; + + if (!empty($context['board']['is_recycle'])) + echo ' +
    ', $txt['mboards_redirect_disabled_recycle'], '
    '; + + if (empty($context['board']['is_recycle']) && !empty($context['board']['topics'])) + echo ' +
    + ', $txt['mboards_redirect'],'
    + ', $txt['mboards_redirect_disabled'], ' +
    '; + + if (!$context['board']['topics'] && empty($context['board']['is_recycle'])) + { + echo ' +
    +
    +
    + ', $txt['mboards_redirect_url'], ':
    + ', $txt['mboards_redirect_url_desc'], '
    +
    +
    + +
    +
    +
    '; + + if ($context['board']['redirect']) + echo ' +
    +
    +
    + ', $txt['mboards_redirect_reset'], ':
    + ', $txt['mboards_redirect_reset_desc'], '
    +
    +
    + + (', sprintf($txt['mboards_current_redirects'], $context['board']['posts']), ') +
    +
    +
    '; + } + + echo ' +
    +
    +
    + ', $txt['mboards_count_posts'], ':
    + ', $txt['mboards_count_posts_desc'], '
    +
    +
    + +
    +
    +
    '; + + // Here the user can choose to force this board to use a theme other than the default theme for the forum. + echo ' +
    +
    +
    + ', $txt['mboards_theme'], ':
    + ', $txt['mboards_theme_desc'], '
    +
    +
    + +
    +
    +
    +
    +
    +
    + ', $txt['mboards_override_theme'], ':
    + ', $txt['mboards_override_theme_desc'], '
    +
    +
    + +
    +
    +
    '; + + if (!empty($context['board']['is_recycle'])) + echo '
    ', $txt['mboards_recycle_disabled_delete'], '
    '; + + echo ' + + '; + + // If this board has no children don't bother with the next confirmation screen. + if ($context['board']['no_children']) + echo ' + '; + + if (isset($context['board']['is_new'])) + echo ' + + '; + else + echo ' + '; + + if (!isset($context['board']['is_new']) && empty($context['board']['is_recycle'])) + echo ' + ' : '>', ''; + echo ' +
    + +
    +
    +
    +
    + +'; + + // Javascript for deciding what to show. + echo ' + '; +} + +// A template used when a user is deleting a board with child boards in it - to see what they want to do with them. +function template_confirm_board_delete() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Print table header. + echo ' +
    +
    + + +
    +

    ', $txt['mboards_delete_board'], '

    +
    +
    + +
    +

    ', $txt['mboards_delete_board_contains'], '

    +
      '; + + foreach ($context['children'] as $child) + echo ' +
    • ', $child['node']['name'], '
    • '; + + echo ' +
    +
    + +
    +
    +

    ', $txt['mboards_delete_what_do'], '

    +
    +
    + +
    +

    +
    + : + +

    + + + + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageCalendar.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageCalendar.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,117 @@ +'; + + // Show a form for all the holiday information. + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +
    +
    + ', $txt['holidays_title_label'], ': +
    +
    + +
    +
    + ', $txt['calendar_year'], ' +
    +
    +   + ', $txt['calendar_month'], '  +   + ', $txt['calendar_day'], '  + +
    +
    '; + + if ($context['is_new']) + echo ' + '; + else + echo ' + + + '; + echo ' + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageMail.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageMail.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,42 @@ + +
    +

    ', $txt['mailqueue_stats'], '

    +
    +
    + +
    +
    +
    ', $txt['mailqueue_size'], '
    +
    ', $context['mail_queue_size'], '
    +
    ', $txt['mailqueue_oldest'], '
    +
    ', $context['oldest_mail'], '
    +
    +
    + +
    '; + + template_show_list('mail_queue'); + + echo ' + +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageMaintenance.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageMaintenance.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,622 @@ + + ', sprintf($txt['maintain_done'], $context['maintenance_finished']), ' + '; + + echo ' +
    +
    +

    ', $txt['maintain_optimize'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_optimize_info'], '

    + + +
    +
    + +
    + +
    +

    + ', $txt['help'], ' ', $txt['maintain_backup'], ' +

    +
    + +
    + +
    +
    +

    ', $txt['maintain_backup_info'], '

    '; + + if ($db_type == 'sqlite') + echo ' +

    '; + else + echo ' +


    +
    +

    +

    '; + + echo ' + +
    +
    + +
    '; + + // Show an option to convert to UTF-8 if we're not on UTF-8 yet. + if ($context['convert_utf8']) + { + echo ' +
    +

    ', $txt['utf8_title'], '

    +
    +
    + +
    +
    +

    ', $txt['utf8_introduction'], '

    + ', !empty($modSettings['search_index']) && $modSettings['search_index'] == 'fulltext' ? '

    ' . $txt['utf8_cannot_convert_fulltext'] . '

    ' : '', ' + + +
    +
    + +
    '; + } + + // We might want to convert entities if we're on UTF-8. + if ($context['convert_entities']) + { + echo ' +
    +

    ', $txt['entity_convert_title'], '

    +
    +
    + +
    +
    +

    ', $txt['entity_convert_introduction'], '

    + + +
    +
    + +
    '; + } + + echo ' +
    +
    '; +} + +// Template for the routine maintenance tasks. +function template_maintain_routine() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // If maintenance has finished tell the user. + if (!empty($context['maintenance_finished'])) + echo ' +
    + ', sprintf($txt['maintain_done'], $context['maintenance_finished']), ' +
    '; + + // Starts off with general maintenance procedures. + echo ' +
    +
    +

    ', $txt['maintain_version'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_version_info'], '

    + + +
    +
    + +
    +
    +

    ', $txt['maintain_errors'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_errors_info'], '

    + + +
    +
    + +
    +
    +

    ', $txt['maintain_recount'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_recount_info'], '

    + + +
    +
    + +
    +
    +

    ', $txt['maintain_logs'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_logs_info'], '

    + + +
    +
    + +
    +
    +

    ', $txt['maintain_cache'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_cache_info'], '

    + + +
    +
    + +
    +
    +
    '; +} + +// Template for the member maintenance tasks. +function template_maintain_members() +{ + global $context, $settings, $options, $txt, $scripturl; + + // If maintenance has finished tell the user. + if (!empty($context['maintenance_finished'])) + echo ' +
    + ', sprintf($txt['maintain_done'], $context['maintenance_finished']), ' +
    '; + + echo ' + +
    +
    +

    ', $txt['maintain_reattribute_posts'], '

    +
    +
    + +
    +
    +

    ', $txt['reattribute_guest_posts'], '

    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +

    + + +

    + + +
    +
    + +
    +
    +

    + + ', $txt['help'], ' ', $txt['maintain_members'], ' + +

    +
    +
    + +
    +
    +

    ', $txt['maintain_members_since1'], ' + ', $txt['maintain_members_since2'], ' ', $txt['maintain_members_since3'], '

    '; + + echo ' +

    + ', $txt['maintain_members_all'], '

    + + + +
    +
    + +
    +
    +
    + + + '; +} + +// Template for the topic maintenance tasks. +function template_maintain_topics() +{ + global $scripturl, $txt, $context, $settings, $modSettings; + + // If maintenance has finished tell the user. + if (!empty($context['maintenance_finished'])) + echo ' +
    + ', sprintf($txt['maintain_done'], $context['maintenance_finished']), ' +
    '; + + // Bit of javascript for showing which boards to prune in an otherwise hidden list. + echo ' + '; + + echo ' +
    +
    +

    ', $txt['maintain_old'], '

    +
    +
    + +
    +
    '; + + // The otherwise hidden "choose which boards to prune". + echo ' +

    + ', $txt['maintain_old_since_days1'], '', $txt['maintain_old_since_days2'], ' +

    +

    +
    +
    +
    +

    '; + + if (!empty($modSettings['enableStickyTopics'])) + echo ' +

    +
    +

    '; + + echo ' +

    + + ', $txt['maintain_old_all'], ' +

    + + + +
    +
    + +
    +
    +

    ', $txt['move_topics_maintenance'], '

    +
    +
    + +
    +
    +

    + + +

    + + +
    +
    + +
    +
    +
    '; +} + +// Simple template for showing results of our optimization... +function template_optimize() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['maintain_optimize'], '

    +
    +
    + +
    +

    + ', $txt['database_numb_tables'], '
    + ', $txt['database_optimize_attempt'], '
    '; + + // List each table being optimized... + foreach ($context['optimized_tables'] as $table) + echo ' + ', sprintf($txt['database_optimizing'], $table['name'], $table['data_freed']), '
    '; + + // How did we go? + echo ' +
    ', $context['num_tables_optimized'] == 0 ? $txt['database_already_optimized'] : $context['num_tables_optimized'] . ' ' . $txt['database_optimized']; + + echo ' +

    +

    ', $txt['maintain_return'], '

    +
    + +
    +
    +
    '; +} + +function template_convert_utf8() +{ + global $context, $txt, $settings, $scripturl; + + echo ' +
    +
    +

    ', $txt['utf8_title'], '

    +
    +
    + +
    +
    +

    ', $txt['utf8_introduction'], '

    +
    ', $txt['utf8_warning'], '
    + +
    +
    ', $txt['utf8_source_charset'], ':
    +
    +
    ', $txt['utf8_database_charset'], ':
    +
    ', $context['database_charset'], '
    +
    ', $txt['utf8_target_charset'], ':
    +
    ', $txt['utf8_utf8'], '
    +
    + + + +
    +
    + +
    +
    +
    '; +} + +function template_convert_entities() +{ + global $context, $txt, $settings, $scripturl; + + echo ' +
    +
    +

    ', $txt['entity_convert_title'], '

    +
    +
    + +
    +

    ', $txt['entity_convert_introduction'], '

    +
    + +
    +
    + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageMembergroups.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageMembergroups.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,610 @@ +
    '; + template_show_list('post_count_membergroups_list'); + +} + +function template_new_group() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['membergroups_new_group'], '

    +
    +
    + +
    +
    +
    + +
    +
    + +
    '; + if ($context['undefined_group']) + { + echo ' +
    + +
    +
    +
    + ', $txt['membergroups_edit_select_group_type'], ' +
    '; + + if ($context['allow_protected']) + echo ' +
    '; + + echo ' +
    +
    +
    +
    +
    '; + } + + if ($context['post_group'] || $context['undefined_group']) + echo ' +
    + ', $txt['membergroups_min_posts'], ': +
    +
    + +
    '; + if (!$context['post_group'] || !empty($modSettings['permission_enable_postgroups'])) + { + echo ' +
    +
    + ', $txt['membergroups_can_edit_later'], ' +
    +
    +
    + ', $txt['membergroups_select_permission_type'], ' + + +
    + + + +
    + + + + +
    +
    '; + } + echo ' +
    + ', $txt['membergroups_new_board'], ':', $context['post_group'] ? '
    + ' . $txt['membergroups_new_board_post_groups'] . '' : '', ' +
    +
    +
    + ', $txt['membergroups_new_board_desc'], ''; + foreach ($context['boards'] as $board) + echo ' +
    '; + + echo ' +
    + +
    +
    +
    +
    + +
    +
    + +
    '; + if ($context['undefined_group']) + { + echo ' + '; + } + echo ' + +
    +
    +
    '; +} + +function template_edit_group() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +
    +

    ', $txt['membergroups_edit_group'], ' - ', $context['group']['name'], ' +

    +
    +
    + +
    +
    +
    + +
    +
    + +
    '; + + if ($context['group']['id'] != 3 && $context['group']['id'] != 4) + echo ' + +
    + +
    +
    + +
    '; + + // Group type... + if ($context['group']['allow_post_group']) + { + echo ' +
    + +
    +
    +
    + ', $txt['membergroups_edit_select_group_type'], ' +
    '; + + if ($context['group']['allow_protected']) + echo ' +
    '; + + echo ' +
    +
    +
    +
    +
    '; + } + + if ($context['group']['id'] != 3 && $context['group']['id'] != 4) + echo ' +
    + +
    +
    + +
    +
    +
    + +
    +
    + +
    '; + + // Can they inherit permissions? + if ($context['group']['id'] > 1 && $context['group']['id'] != 3) + { + echo ' +
    + :
    + ', $txt['membergroups_edit_inherit_permissions_desc'], ' +
    +
    + + +
    '; + } + + if ($context['group']['allow_post_group']) + echo ' + +
    + +
    +
    + +
    '; + echo ' +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + ', $txt['membergroups_star_image_note'], ' +
    +
    + ', $txt['membergroups_images_url'], ' + + * +
    +
    +
    + ', $txt['membergroups_max_messages_note'], ' +
    +
    + +
    '; + if (!empty($context['boards'])) + { + echo ' +
    + ', $txt['membergroups_new_board'], ':', $context['group']['is_post_group'] ? '
    + ' . $txt['membergroups_new_board_post_groups'] . '' : '', ' +
    +
    +
    + ', $txt['membergroups_new_board_desc'], ''; + foreach ($context['boards'] as $board) + echo ' +
    '; + + echo ' +
    + +
    + + +
    '; + } + echo ' +
    +
    + ', $context['group']['allow_delete'] ? ' + ' : '', ' +
    +
    + +
    + +
    +
    +
    + + '; + + if ($context['group']['allow_post_group']) + echo ' + '; +} + +// Templating for viewing the members of a group. +function template_group_members() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +
    +
    + ', $txt['name'], ': +
    +
    + ', $context['group']['name'], ' ', $context['group']['stars'], ' +
    '; + //Any description to show? + if (!empty($context['group']['description'])) + echo ' +
    + ' . $txt['membergroups_members_description'] . ': +
    +
    + ', $context['group']['description'] ,' +
    '; + + echo ' +
    + ', $txt['membergroups_members_top'], ': +
    +
    + ', $context['total_members'] ,' +
    '; + // Any group moderators to show? + if (!empty($context['group']['moderators'])) + { + $moderators = array(); + foreach ($context['group']['moderators'] as $moderator) + $moderators[] = '' . $moderator['name'] . ''; + + echo ' +
    + ', $txt['membergroups_members_group_moderators'], ': +
    +
    + ', implode(', ', $moderators) ,' +
    '; + } + + echo ' +
    +
    + +
    + +
    +
    +

    ', $txt['membergroups_members_group_members'], '

    +
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    + + + + + + + + ', $txt['posts'], $context['sort_by'] == 'posts' ? ' ' : '', ''; + if (!empty($context['group']['assignable'])) + echo ' + '; + echo ' + + + '; + + if (empty($context['members'])) + echo ' + + + '; + + foreach ($context['members'] as $member) + { + echo ' + + + '; + + // Is it totally hidden? + if ($member['show_email'] == 'no') + echo ' + ', $txt['hidden'], ''; + // ... otherwise they want it hidden but it's not to this person? + elseif ($member['show_email'] == 'yes_permission_override') + echo ' + ', $member['email'], ''; + // ... otherwise it's visible - but only via an image? + elseif ($member['show_email'] == 'no_through_forum') + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt['email'] . '' : $txt['email']), ''; + // ... otherwise it must be a 'yes', show it and show it fully. + else + echo ' + ', $member['email'], ''; + + echo ' + + + + ', $member['posts'], ''; + if (!empty($context['group']['assignable'])) + echo ' + '; + echo ' + '; + } + + echo ' + +
    ', $txt['name'], $context['sort_by'] == 'name' ? ' ' : '', '', $txt['email'], $context['sort_by'] == 'email' ? ' ' : '', '', $txt['membergroups_members_last_active'], $context['sort_by'] == 'active' ? ' ' : '', '', $txt['date_registered'], $context['sort_by'] == 'registered' ? ' ' : '', '
    ', $txt['membergroups_members_no_members'], '
    ', $member['name'], '', $member['last_online'], '', $member['registered'], '
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + + if (!empty($context['group']['assignable'])) + echo ' +
    '; + echo ' +
    +
    '; + + if (!empty($context['group']['assignable'])) + { + echo ' +
    +

    ', $txt['membergroups_members_add_title'], '

    +
    +
    + +
    + ', $txt['membergroups_members_add_desc'], ': + +
    + +
    + +
    '; + } + + echo ' + +
    +
    +
    '; + + if (!empty($context['group']['assignable'])) + echo ' + + '; +} + +// Allow the moderator to enter a reason to each user being rejected. +function template_group_request_reason() +{ + global $settings, $options, $context, $txt, $scripturl; + + // Show a welcome message to the user. + echo ' +
    +
    +
    +

    ', $txt['mc_groups_reason_title'], '

    +
    +
    + +
    +
    '; + + // Loop through and print out a reason box for each... + foreach ($context['group_requests'] as $request) + echo ' +
    + ', sprintf($txt['mc_groupr_reason_desc'], $request['member_link'], $request['group_link']), ': +
    +
    + + +
    '; + + echo ' +
    + + + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageMembers.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageMembers.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,332 @@ + +
    +
    +

    + ', $txt['search_for'], ' + ', $txt['wild_cards_allowed'], ' +

    +
    + +
    + +
    +
    +
    +
    +
    + ', $txt['member_id'], ': + +
    +
    + +
    +
    + ', $txt['age'], ': + +
    +
    + +
    +
    + ', $txt['member_postcount'], ': + +
    +
    + +
    +
    + ', $txt['date_registered'], ': + +
    +
    + ', $txt['date_format'], ' +
    +
    + ', $txt['viewmembers_online'], ': + +
    +
    + ', $txt['date_format'], ' +
    +
    +
    +
    +
    +
    + ', $txt['username'], ': +
    +
    + +
    +
    + ', $txt['email_address'], ': +
    +
    + +
    +
    + ', $txt['website'], ': +
    +
    + +
    +
    + ', $txt['location'], ': +
    +
    + +
    +
    + ', $txt['ip_address'], ': +
    +
    + +
    +
    + ', $txt['messenger_address'], ': +
    +
    + +
    +
    +
    +
    +
    +
    +
    + ', $txt['gender'], ' +    +    + +
    +
    +
    +
    + ', $txt['activation_status'], ' +    + +
    +
    +
    +
    + +
    +
    +
    +

    ', $txt['member_part_of_these_membergroups'], '

    +
    +
    + + + + + + + + + '; + + foreach ($context['membergroups'] as $membergroup) + echo ' + + + + + '; + + echo ' + + + + + + +
    ', $txt['membergroups'], '', $txt['primary'], '', $txt['additional'], '
    ', $membergroup['name'], ' + + + ', $membergroup['can_be_additional'] ? '' : '', ' +
    + ', $txt['check_all'], ' + + + + +
    + + + + + + + + + '; + + foreach ($context['postgroups'] as $postgroup) + echo ' + + + + '; + + echo ' + + + + + +
    + ', $txt['membergroups_postgroups'], ' +  
    + ', $postgroup['name'], ' + + +
    + ', $txt['check_all'], ' + + +
    +

    +
    + +
    +
    + +
    '; +} + +function template_admin_browse() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    '; + + template_show_list('approve_list'); + + // If we have lots of outstanding members try and make the admin's life easier. + if ($context['approve_list']['total_num_items'] > 20) + { + echo ' +
    +
    +
    +

    ', $txt['admin_browse_outstanding'], '

    +
    + + +
    + +
    +
    +
    + ', $txt['admin_browse_outstanding_days_1'], ': +
    +
    + ', $txt['admin_browse_outstanding_days_2'], '. +
    +
    + ', $txt['admin_browse_outstanding_perform'], ': +
    +
    + +
    +
    + + + + + + ', !empty($context['approve_list']['sort']['desc']) ? ' + ' : '', ' +
    + +
    + +
    '; + } + + echo ' +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageNews.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageNews.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,355 @@ + +
    + + + + + + + + + '; + + // Loop through all the current news items so you can edit/remove them. + foreach ($context['admin_current_news'] as $admin_news) + echo ' + + + '; + + // This provides an empty text box to add a news item to the site. + echo ' + + + + + + +
    ', $txt['admin_edit_news'], '', $txt['preview'], '
    + +
    +
    +
    ', $admin_news['parsed'], '
    +
    + +
    +
    + + + +
    +
    + +
    + +
    + +
    '; +} + +function template_email_members() +{ + global $context, $settings, $options, $txt, $scripturl; + + // This is some javascript for the simple/advanced toggling stuff. + echo ' + '; + + echo ' +
    +
    +
    +

    ', $txt['admin_newsletters'], '

    +
    +
    + ', $txt['admin_news_select_recipients'], ' +
    +
    + +
    +
    +
    + ', $txt['admin_news_select_group'], ':
    + ', $txt['admin_news_select_group_desc'], ' +
    +
    '; + + foreach ($context['groups'] as $group) + echo ' + (', $group['member_count'], ')
    '; + + echo ' +
    + '; + + echo ' +
    +

    +
    + +
    +
    + + + + +
    + + +
    +
    +
    +
    '; + + // Make the javascript stuff visible. + echo ' + + '; +} + +function template_email_members_compose() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $txt['help'], ' ', $txt['admin_newsletters'], ' +

    +
    +
    + ', $txt['email_variables'], ' +
    +
    + +
    +

    + +

    +

    + +

    +
      +
    • +
    • +
    • +
    +

    + +

    +
    + +
    + + + + '; + + foreach ($context['recipients'] as $key => $values) + echo ' + '; + + echo ' +
    +
    +
    '; +} + +function template_email_members_send() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $txt['help'], ' ', $txt['admin_newsletters'], ' +

    +
    +
    + +
    +

    + ', $context['percentage_done'], '% ', $txt['email_done'], ' +

    + + + + + + + + + + '; + + // All the things we must remember! + foreach ($context['recipients'] as $key => $values) + echo ' + '; + + echo ' +
    + +
    +
    +
    +
    + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManagePaid.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManagePaid.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,676 @@ +'; + + echo ' +
    +
    +
    +

    ', $txt['paid_' . $context['action_type'] . '_subscription'], '

    +
    '; + + if (!empty($context['disable_groups'])) + echo ' +
    + ', $txt['paid_mod_edit_note'], ' +
    + '; + echo ' +
    + +
    +
    +
    + ', $txt['paid_mod_name'], ': +
    +
    + +
    +
    + ', $txt['paid_mod_desc'], ': +
    +
    + +
    +
    + : +
    +
    + +
    +
    + :
    ', $txt['paid_mod_active_desc'], ' +
    +
    + +
    +
    +
    +
    +
    + ', $txt['paid_mod_prim_group'], ':
    ', $txt['paid_mod_prim_group_desc'], ' +
    +
    + +
    +
    + ', $txt['paid_mod_add_groups'], ':
    ', $txt['paid_mod_add_groups_desc'], ' +
    +
    '; + + // Put a checkbox in for each group + foreach ($context['groups'] as $id => $name) + echo ' +
    '; + + echo ' +
    +
    + ', $txt['paid_mod_reminder'], ':
    ', $txt['paid_mod_reminder_desc'], ' +
    +
    + +
    +
    + ', $txt['paid_mod_email'], ':
    ', $txt['paid_mod_email_desc'], ' +
    +
    + +
    +
    +
    + + ', $txt['paid_mod_fixed_price'], ' +
    +
    +
    +
    +
    + ', $txt['paid_cost'], ' (', str_replace('%1.2f', '', $modSettings['paid_currency_symbol']), '): +
    +
    + +
    +
    + ', $txt['paid_mod_span'], ': +
    +
    + + +
    +
    +
    +
    + + ', $txt['paid_mod_flexible_price'], ' +
    +
    +
    '; + + //!! Removed until implemented + if (!empty($sdflsdhglsdjgs)) + echo ' +
    +
    + :
    ', $txt['paid_mod_allow_partial_desc'], ' +
    +
    + +
    +
    '; + + echo ' +
    + ', $txt['paid_mod_price_breakdown'], '
    + ', $txt['paid_mod_price_breakdown_desc'], ' +
    +
    +
    + ', $txt['paid_duration'], ' +
    +
    + ', $txt['paid_cost'], ' (', preg_replace('~%[df\.\d]+~', '', $modSettings['paid_currency_symbol']), ') +
    +
    + ', $txt['paid_per_day'], ': +
    +
    + +
    +
    + ', $txt['paid_per_week'], ': +
    +
    + +
    +
    + ', $txt['paid_per_month'], ': +
    +
    + +
    +
    + ', $txt['paid_per_year'], ': +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    '; + +} + +function template_delete_subscription() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['paid_delete_subscription'], '

    +
    +
    + +
    +

    ', $txt['paid_mod_delete_warning'], '

    + + + +
    + +
    +
    +
    +
    '; + +} + +// Add or edit an existing subscriber. +function template_modify_user_subscription() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Some quickly stolen javascript from Post, could do with being more efficient :) + echo ' + '; + + echo ' +
    +
    +
    +

    + ', $txt['paid_' . $context['action_type'] . '_subscription'], ' - ', $context['current_subscription']['name'], ' + ', empty($context['sub']['username']) ? '' : ' (' . $txt['user'] . ': ' . $context['sub']['username'] . ')', ' +

    +
    +
    + +
    +
    '; + + // Do we need a username? + if ($context['action_type'] == 'add') + echo ' + +
    + ', $txt['paid_username'], ':
    + ', $txt['one_username'], ' +
    +
    + +
    '; + + echo ' +
    + ', $txt['paid_status'], ': +
    +
    + +
    +
    +
    + ', $txt['start_date_and_time'], ' +   + ', (isset($txt['calendar_month']) ? $txt['calendar_month'] : $txt['calendar_month']), '  +   + ', (isset($txt['calendar_day']) ? $txt['calendar_day'] : $txt['calendar_day']), '  + + ', $txt['hour'], ': + ', $txt['minute'], ': +
    +
    + ', $txt['end_date_and_time'], ' +   + ', (isset($txt['calendar_month']) ? $txt['calendar_month'] : $txt['calendar_month']), '  +   + ', (isset($txt['calendar_day']) ? $txt['calendar_day'] : $txt['calendar_day']), '  + + ', $txt['hour'], ': + ', $txt['minute'], ': +
    + +
    + +
    + +
    + + '; + + if (!empty($context['pending_payments'])) + { + echo ' +
    +

    ', $txt['pending_payments'], '

    +
    +
    + ', $txt['pending_payments_desc'], ' +
    +
    +

    ', $txt['pending_payments_value'], '

    +
    +
    + +
    + +
    + +
    '; + } + + echo ' +
    +
    '; +} + +// Template for a user to edit/pick their subscriptions. +function template_user_subscription() +{ + global $context, $txt, $scripturl, $modSettings; + + echo ' + +
    '; +} + +// The "choose payment" dialog. +function template_choose_payment() +{ + global $context, $txt, $modSettings, $scripturl; + + echo ' + +
    '; +} + +// The "thank you" bit... +function template_paid_done() +{ + global $context, $txt, $modSettings, $scripturl; + + echo ' + +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManagePermissions.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManagePermissions.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1192 @@ + + ', sprintf($txt['permission_cannot_edit'], $scripturl . '?action=admin;area=permissions;sa=profiles'), ' + '; + + echo ' +
    +
    '; + + if (!empty($context['profile'])) + echo ' +
    +

    ', $txt['permissions_for_profile'], ': "', $context['profile']['name'], '"

    +
    '; + + echo ' + + + + + '; + + if (empty($modSettings['permission_enable_deny'])) + echo ' + '; + else + echo ' + + '; + + echo ' + + + + + '; + + $alternate = false; + foreach ($context['groups'] as $group) + { + $alternate = !$alternate; + echo ' + + + '; + + if (empty($modSettings['permission_enable_deny'])) + echo ' + '; + else + echo ' + + '; + + echo ' + + + '; + } + + echo ' + +
    ', $txt['membergroups_name'], '', $txt['membergroups_members_top'], '', $txt['membergroups_permissions'], '', $txt['permissions_allowed'], '', $txt['permissions_denied'], '', $context['can_modify'] ? $txt['permissions_modify'] : $txt['permissions_view'], ' + ', $context['can_modify'] ? '' : '', ' +
    + ', $group['name'], $group['id'] == -1 ? ' (?)' : ($group['id'] == 0 ? ' (?)' : ($group['id'] == 1 ? ' (?)' : ($group['id'] == 3 ? ' (?)' : ''))); + + if (!empty($group['children'])) + echo ' +
    ', $txt['permissions_includes_inherited'], ': "', implode('", "', $group['children']), '"'; + + echo ' +
    ', $group['can_search'] ? $group['link'] : $group['num_members'], '', $group['num_permissions']['allowed'], '', $group['num_permissions']['allowed'], '', $group['num_permissions']['denied'], '', $group['allow_modify'] ? '' . ($context['can_modify'] ? $txt['permissions_modify'] : $txt['permissions_view']). '' : '', '', $group['allow_modify'] && $context['can_modify'] ? '' : '', '
    +
    '; + + // Advanced stuff... + if ($context['can_modify']) + { + echo ' +
    +

    + + * ', $txt['permissions_advanced_options'], ' + +

    +
    +
    + +
    +
    + ', $txt['permissions_with_selection'], ' +
    +
    + ', $txt['permissions_apply_pre_defined'], ' (?): +
    +
    + +
    +
    + ', $txt['permissions_like_group'], ': +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    '; + + // Javascript for the advanced stuff. + echo ' + '; + + if (!empty($context['profile'])) + echo ' + '; + + echo ' + '; + } + else + echo ' + '; + + echo ' +
    +
    +
    '; +} + +function template_by_board() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +

    ', $txt['permissions_boards'], '

    +
    +
    + ', $txt['permissions_boards_desc'], ' +
    +
    +

    + ', $txt['board_name'], ' + ', $txt['permission_profile'], ' +

    +
    '; + + if (!$context['edit_all']) + echo ' + '; + + foreach ($context['categories'] as $category) + { + echo ' +
    +

    ', $category['name'], '

    +
    '; + + if (!empty($category['boards'])) + echo ' +
    + +
    + +
    + +
    '; + } + + echo ' +
    '; + + if ($context['edit_all']) + echo ' + '; + else + echo ' + [', $txt['permissions_board_all'], ']'; + + echo ' + +
    +
    +
    '; +} + +// Edit permission profiles (predefined). +function template_edit_profiles() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['permissions_profile_edit'], '

    +
    + + + + + + + + + + '; + $alternate = false; + foreach ($context['profiles'] as $profile) + { + echo ' + + + + + '; + $alternate = !$alternate; + } + + echo ' + +
    ', $txt['permissions_profile_name'], '', $txt['permissions_profile_used_by'], '', $txt['delete'], '
    '; + + if (!empty($context['show_rename_boxes']) && $profile['can_edit']) + echo ' + '; + else + echo ' + ', $profile['name'], ''; + + echo ' + + ', !empty($profile['boards_text']) ? $profile['boards_text'] : $txt['permissions_profile_used_by_none'], ' + + +
    +
    + '; + + if ($context['can_edit_something']) + echo ' + '; + + echo ' + +
    +
    +
    +
    +
    +

    ', $txt['permissions_profile_new'], '

    +
    +
    + +
    +
    +
    + ', $txt['permissions_profile_name'], ': +
    +
    + +
    +
    + ', $txt['permissions_profile_copy_from'], ': +
    +
    + +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +function template_modify_group() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Cannot be edited? + if (!$context['profile']['can_modify']) + { + echo ' +
    + ', sprintf($txt['permission_cannot_edit'], $scripturl . '?action=admin;area=permissions;sa=profiles'), ' +
    '; + } + else + { + echo ' + '; + } + + echo ' +
    +
    '; + + if (!empty($modSettings['permission_enable_deny']) && $context['group']['id'] != -1) + echo ' +
    + ', $txt['permissions_option_desc'], ' +
    '; + + echo ' +
    +

    '; + if ($context['permission_type'] == 'board') + echo ' + ', $txt['permissions_local_for'], ' "', $context['group']['name'], '" ', $txt['permissions_on'], ' "', $context['profile']['name'], '"'; + else + echo ' + ', $context['permission_type'] == 'membergroup' ? $txt['permissions_general'] : $txt['permissions_board'], ' - "', $context['group']['name'], '"'; + echo ' +

    +
    +
    + +
    + ', $txt['permissions_change_view'], ': ', ($context['view_type'] == 'simple' ? '*' : ''), '', $txt['permissions_view_simple'], ' | + ', ($context['view_type'] == 'classic' ? '*' : ''), '', $txt['permissions_view_classic'], ' +
    + +
    +
    '; + + // Draw out the main bits. + if ($context['view_type'] == 'simple') + template_modify_group_simple($context['permission_type']); + else + template_modify_group_classic($context['permission_type']); + + // If this is general permissions also show the default profile. + if ($context['permission_type'] == 'membergroup') + { + echo ' +
    +
    +
    +

    ', $txt['permissions_board'], '

    +
    +
    + ', $txt['permissions_board_desc'], ' +
    +
    '; + + if ($context['view_type'] == 'simple') + template_modify_group_simple('board'); + else + template_modify_group_classic('board'); + + echo ' +
    '; + } + + if ($context['profile']['can_modify']) + echo ' +
    + +
    '; + + echo ' + +
    +
    +
    '; + +} + +// A javascript enabled clean permissions view. +function template_modify_group_simple($type) +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Simple only has one column so we only need bother ourself with that one. + $permission_data = &$context['permissions'][$type]['columns'][0]; + + // Short cut for disabling fields we can't change. + $disable_field = $context['profile']['can_modify'] ? '' : 'disabled="disabled" '; + + echo ' + + + + '; + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + echo ' + + + '; + + foreach ($permission_data as $id_group => $permissionGroup) + { + if (empty($permissionGroup['permissions'])) + continue; + + // Are we likely to have something in this group to display or is it all hidden? + $has_display_content = false; + if (!$permissionGroup['hidden']) + { + // Before we go any further check we are going to have some data to print otherwise we just have a silly heading. + foreach ($permissionGroup['permissions'] as $permission) + if (!$permission['hidden']) + $has_display_content = true; + + if ($has_display_content) + { + echo ' + + '; + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + echo ' + '; + } + } + + $alternate = false; + foreach ($permissionGroup['permissions'] as $permission) + { + // If it's hidden keep the last value. + if ($permission['hidden'] || $permissionGroup['hidden']) + { + echo ' + + + '; + } + else + { + echo ' + + + '; + + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + + echo ' + '; + } + $alternate = !$alternate; + } + + if (!$permissionGroup['hidden'] && $has_display_content) + echo ' + + + '; + } + echo ' + +
     ', $txt['permissions_option_on'], '', $txt['permissions_option_off'], '', $txt['permissions_option_deny'], '
    + + * ', $permissionGroup['name'], ' + + +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    + ', $permission['help_index'] ? '' . $txt['help'] . '' : '', ' + ', $permission['name'], '
    + '; +} + +// The SMF 1.x way of looking at permissions. +function template_modify_group_classic($type) +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + $permission_type = &$context['permissions'][$type]; + $disable_field = $context['profile']['can_modify'] ? '' : 'disabled="disabled" '; + + echo ' +
    + +
    '; + + foreach ($permission_type['columns'] as $column) + { + echo ' + '; + + foreach ($column as $permissionGroup) + { + if (empty($permissionGroup['permissions'])) + continue; + + // Are we likely to have something in this group to display or is it all hidden? + $has_display_content = false; + if (!$permissionGroup['hidden']) + { + // Before we go any further check we are going to have some data to print otherwise we just have a silly heading. + foreach ($permissionGroup['permissions'] as $permission) + if (!$permission['hidden']) + $has_display_content = true; + + if ($has_display_content) + { + echo ' + + '; + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + echo ' + '; + } + } + + $alternate = false; + foreach ($permissionGroup['permissions'] as $permission) + { + // If it's hidden keep the last value. + if ($permission['hidden'] || $permissionGroup['hidden']) + { + echo ' + + + '; + } + else + { + echo ' + + '; + + if ($permission['has_own_any']) + { + echo ' + + '; + + // Guests can't do their own thing. + if ($context['group']['id'] != -1) + { + echo ' + + '; + + if (empty($modSettings['permission_enable_deny'])) + echo ' + '; + else + echo ' + + + '; + + echo ' + '; + } + + echo ' + + '; + + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + + echo ' + '; + } + else + { + echo ' + '; + + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + + echo ' + '; + } + } + $alternate = !$alternate; + } + + if (!$permissionGroup['hidden'] && $has_display_content) + echo ' + + + '; + } + echo ' +
    ', $permissionGroup['name'], '
    ', $txt['permissions_option_on'], '
    ', $txt['permissions_option_off'], '
    ', $txt['permissions_option_deny'], '
    '; + + if ($permission['has_own_any']) + { + // Guests can't have own permissions. + if ($context['group']['id'] != -1) + echo ' + '; + + echo ' + '; + } + else + echo ' + '; + echo ' +
    + ', $permission['show_help'] ? '' . $txt['help'] . '' : '', ' + ', $permission['name'], '
    ', $permission['own']['name'], ':
    ', $permission['any']['name'], ':
    ', $permission['name'], '
    '; + } + echo ' +
    +
    + +
    '; +} + +function template_inline_permissions() +{ + global $context, $settings, $options, $txt, $modSettings; + + echo ' +
    + ', $txt['avatar_select_permission'], ''; + if (empty($modSettings['permission_enable_deny'])) + echo ' +
      '; + else + echo ' +
      ', $txt['permissions_option_desc'], '
      +
      +
      + ', $txt['permissions_option_on'], ' + ', $txt['permissions_option_off'], ' + ', $txt['permissions_option_deny'], ' +
      +
      +
      '; + foreach ($context['member_groups'] as $group) + { + if (!empty($modSettings['permission_enable_deny'])) + echo ' +
      '; + else + echo ' +
    • '; + + if (empty($modSettings['permission_enable_deny'])) + echo ' + '; + else + echo ' + + + '; + + if (!empty($modSettings['permission_enable_deny'])) + echo ' +
    • +
      + ', $group['name'], ' +
      '; + else + echo ' + ', $group['name'], ' + '; + } + + if (empty($modSettings['permission_enable_deny'])) + echo ' +
    '; + else + echo ' + '; + + echo ' +
    + + + + '; +} + +// Edit post moderation permissions. +function template_postmod_permissions() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['permissions_post_moderation'], '

    +
    '; + + // Got advanced permissions - if so warn! + if (!empty($modSettings['permission_enable_deny'])) + echo ' +
    ', $txt['permissions_post_moderation_deny_note'], '
    '; + + echo ' +
    + ', $txt['permissions_post_moderation_select'], ': + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + '; + + foreach ($context['profile_groups'] as $group) + { + echo ' + + + + + + + + + + + + + + + '; + } + + echo ' + +
    + ', $txt['permissions_post_moderation_new_topics'], ' + + ', $txt['permissions_post_moderation_replies_own'], ' + + ', $txt['permissions_post_moderation_replies_any'], ' + + ', $txt['permissions_post_moderation_attachments'], ' +
    + ', $txt['permissions_post_moderation_group'], ' + ', $txt['permissions_post_moderation_allow'], '', $txt['permissions_post_moderation_moderate'], '', $txt['permissions_post_moderation_disallow'], '', $txt['permissions_post_moderation_allow'], '', $txt['permissions_post_moderation_moderate'], '', $txt['permissions_post_moderation_disallow'], '', $txt['permissions_post_moderation_allow'], '', $txt['permissions_post_moderation_moderate'], '', $txt['permissions_post_moderation_disallow'], '', $txt['permissions_post_moderation_allow'], '', $txt['permissions_post_moderation_moderate'], '', $txt['permissions_post_moderation_disallow'], '
    + ', $group['name'], ''; + if (!empty($group['children'])) + echo ' +
    ', $txt['permissions_includes_inherited'], ': "', implode('", "', $group['children']), '"'; + + echo ' +
    +
    + +
    +
    +

    + ', $txt['permissions_post_moderation_legend'], ':
    + ', $txt['permissions_post_moderation_allow'], ' - ', $txt['permissions_post_moderation_allow'], '
    + ', $txt['permissions_post_moderation_moderate'], ' - ', $txt['permissions_post_moderation_moderate'], '
    + ', $txt['permissions_post_moderation_disallow'], ' - ', $txt['permissions_post_moderation_disallow'], ' +

    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageScheduledTasks.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageScheduledTasks.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,95 @@ + + ', $txt['scheduled_tasks_were_run'], ' + '; + + template_show_list('scheduled_tasks'); +} + +// A template for, you guessed it, editing a task! +function template_edit_scheduled_tasks() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // Starts off with general maintenance procedures. + echo ' +
    +
    +
    +

    ', $txt['scheduled_task_edit'], '

    +
    +
    + ', sprintf($txt['scheduled_task_time_offset'], $context['server_time']), ' +
    +
    + +
    +
    +
    + ', $txt['scheduled_tasks_name'], ': +
    +
    + ', $context['task']['name'], '
    + ', $context['task']['desc'], ' +
    +
    + ', $txt['scheduled_task_edit_interval'], ': +
    +
    + ', $txt['scheduled_task_edit_repeat'], ' + + +
    +
    + ', $txt['scheduled_task_edit_start_time'], ':
    + ', $txt['scheduled_task_edit_start_time_desc'], ' +
    +
    + +
    +
    + ', $txt['scheduled_tasks_enabled'], ': +
    +
    + +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageSearch.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageSearch.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,428 @@ + +
    +
    +

    ', $txt['search_weights'], '

    +
    +
    + +
    +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_frequency'], ': +
    +
    + + ', $context['relative_weights']['search_weight_frequency'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_age'], ': +
    +
    + + ', $context['relative_weights']['search_weight_age'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_length'], ': +
    +
    + + ', $context['relative_weights']['search_weight_length'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_subject'], ': +
    +
    + + ', $context['relative_weights']['search_weight_subject'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_first_message'], ': +
    +
    + + ', $context['relative_weights']['search_weight_first_message'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_sticky'], ': +
    +
    + + ', $context['relative_weights']['search_weight_sticky'], '% +
    +
    + ', $txt['search_weights_total'], ' +
    +
    + ', $context['relative_weights']['total'], ' + 100% +
    +
    + +
    +
    + +
    +
    + +
    + '; +} + +function template_select_search_method() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['search_method'], '

    +
    + +
    + +
    +
    + + '; + if (!empty($context['table_info'])) + echo ' +
    + ', $txt['search_method_messages_table_space'], ': +
    +
    + ', $context['table_info']['data_length'], ' +
    +
    + ', $txt['search_method_messages_index_space'], ': +
    +
    + ', $context['table_info']['index_length'], ' +
    '; + echo ' +
    + ', $context['double_index'] ? '
    + ' . $txt['search_double_index'] . '
    ' : '', ' +
    + ', $txt['search_index'], ' +
    +
    + ', $txt['search_index_none'], ' +
    '; + + if ($context['supports_fulltext']) + { + echo ' +
    + + ', $txt['search_method_fulltext_index'], ' +
    +
    + + '; + if (empty($context['fulltext_index']) && empty($context['cannot_create_fulltext'])) + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_no_index_exists'], ' [', $txt['search_method_fulltext_create'], ']'; + elseif (empty($context['fulltext_index']) && !empty($context['cannot_create_fulltext'])) + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_fulltext_cannot_create']; + else + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_index_already_exists'], ' [', $txt['search_method_fulltext_remove'], ']
    + ', $txt['search_index_size'], ': ', $context['table_info']['fulltext_length']; + echo ' +
    +
    '; + } + + echo ' +
    + + ', $txt['search_index_custom'], ' +
    +
    + '; + if ($context['custom_index']) + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_index_already_exists'], ' [', $txt['search_index_custom_remove'], ']
    + ', $txt['search_index_size'], ': ', $context['table_info']['custom_index_length']; + elseif ($context['partial_custom_index']) + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_index_partial'], ' [', $txt['search_index_custom_remove'], '] [', $txt['search_index_custom_resume'], ']
    + ', $txt['search_index_size'], ': ', $context['table_info']['custom_index_length']; + else + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_no_index_exists'], ' [', $txt['search_index_create_custom'], ']'; + echo ' +
    +
    '; + + foreach ($context['search_apis'] as $api) + { + if (empty($api['label']) || $api['has_template']) + continue; + + echo ' +
    + + ', $api['label'] ,' +
    '; + + if ($api['desc']) + echo ' +
    + ', $api['desc'], ' +
    '; + } + + echo ' +
    +
    +
    + ', $txt['search_method'], ' +
    + +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +function template_create_index() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +
    +

    ', $txt['search_create_index'], '

    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + + +
    + +
    + +
    +
    +
    '; +} + +function template_create_index_progress() +{ + global $context, $settings, $options, $scripturl, $txt; + echo ' +
    +
    +
    +

    ', $txt['search_create_index'], '

    +
    +
    + +
    +

    + ', $txt['search_create_index_not_ready'], ' +

    +

    + ', $txt['search_create_index_progress'], ': ', $context['percentage'], '% +

    + +
    + +
    + + + + +
    +
    +
    + '; + +} + +function template_create_index_done() +{ + global $context, $settings, $options, $scripturl, $txt; + echo ' +
    +
    +

    ', $txt['search_create_index'], '

    +
    +
    + +
    +

    ', $txt['search_create_index_done'], '

    +

    + ', $txt['search_create_index_done_link'], ' +

    +
    + +
    +
    +
    '; +} + +// Add or edit a search engine spider. +function template_spider_edit() +{ + global $context, $settings, $options, $scripturl, $txt; + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    +
    + ', $txt['add_spider_desc'], ' +
    +
    + +
    +
    +
    + ', $txt['spider_name'], ':
    + ', $txt['spider_name_desc'], ' +
    +
    + +
    +
    + ', $txt['spider_agent'], ':
    + ', $txt['spider_agent_desc'], ' +
    +
    + +
    +
    + ', $txt['spider_ip_info'], ':
    + ', $txt['spider_ip_info_desc'], ' +
    +
    + +
    +
    + + +
    + +
    +
    +
    +
    '; +} + +// Show... spider... logs... +function template_show_spider_logs() +{ + global $context, $txt, $settings, $scripturl; + + echo ' +
    '; + + // Standard fields. + template_show_list('spider_logs'); + + echo ' +
    +
    +

    ', $txt['spider_logs_delete'], '

    +
    +
    +
    + +
    +

    + ', $txt['spider_logs_delete_older'], ' + + ', $txt['spider_logs_delete_day'], ' +

    + + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ManageSmileys.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ManageSmileys.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,583 @@ +'; + + template_show_list('smiley_set_list'); + + echo ' +
    +
    +

    ', $txt['smiley_sets_latest'], '

    +
    +
    + +
    +
    ', $txt['smiley_sets_latest_fetch'], '
    +
    + +
    + +
    + '; + + if (empty($modSettings['disable_smf_js'])) + echo ' + '; + + echo ' + '; +} + +// Modifying a smiley set. +function template_modifyset() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    + ', $context['current_set']['is_new'] ? $txt['smiley_set_new'] : $txt['smiley_set_modify_existing'], ' +

    +
    '; + + // If this is an existing set, and there are still un-added smileys - offer an import opportunity. + if (!empty($context['current_set']['can_import'])) + { + echo ' +
    + ', $context['current_set']['can_import'] == 1 ? $txt['smiley_set_import_single'] : $txt['smiley_set_import_multiple'], ' ', $txt['here'], ' ', $context['current_set']['can_import'] == 1 ? $txt['smiley_set_to_import_single'] : $txt['smiley_set_to_import_multiple'], ' +
    '; + } + + echo ' +
    + +
    +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + ', $modSettings['smileys_url'], '/'; + if ($context['current_set']['id'] == 'default') + echo 'default'; + elseif (empty($context['smiley_set_dirs'])) + echo ' + '; + else + { + echo ' + '; + } + echo ' + /.. +
    +
    + : +
    +
    + +
    '; + + // If this is a new smiley set they have the option to import smileys already in the directory. + if ($context['current_set']['is_new'] && !empty($modSettings['smiley_enable'])) + echo ' +
    + : +
    +
    + +
    '; + + echo ' +
    + +
    + +
    + + +
    +
    +
    '; +} + +// Editing an individual smiley +function template_modifysmiley() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['smiley_modify_existing'], '

    +
    +
    + +
    +
    +
    + ', $txt['smiley_preview'], ': +
    +
    + (', $txt['smiley_preview_using'], ': ) +
    +
    + : +
    +
    + +
    +
    + : +
    +
    '; + if (empty($context['filenames'])) + echo ' + '; + else + { + echo ' + '; + } + echo ' +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + + +
    + +
    + + +
    +
    +
    + '; +} + +// Adding a new smiley. +function template_addsmiley() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' + +
    +
    +
    +

    ', $txt['smileys_add_method'], '

    +
    +
    + +
    +
      +
    • + +
    • +
    • + +
    • +
    +
    +
    +
    +
    + +
    +
    + ', $txt['smiley_preview_using'], ': +
    +
    + : +
    +
    '; + if (empty($context['filenames'])) + echo ' + '; + else + { + echo ' + '; + } + + echo ' +
    +
    +
    + + + +
    + +
    +
    +
    +

    ', $txt['smiley_new'], '

    +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + +
    + +
    + +
    +
    +
    + '; +} + +// Ordering smileys. +function template_setorder() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    '; + + foreach ($context['smileys'] as $location) + { + echo ' +
    +
    +

    ', $location['title'], '

    +
    +
    + ', $location['description'], ' +
    +
    + +
    + ', empty($context['move_smiley']) ? $txt['smileys_move_select_smiley'] : $txt['smileys_move_select_destination'], '...
    '; + foreach ($location['rows'] as $row) + { + if (!empty($context['move_smiley'])) + echo ' + ', $txt['smileys_move_here'], ''; + + foreach ($row as $smiley) + { + if (empty($context['move_smiley'])) + echo '', $smiley['description'], ''; + else + echo '', $smiley['description'], '', $txt['smileys_move_here'], ''; + } + + echo ' +
    '; + } + if (!empty($context['move_smiley'])) + echo ' + ', $txt['smileys_move_here'], ''; + echo ' +
    + +
    + +
    +
    '; + } + echo ' +
    +
    '; +} + +// Editing Message Icons +function template_editicons() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + template_show_list('message_icon_list'); +} + +// Editing an individual message icon +function template_editicon() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    + ', $context['new_icon'] ? $txt['icons_new_icon'] : $txt['icons_edit_icon'], ' +

    +
    +
    + +
    +
    '; + if (!$context['new_icon']) + echo ' +
    + ', $txt['smiley_preview'], ': +
    +
    + ', $context['icon']['title'], ' +
    '; + echo ' +
    + :
    ', $txt['icons_filename_all_gif'], ' +
    +
    + +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    '; + + if (!$context['new_icon']) + echo ' + '; + + echo ' + + + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Memberlist.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Memberlist.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,216 @@ + array('text' => 'view_all_members', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=all', 'active'=> true), + 'mlist_search' => array('text' => 'mlist_search', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=search'), + ); + + echo ' +
    +
    +

    + ', $txt['members_list'], ''; + if (!isset($context['old_search'])) + echo ' + ', $context['letter_links'], ''; + echo ' +

    +
    +
    + ', template_button_strip($memberlist_buttons, 'right'), ' + +
    '; + + echo ' +
    + + + '; + + // Display each of the column headers of the table. + foreach ($context['columns'] as $column) + { + // We're not able (through the template) to sort the search results right now... + if (isset($context['old_search'])) + echo ' + '; + // This is a selected column, so underline it or some such. + elseif ($column['selected']) + echo ' + '; + // This is just some column... show the link and be done with it. + else + echo ' + '; + } + echo ' + + + '; + + // Assuming there are members loop through each one displaying their data. + if (!empty($context['members'])) + { + foreach ($context['members'] as $member) + { + echo ' + + + + '; + + if (!isset($context['disabled_fields']['website'])) + echo ' + '; + + // ICQ? + if (!isset($context['disabled_fields']['icq'])) + echo ' + '; + + // AIM? + if (!isset($context['disabled_fields']['aim'])) + echo ' + '; + + // YIM? + if (!isset($context['disabled_fields']['yim'])) + echo ' + '; + + // MSN? + if (!isset($context['disabled_fields']['msn'])) + echo ' + '; + + // Group and date. + echo ' + + '; + + if (!isset($context['disabled_fields']['posts'])) + { + echo ' + + '; + } + + echo ' + '; + } + } + // No members? + else + echo ' + + + '; + + // Show the page numbers again. (makes 'em easier to find!) + echo ' + +
    + ', $column['label'], ' + ' . $column['label'] . ' + ', $column['link'], '
    + ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $member['online']['text'] . '' : $member['online']['label'], $context['can_send_pm'] ? '' : '', ' + ', $member['link'], '', $member['show_email'] == 'no' ? '' : '' . $txt['email'] . '', '', $member['website']['url'] != '' ? '' . $member['website']['title'] . '' : '', '', $member['icq']['link'], '', $member['aim']['link'], '', $member['yim']['link'], '', $member['msn']['link'], '', empty($member['group']) ? $member['post_group'] : $member['group'], '', $member['registered_date'], '', $member['posts'], ''; + + if (!empty($member['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' +
    ', $txt['search_no_results'], '
    +
    '; + + echo ' +
    + '; + + // If it is displaying the result of a search show a "search again" link to edit their criteria. + if (isset($context['old_search'])) + echo ' + '; + echo ' +
    +
    '; + +} + +// A page allowing people to search the member list. +function template_search() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Build the memberlist button array. + $memberlist_buttons = array( + 'view_all_members' => array('text' => 'view_all_members', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=all'), + 'mlist_search' => array('text' => 'mlist_search', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=search', 'active' => true), + ); + + // Start the submission form for the search! + echo ' +
    +
    +
    +

    + ', !empty($settings['use_buttons']) ? '' : '', $txt['mlist_search'], ' +

    +
    +
    + ', template_button_strip($memberlist_buttons, 'right'), ' +
    '; + // Display the input boxes for the form. + echo ' +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/MessageIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/MessageIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,466 @@ +'; + + if (!empty($context['boards']) && (!empty($options['show_children']) || $context['start'] == 0)) + { + echo ' +
    +
    +

    ', $txt['parent_boards'], '

    +
    +
    + + '; + + foreach ($context['boards'] as $board) + { + echo ' + + + + + + '; + + // Show the "Child Boards: ". (there's a link_children but we're going to bold the new ones...) + if (!empty($board['children'])) + { + // Sort the links into an array with new boards bold so it can be imploded. + $children = array(); + /* Each child in each board's children has: + id, name, description, new (is it new?), topics (#), posts (#), href, link, and last_post. */ + foreach ($board['children'] as $child) + { + if (!$child['is_redirect']) + $child['link'] = '' . $child['name'] . ($child['new'] ? '' : '') . ''; + else + $child['link'] = '' . $child['name'] . ''; + + // Has it posts awaiting approval? + if ($child['can_approve_posts'] && ($child['unapproved_posts'] | $child['unapproved_topics'])) + $child['link'] .= ' (!)'; + + $children[] = $child['new'] ? '' . $child['link'] . '' : $child['link']; + } + echo ' + '; + } + } + echo ' + +
    + '; + + // If the board or children is new, show an indicator. + if ($board['new'] || $board['children_new']) + echo ' + ', $txt['new_posts'], ''; + // Is it a redirection board? + elseif ($board['is_redirect']) + echo ' + *'; + // No new posts at all! The agony!! + else + echo ' + ', $txt['old_posts'], ''; + + echo ' + + + ', $board['name'], ''; + + // Has it outstanding posts for approval? + if ($board['can_approve_posts'] && ($board['unapproved_posts'] || $board['unapproved_topics'])) + echo ' + (!)'; + + echo ' + +

    ', $board['description'] , '

    '; + + // Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.) + if (!empty($board['moderators'])) + echo ' +

    ', count($board['moderators']) === 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $board['link_moderators']), '

    '; + + // Show some basic information about the number of posts, etc. + echo ' +
    +

    ', comma_format($board['posts']), ' ', $board['is_redirect'] ? $txt['redirects'] : $txt['posts'], '
    + ', $board['is_redirect'] ? '' : comma_format($board['topics']) . ' ' . $txt['board_topics'], ' +

    +
    '; + + /* The board's and children's 'last_post's have: + time, timestamp (a number that represents the time.), id (of the post), topic (topic id.), + link, href, subject, start (where they should go for the first unread post.), + and member. (which has id, name, link, href, username in it.) */ + if (!empty($board['last_post']['id'])) + echo ' +

    ', $txt['last_post'], ' ', $txt['by'], ' ', $board['last_post']['member']['link'], '
    + ', $txt['in'], ' ', $board['last_post']['link'], '
    + ', $txt['on'], ' ', $board['last_post']['time'],' +

    '; + + echo ' +
    ', $txt['parent_boards'], ': ', implode(', ', $children), '
    +
    +
    '; + } + + if (!empty($options['show_board_desc']) && $context['description'] != '') + echo ' +

    ', $context['description'], '

    '; + + // Create the button set... + $normal_buttons = array( + 'new_topic' => array('test' => 'can_post_new', 'text' => 'new_topic', 'image' => 'new_topic.gif', 'lang' => true, 'url' => $scripturl . '?action=post;board=' . $context['current_board'] . '.0', 'active' => true), + 'post_poll' => array('test' => 'can_post_poll', 'text' => 'new_poll', 'image' => 'new_poll.gif', 'lang' => true, 'url' => $scripturl . '?action=post;board=' . $context['current_board'] . '.0;poll'), + 'notify' => array('test' => 'can_mark_notify', 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', 'image' => ($context['is_marked_notify'] ? 'un' : ''). 'notify.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . ($context['is_marked_notify'] ? $txt['notification_disable_board'] : $txt['notification_enable_board']) . '\');"', 'url' => $scripturl . '?action=notifyboard;sa=' . ($context['is_marked_notify'] ? 'off' : 'on') . ';board=' . $context['current_board'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), + 'markread' => array('text' => 'mark_read_short', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=board;board=' . $context['current_board'] . '.0;' . $context['session_var'] . '=' . $context['session_id']), + ); + + // They can only mark read if they are logged in and it's enabled! + if (!$context['user']['is_logged'] || !$settings['show_mark_read']) + unset($normal_buttons['markread']); + + // Allow adding new buttons easily. + call_integration_hook('integrate_messageindex_buttons', array(&$normal_buttons)); + + if (!$context['no_topic_listing']) + { + echo ' +
    + + ', template_button_strip($normal_buttons, 'right'), ' +
    '; + + // If Quick Moderation is enabled start the form. + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] > 0 && !empty($context['topics'])) + echo ' +
    '; + + echo ' +
    + + + '; + + // Are there actually any topics to show? + if (!empty($context['topics'])) + { + echo ' + + + '; + // Show a "select all" box for quick moderation? + if (empty($context['can_quick_mod'])) + echo ' + '; + else + echo ' + '; + + // Show a "select all" box for quick moderation? + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] == 1) + echo ' + '; + + // If it's on in "image" mode, don't show anything but the column. + elseif (!empty($context['can_quick_mod'])) + echo ' + '; + } + // No topics.... just say, "sorry bub". + else + echo ' + + + '; + + echo ' + + + '; + + if (!empty($settings['display_who_viewing'])) + { + echo ' + + + '; + } + + // If this person can approve items and we have some awaiting approval tell them. + if (!empty($context['unapproved_posts_message'])) + { + echo ' + + + '; + } + + foreach ($context['topics'] as $topic) + { + // Is this topic pending approval, or does it have any posts pending approval? + if ($context['can_approve_posts'] && $topic['unapproved_posts']) + $color_class = !$topic['approved'] ? 'approvetbg' : 'approvebg'; + // We start with locked and sticky topics. + elseif ($topic['is_sticky'] && $topic['is_locked']) + $color_class = 'stickybg locked_sticky'; + // Sticky topics should get a different color, too. + elseif ($topic['is_sticky']) + $color_class = 'stickybg'; + // Locked topics get special treatment as well. + elseif ($topic['is_locked']) + $color_class = 'lockedbg'; + // Last, but not least: regular topics. + else + $color_class = 'windowbg'; + + // Some columns require a different shade of the color class. + $alternate_class = $color_class . '2'; + + echo ' + + + + + + '; + + // Show the quick moderation options? + if (!empty($context['can_quick_mod'])) + { + echo ' + '; + } + echo ' + '; + } + + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + { + echo ' + + + '; + } + + echo ' + +
     ', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', ' / ', $txt['started_by'], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt['replies'], $context['sort_by'] == 'replies' ? ' ' : '', ' / ', $txt['views'], $context['sort_by'] == 'views' ? ' ' : '', '', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', '', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', '  ', $txt['msg_alert_none'], ' 
    '; + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) === 1 ? $txt['who_member'] : $txt['members']; + else + echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) or $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_board'], ' +
    + ! ', $context['unapproved_posts_message'], ' +
    + + + + +
    + ', $topic['is_sticky'] ? '' : '', '', $topic['first_post']['link'], (!$context['can_approve_posts'] && !$topic['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), '', $topic['is_sticky'] ? '' : ''; + + // Is this topic new? (assuming they are logged in!) + if ($topic['new'] && $context['user']['is_logged']) + echo ' + ', $txt['new'], ''; + + echo ' +

    ', $txt['started_by'], ' ', $topic['first_post']['member']['link'], ' + ', $topic['pages'], ' +

    +
    +
    + ', $topic['replies'], ' ', $txt['replies'], ' +
    + ', $topic['views'], ' ', $txt['views'], ' +
    + ', $txt['last_post'], ' + ', $topic['last_post']['time'], '
    + ', $txt['by'], ' ', $topic['last_post']['member']['link'], ' +
    '; + if ($options['display_quick_mod'] == 1) + echo ' + '; + else + { + // Check permissions on each and show only the ones they are allowed to use. + if ($topic['quick_mod']['remove']) + echo '', $txt['remove_topic'], ''; + + if ($topic['quick_mod']['lock']) + echo '', $txt['set_lock'], ''; + + if ($topic['quick_mod']['lock'] || $topic['quick_mod']['remove']) + echo '
    '; + + if ($topic['quick_mod']['sticky']) + echo '', $txt['set_sticky'], ''; + + if ($topic['quick_mod']['move']) + echo '', $txt['move_topic'], ''; + } + echo ' +
    + '; + + // Show a list of boards they can move the topic to. + if ($context['can_move']) + { + echo ' + '; + } + + echo ' + +
    +
    + '; + + // Finish off the form - again. + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] > 0 && !empty($context['topics'])) + echo ' + +
    '; + + echo ' +
    + ', template_button_strip($normal_buttons, 'right'), ' + +
    '; + } + + // Show breadcrumbs at the bottom too. + theme_linktree(); + + echo ' +
    +
    +

     

    '; + + if (!$context['no_topic_listing']) + echo ' +

    ', !empty($modSettings['enableParticipation']) && $context['user']['is_logged'] ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ' . $txt['normal_topic'] . '
    + ' . sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']) . '
    + ' . sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']) . ' +

    +

    + ' . $txt['locked_topic'] . '
    ' . ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['sticky_topic'] . '
    ' : '') . ($modSettings['pollMode'] == '1' ? ' + ' . $txt['poll'] : '') . ' +

    '; + + echo ' + +
    +
    +
    '; + + // Javascript for inline editing. + echo ' + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/ModerationCenter.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/ModerationCenter.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,860 @@ + +
    +

    ', $txt['moderation_center'], '

    +
    +
    + ', $txt['hello_guest'], ' ', $context['user']['name'], '! +

    + ', $txt['mc_description'], ' +

    + +
    '; + + $alternate = true; + // Show all the blocks they want to see. + foreach ($context['mod_blocks'] as $block) + { + $block_function = 'template_' . $block; + + echo ' +
    ', function_exists($block_function) ? $block_function() : '', '
    '; + + if (!$alternate) + echo ' +
    '; + + $alternate = !$alternate; + } + + echo ' + +
    '; +} + +function template_latest_news() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' +
    +

    + ', $txt['help'], ' ', $txt['mc_latest_news'], ' +

    +
    +
    + +
    +
    ', $txt['mc_cannot_connect_sm'], '
    +
    + +
    '; + + // This requires a lot of javascript... + //!!! Put this in it's own file!! + echo ' + + + + '; + +} + +// Show all the group requests the user can see. +function template_group_requests_block() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' + +
    + +
    +
      '; + + foreach ($context['group_requests'] as $request) + echo ' +
    • + ', $request['group']['name'], ' ', $txt['mc_groupr_by'], ' ', $request['member']['link'], ' +
    • '; + + // Don't have any watched users right now? + if (empty($context['group_requests'])) + echo ' +
    • + ', $txt['mc_group_requests_none'], ' +
    • '; + + echo ' +
    +
    + +
    '; +} + +// A block to show the current top reported posts. +function template_reported_posts_block() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' + +
    + +
    +
      '; + + foreach ($context['reported_posts'] as $report) + echo ' +
    • + ', $report['subject'], ' ', $txt['mc_reportedp_by'], ' ', $report['author']['link'], ' +
    • '; + + // Don't have any watched users right now? + if (empty($context['reported_posts'])) + echo ' +
    • + ', $txt['mc_recent_reports_none'], ' +
    • '; + + echo ' +
    +
    + +
    '; +} + +function template_watched_users() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' + +
    + +
    +
      '; + + foreach ($context['watched_users'] as $user) + echo ' +
    • + ', sprintf(!empty($user['last_login']) ? $txt['mc_seen'] : $txt['mc_seen_never'], $user['link'], $user['last_login']), ' +
    • '; + + // Don't have any watched users right now? + if (empty($context['watched_users'])) + echo ' +
    • + ', $txt['mc_watched_users_none'], ' +
    • '; + + echo ' +
    +
    + +
    '; +} + +// Little section for making... notes. +function template_notes() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['mc_notes'], '

    +
    +
    + +
    '; + + if (!empty($context['notes'])) + { + echo ' +
      '; + + // Cycle through the notes. + foreach ($context['notes'] as $note) + echo ' +
    • ', $note['author']['link'], ': ', $note['text'], '
    • '; + + echo ' +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + + echo ' +
    + +
    +
    + +
    +
    +
    + +
    + +
    '; +} + +function template_reported_posts() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' +
    +
    +

    + ', $context['view_closed'] ? $txt['mc_reportedp_closed'] : $txt['mc_reportedp_active'], ' +

    +
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
    '; + + // Make the buttons. + $close_button = create_button('close.gif', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', 'align="middle"'); + $details_button = create_button('details.gif', 'mc_reportedp_details', 'mc_reportedp_details', 'align="middle"'); + $ignore_button = create_button('ignore.gif', 'mc_reportedp_ignore', 'mc_reportedp_ignore', 'align="middle"'); + $unignore_button = create_button('ignore.gif', 'mc_reportedp_unignore', 'mc_reportedp_unignore', 'align="middle"'); + + foreach ($context['reports'] as $report) + { + echo ' +
    + +
    +
    +
    + ', $report['subject'], ' ', $txt['mc_reportedp_by'], ' ', $report['author']['link'], ' +
    + +

    +
    + « ', $txt['mc_reportedp_last_reported'], ': ', $report['last_updated'], ' »
    '; + + // Prepare the comments... + $comments = array(); + foreach ($report['comments'] as $comment) + $comments[$comment['member']['id']] = $comment['member']['link']; + + echo ' + « ', $txt['mc_reportedp_reported_by'], ': ', implode(', ', $comments), ' » +
    +
    + ', $report['body'], ' +
    + +
    '; + } + + // Were none found? + if (empty($context['reports'])) + echo ' +
    + +
    +

    ', $txt['mc_reportedp_none_found'], '

    +
    + +
    '; + + echo ' +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    +
    + ', !$context['view_closed'] ? '' : '', ' +
    +
    + +
    +
    '; +} + +// Show a list of all the unapproved posts +function template_unapproved_posts() +{ + global $settings, $options, $context, $txt, $scripturl; + + // Just a big table of it all really... + echo ' +
    +
    +
    +

    ', $txt['mc_unapproved_posts'], '

    +
    '; + + // Make up some buttons + $approve_button = create_button('approve.gif', 'approve', 'approve', 'align="middle"'); + $remove_button = create_button('delete.gif', 'remove_message', 'remove', 'align="middle"'); + + // No posts? + if (empty($context['unapproved_items'])) + echo ' +
    + +
    +

    ', $txt['mc_unapproved_' . $context['current_view'] . '_none_found'], '

    +
    + +
    '; + else + echo ' +
    + +
    '; + + foreach ($context['unapproved_items'] as $item) + { + echo ' +
    +

    + ', $item['counter'], '  + ', $item['category']['name'], ' / ', $item['board']['name'], ' / ', $item['subject'], ' + ', $txt['mc_unapproved_by'], ' ', $item['poster']['link'], ' ', $txt['on'], ': ', $item['time'], ' +

    +
    +
    + +
    +
    ', $item['body'], '
    + + ', $approve_button, ''; + + if ($item['can_delete']) + echo ' + ', $context['menu_separator'], ' + ', $remove_button, ''; + + echo ' + '; + + echo ' + +
    +
    + +
    '; + } + + echo ' +
    +
    + + +
    '; + + if (!empty($context['unapproved_items'])) + echo ' +
    + +
    '; + + echo ' +
    + +
    +
    +
    '; +} + +// List all attachments awaiting approval. +function template_unapproved_attachments() +{ + global $settings, $options, $context, $txt, $scripturl; + + // Show all the attachments still oustanding. + echo ' +
    +
    +
    +

    ', $txt['mc_unapproved_attachments'], '

    +
    '; + + // The ever popular approve button, with the massively unpopular delete. + $approve_button = create_button('approve.gif', 'approve', 'approve', 'align="middle"'); + $remove_button = create_button('delete.gif', 'remove_message', 'remove', 'align="middle"'); + + // None awaiting? + if (empty($context['unapproved_items'])) + echo ' +
    + +
    +

    ', $txt['mc_unapproved_attachments_none_found'], '

    +
    + +
    '; + else + echo ' +
    + +
    + + + + + + + + + + + '; + + foreach ($context['unapproved_items'] as $item) + { + echo ' + + + + + + + '; + } + + if (!empty($context['unapproved_items'])) + echo ' + +
    ', $txt['mc_unapproved_attach_name'], '', $txt['mc_unapproved_attach_size'], '', $txt['mc_unapproved_attach_poster'], '', $txt['date'], '
    + ', $item['filename'], ' + + ', $item['size'], $txt['kilobyte'], ' + + ', $item['poster']['link'], ' + + ', $item['time'], '
    ', $txt['in'], ' ', $item['message']['subject'], ' +
    + +
    '; + + echo ' +
    +
    + + +
    '; + + if (!empty($context['unapproved_items'])) + echo ' +
    + +
    '; + + echo ' +
    + +
    +
    +
    '; +} + +function template_viewmodreport() +{ + global $context, $scripturl, $txt; + + echo ' +
    +
    +
    +

    + ', sprintf($txt['mc_viewmodreport'], $context['report']['message_link'], $context['report']['author']['link']), ' +

    +
    +
    +

    + + ', sprintf($txt['mc_modreport_summary'], $context['report']['num_reports'], $context['report']['last_updated']), ' + + '; + + // Make the buttons. + $close_button = create_button('close.gif', $context['report']['closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', $context['report']['closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', 'align="middle"'); + $ignore_button = create_button('ignore.gif', 'mc_reportedp_ignore', 'mc_reportedp_ignore', 'align="middle"'); + $unignore_button = create_button('ignore.gif', 'mc_reportedp_unignore', 'mc_reportedp_unignore', 'align="middle"'); + + echo ' + ', $context['report']['ignore'] ? $unignore_button : $ignore_button, ' + ', $close_button, ' + +

    +
    +
    + +
    + ', $context['report']['body'], ' +
    + +
    +
    +
    +

    ', $txt['mc_modreport_whoreported_title'], '

    +
    '; + + foreach ($context['report']['comments'] as $comment) + echo ' +
    + +
    +

    ', sprintf($txt['mc_modreport_whoreported_data'], $comment['member']['link'] . (empty($comment['member']['id']) && !empty($comment['member']['ip']) ? ' (' . $comment['member']['ip'] . ')' : ''), $comment['time']), '

    +

    ', $comment['message'], '

    +
    + +
    '; + + echo ' +
    +
    +

    ', $txt['mc_modreport_mod_comments'], '

    +
    +
    + +
    '; + + if (empty($context['report']['mod_comments'])) + echo ' +

    ', $txt['mc_modreport_no_mod_comment'], '

    '; + + foreach ($context['report']['mod_comments'] as $comment) + echo + '

    ', $comment['member']['link'], ': ', $comment['message'], ' (', $comment['time'], ')

    '; + + echo ' + +
    + +
    +
    + +
    +
    '; + + $alt = false; + + template_show_list('moderation_actions_list'); + + if (!empty($context['entries'])) + { + echo ' +
    +

    ', $txt['mc_modreport_modactions'], '

    +
    + + + + + + + + + + + '; + + foreach ($context['entries'] as $entry) + { + echo ' + + + + + + + + + + '; + } + echo ' + +
    ', $txt['modlog_action'], '', $txt['modlog_date'], '', $txt['modlog_member'], '', $txt['modlog_position'], '', $txt['modlog_ip'], '
    ', $entry['action'], '', $entry['time'], '', $entry['moderator']['link'], '', $entry['position'], '', $entry['ip'], '
    '; + + foreach ($entry['extra'] as $key => $value) + echo ' + ', $key, ': ', $value; + echo ' +
    '; + } + + echo ' + +
    +
    +
    '; +} + +// Callback function for showing a watched users post in the table. +function template_user_watch_post_callback($post) +{ + global $scripturl, $context, $txt, $delete_button; + + // We'll have a delete please bob. + if (empty($delete_button)) + $delete_button = create_button('delete.gif', 'remove_message', 'remove', 'align="middle"'); + + $output_html = ' +
    +
    + ' . $post['subject'] . ' ' . $txt['mc_reportedp_by'] . ' ' . $post['author_link'] . ' +
    +
    '; + + if ($post['can_delete']) + $output_html .= ' + ' . $delete_button . ' + '; + + $output_html .= ' +
    +

    +
    + « ' . $txt['mc_watched_users_posted'] . ': ' . $post['poster_time'] . ' » +
    +
    + ' . $post['body']; + + return $output_html; +} + +// Moderation settings +function template_moderation_settings() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' +
    +
    +
    +

    ', $txt['mc_prefs_title'], '

    +
    +
    + ', $txt['mc_prefs_desc'], ' +
    +
    + +
    +
    +
    + ', $txt['mc_prefs_homepage'], ': +
    +
    '; + + foreach ($context['homepage_blocks'] as $k => $v) + echo ' +
    '; + + echo ' +
    '; + + // If they can moderate boards they have more options! + if ($context['can_moderate_boards']) + { + echo ' +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    '; + + } + + if ($context['can_moderate_approvals']) + { + echo ' + +
    + : +
    +
    + +
    '; + } + + echo ' +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +// Show a notice sent to a user. +function template_show_notice() +{ + global $txt, $settings, $options, $context; + + // We do all the HTML for this one! + echo ' + + + + ', $context['page_title'], ' + + + +
    +

    ', $txt['show_notice'], '

    +
    +
    +

    ', $txt['show_notice_subject'], ': ', $context['notice_subject'], '

    +
    +
    + +
    +
    +
    + ', $txt['show_notice_text'], ': +
    +
    + ', $context['notice_body'], ' +
    +
    +
    + +
    + +'; + +} + +// Add or edit a warning template. +function template_warn_template() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    +
    + ', $txt['mc_warning_template_desc'], ' +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    + :
    + ', $txt['mc_warning_template_body_desc'], ' +
    +
    + +
    +
    '; + + if ($context['template_data']['can_edit_personal']) + echo ' + + +
    + ', $txt['mc_warning_template_personal_desc'], ' +
    '; + + echo ' + +
    + +
    + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Modlog.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Modlog.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,115 @@ + + + + +
    + + + + + + + '; + + // Only display page numbers if not a result of a search. + if (!empty($context['page_index'])) + echo ' + + + '; + echo ' +
    +
    ' . $txt[119] . ' ', $txt['modlog_moderation_log'], '
    +
    ', empty($context['search_params']) ? $txt['modlog_total_entries'] : $txt['modlog_search_result'], ': ', $context['entry_count'], '
    +
    ', $txt['modlog_moderation_log_desc'], '
    ', $txt[139], ': ', $context['page_index'], '
    + + + '; + + foreach ($context['columns'] as $column) + { + if (!empty($column['not_sortable'])) + echo ' + '; + else + { + echo ' + '; + } + } + + echo ' + '; + + foreach ($context['entries'] as $entry) + { + echo ' + + + + + + + + + + + '; + } + + if (empty($context['entries'])) + echo ' + + + '; + + echo ' +
    ', $column['label'], ''; + if ($column['selected']) + echo '', $column['label'], ' '; + else + echo $column['label']; + echo '
    ', $entry['action'], '', $entry['time'], '', $entry['moderator']['link'], '', $entry['position'], '', $entry['ip'], '
    '; + + foreach ($entry['extra'] as $key => $value) + echo ' + ', $key, ': ', $value; + echo ' +
    + ', $txt['modlog_no_entries_found'], ' +
    + + + + '; + + if (!empty($context['page_index'])) + echo ' + + + '; + + echo ' +
    +
    + ', $txt['modlog_search'], ' (', $txt['modlog_by'], ': ', $context['search']['label'], '): + +
    + + + +
    ', $txt[139], ': ', $context['page_index'], '
    +
    + + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/MoveTopic.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/MoveTopic.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,92 @@ + +
    +
    +

    ', $txt['move_topic'], '

    +
    +
    + +
    +
    +
    +
    + ', $txt['move_to'], ': +
    +
    + +
    '; + + // Disable the reason textarea when the postRedirect checkbox is unchecked... + echo ' +
    +
    + + +
    +
    +
    + ', $txt['moved_why'], ' +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + +
    '; + + if ($context['back_to_topic']) + echo ' + '; + + echo ' + + +
    + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Notify.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Notify.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,53 @@ + +

    + ', $txt['notify'], ' +

    + + +
    +

    ', $context['notification_set'] ? $txt['notify_deactivate'] : $txt['notify_request'], '

    +

    + ', $txt['yes'], ' - ', $txt['no'], ' +

    +
    + '; +} + +function template_notify_board() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +

    + ', $txt['notify'], ' +

    +
    + +
    +

    ', $context['notification_set'] ? $txt['notifyboard_turnoff'] : $txt['notifyboard_turnon'], '

    +

    + ', $txt['yes'], ' - ', $txt['no'], ' +

    +
    + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Packages.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Packages.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2142 @@ + +
    +

    ', $txt[($context['uninstalling'] ? 'un' : '') . 'install_mod'], '

    +
    +
    '; + + if ($context['is_installed']) + echo ' + ', $txt['package_installed_warning1'], '
    +
    + ', $txt['package_installed_warning2'], '
    +
    '; + + echo $txt['package_installed_warning3'], ' +
    '; + + // Do errors exist in the install? If so light them up like a christmas tree. + if ($context['has_failure']) + { + echo ' +
    + ', $txt['package_will_fail_title'], '
    + ', $txt['package_will_fail_warning'], ' +
    '; + } + + if (isset($context['package_readme'])) + { + echo ' +
    +

    ', $txt['package_' . ($context['uninstalling'] ? 'un' : '') . 'install_readme'], '

    +
    +
    + +
    + ', $context['package_readme'], ' + ', $txt['package_available_readme_language'], ' + + +
    + +
    +
    '; + } + + echo ' +
    +
    +

    + ', $context['uninstalling'] ? $txt['package_uninstall_actions'] : $txt['package_install_actions'], ' "', $context['package_name'], '" +

    +
    '; + + // Are there data changes to be removed? + if ($context['uninstalling'] && !empty($context['database_changes'])) + { + echo ' +
    + +
    + [', $txt['package_db_uninstall_details'], '] +
    + ', $txt['package_db_uninstall_actions'], ': +
      '; + + foreach ($context['database_changes'] as $change) + echo ' +
    • ', $change, '
    • '; + echo ' +
    +
    +
    + +
    '; + } + + echo ' +
    '; + + if (empty($context['actions']) && empty($context['database_changes'])) + echo ' + ', $txt['corrupt_compatible'], ' +
    '; + else + { + echo ' + ', $txt['perform_actions'], ' + + + + + + + + + + + + '; + + $alternate = true; + $i = 1; + $action_num = 1; + $js_operations = array(); + foreach ($context['actions'] as $packageaction) + { + // Did we pass or fail? Need to now for later on. + $js_operations[$action_num] = isset($packageaction['failed']) ? $packageaction['failed'] : 0; + + echo ' + + + + + + + '; + + // Is there water on the knee? Operation! + if (isset($packageaction['operations'])) + { + echo ' + + + '; + + // Increase it. + $action_num++; + } + $alternate = !$alternate; + } + echo ' + +
    ', $txt['package_install_type'], '', $txt['package_install_action'], '', $txt['package_install_desc'], '
    ', isset($packageaction['operations']) ? '' : '', '', $i++, '.', $packageaction['type'], '', $packageaction['action'], '', $packageaction['description'], '
    + '; + + // Show the operations. + $alternate2 = true; + $operation_num = 1; + foreach ($packageaction['operations'] as $operation) + { + // Determine the position text. + $operation_text = $operation['position'] == 'replace' ? 'operation_replace' : ($operation['position'] == 'before' ? 'operation_after' : 'operation_before'); + + echo ' + + + + + + + + '; + + $operation_num++; + $alternate2 = !$alternate2; + } + + echo ' +
    ', $operation_num, '.', $txt[$operation_text], '', $operation['action'], '', $operation['description'], !empty($operation['ignore_failure']) ? ' (' . $txt['operation_ignore'] . ')' : '', '
    +
    + '; + + // What if we have custom themes we can install into? List them too! + if (!empty($context['theme_actions'])) + { + echo ' +
    +
    +

    + ', $context['uninstalling'] ? $txt['package_other_themes_uninstall'] : $txt['package_other_themes'], ' +

    +
    +
    +
    + ', $txt['package_other_themes_desc'], ' +
    + '; + + // Loop through each theme and display it's name, and then it's details. + foreach ($context['theme_actions'] as $id => $theme) + { + // Pass? + $js_operations[$action_num] = !empty($theme['has_failure']); + + echo ' + + + + + '; + + foreach ($theme['actions'] as $action) + { + echo ' + + + + + + + '; + + // Is there water on the knee? Operation! + if (isset($action['operations'])) + { + echo ' + + + '; + + // Increase it. + $action_num++; + } + } + + $alternate = !$alternate; + } + + echo ' +
    '; + if (!empty($context['themes_locked'])) + echo ' + '; + echo ' + + + ', $theme['name'], ' +
    ', isset($packageaction['operations']) ? '' : '', ' + + ', $action['type'], '', $action['action'], '', $action['description'], '
    + '; + + $alternate2 = true; + $operation_num = 1; + foreach ($action['operations'] as $operation) + { + // Determine the possition text. + $operation_text = $operation['position'] == 'replace' ? 'operation_replace' : ($operation['position'] == 'before' ? 'operation_after' : 'operation_before'); + + echo ' + + + + + + + + '; + $operation_num++; + $alternate2 = !$alternate2; + } + + echo ' +
    ', $operation_num, '.', $txt[$operation_text], '', $operation['action'], '', $operation['description'], !empty($operation['ignore_failure']) ? ' (' . $txt['operation_ignore'] . ')' : '', '
    +
    +
    '; + } + } + + // Are we effectively ready to install? + if (!$context['ftp_needed'] && (!empty($context['actions']) || !empty($context['database_changes']))) + { + echo ' +
    + +
    '; + } + // If we need ftp information then demand it! + elseif ($context['ftp_needed']) + { + echo ' +
    +

    ', $txt['package_ftp_necessary'], '

    +
    +
    + ', template_control_chmod(), ' +
    '; + } + echo ' + + ', (isset($context['form_sequence_number']) && !$context['ftp_needed']) ? ' + ' : '', ' +
    + +
    '; + + // Toggle options. + echo ' + '; + + // And a bit more for database changes. + if (!empty($context['database_changes'])) + echo ' + '; +} +function template_extract_package() +{ + global $context, $settings, $options, $txt, $scripturl; + + if (!empty($context['redirect_url'])) + { + echo ' + '; + } + + echo ' +
    '; + + if (empty($context['redirect_url'])) + { + echo ' +
    +

    ', $context['uninstalling'] ? $txt['uninstall'] : $txt['extracting'], '

    +
    +
    ', $txt['package_installed_extract'], '
    '; + } + else + echo ' +
    +

    ', $txt['package_installed_redirecting'], '

    +
    '; + + echo ' +
    + +
    '; + + // If we are going to redirect we have a slightly different agenda. + if (!empty($context['redirect_url'])) + { + echo ' + ', $context['redirect_text'], '

    + ', $txt['package_installed_redirect_go_now'], ' | ', $txt['package_installed_redirect_cancel'], ''; + } + elseif ($context['uninstalling']) + echo ' + ', $txt['package_uninstall_done']; + elseif ($context['install_finished']) + { + if ($context['extract_type'] == 'avatar') + echo ' + ', $txt['avatars_extracted']; + elseif ($context['extract_type'] == 'language') + echo ' + ', $txt['language_extracted']; + else + echo ' + ', $txt['package_installed_done']; + } + else + echo ' + ', $txt['corrupt_compatible']; + + echo ' +
    + +
    '; + + // Show the "restore permissions" screen? + if (function_exists('template_show_list') && !empty($context['restore_file_permissions']['rows'])) + { + echo '
    '; + template_show_list('restore_file_permissions'); + } + + echo ' +
    +
    '; +} + +function template_list() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['list_file'], '

    +
    +
    +

    ', $txt['files_archive'], ' ', $context['filename'], ':

    +
    +
    + +
    +
      '; + + foreach ($context['files'] as $fileinfo) + echo ' +
    1. ', $fileinfo['filename'], ' (', $fileinfo['size'], ' ', $txt['package_bytes'], ')
    2. '; + + echo ' +
    +
    + [ ', $txt['back'], ' ] +
    + +
    +
    +
    '; +} + +function template_examine() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['package_examine_file'], '

    +
    +
    +

    ', $txt['package_file_contents'], ' ', $context['filename'], ':

    +
    +
    + +
    +
    ', $context['filedata'], '
    + [ ', $txt['list_files'], ' ] +
    + +
    +
    +
    '; +} + +function template_view_installed() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ' . $txt['view_and_remove'] . '

    +
    '; + + if (empty($context['installed_mods'])) + { + echo ' +
    + ', $txt['no_mods_installed'], ' +
    '; + } + else + { + echo ' + + + + + + + + + + '; + + $alt = false; + foreach ($context['installed_mods'] as $i => $file) + { + echo ' + + + + + + '; + $alt = !$alt; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ', ++$i, '.', $file['name'], '', $file['version'], '[ ', $txt['uninstall'], ' ]
    +
    + [ ', $txt['delete_list'], ' ]'; + } + + echo ' +
    +
    '; +} + +function template_browse() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings, $forum_version; + + echo ' +
    +
    +

    + ', $txt['help'], ' ', $txt['packages_latest'], ' +

    +
    +
    + +
    +
    ', $txt['packages_latest_fetch'], '
    +
    + +
    + + '; + + if (empty($modSettings['disable_smf_js'])) + echo ' + '; + + echo ' + '; + + echo ' + '; + + echo ' +
    +
    +

    ', $txt['browse_packages'], '

    +
    '; + + if (!empty($context['available_mods'])) + { + echo ' +
    +
    +

    ', $txt['modification_package'], '

    +
    + + + + + + + + + + + '; + + $alt = false; + foreach ($context['available_mods'] as $i => $package) + { + echo ' + + + + + + '; + $alt = !$alt; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ', ++$i, '.', $package['name'], ' + ', $package['version']; + + if ($package['is_installed'] && !$package['is_newer']) + echo ' + '; + + echo ' + '; + + if ($package['can_uninstall']) + echo ' + [ ', $txt['uninstall'], ' ]'; + elseif ($package['can_upgrade']) + echo ' + [ ', $txt['package_upgrade'], ' ]'; + elseif ($package['can_install']) + echo ' + [ ', $txt['install_mod'], ' ]'; + + echo ' + [ ', $txt['list_files'], ' ] + [ ', $txt['package_delete'], ' ] +
    '; + } + + if (!empty($context['available_avatars'])) + { + echo ' +
    +
    +

    ', $txt['avatar_package'], '

    +
    + + + + + + + + + + '; + + foreach ($context['available_avatars'] as $i => $package) + { + echo ' + + + + + + '; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ', ++$i, '.', $package['name'], '', $package['version']; + + if ($package['is_installed'] && !$package['is_newer']) + echo ' + '; + + echo ' + '; + + if ($package['can_uninstall']) + echo ' + [ ', $txt['uninstall'], ' ]'; + elseif ($package['can_upgrade']) + echo ' + [ ', $txt['package_upgrade'], ' ]'; + elseif ($package['can_install']) + echo ' + [ ', $txt['install_mod'], ' ]'; + + echo ' + [ ', $txt['list_files'], ' ] + [ ', $txt['package_delete'], ' ] +
    '; + } + + if (!empty($context['available_languages'])) + { + echo ' +
    +
    +

    ' . $txt['language_package'] . '

    +
    + + + + + + + + + + '; + + foreach ($context['available_languages'] as $i => $package) + { + echo ' + + + + + + '; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ' . ++$i . '.' . $package['name'] . '' . $package['version']; + + if ($package['is_installed'] && !$package['is_newer']) + echo ' + '; + + echo ' + '; + + if ($package['can_uninstall']) + echo ' + [ ', $txt['uninstall'], ' ]'; + elseif ($package['can_upgrade']) + echo ' + [ ', $txt['package_upgrade'], ' ]'; + elseif ($package['can_install']) + echo ' + [ ', $txt['install_mod'], ' ]'; + + echo ' + [ ', $txt['list_files'], ' ] + [ ', $txt['package_delete'], ' ] +
    '; + } + + if (!empty($context['available_other'])) + { + echo ' +
    +
    +

    ' . $txt['unknown_package'] . '

    +
    + + + + + + + + + + '; + + foreach ($context['available_other'] as $i => $package) + { + echo ' + + + + + + '; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ' . ++$i . '.' . $package['name'] . '' . $package['version']; + + if ($package['is_installed'] && !$package['is_newer']) + echo ' + '; + + echo ' + '; + + if ($package['can_uninstall']) + echo ' + [ ', $txt['uninstall'], ' ]'; + elseif ($package['can_upgrade']) + echo ' + [ ', $txt['package_upgrade'], ' ]'; + elseif ($package['can_install']) + echo ' + [ ', $txt['install_mod'], ' ]'; + + echo ' + [ ', $txt['list_files'], ' ] + [ ', $txt['package_delete'], ' ] +
    '; + } + + if (empty($context['available_mods']) && empty($context['available_avatars']) && empty($context['available_languages']) && empty($context['available_other'])) + echo ' +
    ', $txt['no_packages'], '
    '; + + echo ' +
    +
    + ', $txt['package_installed_key'], ' + ', $txt['package_installed_current'], ' + ', $txt['package_installed_old'], ' +
    + +
    +
    + + + + +
    +
    +
    '; +} + +function template_servers() +{ + global $context, $settings, $options, $txt, $scripturl; + + if (!empty($context['package_ftp']['error'])) + echo ' +
    + ', $context['package_ftp']['error'], ' +
    '; + + echo ' +
    +
    +

    ', $txt['download_new_package'], '

    +
    '; + + if ($context['package_download_broken']) + { + echo ' +
    +

    ', $txt['package_ftp_necessary'], '

    +
    +
    + +
    +

    + ', $txt['package_ftp_why_download'], ' +

    +
    +
    +
    + +
    +
    + + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    '; + } + + echo ' +
    + +
    +
    + ' . $txt['package_servers'] . ' + +
    +
    + ' . $txt['add_server'] . ' +
    +
    +
    + ' . $txt['server_name'] . ': +
    +
    + +
    +
    + ' . $txt['serverurl'] . ': +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    + ', $txt['package_download_by_url'], ' +
    +
    +
    + ' . $txt['serverurl'] . ': +
    +
    + +
    +
    + ', $txt['package_download_filename'], ': +
    +
    +
    + ', $txt['package_download_filename_info'], ' +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +

    ' . $txt['package_upload_title'] . '

    +
    +
    + +
    +
    +
    +
    + ' . $txt['package_upload_select'] . ': +
    +
    + +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    '; +} + +function template_package_confirm() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    + +
    +
    '; +} + +function template_package_list() +{ + global $context, $settings, $options, $txt, $scripturl, $smcFunc; + + echo ' +
    +
    +

    ' . $context['page_title'] . '

    +
    +
    + +
    '; + + // No packages, as yet. + if (empty($context['package_list'])) + echo ' +
      +
    • ', $txt['no_packages'], '
    • +
    '; + // List out the packages... + else + { + echo ' +
      '; + foreach ($context['package_list'] as $i => $packageSection) + { + echo ' +
    • + ', $packageSection['title'], ''; + + if (!empty($packageSection['text'])) + echo ' +
      ', $packageSection['text'], '
      '; + + echo ' + <', $context['list_type'], ' id="package_section_', $i, '" class="packages">'; + + $alt = false; + + foreach ($packageSection['items'] as $id => $package) + { + echo ' +
    • '; + // Textual message. Could be empty just for a blank line... + if ($package['is_text']) + echo ' + ', empty($package['name']) ? ' ' : $package['name']; + // This is supposed to be a rule.. + elseif ($package['is_line']) + echo ' +
      '; + // A remote link. + elseif ($package['is_remote']) + { + echo ' + ', $package['link'], ''; + } + // A title? + elseif ($package['is_heading'] || $package['is_title']) + { + echo ' + ', $package['name'], ''; + } + // Otherwise, it's a package. + else + { + // 1. Some mod [ Download ]. + echo ' + ', $package['can_install'] ? '' . $package['name'] . ' [ ' . $txt['download'] . ' ]': $package['name']; + + // Mark as installed and current? + if ($package['is_installed'] && !$package['is_newer']) + echo '', $package['is_current'] ? $txt['package_installed_current'] : $txt['package_installed_old'], ''; + + echo ' + +
        '; + + // Show the mod type? + if ($package['type'] != '') + echo ' +
      • ', $txt['package_type'], ':  ', $smcFunc['ucwords']($smcFunc['strtolower']($package['type'])), '
      • '; + // Show the version number? + if ($package['version'] != '') + echo ' +
      • ', $txt['mod_version'], ':  ', $package['version'], '
      • '; + // How 'bout the author? + if (!empty($package['author']) && $package['author']['name'] != '' && isset($package['author']['link'])) + echo ' +
      • ', $txt['mod_author'], ':  ', $package['author']['link'], '
      • '; + // The homepage.... + if ($package['author']['website']['link'] != '') + echo ' +
      • ', $txt['author_website'], ':  ', $package['author']['website']['link'], '
      • '; + + // Desciption: bleh bleh! + // Location of file: http://someplace/. + echo ' +
      • ', $txt['file_location'], ':  ', $package['href'], '
      • +
      • ', $txt['package_description'], ':  ', $package['description'], '
      • +
      '; + } + $alt = !$alt; + echo ' +
    • '; + } + echo ' + + '; + } + echo ' +
    '; + + } + + echo ' +
    + +
    +
    + ', $txt['package_installed_key'], ' + ', $txt['package_installed_current'], ' + ', $txt['package_installed_old'], ' +
    +
    +
    + + '; + // Now go through and turn off all the sections. + if (!empty($context['package_list'])) + { + $section_count = count($context['package_list']); + echo ' + '; + } +} + +function template_downloaded() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +

    ', (empty($context['package_server']) ? $txt['package_uploaded_successfully'] : $txt['package_downloaded_successfully']), '

    +
      +
    • ', $context['package']['name'], ' + ', $context['package']['list_files']['link'], ' + ', $context['package']['install']['link'], ' +
    • +
    +

    +

    [ ', $txt['back'], ' ]

    +
    + +
    +
    +
    '; +} + +function template_install_options() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['package_install_options'], '

    +
    +
    + ', $txt['package_install_options_ftp_why'], ' +
    + +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +

    +
    + + +
    +
    +
    + +
    +
    +
    '; +} + +function template_control_chmod() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Nothing to do? Brilliant! + if (empty($context['package_ftp'])) + return false; + + if (empty($context['package_ftp']['form_elements_only'])) + { + echo ' + ', sprintf($txt['package_ftp_why'], 'document.getElementById(\'need_writable_list\').style.display = \'\'; return false;'), '
    +
    + ', $txt['package_ftp_why_file_list'], ' +
      '; + if (!empty($context['notwritable_files'])) + foreach ($context['notwritable_files'] as $file) + echo ' +
    • ', $file, '
    • '; + + echo ' +
    +
    '; + } + + echo ' +
    + ', !empty($context['package_ftp']['error']) ? $context['package_ftp']['error'] : '', ' +
    '; + + if (!empty($context['package_ftp']['destination'])) + echo ' +
    '; + + echo ' +
    +
    +
    + +
    +
    + + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    '; + + if (empty($context['package_ftp']['form_elements_only'])) + echo ' + +
    + + +
    '; + + if (!empty($context['package_ftp']['destination'])) + echo ' + +
    '; + + // Hide the details of the list. + if (empty($context['package_ftp']['form_elements_only'])) + echo ' + '; + + // Quick generate the test button. + echo ' + '; + + // Make sure the button gets generated last. + $context['insert_after_template'] .= ' + '; +} + +function template_ftp_required() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    + + ', $txt['package_ftp_necessary'], ' + +
    + ', template_control_chmod(), ' +
    +
    '; +} + +function template_view_operations() +{ + global $context, $txt, $settings; + + echo ' + + + ', $txt['operation_title'], ' + + + + + + + +
    +
    + ', $context['operations']['search'], ' +
    +
    + ', $context['operations']['replace'], ' +
    +
    + +'; +} + +function template_file_permissions() +{ + global $txt, $scripturl, $context, $settings; + + // This will handle expanding the selection. + echo ' + '; + + echo ' +
    +
    + ', $txt['package_file_perms_warning'], ': +
    +
      + ', $txt['package_file_perms_warning_desc'], ' +
    +
    +
    +
    + +
    +
    +

    + ', $txt['package_file_perms'], '', $txt['package_file_perms_new_status'], ' +

    +
    + + + + + + + + + + + + '; + + foreach ($context['file_tree'] as $name => $dir) + { + echo ' + + + + + + + + + + + '; + + if (!empty($dir['contents'])) + template_permission_show_contents($name, $dir['contents'], 1); + } + + echo ' + +
     ', $txt['package_file_perms_name'], ' ', $txt['package_file_perms_status'], '', $txt['package_file_perms_status_read'], '', $txt['package_file_perms_status_write'], '', $txt['package_file_perms_status_execute'], '', $txt['package_file_perms_status_custom'], '', $txt['package_file_perms_status_no_change'], '
    '; + + if (!empty($dir['type']) && ($dir['type'] == 'dir' || $dir['type'] == 'dir_recursive')) + echo ' + *'; + + echo ' + ', $name, ' + + ', ($dir['perms']['chmod'] ? $txt['package_file_perms_writable'] : $txt['package_file_perms_not_writable']), ' + ', ($dir['perms']['perms'] ? ' (' . $txt['package_file_perms_chmod'] . ': ' . substr(sprintf('%o', $dir['perms']['perms']), -4) . ')' : ''), ' +
    +
    +
    +

    ', $txt['package_file_perms_change'], '

    +
    +
    + +
    +
    +
    +
    + + +
    +
    + ', $txt['package_file_perms_custom'], ':  (?) +
    +
    + + + +
    +
    + ', $txt['package_file_perms_predefined_note'], ' +
    +
    +
    '; + + // Likely to need FTP? + if (empty($context['ftp_connected'])) + echo ' +

    + ', $txt['package_file_perms_ftp_details'], ': +

    + ', template_control_chmod(), ' +
    ', $txt['package_file_perms_ftp_retain'], '
    '; + + echo ' + +
    + + +
    +
    + +
    '; + + // Any looks fors we've already done? + foreach ($context['look_for'] as $path) + echo ' + '; + echo ' +

    '; +} + +function template_permission_show_contents($ident, $contents, $level, $has_more = false) +{ + global $settings, $txt, $scripturl, $context; + $js_ident = preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $ident); + // Have we actually done something? + $drawn_div = false; + + foreach ($contents as $name => $dir) + { + if (isset($dir['perms'])) + { + if (!$drawn_div) + { + $drawn_div = true; + echo ' + + '; + } + + $cur_ident = preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $ident . '/' . $name); + echo ' + + + + + + + + + + '; + + if (!empty($dir['contents'])) + { + template_permission_show_contents($ident . '/' . $name, $dir['contents'], $level + 1, !empty($dir['more_files'])); + + } + } + } + + // We have more files to show? + if ($has_more) + echo ' + + + + '; + + if ($drawn_div) + { + // Hide anything too far down the tree. + $isFound = false; + foreach ($context['look_for'] as $tree) + { + if (substr($tree, 0, strlen($ident)) == $ident) + $isFound = true; + } + + if ($level > 1 && !$isFound) + echo ' +
    ' . str_repeat(' ', $level * 5), ' + ', (!empty($dir['type']) && $dir['type'] == 'dir_recursive') || !empty($dir['list_contents']) ? '' : ''; + + if (!empty($dir['type']) && ($dir['type'] == 'dir' || $dir['type'] == 'dir_recursive')) + echo ' + *'; + + echo ' + ', $name, ' + ', (!empty($dir['type']) && $dir['type'] == 'dir_recursive') || !empty($dir['list_contents']) ? '' : '', ' + + ', ($dir['perms']['chmod'] ? $txt['package_file_perms_writable'] : $txt['package_file_perms_not_writable']), ' + ', ($dir['perms']['perms'] ? ' (' . $txt['package_file_perms_chmod'] . ': ' . substr(sprintf('%o', $dir['perms']['perms']), -4) . ')' : ''), ' +
    ' . str_repeat(' ', $level * 5), ' + « ', $txt['package_file_perms_more_files'], ' » +
    + + '; + } +} + +function template_action_permissions() +{ + global $txt, $scripturl, $context, $settings; + + $countDown = 3; + + echo ' +
    +
    +
    +

    ', $txt['package_file_perms_applying'], '

    +
    '; + + if (!empty($context['skip_ftp'])) + echo ' +
    + ', $txt['package_file_perms_skipping_ftp'], ' +
    '; + + // How many have we done? + $remaining_items = count($context['method'] == 'individual' ? $context['to_process'] : $context['directory_list']); + $progress_message = sprintf($context['method'] == 'individual' ? $txt['package_file_perms_items_done'] : $txt['package_file_perms_dirs_done'], $context['total_items'] - $remaining_items, $context['total_items']); + $progress_percent = round(($context['total_items'] - $remaining_items) / $context['total_items'] * 100, 1); + + echo ' +
    + +
    +
    + ', $progress_message, ' +
    +
    ', $progress_percent, '%
    +
     
    +
    +
    '; + + // Second progress bar for a specific directory? + if ($context['method'] != 'individual' && !empty($context['total_files'])) + { + $file_progress_message = sprintf($txt['package_file_perms_files_done'], $context['file_offset'], $context['total_files']); + $file_progress_percent = round($context['file_offset'] / $context['total_files'] * 100, 1); + + echo ' +
    +
    + ', $file_progress_message, ' +
    +
    ', $file_progress_percent, '%
    +
     
    +
    +
    '; + } + + echo ' +
    '; + + // Put out the right hidden data. + if ($context['method'] == 'individual') + echo ' + + + '; + else + echo ' + + + + + '; + + // Are we not using FTP for whatever reason. + if (!empty($context['skip_ftp'])) + echo ' + '; + + // Retain state. + foreach ($context['back_look_data'] as $path) + echo ' + '; + + echo ' + + +
    + +
    +
    + +
    + +
    +
    '; + + // Just the countdown stuff + echo ' + '; + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/PersonalMessage.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/PersonalMessage.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1747 @@ +'; + + // Show the capacity bar, if available. + if (!empty($context['limit_bar'])) + echo ' +
    +

    + ', $txt['pm_capacity'], ': + + + + ', $context['limit_bar']['text'], ' +

    +
    '; + + // Message sent? Show a small indication. + if (isset($context['pm_sent'])) + echo ' +
    + ', $txt['pm_sent'], ' +
    '; +} + +// Just the end of the index bar, nothing special. +function template_pm_below() +{ + global $context, $settings, $options; + + echo ' + '; +} + +function template_folder() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // The every helpful javascript! + echo ' + '; + + echo ' +'; + + // If we are not in single display mode show the subjects on the top! + if ($context['display_mode'] != 1) + { + template_subject_list(); + echo '

    '; + } + + // Got some messages to display? + if ($context['get_pmessage']('message', true)) + { + // Show the helpful titlebar - generally. + if ($context['display_mode'] != 1) + echo ' +
    +

    + ', $txt['author'], ' + ', $txt[$context['display_mode'] == 0 ? 'messages' : 'conversation'], ' +

    +
    '; + + // Show a few buttons if we are in conversation mode and outputting the first message. + if ($context['display_mode'] == 2) + { + // Build the normal button array. + $conversation_buttons = array( + 'reply' => array('text' => 'reply_to_all', 'image' => 'reply.gif', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=send;f=' . $context['folder'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';pmsg=' . $context['current_pm'] . ';u=all', 'active' => true), + 'delete' => array('text' => 'delete_conversation', 'image' => 'delete.gif', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=pmactions;pm_actions[' . $context['current_pm'] . ']=delete;conversation;f=' . $context['folder'] . ';start=' . $context['start'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'], 'custom' => 'onclick="return confirm(\'' . addslashes($txt['remove_message']) . '?\');"'), + ); + + // Show the conversation buttons. + echo ' +
    '; + + template_button_strip($conversation_buttons, 'right'); + + echo ' +
    '; + } + + while ($message = $context['get_pmessage']('message')) + { + $window_class = $message['alternate'] == 0 ? 'windowbg' : 'windowbg2'; + + echo ' +
    + +
    + +

    '; + + // Show online and offline buttons? + if (!empty($modSettings['onlineEnable']) && !$message['member']['is_guest']) + echo ' + ', $message['member']['online']['text'], ''; + + echo ' + ', $message['member']['link'], ' +

    +
      '; + + // Show the member's custom title, if they have one. + if (isset($message['member']['title']) && $message['member']['title'] != '') + echo ' +
    • ', $message['member']['title'], '
    • '; + + // Show the member's primary group (like 'Administrator') if they have one. + if (isset($message['member']['group']) && $message['member']['group'] != '') + echo ' +
    • ', $message['member']['group'], '
    • '; + + // Don't show these things for guests. + if (!$message['member']['is_guest']) + { + // Show the post group if and only if they have no other group or the option is on, and they are in a post group. + if ((empty($settings['hide_post_group']) || $message['member']['group'] == '') && $message['member']['post_group'] != '') + echo ' +
    • ', $message['member']['post_group'], '
    • '; + echo ' +
    • ', $message['member']['group_stars'], '
    • '; + + // Show avatars, images, etc.? + if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image'])) + echo ' +
    • + + ', $message['member']['avatar']['image'], ' + +
    • '; + + // Show how many posts they have made. + if (!isset($context['disabled_fields']['posts'])) + echo ' +
    • ', $txt['member_postcount'], ': ', $message['member']['posts'], '
    • '; + + // Is karma display enabled? Total or +/-? + if ($modSettings['karmaMode'] == '1') + echo ' +
    • ', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '
    • '; + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    • ', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '
    • '; + + // Is this user allowed to modify this member's karma? + if ($message['member']['karma']['allow']) + echo ' +
    • + ', $modSettings['karmaApplaudLabel'], ' ', $modSettings['karmaSmiteLabel'], ' +
    • '; + + // Show the member's gender icon? + if (!empty($settings['show_gender']) && $message['member']['gender']['image'] != '' && !isset($context['disabled_fields']['gender'])) + echo ' +
    • ', $txt['gender'], ': ', $message['member']['gender']['image'], '
    • '; + + // Show their personal text? + if (!empty($settings['show_blurb']) && $message['member']['blurb'] != '') + echo ' +
    • ', $message['member']['blurb'], '
    • '; + + // Any custom fields to show as icons? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 1 || empty($custom['value'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    • +
        '; + } + echo ' +
      • ', $custom['value'], '
      • '; + } + if ($shown) + echo ' +
      +
    • '; + } + + // This shows the popular messaging icons. + if ($message['member']['has_messenger'] && $message['member']['can_view_profile']) + echo ' +
    • +
        ', !isset($context['disabled_fields']['icq']) && !empty($message['member']['icq']['link']) ? ' +
      • ' . $message['member']['icq']['link'] . '
      • ' : '', !isset($context['disabled_fields']['msn']) && !empty($message['member']['msn']['link']) ? ' +
      • ' . $message['member']['msn']['link'] . '
      • ' : '', !isset($context['disabled_fields']['aim']) && !empty($message['member']['aim']['link']) ? ' +
      • ' . $message['member']['aim']['link'] . '
      • ' : '', !isset($context['disabled_fields']['yim']) && !empty($message['member']['yim']['link']) ? ' +
      • ' . $message['member']['yim']['link'] . '
      • ' : '', ' +
      +
    • '; + + // Show the profile, website, email address, and personal message buttons. + if ($settings['show_profile_buttons']) + { + echo ' +
    • + +
    • '; + } + + // Any custom fields for standard placement? + if (!empty($message['member']['custom_fields'])) + { + foreach ($message['member']['custom_fields'] as $custom) + if (empty($custom['placement']) || empty($custom['value'])) + echo ' +
    • ', $custom['title'], ': ', $custom['value'], '
    • '; + } + + // Are we showing the warning status? + if ($message['member']['can_see_warning']) + echo ' +
    • ', $context['can_issue_warning'] ? '' : '', '', $txt['user_warn_' . $message['member']['warning_status']], '', $context['can_issue_warning'] ? '' : '', '', $txt['warn_' . $message['member']['warning_status']], '
    • '; + } + + // Done with the information about the poster... on to the post itself. + echo ' +
    +
    +
    +
    +
    +
    + ', $message['subject'], ' +
    '; + + // Show who the message was sent to. + echo ' + « ', $txt['sent_to'], ': '; + + // People it was sent directly to.... + if (!empty($message['recipients']['to'])) + echo implode(', ', $message['recipients']['to']); + // Otherwise, we're just going to say "some people"... + elseif ($context['folder'] != 'sent') + echo '(', $txt['pm_undisclosed_recipients'], ')'; + + echo ' + ', $txt['on'], ': ', $message['time'], ' » + '; + + // If we're in the sent items, show who it was sent to besides the "To:" people. + if (!empty($message['recipients']['bcc'])) + echo ' +
    « ', $txt['pm_bcc'], ': ', implode(', ', $message['recipients']['bcc']), ' »'; + + if (!empty($message['is_replied_to'])) + echo ' +
    « ', $txt['pm_is_replied_to'], ' »'; + + echo ' +
    +
      '; + + // Show reply buttons if you have the permission to send PMs. + if ($context['can_send_pm']) + { + // You can't really reply if the member is gone. + if (!$message['member']['is_guest']) + { + // Is there than more than one recipient you can reply to? + if ($message['number_recipients'] > 1 && $context['display_mode'] != 2) + echo ' +
    • ', $txt['reply_to_all'], '
    • '; + + echo ' +
    • ', $txt['reply'], '
    • +
    • ', $txt['quote'], '
    • '; + } + // This is for "forwarding" - even if the member is gone. + else + echo ' +
    • ', $txt['reply_quote'], '
    • '; + } + echo ' +
    • ', $txt['delete'], '
    • '; + + if (empty($context['display_mode'])) + echo ' +
    • '; + + echo ' +
    +
    +
    +
    ', $message['body'], '
    + '; + + // Are there any custom profile fields for above the signature? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 2 || empty($custom['value'])) + continue; + if (!$shown) + { + $shown = true; + echo ' +
    +
      '; + } + echo ' +
    • ', $custom['value'], '
    • '; + } + if ($shown) + echo ' +
    +
    '; + } + + // Show the member's signature? + if (!empty($message['member']['signature']) && empty($options['show_no_signatures']) && $context['signature_enabled']) + echo ' +
    ', $message['member']['signature'], '
    '; + + // Add an extra line at the bottom if we have labels enabled. + if ($context['folder'] != 'sent' && !empty($context['currently_using_labels']) && $context['display_mode'] != 2) + { + echo ' +
    '; + // Add the label drop down box. + if (!empty($context['currently_using_labels'])) + { + echo ' + + '; + } + echo ' +
    '; + } + + echo ' +
    +
    +
    +
    +
    + +
    '; + } + + if (empty($context['display_mode'])) + echo ' + +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
    +
    '; + + // Show a few buttons if we are in conversation mode and outputting the first message. + elseif ($context['display_mode'] == 2 && isset($conversation_buttons)) + { + echo ' + +
    '; + + template_button_strip($conversation_buttons, 'right'); + + echo ' +
    '; + } + + echo ' +
    '; + } + + // Individual messages = buttom list! + if ($context['display_mode'] == 1) + { + template_subject_list(); + echo '
    '; + } + + echo ' + +'; +} + +// Just list all the personal message subjects - to make templates easier. +function template_subject_list() +{ + global $context, $options, $settings, $modSettings, $txt, $scripturl; + + echo ' +
    + + + + + + + + + + '; + if (!$context['show_delete']) + echo ' + + + '; + $next_alternate = false; + + while ($message = $context['get_pmessage']('subject')) + { + echo ' + + + + + + + '; + $next_alternate = !$next_alternate; + } + + echo ' + +
    + ', $txt['pm_change_view'], ' + + ', $txt['date'], $context['sort_by'] == 'date' ? ' ' : '', ' + + ', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', ' + + ', ($context['from_or_to'] == 'from' ? $txt['from'] : $txt['to']), $context['sort_by'] == 'name' ? ' ' : '', ' + + +
    ', $txt['msg_alert_none'], '
    + + ', $message['is_replied_to'] ? '' . $txt['pm_replied'] . '' : '' . $txt['pm_read'] . '', '', $message['time'], '', ($context['display_mode'] != 0 && $context['current_pm'] == $message['id'] ? '*' : ''), '', $message['subject'], '', $message['is_unread'] ? ' ' . $txt['new'] . '' : '', '', ($context['from_or_to'] == 'from' ? $message['member']['link'] : (empty($message['recipients']['to']) ? '' : implode(', ', $message['recipients']['to']))), '
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
     '; + + if ($context['show_delete']) + { + if (!empty($context['currently_using_labels']) && $context['folder'] != 'sent') + { + echo ' + + '; + } + + echo ' + '; + } + + echo ' +
    +
    '; +} + +function template_search() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' + +
    +
    +

    ', $txt['pm_search_title'], '

    +
    '; + + if (!empty($context['search_errors'])) + { + echo ' +
    + ', implode('
    ', $context['search_errors']['messages']), ' +
    '; + } + + if ($context['simple_search']) + { + echo ' + '; + } + + // Advanced search! + else + { + echo ' + '; + + // Do we have some labels setup? If so offer to search by them! + if ($context['currently_using_labels']) + { + echo ' +
    + +
    + +
      '; + + foreach ($context['search_labels'] as $label) + echo ' +
    • + +
    • '; + + echo ' +
    +

    + + +


    +
    + +
    '; + } + } + + echo ' +
    '; +} + +function template_search_results() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    ', $txt['pm_search_results'], '

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + // complete results ? + if (empty($context['search_params']['show_complete']) && !empty($context['personal_messages'])) + echo ' + + + + + + + + + '; + + $alternate = true; + // Print each message out... + foreach ($context['personal_messages'] as $message) + { + // We showing it all? + if (!empty($context['search_params']['show_complete'])) + { + echo ' +
    +

    + ', $txt['search_on'], ': ', $message['time'], ' + ', $message['counter'], '  ', $message['subject'], ' +

    +
    +
    +

    ', $txt['from'], ': ', $message['member']['link'], ', ', $txt['to'], ': '; + + // Show the recipients. + // !!! This doesn't deal with the sent item searching quite right for bcc. + if (!empty($message['recipients']['to'])) + echo implode(', ', $message['recipients']['to']); + // Otherwise, we're just going to say "some people"... + elseif ($context['folder'] != 'sent') + echo '(', $txt['pm_undisclosed_recipients'], ')'; + + echo ' +

    +
    +
    + +
    + ', $message['body'], ' +

    '; + + if ($context['can_send_pm']) + { + $quote_button = create_button('quote.gif', 'reply_quote', 'reply_quote', 'align="middle"'); + $reply_button = create_button('im_reply.gif', 'reply', 'reply', 'align="middle"'); + // You can only reply if they are not a guest... + if (!$message['member']['is_guest']) + echo ' + ', $quote_button , '', $context['menu_separator'], ' + ', $reply_button , ' ', $context['menu_separator']; + // This is for "forwarding" - even if the member is gone. + else + echo ' + ', $quote_button , '', $context['menu_separator']; + } + + echo ' +

    +
    + +
    '; + } + // Otherwise just a simple list! + else + { + // !!! No context at all of the search? + echo ' + + + + + '; + } + + $alternate = !$alternate; + } + + // Finish off the page... + if (empty($context['search_params']['show_complete']) && !empty($context['personal_messages'])) + echo ' + +
    ', $txt['date'], '', $txt['subject'], '', $txt['from'], '
    ', $message['time'], '', $message['link'], '', $message['member']['link'], '
    '; + + // No results? + if (empty($context['personal_messages'])) + echo ' +
    + +
    +

    ', $txt['pm_search_none_found'], '

    +
    + +
    '; + + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + +} + +function template_send() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // Show which messages were sent successfully and which failed. + if (!empty($context['send_log'])) + { + echo ' +
    +

    ', $txt['pm_send_report'], '

    +
    +
    + +
    '; + if (!empty($context['send_log']['sent'])) + foreach ($context['send_log']['sent'] as $log_entry) + echo '', $log_entry, '
    '; + if (!empty($context['send_log']['failed'])) + foreach ($context['send_log']['failed'] as $log_entry) + echo '', $log_entry, '
    '; + echo ' +
    + +
    +
    '; + } + + // Show the preview of the personal message. + if (isset($context['preview_message'])) + echo ' +
    +

    ', $context['preview_subject'], '

    +
    +
    + +
    + ', $context['preview_message'], ' +
    + +
    +
    '; + + // Main message editing box. + echo ' +
    +

    + ', $txt['new_message'], ' ', $txt['new_message'], ' +

    +
    '; + + echo ' +
    +
    + +

    '; + + // If there were errors for sending the PM, show them. + if (!empty($context['post_error']['messages'])) + { + echo ' +
    + ', $txt['error_while_submitting'], ' +
      '; + + foreach ($context['post_error']['messages'] as $error) + echo ' +
    • ', $error, '
    • '; + + echo ' +
    +
    '; + } + + echo ' +
    '; + + // To and bcc. Include a button to search for members. + echo ' +
    + ', $txt['pm_to'], ': +
    '; + + // Autosuggest will be added by the JavaScript later on. + echo ' +
    + '; + + // A link to add BCC, only visible with JavaScript enabled. + echo ' + '; + + // A div that'll contain the items found by the autosuggest. + echo ' +
    '; + + echo ' +
    '; + + // This BCC row will be hidden by default if JavaScript is enabled. + echo ' +
    + ', $txt['pm_bcc'], ': +
    +
    + +
    +
    '; + + // The subject of the PM. + echo ' +
    + ', $txt['subject'], ': +
    +
    + +
    +

    '; + + // Showing BBC? + if ($context['show_bbc']) + { + echo ' +
    '; + } + + // What about smileys? + if (!empty($context['smileys']['postform']) || !empty($context['smileys']['popup'])) + echo ' +
    '; + + // Show BBC buttons, smileys and textbox. + echo ' + ', template_control_richedit($context['post_box_name'], 'smileyBox_message', 'bbcBox_message'); + + // Require an image to be typed to save spamming? + if ($context['require_verification']) + { + echo ' +
    + ', $txt['pm_visual_verification_label'], ': + ', template_control_verification($context['visual_verification_id'], 'all'), ' +
    '; + } + + // Send, Preview, spellcheck buttons. + echo ' +

    +

    + ', $context['browser']['is_firefox'] ? $txt['shortcuts_firefox'] : $txt['shortcuts'], ' +

    +

    + ', template_control_richedit_buttons($context['post_box_name']), ' +

    + + + + + + +
    +
    + +
    +
    '; + + // Show the message you're replying to. + if ($context['reply']) + echo ' +
    +
    +
    +

    ', $txt['subject'], ': ', $context['quoted_message']['subject'], '

    +
    +
    + +
    +
    + ', $txt['on'], ': ', $context['quoted_message']['time'], ' + ', $txt['from'], ': ', $context['quoted_message']['member']['name'], ' +

    + ', $context['quoted_message']['body'], ' +
    + +

    '; + + echo ' + + + '; +} + +// This template asks the user whether they wish to empty out their folder/messages. +function template_ask_delete() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    ', ($context['delete_all'] ? $txt['delete_message'] : $txt['delete_all']), '

    +
    +
    + +
    +

    ', $txt['delete_all_confirm'], '


    + ', $txt['yes'], ' - ', $txt['no'], ' +
    + +
    '; +} + +// This template asks the user what messages they want to prune. +function template_prune() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['pm_prune'], '

    +
    +
    + +
    +

    ', $txt['pm_prune_desc1'], ' ', $txt['pm_prune_desc2'], '

    +
    + +
    +
    + +
    + +
    '; +} + +// Here we allow the user to setup labels, remove labels and change rules for labels (i.e, do quite a bit) +function template_labels() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['pm_manage_labels'], '

    +
    +
    + ', $txt['pm_labels_desc'], ' +
    + + + + + + + + '; + if (count($context['labels']) < 2) + echo ' + + + '; + else + { + $alternate = true; + foreach ($context['labels'] as $label) + { + if ($label['id'] == -1) + continue; + + echo ' + + + + '; + + $alternate = !$alternate; + } + } + echo ' + +
    + ', $txt['pm_label_name'], ' + '; + + if (count($context['labels']) > 2) + echo ' + '; + + echo ' +
    ', $txt['pm_labels_no_exist'], '
    + +
    '; + + if (!count($context['labels']) < 2) + echo ' +
    + + +
    '; + + echo ' + +
    +
    +
    +

    ', $txt['pm_label_add_new'], '

    +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    +
    + +
    +
    + +
    + +

    '; +} + +// Template for reporting a personal message. +function template_report_message() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    + +
    +

    ', $txt['pm_report_title'], '

    +
    +
    + ', $txt['pm_report_desc'], ' +
    +
    + +
    +
    '; + + // If there is more than one admin on the forum, allow the user to choose the one they want to direct to. + // !!! Why? + if ($context['admin_count'] > 1) + { + echo ' +
    + ', $txt['pm_report_admins'], ': +
    +
    + +
    '; + } + + echo ' +
    + ', $txt['pm_report_reason'], ': +
    +
    + +
    +
    +
    + +
    +
    + +
    + +
    '; +} + +// Little template just to say "Yep, it's been submitted" +function template_report_message_complete() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +

    ', $txt['pm_report_title'], '

    +
    +
    + +
    +

    ', $txt['pm_report_done'], '

    + ', $txt['pm_report_return'], ' +
    + +
    '; +} + +// Manage rules. +function template_rules() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['pm_manage_rules'], '

    +
    +
    + ', $txt['pm_manage_rules_desc'], ' +
    + + + + + + + + '; + + if (empty($context['rules'])) + echo ' + + + '; + + $alternate = false; + foreach ($context['rules'] as $rule) + { + echo ' + + + + '; + $alternate = !$alternate; + } + + echo ' + +
    + ', $txt['pm_rule_title'], ' + '; + + if (!empty($context['rules'])) + echo ' + '; + + echo ' +
    + ', $txt['pm_rules_none'], ' +
    + ', $rule['name'], ' + + +
    +
    + [', $txt['pm_add_rule'], ']'; + + if (!empty($context['rules'])) + echo ' + [', $txt['pm_apply_rules'], ']'; + + if (!empty($context['rules'])) + echo ' + + '; + + echo ' +
    +
    '; + +} + +// Template for adding/editing a rule. +function template_add_rule() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' + '; + + echo ' +
    +
    +

    ', $context['rid'] == 0 ? $txt['pm_add_rule'] : $txt['pm_edit_rule'], '

    +
    +
    + +
    +
    +
    + ', $txt['pm_rule_name'], ':
    + ', $txt['pm_rule_name_desc'], ' +
    +
    + +
    +
    +
    + ', $txt['pm_rule_criteria'], ''; + + // Add a dummy criteria to allow expansion for none js users. + $context['rule']['criteria'][] = array('t' => '', 'v' => ''); + + // For each criteria print it out. + $isFirst = true; + foreach ($context['rule']['criteria'] as $k => $criteria) + { + if (!$isFirst && $criteria['t'] == '') + echo '
    '; + elseif (!$isFirst) + echo '
    '; + + echo ' + + + + + + + '; + + // If this is the dummy we add a means to hide for non js users. + if ($isFirst) + $isFirst = false; + elseif ($criteria['t'] == '') + echo '
    '; + } + + echo ' +
    + +

    + ', $txt['pm_rule_logic'], ': + +
    +
    + ', $txt['pm_rule_actions'], ''; + + // As with criteria - add a dummy action for "expansion". + $context['rule']['actions'][] = array('t' => '', 'v' => ''); + + // Print each action. + $isFirst = true; + foreach ($context['rule']['actions'] as $k => $action) + { + if (!$isFirst && $action['t'] == '') + echo '
    '; + elseif (!$isFirst) + echo '
    '; + + echo ' + + + + '; + + if ($isFirst) + $isFirst = false; + elseif ($action['t'] == '') + echo ' +
    '; + } + + echo ' +
    + +
    +
    + +

    +
    +

    ', $txt['pm_rule_description'], '

    +
    +
    +
    ', $txt['pm_rule_js_disabled'], '
    +
    +
    + + +
    +
    '; + + // Now setup all the bits! + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Poll.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Poll.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,156 @@ +:
  3. '; + + // Start the main poll form. + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    '; + + if (!empty($context['poll_error']['messages'])) + echo ' +
    +
    +
    + ', $context['is_edit'] ? $txt['error_while_editing_poll'] : $txt['error_while_adding_poll'], ': +
    +
    + ', empty($context['poll_error']['messages']) ? '' : implode('
    ', $context['poll_error']['messages']), ' +
    +
    +
    '; + + echo ' +
    + +
    + +
    + ', $txt['poll_question'], ': + +
      '; + + foreach ($context['choices'] as $choice) + { + echo ' +
    • + : + '; + + // Does this option have a vote count yet, or is it new? + if ($choice['votes'] != -1) + echo ' (', $choice['votes'], ' ', $txt['votes'], ')'; + + echo ' +
    • '; + } + + echo ' +
    • +
    + (', $txt['poll_add_option'], ') +
    +
    + ', $txt['poll_options'], ': +
    '; + + if ($context['can_moderate_poll']) + { + echo ' +
    + +
    +
    + +
    +
    +
    + ', $txt['poll_run_limit'], ' +
    +
    + ', $txt['days_word'], ' +
    +
    + +
    +
    + +
    '; + + if ($context['poll']['guest_vote_allowed']) + echo ' +
    + +
    +
    + +
    '; + } + + echo ' +
    + ', $txt['poll_results_visibility'], ': +
    +
    +
    +
    + +
    +
    +
    '; + // If this is an edit, we can allow them to reset the vote counts. + if ($context['is_edit']) + echo ' +
    + ', $txt['reset_votes'], ' + ' . $txt['reset_votes_check'] . ' +
    '; + echo ' +
    + +
    +
    + +
    + + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Post.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Post.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1096 @@ +:
  4. '), '); + }'; + + // If we are making a calendar event we want to ensure we show the current days in a month etc... this is done here. + if ($context['make_event']) + echo ' + var monthLength = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + function generateDays() + { + var dayElement = document.getElementById(\'day\'), yearElement = document.getElementById(\'year\'), monthElement = document.getElementById(\'month\'); + var days, selected = dayElement.selectedIndex; + + monthLength[1] = yearElement.options[yearElement.selectedIndex].value % 4 == 0 ? 29 : 28; + days = monthLength[monthElement.value - 1]; + + while (dayElement.options.length) + dayElement.options[0] = null; + + for (i = 1; i <= days; i++) + dayElement.options[dayElement.length] = new Option(i, i); + + if (selected < days) + dayElement.selectedIndex = selected; + }'; + + // End of the javascript, start the form and display the link tree. + echo ' + // ]]> +
    '; + + // If the user wants to see how their message looks - the preview section is where it's at! + echo ' +
    '; + + if ($context['make_event'] && (!$context['event']['new'] || !empty($context['current_board']))) + echo ' + '; + + // Start the main table. + echo ' +
    +

    ', $context['page_title'], '

    +
    +
    + +
    ', isset($context['current_topic']) ? ' + ' : ''; + + // If an error occurred, explain what happened. + echo ' + '; + + // If this won't be approved let them know! + if (!$context['becomes_approved']) + { + echo ' +

    + ', $txt['wait_for_approval'], ' + +

    '; + } + + // If it's locked, show a message to warn the replyer. + echo ' + '; + + // The post header... important stuff + echo ' +
    '; + + // Guests have to put in their name and email... + if (isset($context['name']) && isset($context['email'])) + { + echo ' +
    + ', $txt['name'], ': +
    +
    + +
    '; + + if (empty($modSettings['guest_post_no_email'])) + echo ' +
    + ', $txt['email'], ': +
    +
    + +
    '; + } + + // Now show the subject box for this post. + echo ' +
    + ', $txt['subject'], ': +
    +
    + +
    +
    + ', $txt['message_icon'], ': +
    +
    + + +
    +

    '; + + // Are you posting a calendar event? + if ($context['make_event']) + { + echo ' +
    +
    + ', $txt['calendar_event_title'], ' + +
    + ', $txt['calendar_year'], ' + + ', $txt['calendar_month'], ' + + ', $txt['calendar_day'], ' + +
    +
    '; + + if (!empty($modSettings['cal_allowspan']) || ($context['event']['new'] && $context['is_new_post'])) + { + echo ' +
    + ', $txt['calendar_event_options'], ' +
    +
      '; + + // If events can span more than one day then allow the user to select how long it should last. + if (!empty($modSettings['cal_allowspan'])) + { + echo ' +
    • + ', $txt['calendar_numb_days'], ' + +
    • '; + } + + // If this is a new event let the user specify which board they want the linked post to be put into. + if ($context['event']['new'] && $context['is_new_post']) + { + echo ' +
    • + ', $txt['calendar_post_in'], ' + +
    • '; + } + + echo ' +
    +
    +
    '; + } + + echo ' +
    '; + } + + // If this is a poll then display all the poll options! + if ($context['make_poll']) + { + echo ' +
    +
    + ', $txt['poll_question'], ' + +
      '; + + // Loop through all the choices and print them out. + foreach ($context['choices'] as $choice) + { + echo ' +
    • + : + +
    • '; + } + + echo ' +
    • +
    + (', $txt['poll_add_option'], ') +
    +
    + ', $txt['poll_options'], ' +
    +
    + +
    +
    + +
    +
    +
    + ', $txt['poll_run_limit'], ' +
    +
    + ', $txt['days_word'], ' +
    +
    + +
    +
    + +
    '; + + if ($context['poll_options']['guest_vote_enabled']) + echo ' +
    + +
    +
    + +
    '; + + echo ' +
    + ', $txt['poll_results_visibility'], ': +
    +
    +
    +
    + +
    +
    +
    +
    '; + } + + // Show the actual posting area... + if ($context['show_bbc']) + { + echo ' +
    '; + } + + // What about smileys? + if (!empty($context['smileys']['postform']) || !empty($context['smileys']['popup'])) + echo ' +
    '; + + echo ' + ', template_control_richedit($context['post_box_name'], 'smileyBox_message', 'bbcBox_message'); + + // If this message has been edited in the past - display when it was. + if (isset($context['last_modified'])) + echo ' +
    + ', $txt['last_edit'], ': + ', $context['last_modified'], ' +
    '; + + // If the admin has enabled the hiding of the additional options - show a link and image for it. + if (!empty($settings['additional_options_collapsable'])) + echo ' + '; + + // Display the check boxes for all the standard options - if they are available to the user! + echo ' +
    +
      + ', $context['can_notify'] ? '
    • ' : '', ' + ', $context['can_lock'] ? '
    • ' : '', ' +
    • + ', $context['can_sticky'] ? '
    • ' : '', ' +
    • ', ' + ', $context['can_move'] ? '
    • ' : '', ' + ', $context['can_announce'] && $context['is_first_post'] ? '
    • ' : '', ' + ', $context['show_approval'] ? '
    • ' : '', ' +
    +
    '; + + // If this post already has attachments on it - give information about them. + if (!empty($context['current_attachments'])) + { + echo ' +
    +
    + ', $txt['attached'], ': +
    +
    + + ', $txt['uncheck_unwatchd_attach'], ': +
    '; + foreach ($context['current_attachments'] as $attachment) + echo ' +
    + +
    '; + echo ' +
    '; + } + + // Is the user allowed to post any additional ones? If so give them the boxes to do it! + if ($context['can_post_attachment']) + { + echo ' +
    +
    + ', $txt['attach'], ': +
    +
    + (', $txt['clean_attach'], ')'; + + // Show more boxes only if they aren't approaching their limit. + if ($context['num_allowed_attachments'] > 1) + echo ' + +
    +
    (', $txt['more_attachments'], ')
    '; + + echo ' +
    '; + + // Show some useful information such as allowed extensions, maximum size and amount of attachments allowed. + if (!empty($modSettings['attachmentCheckExtensions'])) + echo ' + ', $txt['allowed_types'], ': ', $context['allowed_extensions'], '
    '; + + if (!empty($context['attachment_restrictions'])) + echo ' + ', $txt['attach_restrictions'], ' ', implode(', ', $context['attachment_restrictions']), '
    '; + + if (!$context['can_post_attachment_unapproved']) + echo ' + ', $txt['attachment_requires_approval'], '', '
    '; + + echo ' +
    +
    '; + } + + // Is visual verification enabled? + if ($context['require_verification']) + { + echo ' +
    + + ', $txt['verification'], ': + + ', template_control_verification($context['visual_verification_id'], 'all'), ' +
    '; + } + + // Finally, the submit buttons. + echo ' +

    + ', $context['browser']['is_firefox'] ? $txt['shortcuts_firefox'] : $txt['shortcuts'], ' +

    +

    + ', template_control_richedit_buttons($context['post_box_name']); + + // Option to delete an event if user is editing one. + if ($context['make_event'] && !$context['event']['new']) + echo ' + '; + + echo ' +

    +
    + +
    +
    '; + + // Assuming this isn't a new topic pass across the last message id. + if (isset($context['topic_last_message'])) + echo ' + '; + + echo ' + + + +
    '; + + echo ' + '; + + // If the user is replying to a topic show the previous posts. + if (isset($context['previous_posts']) && count($context['previous_posts']) > 0) + { + echo ' +
    +
    +

    ', $txt['topic_summary'], '

    +
    + '; + + $ignored_posts = array(); + foreach ($context['previous_posts'] as $post) + { + $ignoring = false; + if (!empty($post['is_ignored'])) + $ignored_posts[] = $ignoring = $post['id']; + + echo ' +
    + +
    +
    +
    ', $txt['posted_by'], ': ', $post['poster'], '
    + « ', $txt['on'], ': ', $post['time'], ' » +
    '; + + if ($context['can_quote']) + { + echo ' + '; + } + + echo ' +
    '; + + if ($ignoring) + { + echo ' +
    + ', $txt['ignoring_user'], ' + +
    '; + } + + echo ' +
    ', $post['message'], '
    +
    + +
    '; + } + + echo ' +
    + '; + } +} + +// The template for the spellchecker. +function template_spellcheck() +{ + global $context, $settings, $options, $txt; + + // The style information that makes the spellchecker look... like the forum hopefully! + echo ' + + + ', $txt['spell_check'], ' + + + + + + + + + +
    +
     
    + + + +
    + ', $txt['spellcheck_change_to'], '
    + +
    + ', $txt['spellcheck_suggest'], '
    + +
    +
    + + + + +
    +
    + +'; +} + +function template_quotefast() +{ + global $context, $settings, $options, $txt; + + echo ' + + + + ', $txt['retrieving_quote'], ' + + + + ', $txt['retrieving_quote'], ' + + + +'; +} + +function template_announce() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    ', $txt['announce_title'], '

    +
    +
    + ', $txt['announce_desc'], ' +
    +
    + +
    +

    + ', $txt['announce_this_topic'], ' ', $context['topic_subject'], ' +

    +
      '; + + foreach ($context['groups'] as $group) + echo ' +
    • + (', $group['member_count'], ') +
    • '; + + echo ' +
    • + +
    • +
    +
    + + + + + +
    +
    + +
    +
    +
    +
    '; +} + +function template_announcement_send() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    + +
    +

    ', $txt['announce_sending'], ' ', $context['topic_subject'], '

    +

    ', $context['percentage_done'], '% ', $txt['announce_done'], '

    +
    + + + + + + + +
    +
    + +
    +
    +
    +
    + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Printpage.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Printpage.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,137 @@ + + + + + + + ', $txt['print_page'], ' - ', $context['topic_subject'], ' + + + +

    ', $context['forum_name_html_safe'], '

    +

    ', $context['category_name'], ' => ', (!empty($context['parent_boards']) ? implode(' => ', $context['parent_boards']) . ' => ' : ''), $context['board_name'], ' => ', $txt['topic_started'], ': ', $context['poster_name'], ' ', $txt['search_on'], ' ', $context['post_time'], '

    +
    '; +} + +function template_main() +{ + global $context, $settings, $options, $txt; + + foreach ($context['posts'] as $post) + echo ' +
    + ', $txt['title'], ': ', $post['subject'], '
    + ', $txt['post_by'], ': ', $post['member'], ' ', $txt['search_on'], ' ', $post['time'], ' +
    +
    + ', $post['body'], ' +
    '; +} + +function template_print_below() +{ + global $context, $settings, $options; + + echo ' +
    + + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Profile.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Profile.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2982 @@ +'; + + // Prevent Chrome from auto completing fields when viewing/editing other members profiles + if ($context['browser']['is_chrome'] && !$context['user']['is_owner']) + echo ' + '; + + // If an error occurred while trying to save previously, give the user a clue! + if (!empty($context['post_errors'])) + echo ' + ', template_error_message(); + + // If the profile was update successfully, let the user know this. + if (!empty($context['profile_updated'])) + echo ' +
    + ', $context['profile_updated'], ' +
    '; +} + +// Template for closing off table started in profile_above. +function template_profile_below() +{ +} + +// This template displays users details without any option to edit them. +function template_summary() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // Display the basic information about the user + echo ' +
    +
    +

    + ', $txt['summary'], ' +

    +
    +
    +
    + +
    +

    ', $context['member']['name'], ' ', (!empty($context['member']['group']) ? $context['member']['group'] : $context['member']['post_group']), '

    + ', $context['member']['avatar']['image'], ' +
      '; + + // What about if we allow email only via the forum?? + if ($context['member']['show_email'] === 'yes' || $context['member']['show_email'] === 'no_through_forum' || $context['member']['show_email'] === 'yes_permission_override') + echo ' +
    • ', $txt['email'], '
    • '; + + // Don't show an icon if they haven't specified a website. + if ($context['member']['website']['url'] !== '' && !isset($context['disabled_fields']['website'])) + echo ' +
    • ', ($settings['use_image_buttons'] ? '' . $context['member']['website']['title'] . '' : $txt['www']), '
    • '; + + // Are there any custom profile fields for the summary? + if (!empty($context['custom_fields'])) + { + foreach ($context['custom_fields'] as $field) + if (($field['placement'] == 1 || empty($field['output_html'])) && !empty($field['value'])) + echo ' +
    • ', $field['output_html'], '
    • '; + } + + echo ' + ', !isset($context['disabled_fields']['icq']) && !empty($context['member']['icq']['link']) ? '
    • ' . $context['member']['icq']['link'] . '
    • ' : '', ' + ', !isset($context['disabled_fields']['msn']) && !empty($context['member']['msn']['link']) ? '
    • ' . $context['member']['msn']['link'] . '
    • ' : '', ' + ', !isset($context['disabled_fields']['aim']) && !empty($context['member']['aim']['link']) ? '
    • ' . $context['member']['aim']['link'] . '
    • ' : '', ' + ', !isset($context['disabled_fields']['yim']) && !empty($context['member']['yim']['link']) ? '
    • ' . $context['member']['yim']['link'] . '
    • ' : '', ' +
    + ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $context['member']['online']['text'] . '' : $context['member']['online']['text'], $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? ' ' . $context['member']['online']['text'] . '' : ''; + + // Can they add this member as a buddy? + if (!empty($context['can_have_buddy']) && !$context['user']['is_owner']) + echo ' +
    [', $txt['buddy_' . ($context['member']['is_buddy'] ? 'remove' : 'add')], ']'; + + echo ' +
    '; + + echo ' + '; + + echo ' +
    + +
    +
    +
    +
    + +
    +
    '; + + if ($context['user']['is_owner'] || $context['user']['is_admin']) + echo ' +
    ', $txt['username'], ':
    +
    ', $context['member']['username'], '
    '; + + if (!isset($context['disabled_fields']['posts'])) + echo ' +
    ', $txt['profile_posts'], ':
    +
    ', $context['member']['posts'], ' (', $context['member']['posts_per_day'], ' ', $txt['posts_per_day'], ')
    '; + + // Only show the email address fully if it's not hidden - and we reveal the email. + if ($context['member']['show_email'] == 'yes') + echo ' +
    ', $txt['email'], ':
    +
    ', $context['member']['email'], '
    '; + + // ... Or if the one looking at the profile is an admin they can see it anyway. + elseif ($context['member']['show_email'] == 'yes_permission_override') + echo ' +
    ', $txt['email'], ':
    +
    ', $context['member']['email'], '
    '; + + if (!empty($modSettings['titlesEnable']) && !empty($context['member']['title'])) + echo ' +
    ', $txt['custom_title'], ':
    +
    ', $context['member']['title'], '
    '; + + if (!empty($context['member']['blurb'])) + echo ' +
    ', $txt['personal_text'], ':
    +
    ', $context['member']['blurb'], '
    '; + + // If karma enabled show the members karma. + if ($modSettings['karmaMode'] == '1') + echo ' +
    ', $modSettings['karmaLabel'], '
    +
    ', ($context['member']['karma']['good'] - $context['member']['karma']['bad']), '
    '; + + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    ', $modSettings['karmaLabel'], '
    +
    +', $context['member']['karma']['good'], '/-', $context['member']['karma']['bad'], '
    '; + + if (!isset($context['disabled_fields']['gender']) && !empty($context['member']['gender']['name'])) + echo ' +
    ', $txt['gender'], ':
    +
    ', $context['member']['gender']['name'], '
    '; + + echo ' +
    ', $txt['age'], ':
    +
    ', $context['member']['age'] . ($context['member']['today_is_birthday'] ? '   ' : ''), '
    '; + + if (!isset($context['disabled_fields']['location']) && !empty($context['member']['location'])) + echo ' +
    ', $txt['location'], ':
    +
    ', $context['member']['location'], '
    '; + + echo ' +
    '; + + // Any custom fields for standard placement? + if (!empty($context['custom_fields'])) + { + $shown = false; + foreach ($context['custom_fields'] as $field) + { + if ($field['placement'] != 0 || empty($field['output_html'])) + continue; + + if (empty($shown)) + { + echo ' +
    '; + $shown = true; + } + + echo ' +
    ', $field['name'], ':
    +
    ', $field['output_html'], '
    '; + } + + if (!empty($shown)) + echo ' +
    '; + } + + echo ' +
    '; + + // Can they view/issue a warning? + if ($context['can_view_warning'] && $context['member']['warning']) + { + echo ' +
    ', $txt['profile_warning_level'], ':
    +
    + ', $context['member']['warning'], '%'; + + // Can we provide information on what this means? + if (!empty($context['warning_status'])) + echo ' + (', $context['warning_status'], ')'; + + echo ' +
    '; + } + + // Is this member requiring activation and/or banned? + if (!empty($context['activate_message']) || !empty($context['member']['bans'])) + { + + // If the person looking at the summary has permission, and the account isn't activated, give the viewer the ability to do it themselves. + if (!empty($context['activate_message'])) + echo ' +
    ', $context['activate_message'], ' (', $context['activate_link_text'], ')
    '; + + // If the current member is banned, show a message and possibly a link to the ban. + if (!empty($context['member']['bans'])) + { + echo ' +
    ', $txt['user_is_banned'], ' [' . $txt['view_ban'] . ']
    + '; + } + } + + echo ' +
    ', $txt['date_registered'], ':
    +
    ', $context['member']['registered'], '
    '; + + // If the person looking is allowed, they can check the members IP address and hostname. + if ($context['can_see_ip']) + { + if (!empty($context['member']['ip'])) + echo ' +
    ', $txt['ip'], ':
    +
    ', $context['member']['ip'], '
    '; + + if (empty($modSettings['disableHostnameLookup']) && !empty($context['member']['ip'])) + echo ' +
    ', $txt['hostname'], ':
    +
    ', $context['member']['hostname'], '
    '; + } + + echo ' +
    ', $txt['local_time'], ':
    +
    ', $context['member']['local_time'], '
    '; + + if (!empty($modSettings['userLanguage']) && !empty($context['member']['language'])) + echo ' +
    ', $txt['language'], ':
    +
    ', $context['member']['language'], '
    '; + + echo ' +
    ', $txt['lastLoggedIn'], ':
    +
    ', $context['member']['last_login'], '
    +
    '; + + // Are there any custom profile fields for the summary? + if (!empty($context['custom_fields'])) + { + $shown = false; + foreach ($context['custom_fields'] as $field) + { + if ($field['placement'] != 2 || empty($field['output_html'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    +
      '; + } + echo ' +
    • ', $field['output_html'], '
    • '; + } + if ($shown) + echo ' +
    +
    '; + } + + // Show the users signature. + if ($context['signature_enabled'] && !empty($context['member']['signature'])) + echo ' +
    +
    ', $txt['signature'], ':
    + ', $context['member']['signature'], ' +
    '; + + echo ' +
    + +
    +
    +
    +
    '; +} + +// Template for showing all the posts of the user, in chronological order. +function template_showPosts() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    + ', (!isset($context['attachments']) && empty($context['is_topics']) ? $txt['showMessages'] : (!empty($context['is_topics']) ? $txt['showTopics'] : $txt['showAttachments'])), ' - ', $context['member']['name'], ' +

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + // Button shortcuts + $quote_button = create_button('quote.gif', 'reply_quote', 'quote', 'align="middle"'); + $reply_button = create_button('reply_sm.gif', 'reply', 'reply', 'align="middle"'); + $remove_button = create_button('delete.gif', 'remove_message', 'remove', 'align="middle"'); + $notify_button = create_button('notify_sm.gif', 'notify_replies', 'notify', 'align="middle"'); + + // Are we displaying posts or attachments? + if (!isset($context['attachments'])) + { + // For every post to be displayed, give it its own div, and show the important details of the post. + foreach ($context['posts'] as $post) + { + echo ' +
    +
    + +
    +
    ', $post['counter'], '
    +
    +
    ', $post['board']['name'], ' / ', $post['subject'], '
    + « ', $txt['on'], ': ', $post['time'], ' » +
    +
    '; + + if (!$post['approved']) + echo ' +
    + ', $txt['post_awaiting_approval'], ' +
    '; + + echo ' + ', $post['body'], ' +
    +
    '; + + if ($post['can_reply'] || $post['can_mark_notify'] || $post['can_delete']) + echo ' +
    +
      '; + + // If they *can* reply? + if ($post['can_reply']) + echo ' +
    • ', $txt['reply'], '
    • '; + + // If they *can* quote? + if ($post['can_quote']) + echo ' +
    • ', $txt['quote'], '
    • '; + + // Can we request notification of topics? + if ($post['can_mark_notify']) + echo ' +
    • ', $txt['notify'], '
    • '; + + // How about... even... remove it entirely?! + if ($post['can_delete']) + echo ' +
    • ', $txt['remove'], '
    • '; + + if ($post['can_reply'] || $post['can_mark_notify'] || $post['can_delete']) + echo ' +
    +
    '; + + echo ' +
    + +
    +
    '; + } + } + else + { + echo ' + + + + + + + + + + '; + + // Looks like we need to do all the attachments instead! + $alternate = false; + foreach ($context['attachments'] as $attachment) + { + echo ' + + + + + + '; + $alternate = !$alternate; + } + + // No posts? Just end the table with a informative message. + if ((isset($context['attachments']) && empty($context['attachments'])) || (!isset($context['attachments']) && empty($context['posts']))) + echo ' + + + '; + + echo ' + +
    + + ', $txt['show_attach_filename'], ' + ', ($context['sort_order'] == 'filename' ? '' : ''), ' + + + + ', $txt['show_attach_downloads'], ' + ', ($context['sort_order'] == 'downloads' ? '' : ''), ' + + + + ', $txt['message'], ' + ', ($context['sort_order'] == 'subject' ? '' : ''), ' + + + + ', $txt['show_attach_posted'], ' + ', ($context['sort_order'] == 'posted' ? '' : ''), ' + +
    ', $attachment['filename'], '', !$attachment['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : '', '', $attachment['downloads'], '', $attachment['subject'], '', $attachment['posted'], '
    + ', isset($context['attachments']) ? $txt['show_attachments_none'] : ($context['is_topics'] ? $txt['show_topics_none'] : $txt['show_posts_none']), ' +
    '; + } + // Show more page numbers. + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; +} + +// Template for showing all the buddies of the current user. +function template_editBuddies() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    + ', $txt['editBuddies'], ' +

    +
    + + + + + + + + + + + '; + + // If they don't have any buddies don't list them! + if (empty($context['buddies'])) + echo ' + + + '; + + // Now loop through each buddy showing info on each. + $alternate = false; + foreach ($context['buddies'] as $buddy) + { + echo ' + + + + + + + + + + '; + + $alternate = !$alternate; + } + + echo ' +
    ', $txt['name'], '', $txt['status'], '', $txt['email'], '', $txt['icq'], '', $txt['aim'], '', $txt['yim'], '', $txt['msn'], '
    ', $txt['no_buddies'], '
    ', $buddy['link'], '', $buddy['online']['label'], '', ($buddy['show_email'] == 'no' ? '' : '' . $txt['email'] . ''), '', $buddy['icq']['link'], '', $buddy['aim']['link'], '', $buddy['yim']['link'], '', $buddy['msn']['link'], '', $txt['buddy_remove'], '
    '; + + // Add a new buddy? + echo ' +
    +
    +
    +
    +

    ', $txt['buddy_add'], '

    +
    + +
    + + + +
    + +
    +
    + + '; +} + +// Template for showing the ignore list of the current user. +function template_editIgnoreList() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    + ', $txt['editIgnoreList'], ' +

    +
    + + + + + + + + + + + '; + + // If they don't have anyone on their ignore list, don't list it! + if (empty($context['ignore_list'])) + echo ' + + + '; + + // Now loop through each buddy showing info on each. + $alternate = false; + foreach ($context['ignore_list'] as $member) + { + echo ' + + + + + + + + + + '; + + $alternate = !$alternate; + } + + echo ' +
    ', $txt['name'], '', $txt['status'], '', $txt['email'], '', $txt['icq'], '', $txt['aim'], '', $txt['yim'], '', $txt['msn'], '
    ', $txt['no_ignore'], '
    ', $member['link'], '', $member['online']['label'], '', ($member['show_email'] == 'no' ? '' : '' . $txt['email'] . ''), '', $member['icq']['link'], '', $member['aim']['link'], '', $member['yim']['link'], '', $member['msn']['link'], '', $txt['ignore_remove'], '
    '; + + // Add a new buddy? + echo ' +
    +
    +
    +
    +

    ', $txt['ignore_add'], '

    +
    + +
    + + + +
    + +
    +
    + + '; +} + +// This template shows an admin information on a users IP addresses used and errors attributed to them. +function template_trackActivity() +{ + global $context, $settings, $options, $scripturl, $txt; + + // The first table shows IP information about the user. + echo ' +
    +

    ', $txt['view_ips_by'], ' ', $context['member']['name'], '

    +
    '; + + // The last IP the user used. + echo ' +
    + +
    +
    +
    ', $txt['most_recent_ip'], ': + ', (empty($context['last_ip2']) ? '' : '
    + (' . $txt['why_two_ip_address'] . ')'), ' +
    +
    + ', $context['last_ip'], ''; + + // Second address detected? + if (!empty($context['last_ip2'])) + echo ' + , ', $context['last_ip2'], ''; + + echo ' +
    '; + + // Lists of IP addresses used in messages / error messages. + echo ' +
    ', $txt['ips_in_messages'], ':
    +
    + ', (count($context['ips']) > 0 ? implode(', ', $context['ips']) : '(' . $txt['none'] . ')'), ' +
    +
    ', $txt['ips_in_errors'], ':
    +
    + ', (count($context['ips']) > 0 ? implode(', ', $context['error_ips']) : '(' . $txt['none'] . ')'), ' +
    '; + + // List any members that have used the same IP addresses as the current member. + echo ' +
    ', $txt['members_in_range'], ':
    +
    + ', (count($context['members_in_range']) > 0 ? implode(', ', $context['members_in_range']) : '(' . $txt['none'] . ')'), ' +
    +
    +
    + +
    +
    '; + + // Show the track user list. + template_show_list('track_user_list'); +} + +// The template for trackIP, allowing the admin to see where/who a certain IP has been used. +function template_trackIP() +{ + global $context, $settings, $options, $scripturl, $txt; + + // This function always defaults to the last IP used by a member but can be set to track any IP. + // The first table in the template gives an input box to allow the admin to enter another IP to track. + echo ' +
    +

    ', $txt['trackIP'], '

    +
    +
    + +
    +
    ', $txt['enter_ip'], ':    
    +
    + +
    +
    '; + + // The table inbetween the first and second table shows links to the whois server for every region. + if ($context['single_ip']) + { + echo ' +
    +

    ', $txt['whois_title'], ' ', $context['ip'], '

    +
    +
    + +
    '; + foreach ($context['whois_servers'] as $server) + echo ' + ', $server['name'], '
    '; + echo ' +
    + +
    +
    '; + } + + // The second table lists all the members who have been logged as using this IP address. + echo ' +
    +

    ', $txt['members_from_ip'], ' ', $context['ip'], '

    +
    '; + if (empty($context['ips'])) + echo ' +

    ', $txt['no_members_from_ip'], '

    '; + else + { + echo ' + + + + + + + + '; + + // Loop through each of the members and display them. + foreach ($context['ips'] as $ip => $memberlist) + echo ' + + + + '; + + echo ' + +
    ', $txt['ip_address'], '', $txt['display_name'], '
    ', $ip, '', implode(', ', $memberlist), '
    +
    '; + } + + template_show_list('track_message_list'); + + echo '
    '; + + template_show_list('track_user_list'); +} + +function template_showPermissions() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +

    + ', $txt['showPermissions'], ' +

    +
    '; + + if ($context['member']['has_all_permissions']) + { + echo ' +

    ', $txt['showPermissions_all'], '

    '; + } + else + { + echo ' +

    ',$txt['showPermissions_help'],'

    +
    '; + + if (!empty($context['no_access_boards'])) + { + echo ' +
    +

    ', $txt['showPermissions_restricted_boards'], '

    +
    +
    + +
    ', $txt['showPermissions_restricted_boards_desc'], ':
    '; + foreach ($context['no_access_boards'] as $no_access_board) + echo ' + ', $no_access_board['name'], '', $no_access_board['is_last'] ? '' : ', '; + echo ' +
    + +
    '; + } + + // General Permissions section. + echo ' +
    +
    +

    ', $txt['showPermissions_general'], '

    +
    '; + if (!empty($context['member']['permissions']['general'])) + { + echo ' + + + + + + + + '; + + foreach ($context['member']['permissions']['general'] as $permission) + { + echo ' + + + + '; + } + echo ' + +
    ', $txt['showPermissions_permission'], '', $txt['showPermissions_status'], '
    + ', $permission['is_denied'] ? '' . $permission['name'] . '' : $permission['name'], ' + '; + + if ($permission['is_denied']) + echo ' + ', $txt['showPermissions_denied'], ': ', implode(', ', $permission['groups']['denied']),''; + else + echo ' + ', $txt['showPermissions_given'], ': ', implode(', ', $permission['groups']['allowed']); + + echo ' +
    +

    '; + } + else + echo ' +

    ', $txt['showPermissions_none_general'], '

    '; + + // Board permission section. + echo ' +
    +
    +
    +

    + ', $txt['showPermissions_select'], ': + +

    +
    +
    '; + if (!empty($context['member']['permissions']['board'])) + { + echo ' + + + + + + + + '; + foreach ($context['member']['permissions']['board'] as $permission) + { + echo ' + + + + '; + } + echo ' + +
    ', $txt['showPermissions_permission'], '', $txt['showPermissions_status'], '
    + ', $permission['is_denied'] ? '' . $permission['name'] . '' : $permission['name'], ' + '; + + if ($permission['is_denied']) + { + echo ' + ', $txt['showPermissions_denied'], ': ', implode(', ', $permission['groups']['denied']), ''; + } + else + { + echo ' + ', $txt['showPermissions_given'], ':  ', implode(', ', $permission['groups']['allowed']); + } + echo ' +
    '; + } + else + echo ' +

    ', $txt['showPermissions_none_board'], '

    '; + echo ' +
    +
    '; + } +} + +// Template for user statistics, showing graphs and the like. +function template_statPanel() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // First, show a few text statistics such as post/topic count. + echo ' +
    +
    +
    +

    + + ', $txt['statPanel_generalStats'], ' - ', $context['member']['name'], ' + +

    +
    +
    + +
    +
    +
    ', $txt['statPanel_total_time_online'], ':
    +
    ', $context['time_logged_in'], '
    +
    ', $txt['statPanel_total_posts'], ':
    +
    ', $context['num_posts'], ' ', $txt['statPanel_posts'], '
    +
    ', $txt['statPanel_total_topics'], ':
    +
    ', $context['num_topics'], ' ', $txt['statPanel_topics'], '
    +
    ', $txt['statPanel_users_polls'], ':
    +
    ', $context['num_polls'], ' ', $txt['statPanel_polls'], '
    +
    ', $txt['statPanel_users_votes'], ':
    +
    ', $context['num_votes'], ' ', $txt['statPanel_votes'], '
    +
    +
    + +
    +
    '; + + // This next section draws a graph showing what times of day they post the most. + echo ' +
    +
    +

    + ', $txt['statPanel_activityTime'], ' +

    +
    +
    + +
    '; + + // If they haven't post at all, don't draw the graph. + if (empty($context['posts_by_time'])) + echo ' + ', $txt['statPanel_noPosts'], ''; + // Otherwise do! + else + { + echo ' +
      '; + + // The labels. + foreach ($context['posts_by_time'] as $time_of_day) + { + echo ' + +
      +
      + ', sprintf($txt['statPanel_activityTime_posts'], $time_of_day['posts'], $time_of_day['posts_percent']), ' +
      +
      + ', $time_of_day['hour_format'], ' + '; + } + + echo ' + +
    '; + } + + echo ' + +
    + +
    +
    '; + + // Two columns with the most popular boards by posts and activity (activity = users posts / total posts). + echo ' +
    +
    +
    +

    + ', $txt['statPanel_topBoards'], ' +

    +
    +
    + +
    '; + + if (empty($context['popular_boards'])) + echo ' + ', $txt['statPanel_noPosts'], ''; + + else + { + echo ' +
    '; + + // Draw a bar for every board. + foreach ($context['popular_boards'] as $board) + { + echo ' +
    ', $board['link'], '
    +
    +
    + ', sprintf($txt['statPanel_topBoards_memberposts'], $board['posts'], $board['total_posts_member'], $board['posts_percent']), ' +
    + ', empty($context['hide_num_posts']) ? $board['posts'] : '', ' +
    '; + } + + echo ' +
    '; + } + echo ' +
    + +
    +
    '; + echo ' +
    +
    +

    + ', $txt['statPanel_topBoardsActivity'], ' +

    +
    +
    + +
    '; + + if (empty($context['board_activity'])) + echo ' + ', $txt['statPanel_noPosts'], ''; + else + { + echo ' +
    '; + + // Draw a bar for every board. + foreach ($context['board_activity'] as $activity) + { + echo ' +
    ', $activity['link'], '
    +
    +
    + ', sprintf($txt['statPanel_topBoards_posts'], $activity['posts'], $activity['total_posts'], $activity['posts_percent']), ' +
    + ', $activity['percent'], '% +
    '; + } + + echo ' +
    '; + } + echo ' +
    + +
    +
    +
    '; + + echo ' +
    +
    '; +} + +// Template for editing profile options. +function template_edit_options() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // The main header! + echo ' +
    +
    +

    + '; + + // Don't say "Profile" if this isn't the profile... + if (!empty($context['profile_header_text'])) + echo ' + ', $context['profile_header_text']; + else + echo ' + ', $txt['profile']; + + echo ' + +

    +
    '; + + // Have we some description? + if ($context['page_desc']) + echo ' +

    ', $context['page_desc'], '

    '; + + echo ' +
    + +
    '; + + // Any bits at the start? + if (!empty($context['profile_prehtml'])) + echo ' +
    ', $context['profile_prehtml'], '
    '; + + if (!empty($context['profile_fields'])) + echo ' +
    '; + + // Start the big old loop 'of love. + $lastItem = 'hr'; + foreach ($context['profile_fields'] as $key => $field) + { + // We add a little hack to be sure we never get more than one hr in a row! + if ($lastItem == 'hr' && $field['type'] == 'hr') + continue; + + $lastItem = $field['type']; + if ($field['type'] == 'hr') + { + echo ' +
    +
    +
    '; + } + elseif ($field['type'] == 'callback') + { + if (isset($field['callback_func']) && function_exists('template_profile_' . $field['callback_func'])) + { + $callback_func = 'template_profile_' . $field['callback_func']; + $callback_func(); + } + } + else + { + echo ' +
    + ', $field['label'], ''; + + // Does it have any subtext to show? + if (!empty($field['subtext'])) + echo ' +
    + ', $field['subtext'], ''; + + echo ' +
    +
    '; + + // Want to put something infront of the box? + if (!empty($field['preinput'])) + echo ' + ', $field['preinput']; + + // What type of data are we showing? + if ($field['type'] == 'label') + echo ' + ', $field['value']; + + // Maybe it's a text box - very likely! + elseif (in_array($field['type'], array('int', 'float', 'text', 'password'))) + echo ' + '; + + // You "checking" me out? ;) + elseif ($field['type'] == 'check') + echo ' + '; + + // Always fun - select boxes! + elseif ($field['type'] == 'select') + { + echo ' + '; + } + + // Something to end with? + if (!empty($field['postinput'])) + echo ' + ', $field['postinput']; + + echo ' +
    '; + } + } + + if (!empty($context['profile_fields'])) + echo ' +
    '; + + // Are there any custom profile fields - if so print them! + if (!empty($context['custom_fields'])) + { + if ($lastItem != 'hr') + echo ' +
    '; + + echo ' +
    '; + + foreach ($context['custom_fields'] as $field) + { + echo ' +
    + ', $field['name'], ':
    + ', $field['desc'], ' +
    +
    + ', $field['input_html'], ' +
    '; + } + + echo ' +
    '; + + } + + // Any closing HTML? + if (!empty($context['profile_posthtml'])) + echo ' +
    ', $context['profile_posthtml'], '
    '; + elseif ($lastItem != 'hr') + echo ' +
    '; + + // Only show the password box if it's actually needed. + if ($context['require_password']) + echo ' +
    +
    + ', $txt['current_password'], ':
    + ', $txt['required_security_reasons'], ' +
    +
    + +
    +
    '; + + echo ' +
    '; + + // The button shouldn't say "Change profile" unless we're changing the profile... + if (!empty($context['submit_button_text'])) + echo ' + '; + else + echo ' + '; + + echo ' + + + +
    +
    + +
    +
    +
    '; + + // Some javascript! + echo ' + '; + + // Any final spellchecking stuff? + if (!empty($context['show_spellchecking'])) + echo ' +
    '; +} + +// Personal Message settings. +function template_profile_pm_settings() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    + +
    +
    + +
    +
    + +
    +
    + + +
    + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    '; + +} + +// Template for showing theme settings. Note: template_options() actually adds the theme specific options. +function template_profile_theme_settings() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +
    +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • '; + + if ($settings['allow_no_censored']) + echo ' +
    • + + +
    • '; + + echo ' +
    • + + +
    • +
    • + + +
    • '; + + if (!empty($modSettings['enable_buddylist'])) + echo ' +
    • + + +
    • '; + + echo ' +
    • + + +
    • '; + + // Choose WYSIWYG settings? + if (empty($modSettings['disable_wysiwyg'])) + echo ' +
    • + + +
    • '; + + if (empty($modSettings['disableCustomPerPage'])) + { + echo ' +
    • + + +
    • +
    • + + +
    • '; + } + + if (!empty($modSettings['cal_enabled'])) + echo ' +
    • + + +
    • '; + + echo ' +
    • + + +
    • +
    • + + +
    • +
    +
    +
    '; +} + +function template_notification() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // The main containing header. + echo ' +
    +

    + ', $txt['profile'], ' +

    +
    +

    ', $txt['notification_info'], '

    +
    + +
    +
    '; + + // Allow notification on announcements to be disabled? + if (!empty($modSettings['allow_disableAnnounce'])) + echo ' + +
    '; + + // More notification options. + echo ' + +
    '; + + if (empty($modSettings['disallow_sendBody'])) + echo ' + +
    '; + + echo ' +
    + + +

    + +
    + +
    + + + + +

    +
    +
    + +
    +
    '; + + template_show_list('topic_notification_list'); + + echo ' +
    '; + + template_show_list('board_notification_list'); +} + +// Template for choosing group membership. +function template_groupMembership() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // The main containing header. + echo ' +
    +
    +

    + ', $txt['profile'], ' +

    +
    +

    ', $txt['groupMembership_info'], '

    '; + + // Do we have an update message? + if (!empty($context['update_message'])) + echo ' +
    + ', $context['update_message'], '. +
    '; + + // Requesting membership to a group? + if (!empty($context['group_request'])) + { + echo ' +
    +
    +

    ', $txt['request_group_membership'], '

    +
    + +
    + ', $txt['request_group_membership_desc'], ': + +
    + + +
    +
    + +
    '; + } + else + { + echo ' + + + + + + + + '; + + $alternate = true; + foreach ($context['groups']['member'] as $group) + { + echo ' + '; + + if ($context['can_edit_primary']) + echo ' + '; + + echo ' + + + '; + $alternate = !$alternate; + } + + echo ' + +
    ', $txt['current_membergroups'], '
    + + + + '; + + // Can they leave their group? + if ($group['can_leave']) + echo ' + ' . $txt['leave_group'] . ''; + echo ' +
    '; + + if ($context['can_edit_primary']) + echo ' +
    + +
    '; + + // Any groups they can join? + if (!empty($context['groups']['available'])) + { + echo ' +
    + + + + + + + + '; + + $alternate = true; + foreach ($context['groups']['available'] as $group) + { + echo ' + + + + '; + $alternate = !$alternate; + } + echo ' + +
    + ', $txt['available_groups'], ' +
    + ', (empty($group['color']) ? $group['name'] : '' . $group['name'] . ''), '', (!empty($group['desc']) ? '
    ' . $group['desc'] . '' : ''), ' +
    '; + + if ($group['type'] == 3) + echo ' + ', $txt['join_group'], ''; + elseif ($group['type'] == 2 && $group['pending']) + echo ' + ', $txt['approval_pending']; + elseif ($group['type'] == 2) + echo ' + ', $txt['request_group'], ''; + + echo ' +
    '; + } + + // Javascript for the selector stuff. + echo ' + '; + } + + echo ' + + +
    '; +} + +function template_ignoreboards() +{ + global $context, $txt, $settings, $scripturl; + // The main containing header. + echo ' + + +
    +
    +

    + ', $txt['profile'], ' +

    +
    +

    ', $txt['ignoreboards_info'], '

    +
    + +
    +
      '; + + $i = 0; + $limit = ceil($context['num_boards'] / 2); + foreach ($context['categories'] as $category) + { + if ($i == $limit) + { + echo ' +
    +
      '; + + $i++; + } + + echo ' +
    • + ', $category['name'], ' +
        '; + + foreach ($category['boards'] as $board) + { + if ($i == $limit) + echo ' +
      +
    • +
    +
      +
    • +
        '; + + echo ' +
      • + +
      • '; + + $i++; + } + + echo ' +
      +
    • '; + } + + echo ' +
    +
    '; + + // Show the standard "Save Settings" profile button. + template_profile_save(); + + echo ' +
    + +
    +
    +
    '; +} + +// Simple load some theme variables common to several warning templates. +function template_load_warning_variables() +{ + global $modSettings, $context; + + $context['warningBarWidth'] = 200; + // Setup the colors - this is a little messy for theming. + $context['colors'] = array( + 0 => 'green', + $modSettings['warning_watch'] => 'darkgreen', + $modSettings['warning_moderate'] => 'orange', + $modSettings['warning_mute'] => 'red', + ); + + // Work out the starting color. + $context['current_color'] = $context['colors'][0]; + foreach ($context['colors'] as $limit => $color) + if ($context['member']['warning'] >= $limit) + $context['current_color'] = $color; +} + +// Show all warnings of a user? +function template_viewWarning() +{ + global $context, $txt, $scripturl, $settings; + + template_load_warning_variables(); + + echo ' +
    +

    + + ', sprintf($txt['profile_viewwarning_for_user'], $context['member']['name']), ' + +

    +
    +
    + +
    +
    +
    + ', $txt['profile_warning_name'], ': +
    +
    + ', $context['member']['name'], ' +
    +
    + ', $txt['profile_warning_level'], ': +
    +
    +
    +
    +
    +
    ', $context['member']['warning'], '%
    +
     
    +
    +
    +
    +
    '; + + // There's some impact of this? + if (!empty($context['level_effects'][$context['current_level']])) + echo ' +
    + ', $txt['profile_viewwarning_impact'], ': +
    +
    + ', $context['level_effects'][$context['current_level']], ' +
    '; + + echo ' +
    +
    + +
    '; + + template_show_list('view_warnings'); +} + +// Show a lovely interface for issuing warnings. +function template_issueWarning() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + template_load_warning_variables(); + + echo ' + '; + + echo ' +
    +
    +

    + + ', $context['user']['is_owner'] ? $txt['profile_warning_level'] : $txt['profile_issue_warning'], ' + +

    +
    '; + + if (!$context['user']['is_owner']) + echo ' +

    ', $txt['profile_warning_desc'], '

    '; + + echo ' +
    + +
    +
    '; + + if (!$context['user']['is_owner']) + echo ' +
    + ', $txt['profile_warning_name'], ': +
    +
    + ', $context['member']['name'], ' +
    '; + + echo ' +
    + ', $txt['profile_warning_level'], ':'; + + // Is there only so much they can apply? + if ($context['warning_limit']) + echo ' +
    ', sprintf($txt['profile_warning_limit_attribute'], $context['warning_limit']), ''; + + echo ' +
    +
    + +
    +  ', $txt['profile_warning_max'], ' +
    ', $txt['profile_warning_impact'], ':
    '; + // For non-javascript give a better list. + foreach ($context['level_effects'] as $limit => $effect) + echo ' + ', sprintf($txt['profile_warning_effect_text'], $limit, $effect), '
    '; + + echo ' +
    +
    +
    '; + + if (!$context['user']['is_owner']) + { + echo ' +
    + ', $txt['profile_warning_reason'], ':
    + ', $txt['profile_warning_reason_desc'], ' +
    +
    + +
    +
    +
    +
    +
    + ', $txt['profile_warning_notify'], ': +
    +
    + +
    +
    + ', $txt['profile_warning_notify_subject'], ': +
    +
    + +
    +
    + ', $txt['profile_warning_notify_body'], ': +
    +
    + +
    + +
    '; + } + echo ' +
    +
    + + +
    +
    + +
    +
    '; + + // Previous warnings? + echo '
    +
    +

    + ', $txt['profile_warning_previous'], ' +

    +
    + + + + + + + + + + '; + + // Print the warnings. + $alternate = 0; + foreach ($context['previous_warnings'] as $warning) + { + $alternate = !$alternate; + echo ' + + + + + + '; + } + + if (empty($context['previous_warnings'])) + echo ' + + + '; + + echo ' + +
    ', $txt['profile_warning_previous_issued'], '', $txt['profile_warning_previous_time'], '', $txt['profile_warning_previous_reason'], '', $txt['profile_warning_previous_level'], '
    ', $warning['issuer']['link'], '', $warning['time'], ' +
    + ', $warning['reason'], ' +
    '; + + if (!empty($warning['id_notice'])) + echo ' +
    + +
    '; + echo ' +
    ', $warning['counter'], '
    + ', $txt['profile_warning_previous_none'], ' +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + + // Do our best to get pretty javascript enabled. + echo ' + '; +} + +// Template to show for deleting a users account - now with added delete post capability! +function template_deleteAccount() +{ + global $context, $settings, $options, $scripturl, $txt, $scripturl; + + // The main containing header. + echo ' +
    +
    +

    + ', $txt['deleteAccount'], ' +

    +
    '; + // If deleting another account give them a lovely info box. + if (!$context['user']['is_owner']) + echo ' +

    ', $txt['deleteAccount_desc'], '

    '; + echo ' +
    + +
    '; + + // If they are deleting their account AND the admin needs to approve it - give them another piece of info ;) + if ($context['needs_approval']) + echo ' +
    ', $txt['deleteAccount_approval'], '
    '; + + // If the user is deleting their own account warn them first - and require a password! + if ($context['user']['is_owner']) + { + echo ' +
    ', $txt['own_profile_confirm'], '
    +
    + ', $txt['current_password'], ': +      + + + + +
    '; + } + // Otherwise an admin doesn't need to enter a password - but they still get a warning - plus the option to delete lovely posts! + else + { + echo ' +
    ', $txt['deleteAccount_warning'], '
    '; + + // Only actually give these options if they are kind of important. + if ($context['can_delete_posts']) + echo ' +
    + ', $txt['deleteAccount_posts'], ': + +
    '; + + echo ' +
    + +
    +
    + + + + +
    '; + } + echo ' +
    + +
    +
    +
    '; +} + +// Template for the password box/save button stuck at the bottom of every profile page. +function template_profile_save() +{ + global $context, $settings, $options, $txt; + + echo ' + +
    '; + + // Only show the password box if it's actually needed. + if ($context['require_password']) + echo ' +
    +
    + ', $txt['current_password'], ':
    + ', $txt['required_security_reasons'], ' +
    +
    + +
    +
    '; + + echo ' +
    + + + + +
    '; +} + +// Small template for showing an error message upon a save problem in the profile. +function template_error_message() +{ + global $context, $txt; + + echo ' +
    + ', !empty($context['custom_error_title']) ? $context['custom_error_title'] : $txt['profile_errors_occurred'], ': +
      '; + + // Cycle through each error and display an error message. + foreach ($context['post_errors'] as $error) + echo ' +
    • ', isset($txt['profile_error_' . $error]) ? $txt['profile_error_' . $error] : $error, '.
    • '; + + echo ' +
    +
    '; +} + +// Display a load of drop down selectors for allowing the user to change group. +function template_profile_group_manage() +{ + global $context, $txt, $scripturl; + + echo ' +
    + ', $txt['primary_membergroup'], ':
    + (', $txt['moderator_why_missing'], ') +
    +
    + +
    +
    + ', $txt['additional_membergroups'], ': +
    +
    + + '; + // For each membergroup show a checkbox so members can be assigned to more than one group. + foreach ($context['member_groups'] as $member_group) + if ($member_group['can_be_additional']) + echo ' +
    '; + echo ' +
    + + +
    '; + +} + +// Callback function for entering a birthdate! +function template_profile_birthdate() +{ + global $txt, $context; + + // Just show the pretty box! + echo ' +
    + ', $txt['dob'], ':
    + ', $txt['dob_year'], ' - ', $txt['dob_month'], ' - ', $txt['dob_day'], ' +
    +
    + - + - + +
    '; +} + +// Show the signature editing box? +function template_profile_signature_modify() +{ + global $txt, $context, $settings; + + echo ' +
    + ', $txt['signature'], ':
    + ', $txt['sig_info'], '
    +
    '; + + if ($context['show_spellchecking']) + echo ' + '; + + echo ' +
    +
    +
    '; + + // If there is a limit at all! + if (!empty($context['signature_limits']['max_length'])) + echo ' + ', sprintf($txt['max_sig_characters'], $context['signature_limits']['max_length']), ' ', $context['signature_limits']['max_length'], '
    '; + + if ($context['signature_warning']) + echo ' + ', $context['signature_warning'], ''; + + // Load the spell checker? + if ($context['show_spellchecking']) + echo ' + '; + + // Some javascript used to count how many characters have been used so far in the signature. + echo ' + +
    '; +} + +function template_profile_avatar_select() +{ + global $context, $txt, $modSettings; + + // Start with the upper menu + echo ' +
    + ', $txt['personal_picture'], ' +
    + ', !empty($context['member']['avatar']['allow_server_stored']) ? '
    ' : '', ' + ', !empty($context['member']['avatar']['allow_external']) ? '
    ' : '', ' + ', !empty($context['member']['avatar']['allow_upload']) ? '' : '', ' +
    +
    '; + + // If users are allowed to choose avatars stored on the server show selection boxes to choice them from. + if (!empty($context['member']['avatar']['allow_server_stored'])) + { + echo ' +
    +
    + +
    +
    + +
    +
    Do Nothing
    + +
    '; + } + + // If the user can link to an off server avatar, show them a box to input the address. + if (!empty($context['member']['avatar']['allow_external'])) + { + echo ' +
    +
    ', $txt['avatar_by_url'], '
    + +
    '; + } + + // If the user is able to upload avatars to the server show them an upload box. + if (!empty($context['member']['avatar']['allow_upload'])) + { + echo ' +
    + + ', ($context['member']['avatar']['id_attach'] > 0 ? '

    ' : ''), ' +
    '; + } + + echo ' + +
    '; +} + +// Callback for modifying karam. +function template_profile_karma_modify() +{ + global $context, $modSettings, $txt; + + echo ' +
    + ', $modSettings['karmaLabel'], ' +
    +
    + ', $modSettings['karmaApplaudLabel'], ' ', $modSettings['karmaSmiteLabel'], '
    + (', $txt['total'], ': ', ($context['member']['karma']['good'] - $context['member']['karma']['bad']), ') +
    '; +} + +// Select the time format! +function template_profile_timeformat_modify() +{ + global $context, $modSettings, $txt, $scripturl, $settings; + + echo ' +
    + ', $txt['time_format'], ':
    + ', $txt['help'], ' +  ', $txt['date_format'], ' +
    +
    +
    + +
    '; +} + +// Time offset? +function template_profile_timeoffset_modify() +{ + global $txt, $context; + + echo ' +
    + ', $txt['time_offset'], ':
    + ', $txt['personal_time_offset'], ' +
    +
    + ', $txt['timeoffset_autodetect'], '
    ', $txt['current_time'], ': ', $context['current_forum_time'], ' +
    '; +} + +// Theme? +function template_profile_theme_pick() +{ + global $txt, $context, $scripturl; + + echo ' +
    + ', $txt['current_theme'], ': +
    +
    + ', $context['member']['theme']['name'], ' ', $txt['change'], ' +
    '; +} + +// Smiley set picker. +function template_profile_smiley_pick() +{ + global $txt, $context, $modSettings, $settings; + + echo ' +
    + ', $txt['smileys_current'], ': +
    +
    + :) +
    '; +} + +// Change the way you login to the forum. +function template_authentication_method() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // The main header! + echo ' + +
    +
    +

    + ', $txt['authentication'], ' +

    +
    +

    ', $txt['change_authentication'], '

    +
    + +
    +
    +
    +  (?)
    + +
    +
    +
    +
    + ', $txt['authenticate_openid_url'], ': +
    +
    + +
    +
    +
    +
    + ', $txt['choose_pass'], ': +
    +
    + + +
    +
    + ', $txt['verify_pass'], ': +
    +
    + + +
    +
    +
    +
    '; + + if ($context['require_password']) + echo ' +
    +
    +
    + ', $txt['current_password'], ':
    + ', $txt['required_security_reasons'], ' +
    +
    + +
    +
    '; + +echo ' +
    + + + + +
    +
    + +
    +
    '; + + // The password stuff. + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Recent.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Recent.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,446 @@ + +
    +

    + ',$txt['recent_posts'],' +

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + foreach ($context['posts'] as $post) + { + echo ' +
    + +
    +
    ', $post['counter'], '
    +
    +
    ', $post['board']['link'], ' / ', $post['link'], '
    + « ', $txt['last_post'], ' ', $txt['by'], ' ', $post['poster']['link'], ' ', $txt['on'], ' ', $post['time'], ' » +
    +
    ', $post['message'], '
    +
    '; + + if ($post['can_reply'] || $post['can_mark_notify'] || $post['can_delete']) + echo ' +
    +
      '; + + // If they *can* reply? + if ($post['can_reply']) + echo ' +
    • ', $txt['reply'], '
    • '; + + // If they *can* quote? + if ($post['can_quote']) + echo ' +
    • ', $txt['quote'], '
    • '; + + // Can we request notification of topics? + if ($post['can_mark_notify']) + echo ' +
    • ', $txt['notify'], '
    • '; + + // How about... even... remove it entirely?! + if ($post['can_delete']) + echo ' +
    • ', $txt['remove'], '
    • '; + + if ($post['can_reply'] || $post['can_mark_notify'] || $post['can_delete']) + echo ' +
    +
    '; + + echo ' + +
    '; + + } + + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    + '; +} + +function template_unread() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' +
    '; + + $showCheckboxes = !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $settings['show_mark_read']; + + if ($showCheckboxes) + echo ' +
    + + + '; + + if ($settings['show_mark_read']) + { + // Generate the button strip. + $mark_read = array( + 'markread' => array('text' => !empty($context['no_board_limits']) ? 'mark_as_read' : 'mark_read_short', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=' . (!empty($context['no_board_limits']) ? 'all' : 'board' . $context['querystring_board_limits']) . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + if ($showCheckboxes) + $mark_read['markselectread'] = array( + 'text' => 'quick_mod_markread', + 'image' => 'markselectedread.gif', + 'lang' => true, + 'url' => 'javascript:document.quickModForm.submit();', + ); + } + + if (!empty($context['topics'])) + { + echo ' +
    '; + + if (!empty($mark_read) && !empty($settings['use_tabs'])) + template_button_strip($mark_read, 'right'); + + echo ' + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + echo ' +
    + + + + + + '; + + // Show a "select all" box for quick moderation? + if ($showCheckboxes) + echo ' + + '; + else + echo ' + '; + echo ' + + + '; + + foreach ($context['topics'] as $topic) + { + // Calculate the color class of the topic. + $color_class = ''; + if (strpos($topic['class'], 'sticky') !== false) + $color_class = 'stickybg'; + if (strpos($topic['class'], 'locked') !== false) + $color_class .= 'lockedbg'; + + $color_class2 = !empty($color_class) ? $color_class . '2' : ''; + + echo ' + + + + + + '; + + if ($showCheckboxes) + echo ' + '; + echo ' + '; + } + + if (!empty($context['topics']) && !$context['showing_all_topics']) + $mark_read['readall'] = array('text' => 'unread_topics_all', 'image' => 'markreadall.gif', 'lang' => true, 'url' => $scripturl . '?action=unread;all' . $context['querystring_board_limits'], 'active' => true); + + if (empty($settings['use_tabs']) && !empty($mark_read)) + echo ' + + + '; + + if (empty($context['topics'])) + echo ' + '; + + echo ' + +
      + ', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', ' + + ', $txt['replies'], $context['sort_by'] == 'replies' ? ' ' : '', ' + + ', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', ' + + + + ', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', ' +
    + + + + +
    + ', $topic['is_sticky'] ? '' : '', '', $topic['first_post']['link'], '', $topic['is_sticky'] ? '' : '', ' + ', $txt['new'], ' +

    + ', $txt['started_by'], ' ', $topic['first_post']['member']['link'], ' + ', $txt['in'], ' ', $topic['board']['link'], ' + ', $topic['pages'], ' +

    +
    +
    + ', $topic['replies'], ' ', $txt['replies'], ' +
    + ', $topic['views'], ' ', $txt['views'], ' +
    + ', $txt['last_post'], ' + ', $topic['last_post']['time'], '
    + ', $txt['by'], ' ', $topic['last_post']['member']['link'], ' +
    + +
    + ', template_button_strip($mark_read, 'top'), ' +
    +
    +
    '; + + if (!empty($settings['use_tabs']) && !empty($mark_read)) + template_button_strip($mark_read, 'right'); + + echo ' + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + else + echo ' +
    +

    + ', $context['showing_all_topics'] ? $txt['msg_alert_none'] : $txt['unread_topics_visit_none'], ' +

    +
    '; + + if ($showCheckboxes) + echo ' +
    '; + + echo ' +
    +

    + ', !empty($modSettings['enableParticipation']) ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ', $txt['normal_topic'], '
    + ', sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']), '
    + ', sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']), ' +

    +

    + ', $txt['locked_topic'], '
    ', ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['sticky_topic'] . '
    ' : ''), ($modSettings['pollMode'] == '1' ? ' + ' . $txt['poll'] : ''), ' +

    +
    +
    '; +} + +function template_replies() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' +
    '; + + $showCheckboxes = !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $settings['show_mark_read']; + + if ($showCheckboxes) + echo ' +
    + + + '; + + if (isset($context['topics_to_mark']) && !empty($settings['show_mark_read'])) + { + // Generate the button strip. + $mark_read = array( + 'markread' => array('text' => 'mark_as_read', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=unreadreplies;topics=' . $context['topics_to_mark'] . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + if ($showCheckboxes) + $mark_read['markselectread'] = array( + 'text' => 'quick_mod_markread', + 'image' => 'markselectedread.gif', + 'lang' => true, + 'url' => 'javascript:document.quickModForm.submit();', + ); + } + + if (!empty($context['topics'])) + { + echo ' +
    '; + + if (!empty($mark_read) && !empty($settings['use_tabs'])) + template_button_strip($mark_read, 'right'); + + echo ' + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + echo ' +
    + + + + + + '; + + // Show a "select all" box for quick moderation? + if ($showCheckboxes) + echo ' + + '; + else + echo ' + '; + echo ' + + + '; + + foreach ($context['topics'] as $topic) + { + // Calculate the color class of the topic. + $color_class = ''; + if (strpos($topic['class'], 'sticky') !== false) + $color_class = 'stickybg'; + if (strpos($topic['class'], 'locked') !== false) + $color_class .= 'lockedbg'; + + $color_class2 = !empty($color_class) ? $color_class . '2' : ''; + + echo ' + + + + + + '; + + if ($showCheckboxes) + echo ' + '; + echo ' + '; + } + + if (empty($settings['use_tabs']) && !empty($mark_read)) + echo ' + + + '; + + echo ' + +
      + ', $txt['subject'], $context['sort_by'] === 'subject' ? ' ' : '', ' + + ', $txt['replies'], $context['sort_by'] === 'replies' ? ' ' : '', ' + + ', $txt['last_post'], $context['sort_by'] === 'last_post' ? ' ' : '', ' + + + + ', $txt['last_post'], $context['sort_by'] === 'last_post' ? ' ' : '', ' +
    + + + + +
    + ', $topic['is_sticky'] ? '' : '', '', $topic['first_post']['link'], '', $topic['is_sticky'] ? '' : '', ' + ', $txt['new'], ' +

    + ', $txt['started_by'], ' ', $topic['first_post']['member']['link'], ' + ', $txt['in'], ' ', $topic['board']['link'], ' + ', $topic['pages'], ' +

    +
    +
    + ', $topic['replies'], ' ', $txt['replies'], ' +
    + ', $topic['views'], ' ', $txt['views'], ' +
    + ', $txt['last_post'], ' + ', $topic['last_post']['time'], '
    + ', $txt['by'], ' ', $topic['last_post']['member']['link'], ' +
    + +
    + ', template_button_strip($mark_read, 'top'), ' +
    +
    +
    '; + + if (!empty($settings['use_tabs']) && !empty($mark_read)) + template_button_strip($mark_read, 'right'); + + echo ' + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + else + echo ' +
    +

    + ', $context['showing_all_topics'] ? $txt['msg_alert_none'] : $txt['unread_topics_visit_none'], ' +

    +
    '; + + if ($showCheckboxes) + echo ' +
    '; + + echo ' +
    +

    + ', !empty($modSettings['enableParticipation']) ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ', $txt['normal_topic'], '
    + ', sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']), '
    + ', sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']), ' +

    +

    + ', $txt['locked_topic'], '
    ', ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['sticky_topic'] . '
    ' : '') . ($modSettings['pollMode'] == '1' ? ' + ' . $txt['poll'] : '') . ' +

    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Register.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Register.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,715 @@ + +
    +

    ', $txt['registration_agreement'], '

    +
    + +
    +

    ', $context['agreement'], '

    +
    + +
    '; + + // Age restriction in effect? + if ($context['show_coppa']) + echo ' +

    + '; + else + echo ' + '; + + echo ' +
    + + '; + +} + +// Before registering - get their information. +function template_registration_form() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' + + '; + + // Any errors? + if (!empty($context['registration_errors'])) + { + echo ' +
    + ', $txt['registration_errors_occurred'], ' +
      '; + + // Cycle through each error and display an error message. + foreach ($context['registration_errors'] as $error) + echo ' +
    • ', $error, '
    • '; + + echo ' +
    +
    '; + } + + echo ' +
    +
    +

    ', $txt['registration_form'], '

    +
    +
    +

    ', $txt['required_info'], '

    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    + +
    +
    '; + + // If OpenID is enabled, give the user a choice between password and OpenID. + if (!empty($modSettings['enableOpenID'])) + { + echo ' +
    +
    + ', $txt['authenticate_label'], ': + (?) +
    +
    + + +
    +
    '; + } + + echo ' +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    '; + + // If OpenID is enabled, give the user a choice between password and OpenID. + if (!empty($modSettings['enableOpenID'])) + { + echo ' + +
    +
    ', $txt['authenticate_openid_url'], ':
    +
    + +
    +
    '; + + } + + echo ' +
    + +
    '; + + // If we have either of these, show the extra group. + if (!empty($context['profile_fields']) || !empty($context['custom_fields'])) + { + echo ' +
    +

    ', $txt['additional_information'], '

    +
    +
    + +
    +
    '; + } + + if (!empty($context['profile_fields'])) + { + // Any fields we particularly want? + foreach ($context['profile_fields'] as $key => $field) + { + if ($field['type'] == 'callback') + { + if (isset($field['callback_func']) && function_exists('template_profile_' . $field['callback_func'])) + { + $callback_func = 'template_profile_' . $field['callback_func']; + $callback_func(); + } + } + else + { + echo ' +
    + ', $field['label'], ':'; + + // Does it have any subtext to show? + if (!empty($field['subtext'])) + echo ' + ', $field['subtext'], ''; + + echo ' +
    +
    '; + + // Want to put something infront of the box? + if (!empty($field['preinput'])) + echo ' + ', $field['preinput']; + + // What type of data are we showing? + if ($field['type'] == 'label') + echo ' + ', $field['value']; + + // Maybe it's a text box - very likely! + elseif (in_array($field['type'], array('int', 'float', 'text', 'password'))) + echo ' + '; + + // You "checking" me out? ;) + elseif ($field['type'] == 'check') + echo ' + '; + + // Always fun - select boxes! + elseif ($field['type'] == 'select') + { + echo ' + '; + } + + // Something to end with? + if (!empty($field['postinput'])) + echo ' + ', $field['postinput']; + + echo ' +
    '; + } + } + } + + // Are there any custom fields? + if (!empty($context['custom_fields'])) + { + foreach ($context['custom_fields'] as $field) + echo ' +
    + ', $field['name'], ': + ', $field['desc'], ' +
    +
    ', $field['input_html'], '
    '; + } + + // If we have either of these, close the list like a proper gent. + if (!empty($context['profile_fields']) || !empty($context['custom_fields'])) + { + echo ' +
    +
    + +
    '; + } + + if ($context['visual_verification']) + { + echo ' +
    +

    ', $txt['verification'], '

    +
    +
    + +
    + ', template_control_verification($context['visual_verification_id'], 'all'), ' +
    + +
    '; + } + + echo ' +
    + +
    + +
    + '; +} + +// After registration... all done ;). +function template_after() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Not much to see here, just a quick... "you're now registered!" or what have you. + echo ' +
    +
    +

    ', $context['title'], '

    +
    +
    + +

    ', $context['description'], '

    + +
    +
    '; +} + +// Template for giving instructions about COPPA activation. +function template_coppa() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Formulate a nice complicated message! + echo ' +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +

    ', $context['coppa']['body'], '

    +

    + ', $txt['coppa_form_link_popup'], ' | ', $txt['coppa_form_link_download'], ' +

    +

    ', $context['coppa']['many_options'] ? $txt['coppa_send_to_two_options'] : $txt['coppa_send_to_one_option'], '

    '; + + // Can they send by post? + if (!empty($context['coppa']['post'])) + { + echo ' +

    1) ', $txt['coppa_send_by_post'], '

    +
    + ', $context['coppa']['post'], ' +
    '; + } + + // Can they send by fax?? + if (!empty($context['coppa']['fax'])) + { + echo ' +

    ', !empty($context['coppa']['post']) ? '2' : '1', ') ', $txt['coppa_send_by_fax'], '

    +
    + ', $context['coppa']['fax'], ' +
    '; + } + + // Offer an alternative Phone Number? + if ($context['coppa']['phone']) + { + echo ' +

    ', $context['coppa']['phone'], '

    '; + } + echo ' +
    + +
    '; +} + +// An easily printable form for giving permission to access the forum for a minor. +function template_coppa_form() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Show the form (As best we can) + echo ' + + + + + + + + + + +
    ', $context['forum_contacts'], '
    + ', $txt['coppa_form_address'], ': ', $context['ul'], '
    + ', $context['ul'], '
    + ', $context['ul'], '
    + ', $context['ul'], ' +
    + ', $txt['coppa_form_date'], ': ', $context['ul'], ' +

    +
    + ', $context['coppa_body'], ' +
    +
    '; +} + +// Show a window containing the spoken verification code. +function template_verification_sound() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' + + + + ', $context['page_title'], ' + + + + + + + +'; +} + +function template_admin_register() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +

    ', $txt['admin_browse_register_new'], '

    +
    +
    + + +
    '; + + if (!empty($context['registration_done'])) + echo ' +
    + ', $context['registration_done'], ' +
    '; + + echo ' +
    +
    + + ', $txt['admin_register_username_desc'], ' +
    +
    + +
    +
    + + ', $txt['admin_register_email_desc'], ' +
    +
    + +
    +
    + + ', $txt['admin_register_password_desc'], ' +
    +
    + +
    '; + + if (!empty($context['member_groups'])) + { + echo ' +
    + + ', $txt['admin_register_group_desc'], ' +
    +
    + +
    '; + } + + echo ' +
    + + ', $txt['admin_register_email_detail_desc'], ' +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + + +
    +
    + + +
    +
    +
    '; +} + +// Form for editing the agreement shown for people registering to the forum. +function template_edit_agreement() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Just a big box to edit the text file ;). + echo ' +
    +

    ', $txt['registration_agreement'], '

    +
    '; + + // Warning for if the file isn't writable. + if (!empty($context['warning'])) + echo ' +

    ', $context['warning'], '

    '; + + echo ' +
    + +
    '; + + // Is there more than one language to choose from? + if (count($context['editable_agreements']) > 1) + { + echo ' +
    +
    + ', $txt['admin_agreement_select_language'], ':  + +
    + + + +
    +
    +
    '; + } + + echo ' +
    '; + + // Show the actual agreement in an oversized text box. + echo ' +

    + +

    +

    + +

    +
    + + + + +
    +
    +
    + +
    +
    '; +} + +function template_edit_reserved_words() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +

    ', $txt['admin_reserved_set'], '

    +
    +
    + +
    +

    ', $txt['admin_reserved_line'], '

    +

    + +

    +
      +
    • +
    • +
    • +
    • +
    +
    + +
    +
    + + + +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Reminder.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Reminder.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,197 @@ + +
    + + +
    '; +} + +function template_reminder_pick() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    + + + +
    '; +} + +function template_sent() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    + '; +} + +function template_set_password() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' + +
    +
    + + + + +
    + '; +} + +function template_ask() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' + +
    +
    + + + +
    '; + + if ($context['account_type'] == 'password') + echo ' +'; + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Reports.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Reports.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,255 @@ + +
    +
    +

    ', $txt['generate_reports'], '

    +
    +
    + ', $txt['generate_reports_desc'], ' +
    +
    +

    ', $txt['generate_reports_type'], '

    +
    +
    + +
    +
    '; + + // Go through each type of report they can run. + foreach ($context['report_types'] as $type) + { + echo ' +
    + + +
    '; + if (isset($type['description'])) + echo ' +
    ', $type['description'], '
    '; + } + echo ' +
    +
    + + +
    +
    + +
    +
    + +
    '; +} + +// This is the standard template for showing reports in. +function template_main() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Build the reports button array. + $report_buttons = array( + 'generate_reports' => array('text' => 'generate_reports', 'image' => 'print.gif', 'lang' => true, 'url' => $scripturl . '?action=admin;area=reports', 'active' => true), + 'print' => array('text' => 'print', 'image' => 'print.gif', 'lang' => true, 'url' => $scripturl . '?action=admin;area=reports;rt=' . $context['report_type']. ';st=print', 'custom' => 'target="_blank"'), + ); + + echo ' +
    +
    +

    ', $txt['results'], '

    +
    +
    '; + + if (!empty($report_buttons) && !empty($settings['use_tabs'])) + template_button_strip($report_buttons, 'right'); + + echo ' +
    '; + + // Go through each table! + foreach ($context['tables'] as $table) + { + echo ' + '; + + if (!empty($table['title'])) + echo ' + + + + + + '; + + // Now do each row! + $row_number = 0; + $alternate = false; + foreach ($table['data'] as $row) + { + if ($row_number == 0 && !empty($table['shading']['top'])) + echo ' + '; + else + echo ' + '; + + // Now do each column. + $column_number = 0; + + foreach ($row as $key => $data) + { + // If this is a special separator, skip over! + if (!empty($data['separator']) && $column_number == 0) + { + echo ' + '; + break; + } + + // Shaded? + if ($column_number == 0 && !empty($table['shading']['left'])) + echo ' + '; + else + echo ' + '; + + $column_number++; + } + + echo ' + '; + + $row_number++; + $alternate = !$alternate; + } + echo ' + +
    ', $table['title'], '
    + ', $data['v'], ': + + ', $data['v'] == $table['default_value'] ? '' : ($data['v'] . (empty($data['v']) ? '' : ':')), ' + + ', $data['v'], ' +
    '; + } + echo ' +
    +
    '; +} + +// Header of the print page! +function template_print_above() +{ + global $context, $settings, $options, $txt; + + echo ' + + + + ', $context['page_title'], ' + + + '; +} + +function template_print() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Go through each table! + foreach ($context['tables'] as $table) + { + echo ' +
    + '; + + if (!empty($table['title'])) + echo ' + + + '; + + // Now do each row! + $alternate = false; + $row_number = 0; + foreach ($table['data'] as $row) + { + if ($row_number == 0 && !empty($table['shading']['top'])) + echo ' + '; + else + echo ' + '; + + // Now do each column!! + $column_number = 0; + foreach ($row as $key => $data) + { + // If this is a special separator, skip over! + if (!empty($data['separator']) && $column_number == 0) + { + echo ' + '; + break; + } + + // Shaded? + if ($column_number == 0 && !empty($table['shading']['left'])) + echo ' + '; + else + echo ' + '; + + $column_number++; + } + + echo ' + '; + + $row_number++; + $alternate = !$alternate; + } + echo ' +
    + ', $table['title'], ' +
    + ', $data['v'], ': + + ', $data['v'] == $table['default_value'] ? '' : ($data['v'] . (empty($data['v']) ? '' : ':')), ' + + ', $data['v'], ' +
    +

    '; + } +} + +// Footer of the print page. +function template_print_below() +{ + global $context, $settings, $options; + + echo ' + + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Search.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Search.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,519 @@ + +
    +

    + ', !empty($settings['use_buttons']) ? '' : ' ', $txt['set_parameters'], ' +

    +
    '; + + if (!empty($context['search_errors'])) + echo ' +

    ', implode('
    ', $context['search_errors']['messages']), '

    '; + + // Simple Search? + if ($context['simple_search']) + { + echo ' + '; + } + + // Advanced search! + else + { + echo ' + '; + + if (empty($context['search_params']['topic'])) + { + echo ' +
    + +
    + + +
    '; + + echo ' +
    + + + +
    +
    +
    + +
    '; + } + + } + + echo ' + + + '; +} + +function template_results() +{ + global $context, $settings, $options, $txt, $scripturl, $message; + + if (isset($context['did_you_mean']) || empty($context['topics'])) + { + echo ' +
    +
    +

    + ', $txt['search_adjust_query'], ' +

    +
    + +
    '; + + // Did they make any typos or mistakes, perhaps? + if (isset($context['did_you_mean'])) + echo ' +

    ', $txt['search_did_you_mean'], ' ', $context['did_you_mean'], '.

    '; + + echo ' +
    + ', $txt['search_for'], ': + + + + + + + + + '; + + if (!empty($context['search_params']['brd'])) + foreach ($context['search_params']['brd'] as $board_id) + echo ' + '; + + echo ' +
    +
    + +

    '; + } + + if ($context['compact']) + { + // Quick moderation set to checkboxes? Oh, how fun :/. + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1) + echo ' +
    '; + + echo ' +
    +

    + '; + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1) + echo ' + '; + echo ' + +  ', $txt['mlist_search_results'],': ',$context['search_params']['search'],' +

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + while ($topic = $context['get_topics']()) + { + $color_class = ''; + if ($topic['is_sticky']) + $color_class = 'stickybg'; + if ($topic['is_locked']) + $color_class .= 'lockedbg'; + + echo ' +
    +
    + +
    '; + + foreach ($topic['matches'] as $message) + { + echo ' +
    +
    ', $message['counter'], '
    +
    ', $topic['board']['link'], ' / ', $message['subject_highlighted'], '
    + « ',$txt['by'],' ', $message['member']['link'], ' ',$txt['on'],' ', $message['time'], ' » +
    '; + + if (!empty($options['display_quick_mod'])) + { + echo ' +
    '; + + if ($options['display_quick_mod'] == 1) + { + echo ' + '; + } + else + { + if ($topic['quick_mod']['remove']) + echo ' + ', $txt['remove_topic'], ''; + + if ($topic['quick_mod']['lock']) + echo ' + ', $txt['set_lock'], ''; + + if ($topic['quick_mod']['lock'] || $topic['quick_mod']['remove']) + echo ' +
    '; + + if ($topic['quick_mod']['sticky']) + echo ' + ', $txt['set_sticky'], ''; + + if ($topic['quick_mod']['move']) + echo ' + ', $txt['move_topic'], ''; + } + + echo ' +
    '; + } + + if ($message['body_highlighted'] != '') + echo ' +
    +
    ', $message['body_highlighted'], '
    '; + } + + echo ' +
    + +
    +
    '; + + } + if (!empty($context['topics'])) + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + { + echo ' +
    +
    + '; + + if ($context['can_move']) + { + echo ' + '; + } + + echo ' + + +
    +
    +
    '; + } + + + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + echo ' + +
    '; + + } + else + { + echo ' +
    +

    +  ', $txt['mlist_search_results'],': ',$context['search_params']['search'],' +

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + if (empty($context['topics'])) + echo ' +
    (', $txt['search_no_results'], ')
    '; + + while ($topic = $context['get_topics']()) + { + foreach ($topic['matches'] as $message) + { + echo ' +
    +
    + +
    +
    ', $message['counter'], '
    +
    +
    ', $topic['board']['link'], ' / ', $message['subject_highlighted'], '
    + « ', $txt['message'], ' ', $txt['by'], ' ', $message['member']['link'], ' ', $txt['on'], ' ', $message['time'], ' » +
    +
    ', $message['body_highlighted'], '
    '; + + if ($topic['can_reply'] || $topic['can_mark_notify']) + echo ' +
    +
      '; + + // If they *can* reply? + if ($topic['can_reply']) + echo ' +
    • ', $txt['reply'], '
    • '; + + // If they *can* quote? + if ($topic['can_quote']) + echo ' +
    • ', $txt['quote'], '
    • '; + + // Can we request notification of topics? + if ($topic['can_mark_notify']) + echo ' +
    • ', $txt['notify'], '
    • '; + + if ($topic['can_reply'] || $topic['can_mark_notify']) + echo ' +
    +
    '; + echo ' +
    +
    + +
    +
    '; + } + } + + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + + // Show a jump to box for easy navigation. + echo ' +
    +
     
    + '; + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/SendTopic.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/SendTopic.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,280 @@ + +
    +
    +

    + ', $context['page_title'], ' +

    +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    + +
    + +
    '; +} + +// Send an email to a user! +function template_custom_email() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $context['page_title'], ' +

    +
    +
    + +
    +
    +
    + ', $txt['sendtopic_receiver_name'], ': +
    +
    + ', $context['recipient']['link'], ' +
    '; + + // Can the user see the persons email? + if ($context['can_view_receipient_email']) + echo ' +
    + ', $txt['sendtopic_receiver_email'], ': +
    +
    + ', $context['recipient']['email_link'], ' +
    +
    +
    +
    '; + + // If it's a guest we need their details. + if ($context['user']['is_guest']) + echo ' +
    + +
    +
    + +
    +
    +
    + ', $txt['send_email_disclosed'], ' +
    +
    + + '; + // Otherwise show the user that we know their email. + else + echo ' +
    + ', $txt['sendtopic_sender_email'], ':
    + ', $txt['send_email_disclosed'], ' +
    +
    + ', $context['user']['email'], ' +
    '; + + echo ' +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    + +
    '; + + foreach ($context['form_hidden_vars'] as $key => $value) + echo ' + '; + + echo ' + +
    +
    +
    '; +} + +function template_report() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    + +
    +

    ', $txt['report_to_mod'], '

    +
    +
    + +
    '; + + if (!empty($context['post_errors'])) + { + echo ' +
    +
      '; + + foreach ($context['post_errors'] as $error) + echo ' +
    • ', $error, '
    • '; + + echo ' +
    +
    '; + } + + echo ' +

    ', $txt['report_to_mod_func'], '

    +
    +
    '; + + if ($context['user']['is_guest']) + { + echo ' +
    + : +
    +
    + +
    '; + } + + echo ' +
    + : +
    +
    + +
    '; + + if ($context['require_verification']) + { + echo ' +
    + ', $txt['verification'], ': +
    +
    + ', template_control_verification($context['visual_verification_id'], 'all'), ' +
    '; + } + + echo ' +
    +
    + +
    +
    + +
    + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Settings.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Settings.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,278 @@ + 'show_board_desc', + 'label' => $txt['board_desc_inside'], + 'default' => true, + ), + array( + 'id' => 'show_children', + 'label' => $txt['show_children'], + 'default' => true, + ), + array( + 'id' => 'use_sidebar_menu', + 'label' => $txt['use_sidebar_menu'], + 'default' => true, + ), + array( + 'id' => 'show_no_avatars', + 'label' => $txt['show_no_avatars'], + 'default' => true, + ), + array( + 'id' => 'show_no_signatures', + 'label' => $txt['show_no_signatures'], + 'default' => true, + ), + array( + 'id' => 'show_no_censored', + 'label' => $txt['show_no_censored'], + 'default' => true, + ), + array( + 'id' => 'return_to_post', + 'label' => $txt['return_to_post'], + 'default' => true, + ), + array( + 'id' => 'no_new_reply_warning', + 'label' => $txt['no_new_reply_warning'], + 'default' => true, + ), + array( + 'id' => 'view_newest_first', + 'label' => $txt['recent_posts_at_top'], + 'default' => true, + ), + array( + 'id' => 'view_newest_pm_first', + 'label' => $txt['recent_pms_at_top'], + 'default' => true, + ), + array( + 'id' => 'posts_apply_ignore_list', + 'label' => $txt['posts_apply_ignore_list'], + 'default' => false, + ), + array( + 'id' => 'wysiwyg_default', + 'label' => $txt['wysiwyg_default'], + 'default' => false, + ), + array( + 'id' => 'popup_messages', + 'label' => $txt['popup_messages'], + 'default' => true, + ), + array( + 'id' => 'copy_to_outbox', + 'label' => $txt['copy_to_outbox'], + 'default' => true, + ), + array( + 'id' => 'pm_remove_inbox_label', + 'label' => $txt['pm_remove_inbox_label'], + 'default' => true, + ), + array( + 'id' => 'auto_notify', + 'label' => $txt['auto_notify'], + 'default' => true, + ), + array( + 'id' => 'topics_per_page', + 'label' => $txt['topics_per_page'], + 'options' => array( + 0 => $txt['per_page_default'], + 5 => 5, + 10 => 10, + 25 => 25, + 50 => 50, + ), + 'default' => true, + ), + array( + 'id' => 'messages_per_page', + 'label' => $txt['messages_per_page'], + 'options' => array( + 0 => $txt['per_page_default'], + 5 => 5, + 10 => 10, + 25 => 25, + 50 => 50, + ), + 'default' => true, + ), + array( + 'id' => 'calendar_start_day', + 'label' => $txt['calendar_start_day'], + 'options' => array( + 0 => $txt['days'][0], + 1 => $txt['days'][1], + 6 => $txt['days'][6], + ), + 'default' => true, + ), + array( + 'id' => 'display_quick_reply', + 'label' => $txt['display_quick_reply'], + 'options' => array( + 0 => $txt['display_quick_reply1'], + 1 => $txt['display_quick_reply2'], + 2 => $txt['display_quick_reply3'] + ), + 'default' => true, + ), + array( + 'id' => 'display_quick_mod', + 'label' => $txt['display_quick_mod'], + 'options' => array( + 0 => $txt['display_quick_mod_none'], + 1 => $txt['display_quick_mod_check'], + 2 => $txt['display_quick_mod_image'], + ), + 'default' => true, + ), + ); +} + +function template_settings() +{ + global $context, $settings, $options, $scripturl, $txt; + + $context['theme_settings'] = array( + array( + 'id' => 'header_logo_url', + 'label' => $txt['header_logo_url'], + 'description' => $txt['header_logo_url_desc'], + 'type' => 'text', + ), + array( + 'id' => 'site_slogan', + 'label' => $txt['site_slogan'], + 'description' => $txt['site_slogan_desc'], + 'type' => 'text', + ), + array( + 'id' => 'smiley_sets_default', + 'label' => $txt['smileys_default_set_for_theme'], + 'options' => $context['smiley_sets'], + 'type' => 'text', + ), + array( + 'id' => 'forum_width', + 'label' => $txt['forum_width'], + 'description' => $txt['forum_width_desc'], + 'type' => 'text', + 'size' => 8, + ), + '', + array( + 'id' => 'linktree_link', + 'label' => $txt['current_pos_text_img'], + ), + array( + 'id' => 'show_mark_read', + 'label' => $txt['enable_mark_as_read'], + ), + array( + 'id' => 'allow_no_censored', + 'label' => $txt['allow_no_censored'], + ), + array( + 'id' => 'enable_news', + 'label' => $txt['enable_random_news'], + ), + '', + array( + 'id' => 'show_newsfader', + 'label' => $txt['news_fader'], + ), + array( + 'id' => 'newsfader_time', + 'label' => $txt['admin_fader_delay'], + 'type' => 'number', + ), + array( + 'id' => 'number_recent_posts', + 'label' => $txt['number_recent_posts'], + 'description' => $txt['number_recent_posts_desc'], + 'type' => 'number', + ), + array( + 'id' => 'show_stats_index', + 'label' => $txt['show_stats_index'], + ), + array( + 'id' => 'show_latest_member', + 'label' => $txt['latest_members'], + ), + array( + 'id' => 'show_group_key', + 'label' => $txt['show_group_key'], + ), + array( + 'id' => 'display_who_viewing', + 'label' => $txt['who_display_viewing'], + 'options' => array( + 0 => $txt['who_display_viewing_off'], + 1 => $txt['who_display_viewing_numbers'], + 2 => $txt['who_display_viewing_names'], + ), + 'type' => 'number', + ), + '', + array( + 'id' => 'show_modify', + 'label' => $txt['last_modification'], + ), + array( + 'id' => 'show_profile_buttons', + 'label' => $txt['show_view_profile_button'], + ), + array( + 'id' => 'show_user_images', + 'label' => $txt['user_avatars'], + ), + array( + 'id' => 'show_blurb', + 'label' => $txt['user_text'], + ), + array( + 'id' => 'show_gender', + 'label' => $txt['gender_images'], + ), + array( + 'id' => 'hide_post_group', + 'label' => $txt['hide_post_group'], + 'description' => $txt['hide_post_group_desc'], + ), + '', + array( + 'id' => 'show_bbc', + 'label' => $txt['admin_bbc'], + ), + array( + 'id' => 'additional_options_collapsable', + 'label' => $txt['additional_options_collapsable'], + ), + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/SplitTopics.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/SplitTopics.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,486 @@ + +
    + +
    +

    ', $txt['split'], '

    +
    +
    + +
    +

    + : + +

    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    + +
    + +
    + '; +} + +function template_main() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['split'], '

    +
    +
    + +
    +

    ', $txt['split_successful'], '

    + +
    + +
    +
    '; +} + +function template_select() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +
    +

    ', $txt['split'], ' - ', $txt['select_split_posts'], '

    +
    +
    + ', $txt['please_select_split'], ' +
    +
    + ', $txt['pages'], ': ', $context['not_selected']['page_index'], ' +
    +
      '; + + foreach ($context['not_selected']['messages'] as $message) + echo ' +
    • + +
      +
      + -> + ', $message['subject'], ' ', $txt['by'], ' ', $message['poster'], '
      + ', $message['time'], ' +
      +
      ', $message['body'], '
      +
      + +
    • '; + + echo ' +
    • +
    +
    +
    +
    +

    + ', $txt['split_selected_posts'], ' (', $txt['split_reset_selection'], ') +

    +
    +
    + ', $txt['split_selected_posts_desc'], ' +
    +
    + ', $txt['pages'], ': ', $context['selected']['page_index'], ' +
    +
      '; + + if (!empty($context['selected']['messages'])) + foreach ($context['selected']['messages'] as $message) + echo ' +
    • + +
      +
      + <- + ', $message['subject'], ' ', $txt['by'], ' ', $message['poster'], '
      + ', $message['time'], ' +
      +
      ', $message['body'], '
      +
      + +
    • '; + + echo ' +
    • +
    +
    +
    +

    + + + + +

    +
    +
    +
    + '; +} + +function template_merge_done() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['merge'], '

    +
    +
    + +
    +

    ', $txt['merge_successful'], '

    +
    + +
    + +
    +
    +
    '; +} + +function template_merge() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['merge'], '

    +
    +
    + ', $txt['merge_desc'], ' +
    +
    + +
    +
    +
    + ', $txt['topic_to_merge'], ': +
    +
    + ', $context['origin_subject'], ' +
    '; + + if (!empty($context['boards']) && count($context['boards']) > 1) + { + echo ' +
    + ', $txt['target_board'], ': +
    +
    +
    + + + +
    +
    '; + } + + echo ' +
    +
    +
    +
    + ', $txt['merge_to_topic_id'], ': +
    +
    +
    + + + + +
    +
    '; + + echo ' +
    +
    + +

    +
    +

    ', $txt['target_topic'], '

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    +
    + +
    +
      '; + + $merge_button = create_button('merge.gif', 'merge', ''); + + foreach ($context['topics'] as $topic) + echo ' +
    • + ', $merge_button, '  + ', $topic['subject'], ' ', $txt['started_by'], ' ', $topic['poster']['link'], ' +
    • '; + + echo ' +
    +
    + +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    +
    +
    '; +} + +function template_merge_extra_options() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    ', $txt['merge_topic_list'], '

    +
    + + + + + + + + + + + '; + foreach ($context['topics'] as $topic) + echo ' + + + + + + + '; + echo ' + +
    ', $txt['merge_check'], '', $txt['subject'], '', $txt['started_by'], '', $txt['last_post'], '' . $txt['merge_include_notifications'] . '
    + + + ' . $topic['subject'] . ' + + ', $topic['started']['link'], '
    + ', $topic['started']['time'], ' +
    + ' . $topic['updated']['link'] . '
    + ', $topic['updated']['time'], ' +
    + +
    +
    +
    + +
    '; + + echo ' +
    + ', $txt['merge_select_subject'], ' + +
    +
    + +
    '; + + if (!empty($context['boards']) && count($context['boards']) > 1) + { + echo ' +
    + ', $txt['merge_select_target_board'], ' +
      '; + foreach ($context['boards'] as $board) + echo ' +
    • + ' . $board['name'] . ' +
    • '; + echo ' +
    +
    '; + } + if (!empty($context['polls'])) + { + echo ' +
    + ' . $txt['merge_select_poll'] . ' +
      '; + foreach ($context['polls'] as $poll) + echo ' +
    • + ' . $poll['question'] . ' (' . $txt['topic'] . ': ' . $poll['topic']['subject'] . ') +
    • '; + echo ' +
    • + (' . $txt['merge_no_poll'] . ') +
    • +
    +
    '; + } + echo ' + + +
    +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Stats.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Stats.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,473 @@ + +
    +

    ', $context['page_title'], '

    +
    +
    +

    + + ', $txt['general_stats'], ' + +

    +
    +
    +
    +
    + +
    +
    +
    ', $txt['total_members'], ':
    +
    ', $context['show_member_list'] ? '' . $context['num_members'] . '' : $context['num_members'], '
    +
    ', $txt['total_posts'], ':
    +
    ', $context['num_posts'], '
    +
    ', $txt['total_topics'], ':
    +
    ', $context['num_topics'], '
    +
    ', $txt['total_cats'], ':
    +
    ', $context['num_categories'], '
    +
    ', $txt['users_online'], ':
    +
    ', $context['users_online'], '
    +
    ', $txt['most_online'], ':
    +
    ', $context['most_members_online']['number'], ' - ', $context['most_members_online']['date'], '
    +
    ', $txt['users_online_today'], ':
    +
    ', $context['online_today'], '
    '; + + if (!empty($modSettings['hitStats'])) + echo ' +
    ', $txt['num_hits'], ':
    +
    ', $context['num_hits'], '
    '; + + echo ' +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    ', $txt['average_members'], ':
    +
    ', $context['average_members'], '
    +
    ', $txt['average_posts'], ':
    +
    ', $context['average_posts'], '
    +
    ', $txt['average_topics'], ':
    +
    ', $context['average_topics'], '
    +
    ', $txt['total_boards'], ':
    +
    ', $context['num_boards'], '
    +
    ', $txt['latest_member'], ':
    +
    ', $context['common_stats']['latest_member']['link'], '
    +
    ', $txt['average_online'], ':
    +
    ', $context['average_online'], '
    +
    ', $txt['gender_ratio'], ':
    +
    ', $context['gender']['ratio'], '
    '; + + if (!empty($modSettings['hitStats'])) + echo ' +
    ', $txt['average_hits'], ':
    +
    ', $context['average_hits'], '
    '; + + echo ' +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + + ', $txt['top_posters'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_posters'] as $poster) + { + echo ' +
    + ', $poster['link'], ' +
    +
    '; + + if (!empty($poster['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ', $poster['num_posts'], ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +

    + + ', $txt['top_boards'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_boards'] as $board) + { + echo ' +
    + ', $board['link'], ' +
    +
    '; + + if (!empty($board['post_percent'])) + echo ' +
    +
    +
    '; + echo ' + ', $board['num_posts'], ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + + ', $txt['top_topics_replies'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_topics_replies'] as $topic) + { + echo ' +
    + ', $topic['link'], ' +
    +
    '; + if (!empty($topic['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ' . $topic['num_replies'] . ' +
    '; + } + echo ' +
    +
    +
    + +
    +
    + +
    +
    +

    + + ', $txt['top_topics_views'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_topics_views'] as $topic) + { + echo ' +
    ', $topic['link'], '
    +
    '; + + if (!empty($topic['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ' . $topic['num_views'] . ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + + ', $txt['top_starters'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_starters'] as $poster) + { + echo ' +
    + ', $poster['link'], ' +
    +
    '; + + if (!empty($poster['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ', $poster['num_topics'], ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +

    + + ', $txt['most_time_online'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_time_online'] as $poster) + { + echo ' +
    + ', $poster['link'], ' +
    +
    '; + + if (!empty($poster['time_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ', $poster['time_online'], ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + + ', $txt['forum_history'], ' + +

    +
    '; + + if (!empty($context['yearly'])) + { + echo ' + + + + + + + + ', $txt['smf_stats_14'], ''; + + if (!empty($modSettings['hitStats'])) + echo ' + '; + + echo ' + + + '; + + foreach ($context['yearly'] as $id => $year) + { + echo ' + + + + + + '; + + if (!empty($modSettings['hitStats'])) + echo ' + '; + + echo ' + '; + + foreach ($year['months'] as $month) + { + echo ' + + + + + + '; + + if (!empty($modSettings['hitStats'])) + echo ' + '; + + echo ' + '; + + if ($month['expanded']) + { + foreach ($month['days'] as $day) + { + echo ' + + + + + + '; + + if (!empty($modSettings['hitStats'])) + echo ' + '; + + echo ' + '; + } + } + } + } + + echo ' + +
    ', $txt['yearly_summary'], '', $txt['stats_new_topics'], '', $txt['stats_new_posts'], '', $txt['stats_new_members'], '', $txt['page_views'], '
    + * ', $year['year'], ' + ', $year['new_topics'], '', $year['new_posts'], '', $year['new_members'], '', $year['most_members_online'], '', $year['hits'], '
    + ', $month['month'], ' ', $month['year'], ' + ', $month['new_topics'], '', $month['new_posts'], '', $month['new_members'], '', $month['most_members_online'], '', $month['hits'], '
    ', $day['year'], '-', $day['month'], '-', $day['day'], '', $day['new_topics'], '', $day['new_posts'], '', $day['new_members'], '', $day['most_members_online'], '', $day['hits'], '
    +
    + + + '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Themes.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Themes.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1180 @@ + +
    + +
    +

    + ', $txt['help'], ' + ', $txt['themeadmin_title'], ' + +

    +
    +
    + ', $txt['themeadmin_explain'], ' +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + : +
    +
    +
    '; + foreach ($context['themes'] as $theme) + echo ' +
    '; + + echo ' +
    + + +
    +
    + +
    +
    + + ', $txt['theme_select'], ' +
    +
    + : +
    +
    + + ', $txt['theme_select'], ' +
    +
    +
    + +
    +
    + +
    + +
    '; + + // Link to simplemachines.org for latest themes and info! + echo ' +
    +
    +

    + ', $txt['help'], ' ', $txt['theme_latest'], ' +

    +
    +
    + +
    +
    + ', $txt['theme_latest_fetch'], ' +
    +
    + +
    +
    '; + + // Warn them if theme creation isn't possible! + if (!$context['can_create_new']) + echo ' +
    ', $txt['theme_install_writable'], '
    '; + + echo ' +
    +
    +

    + ', $txt['help'], ' ', $txt['theme_install'], ' +

    +
    +
    + +
    +
    '; + + // Here's a little box for installing a new theme. + // !!! Should the value="theme_gz" be there?! + if ($context['can_create_new']) + echo ' +
    + : +
    +
    + +
    '; + + echo ' +
    + : +
    +
    + +
    '; + + if ($context['can_create_new']) + echo ' +
    + +
    +
    + +
    '; + + echo ' +
    +
    + +
    +
    + +
    + +
    + +
    + + '; + + if (empty($modSettings['disable_smf_js'])) + echo ' + '; + + echo ' + '; + + // Gotta love IE4, and its hatefulness... + if ($context['browser']['is_ie4']) + echo ' + '; + else + echo ' + '; +} + +function template_list_themes() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['themeadmin_list_heading'], '

    +
    +
    + ', $txt['themeadmin_list_tip'], ' +
    '; + + // Show each theme.... with X for delete and a link to settings. + foreach ($context['themes'] as $theme) + { + echo ' +
    +

    + ', $theme['name'], '', !empty($theme['version']) ? ' (' . $theme['version'] . ')' : '', ''; + + // You *cannot* delete the default theme. It's important! + if ($theme['id'] != 1) + echo ' + ', $txt['theme_remove'], ''; + + echo ' +

    +
    +
    + +
    +
    +
    ', $txt['themeadmin_list_theme_dir'], ':
    + ', $theme['theme_dir'], $theme['valid_path'] ? '' : ' ' . $txt['themeadmin_list_invalid'], ' +
    ', $txt['themeadmin_list_theme_url'], ':
    +
    ', $theme['theme_url'], '
    +
    ', $txt['themeadmin_list_images_url'], ':
    +
    ', $theme['images_url'], '
    +
    +
    + +
    '; + } + + echo ' + +
    +
    +

    ', $txt['themeadmin_list_reset'], '

    +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + + +
    + +
    + +
    +
    +
    '; +} + +function template_reset_list() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['themeadmin_reset_title'], '

    +
    +
    + ', $txt['themeadmin_reset_tip'], ' +
    '; + + // Show each theme.... with X for delete and a link to settings. + $alternate = false; + + foreach ($context['themes'] as $theme) + { + $alternate = !$alternate; + + echo ' +
    +

    ', $theme['name'], '

    +
    +
    + +
    + +
    + +
    '; + } + + echo ' +
    +
    '; +} + +function template_set_options() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    + +
    +

    ', $txt['theme_options_title'], ' - ', $context['theme_settings']['name'], '

    +
    +
    + ', $context['theme_options_reset'] ? $txt['themeadmin_reset_options_info'] : $txt['theme_options_defaults'], ' +
    +
    + +
    +
      '; + + foreach ($context['options'] as $setting) + { + echo ' +
    • '; + + if ($context['theme_options_reset']) + echo ' + '; + + if ($setting['type'] == 'checkbox') + { + echo ' + + '; + } + elseif ($setting['type'] == 'list') + { + echo ' +   + '; + } + else + echo ' +   + '; + + if (isset($setting['description'])) + echo ' +
      ', $setting['description'], ''; + + echo ' +
    • '; + } + + echo ' +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +function template_set_settings() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +
    +

    + ', $txt['help'], ' ', $txt['theme_settings'], ' - ', $context['theme_settings']['name'], ' +

    +
    '; + + // !!! Why can't I edit the default theme popup. + if ($context['theme_settings']['theme_id'] != 1) + echo ' +
    +

    + ', $txt['theme_edit'], ' +

    +
    + '; + + echo ' +
    +

    + ', $txt['theme_url_config'], ' +

    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    '; + + // Do we allow theme variants? + if (!empty($context['theme_variants'])) + { + echo ' +
    +

    + ', $txt['theme_variants'], ' +

    +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + + +
    +
    + +
    + +
    '; + } + + echo ' +
    +

    + ', $txt['theme_options'], ' +

    +
    +
    + +
    +
    '; + + foreach ($context['settings'] as $setting) + { + // Is this a separator? + if (empty($setting)) + { + echo ' +
    +
    +
    '; + } + // A checkbox? + elseif ($setting['type'] == 'checkbox') + { + echo ' +
    + :'; + + if (isset($setting['description'])) + echo '
    + ', $setting['description'], ''; + + echo ' +
    +
    + + +
    '; + } + // A list with options? + elseif ($setting['type'] == 'list') + { + echo ' +
    + :'; + + if (isset($setting['description'])) + echo '
    + ', $setting['description'], ''; + + echo ' +
    +
    + +
    '; + } + // A regular input box, then? + else + { + echo ' +
    + :'; + + if (isset($setting['description'])) + echo '
    + ', $setting['description'], ''; + + echo ' +
    +
    + +
    '; + } + } + + echo ' +
    +
    + +
    +
    + +
    + +
    +
    +
    '; + + if (!empty($context['theme_variants'])) + { + echo ' + '; + } +} + +// This template allows for the selection of different themes ;). +function template_pick() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    '; + + // Just go through each theme and show its information - thumbnail, etc. + foreach ($context['available_themes'] as $theme) + { + echo ' + +
    + +
    +
    +

    ', $theme['description'], '

    '; + + if (!empty($theme['variants'])) + { + echo ' + : + + '; + } + + echo ' +
    +

    + ', $theme['num_users'], ' ', ($theme['num_users'] == 1 ? $txt['theme_user'] : $txt['theme_users']), ' +

    +
    + +
    + +
    '; + + if (!empty($theme['variants'])) + { + echo ' + '; + } + } + + echo ' +
    +
    +
    '; +} + +// Okay, that theme was installed successfully! +function template_installed() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Not much to show except a link back... + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +

    + ', $context['installed_theme']['name'], ' ', $txt['theme_installed_message'], ' +

    +

    + ', $txt['back'], ' +

    +
    + +
    +
    +
    '; +} + +function template_edit_list() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['themeadmin_edit_title'], '

    +
    '; + + $alternate = false; + + foreach ($context['themes'] as $theme) + { + $alternate = !$alternate; + + echo ' +
    +

    + ', $theme['name'], '', !empty($theme['version']) ? ' + (' . $theme['version'] . ')' : '', ' +

    +
    + '; + } + + echo ' +
    +
    '; +} + +function template_copy_template() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['themeadmin_edit_filename'], '

    +
    +
    + ', $txt['themeadmin_edit_copy_warning'], ' +
    +
    + +
    +
      '; + + $alternate = false; + foreach ($context['available_templates'] as $template) + { + $alternate = !$alternate; + + echo ' +
    • + ', $template['filename'], $template['already_exists'] ? ' (' . $txt['themeadmin_edit_exists'] . ')' : '', ' + '; + + if ($template['can_copy']) + echo '', $txt['themeadmin_edit_do_copy'], ''; + else + echo $txt['themeadmin_edit_no_copy']; + + echo ' + +
    • '; + } + + echo ' +
    +
    + +
    +
    +
    '; +} + +function template_edit_browse() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    + + + + + + + + + '; + + $alternate = false; + + foreach ($context['theme_files'] as $file) + { + $alternate = !$alternate; + + echo ' + + + + + '; + } + + echo ' + +
    ', $txt['themeadmin_edit_filename'], '', $txt['themeadmin_edit_modified'], '', $txt['themeadmin_edit_size'], '
    '; + + if ($file['is_editable']) + echo '', $file['filename'], ''; + + elseif ($file['is_directory']) + echo '', $file['filename'], ''; + + else + echo $file['filename']; + + echo ' + ', !empty($file['last_modified']) ? $file['last_modified'] : '', '', $file['size'], '
    +
    +
    '; +} + +// Wanna edit the stylesheet? +function template_edit_style() +{ + global $context, $settings, $options, $scripturl, $txt; + + if ($context['session_error']) + echo ' +
    + ', $txt['error_session_timeout'], ' +
    '; + + // From now on no one can complain that editing css is difficult. If you disagree, go to www.w3schools.com. + echo ' +
    + + '; + + // Just show a big box.... gray out the Save button if it's not saveable... (ie. not 777.) + echo ' +
    +
    +

    ', $txt['theme_edit'], ' - ', $context['edit_filename'], '

    +
    +
    + +
    '; + + if (!$context['allow_save']) + echo ' + ', $txt['theme_edit_no_save'], ': ', $context['allow_save_filename'], '
    '; + + echo ' +
    +
    + + +
    +
    + +
    + + +
    +
    +
    '; +} + +// This edits the template... +function template_edit_template() +{ + global $context, $settings, $options, $scripturl, $txt; + + if ($context['session_error']) + echo ' +
    + ', $txt['error_session_timeout'], ' +
    '; + + if (isset($context['parse_error'])) + echo ' +
    + ', $txt['themeadmin_edit_error'], ' +
    ', $context['parse_error'], '
    +
    '; + + // Just show a big box.... gray out the Save button if it's not saveable... (ie. not 777.) + echo ' +
    +
    +
    +

    ', $txt['theme_edit'], ' - ', $context['edit_filename'], '

    +
    +
    + +
    '; + + if (!$context['allow_save']) + echo ' + ', $txt['theme_edit_no_save'], ': ', $context['allow_save_filename'], '
    '; + + foreach ($context['file_parts'] as $part) + echo ' + :
    +
    + +
    '; + + echo ' +
    + + + +
    +
    + +
    +
    +
    '; +} + +function template_edit_file() +{ + global $context, $settings, $options, $scripturl, $txt; + + if ($context['session_error']) + echo ' +
    + ', $txt['error_session_timeout'], ' +
    '; + + //Is this file writeable? + if (!$context['allow_save']) + echo ' +
    + ', $txt['theme_edit_no_save'], ': ', $context['allow_save_filename'], ' +
    '; + + // Just show a big box.... gray out the Save button if it's not saveable... (ie. not 777.) + echo ' +
    +
    +
    +

    ', $txt['theme_edit'], ' - ', $context['edit_filename'], '

    +
    +
    + +
    +
    + + + +
    + +
    + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Who.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Who.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,227 @@ + +
    +
    +

    ', $txt['who_title'], '

    +
    +
    +
    + '; + echo ' +
    ', $txt['who_show1'], ' + + +
    +
    + + + + + + + + + '; + + // For every member display their name, time and action (and more for admin). + $alternate = 0; + + foreach ($context['members'] as $member) + { + // $alternate will either be true or false. If it's true, use "windowbg2" and otherwise use "windowbg". + echo ' + + + + + '; + + // Switch alternate to whatever it wasn't this time. (true -> false -> true -> false, etc.) + $alternate = !$alternate; + } + + // No members? + if (empty($context['members'])) + { + echo ' + + + '; + } + + echo ' + +
    ', $txt['who_user'], ' ', $context['sort_by'] == 'user' ? '' : '', '', $txt['who_time'], ' ', $context['sort_by'] == 'time' ? '' : '', '', $txt['who_action'], '
    '; + + // Guests don't have information like icq, msn, y!, and aim... and they can't be messaged. + if (!$member['is_guest']) + { + echo ' + + ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $member['online']['text'] . '' : $member['online']['text'], $context['can_send_pm'] ? '' : '', ' + ', isset($context['disabled_fields']['icq']) ? '' : $member['icq']['link'] , ' ', isset($context['disabled_fields']['msn']) ? '' : $member['msn']['link'], ' ', isset($context['disabled_fields']['yim']) ? '' : $member['yim']['link'], ' ', isset($context['disabled_fields']['aim']) ? '' : $member['aim']['link'], ' + '; + } + + echo ' + + ', $member['is_guest'] ? $member['name'] : '' . $member['name'] . '', ' + '; + + if (!empty($member['ip'])) + echo ' + (' . $member['ip'] . ')'; + + echo ' + ', $member['time'], '', $member['action'], '
    + ', $txt['who_no_online_' . ($context['show_by'] == 'guests' || $context['show_by'] == 'spiders' ? $context['show_by'] : 'members')], ' +
    +
    +
    + '; + + echo ' +
    ', $txt['who_show1'], ' + + +
    +
    +
    + '; +} + +function template_credits() +{ + global $context, $txt; + + // The most important part - the credits :P. + echo ' +
    +
    +

    ', $txt['credits'], '

    +
    '; + + foreach ($context['credits'] as $section) + { + if (isset($section['pretext'])) + echo ' +
    + +
    +

    ', $section['pretext'], '

    +
    + +
    '; + + if (isset($section['title'])) + echo ' +
    +

    ', $section['title'], '

    +
    '; + + echo ' +
    + +
    +
    '; + + foreach ($section['groups'] as $group) + { + if (isset($group['title'])) + echo ' +
    + ', $group['title'], ' +
    +
    '; + + // Try to make this read nicely. + if (count($group['members']) <= 2) + echo implode(' ' . $txt['credits_and'] . ' ', $group['members']); + else + { + $last_peep = array_pop($group['members']); + echo implode(', ', $group['members']), ' ', $txt['credits_and'], ' ', $last_peep; + } + + echo ' +
    '; + } + + echo ' +
    '; + + if (isset($section['posttext'])) + echo ' +

    ', $section['posttext'], '

    '; + + echo ' +
    + +
    '; + } + + echo ' +
    +

    ', $txt['credits_copyright'], '

    +
    +
    + +
    +
    +
    ', $txt['credits_forum'], '
    ', ' +
    ', $context['copyrights']['smf']; + + echo ' +
    +
    '; + + if (!empty($context['copyrights']['mods'])) + { + echo ' +
    +
    ', $txt['credits_modifications'], '
    +
    ', implode('
    ', $context['copyrights']['mods']), '
    +
    '; + } + + echo ' +
    + +
    +
    '; +} +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Wireless.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Wireless.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1521 @@ + + + + +'; +} + +// This is the board index (main page) in WAP 1.1. +function template_wap_boardindex() +{ + global $context, $settings, $options, $scripturl; + + // This is the "main" card... + echo ' + +

    ', $context['forum_name_html_safe'], '

    '; + + // Show an anchor for each category. + foreach ($context['categories'] as $category) + { + // Skip it if it's empty. + if (!empty($category['boards'])) + echo ' +

    ', $category['name'], '

    '; + } + + // Okay, that's it for the main card. + echo ' +
    '; + + // Now fill out the deck of cards with the boards in each category. + foreach ($context['categories'] as $category) + { + // Begin the card, and make the name available. + echo ' + +

    ', strip_tags($category['name']), '

    '; + + // Now show a link for each board. + foreach ($category['boards'] as $board) + echo ' +

    ', $board['name'], '

    '; + + echo ' +
    '; + } +} + +// This is the message index (list of topics in a board) for WAP 1.1. +function template_wap_messageindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + +

    ', $context['name'], '

    '; + + if (isset($context['boards']) && count($context['boards']) > 0) + { + foreach ($context['boards'] as $board) + echo ' +

    - ', $board['name'], '

    '; + echo ' +


    '; + } + + if (!empty($context['topics'])) + { + echo ' +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + + foreach ($context['topics'] as $topic) + echo ' +

    ', $topic['first_post']['subject'], '', (!$topic['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), ' - ', $topic['first_post']['member']['name'], '

    '; + + echo ' +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + } + + echo ' +
    '; +} + +function template_wap_display() +{ + global $context, $settings, $options, $txt; + + echo ' + +

    ' . $context['linktree'][1]['name'] . ' > ' . $context['linktree'][count($context['linktree']) - 2]['name'] . '

    +

    ', $context['subject'], '

    +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + + while ($message = $context['get_message']()) + { + // This is a special modification to the post so it will work on phones: + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  5. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo ' +

    ', $message['member']['name'], ':', (!$message['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), '

    +

    ', $message['body'], '

    '; + } + + echo ' +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    + '; +} + +function template_wap_login() +{ + global $context, $modSettings, $scripturl, $txt; + + echo ' + '; + + if (isset($context['login_errors'])) + foreach ($context['login_errors'] as $error) + echo ' +

    ', $error, '

    '; + + echo ' +

    ', $txt['username'], ':
    +

    + +

    ', $txt['password'], ':
    +

    '; + + // Open ID? + if (!empty($modSettings['enableOpenID'])) + echo ' +

    —', $txt['or'], '—

    + +

    ', $txt['openid'], ':
    +

    '; + + echo ' +

    + + + + + +

    +
    '; +} + +function template_wap_recent() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + +

    ', $_REQUEST['action'] == 'unread' ? $txt['wireless_recent_unread_posts'] : $txt['wireless_recent_unread_replies'], '

    '; + + if (empty($context['topics'])) + echo ' +

    ', $txt['old_posts'], '

    '; + else + { + echo ' +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + foreach ($context['topics'] as $topic) + { + echo ' +

    ', $topic['first_post']['subject'], '

    '; + } + } + + echo ' +
    '; +} + +function template_wap_error() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' + +

    ', $context['error_title'], '

    +

    ', $context['error_message'], '

    +

    ', $txt['wireless_error_home'], '

    +
    '; +} + +function template_wap_below() +{ + global $context, $settings, $options, $txt; + + echo ' + +

    + ', $txt['wireless_go_to_full_version'], ' +

    +
    +'; +} + +// The cHTML protocol used for i-mode starts here. +function template_imode_above() +{ + global $context, $settings, $options, $user_info; + + echo ' + + + '; + + // Present a canonical url for search engines to prevent duplicate content in their indices. + if ($user_info['is_guest'] && !empty($context['canonical_url'])) + echo ' + '; + + echo ' + ', $context['page_title'], ' + + '; +} + +function template_imode_boardindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + + '; + $count = 0; + foreach ($context['categories'] as $category) + { + if (!empty($category['boards']) || $category['is_collapsed']) + echo ' + '; + + foreach ($category['boards'] as $board) + { + $count++; + echo ' + '; + } + } + echo ' + '; + if ($context['user']['is_guest']) + echo ' + '; + else + { + if ($context['allow_pm']) + echo ' + '; + echo ' + + + '; + } + echo ' +
    ', $context['forum_name_html_safe'], '
    ', $category['can_collapse'] ? '' : '', $category['name'], $category['can_collapse'] ? '' : '', '
    ', $board['new'] ? '' : '', $count < 10 ? '&#' . (59105 + $count) . ';' : '-', $board['new'] ? '' : ($board['children_new'] ? '.' : ''), ' ', $board['name'], '
    ', $txt['wireless_options'], '
    ', $txt['wireless_options_login'], '
    ', empty($context['user']['unread_messages']) ? $txt['wireless_pm_inbox'] : sprintf($txt['wireless_pm_inbox_new'], $context['user']['unread_messages']), '
    ', $txt['wireless_recent_unread_posts'], '
    ', $txt['wireless_recent_unread_replies'], '
    ', $txt['wireless_options_logout'], '
    '; +} + +function template_imode_messageindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + + '; + + if (!empty($context['boards'])) + { + echo ' + '; + foreach ($context['boards'] as $board) + echo ' + '; + } + + $count = 0; + if (!empty($context['topics'])) + { + echo ' + + '; + foreach ($context['topics'] as $topic) + { + $count++; + echo ' + '; + } + } + echo ' + + ', !empty($context['links']['next']) ? ' + ' : '', !empty($context['links']['prev']) ? ' + ' : '', $context['can_post_new'] ? ' + ' : '', ' +
    ', $context['name'], '
    ', $txt['parent_boards'], '
    ', $board['new'] ? '- ' : ($board['children_new'] ? '-.' : '- '), '', $board['name'], '
    ', $txt['topics'], '
    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '
    ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $topic['first_post']['subject'], '', (!$topic['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), $topic['new'] && $context['user']['is_logged'] ? ' [' . $txt['new'] . ']' : '', '
    ', $txt['wireless_navigation'], '
    ', $txt['wireless_navigation_up'], '
    ' . $txt['wireless_navigation_next'] . '
    [*] ' . $txt['wireless_navigation_prev'] . '
    ' . $txt['start_new_topic'] . '
    '; +} + +function template_imode_display() +{ + global $context, $settings, $options, $scripturl, $board, $txt; + + echo ' + + + + '; + while ($message = $context['get_message']()) + { + // This is a special modification to the post so it will work on phones: + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  6. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo ' +
  7. '; + } + echo ' + + ', $context['user']['is_logged'] ? ' + ' : '', !empty($context['links']['next']) ? ' + ' : '', !empty($context['links']['prev']) ? ' + ' : '', $context['can_reply'] ? ' + ' : ''; + + if (!empty($context['wireless_more']) && empty($context['wireless_moderate'])) + echo ' + '; + elseif (!empty($context['wireless_moderate'])) + { + if ($context['can_sticky']) + echo ' + '; + if ($context['can_lock']) + echo ' + '; + } + + echo ' +
    ' . $context['linktree'][1]['name'] . ' > ' . $context['linktree'][count($context['linktree']) - 2]['name'] . '
    ', $context['subject'], '
    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '
    ', $message['first_new'] ? ' + ' : '', + $context['wireless_moderate'] && $message['member']['id'] ? '' . $message['member']['name'] . '' : '' . $message['member']['name'] . '', ': + ', ((empty($context['wireless_more']) && $message['can_modify']) || !empty($context['wireless_moderate']) ? '[' . $txt['wireless_display_edit'] . ']' : ''), (!$message['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), '
    + ', $message['body'], ' +
    ', $txt['wireless_navigation'], '
    ', $txt['wireless_navigation_index'], '
    ' . $txt['mark_unread'] . '
    ' . $txt['wireless_navigation_next'] . '
    ' . $txt['wireless_navigation_prev'] . '
    ' . $txt['reply'] . '
    ', $txt['wireless_display_moderate'], '
    ', $txt['wireless_display_' . ($context['is_sticky'] ? 'unsticky' : 'sticky')], '
    ', $txt['wireless_display_' . ($context['is_locked'] ? 'unlock' : 'lock')], '
    '; +} + +function template_imode_post() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // !!! $modSettings['guest_post_no_email'] + echo ' +
    + '; + + if (!$context['becomes_approved']) + echo ' + '; + + if ($context['locked']) + echo ' + '; + + if (isset($context['name']) && isset($context['email'])) + { + echo ' + + '; + + if (empty($modSettings['guest_post_no_email'])) + echo ' + + '; + } + + // !!! Needs a more specific imode template. + if ($context['require_verification']) + echo ' + + '; + + echo ' + + + + + + +
    ' . $txt['wait_for_approval'] . '
    ' . $txt['topic_locked_no_reply'] . '
    ', isset($context['post_error']['long_name']) || isset($context['post_error']['no_name']) ? '' . $txt['username'] . '' : $txt['username'], ':
    ', isset($context['post_error']['no_email']) || isset($context['post_error']['bad_email']) ? '' . $txt['email'] . '' : $txt['email'], ':
    ', !empty($context['post_error']['need_qr_verification']) ? '' . $txt['verification'] . '' : $txt['verification'], ':
    ', template_control_verification($context['visual_verification_id'], 'all'), '
    ', isset($context['post_error']['no_subject']) ? '' . $txt['subject'] . '' : $txt['subject'], ':
    ', isset($context['post_error']['no_message']) || isset($context['post_error']['long_message']) ? '' . $txt['message'] . '' : $txt['message'], ':
    + + + + + ', isset($context['current_topic']) ? ' + ' : '', ' + +
    +  ', !empty($context['current_topic']) ? '' . $txt['wireless_navigation_topic'] . '' : '' . $txt['wireless_navigation_index'] . '', ' +
    +
    '; +} + +function template_imode_login() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    + + '; + if (isset($context['login_errors'])) + foreach ($context['login_errors'] as $error) + echo ' + '; + echo ' + + + + '; + + // Open ID? + if (!empty($modSettings['enableOpenID'])) + echo ' + + + '; + + echo ' + + + +
    ', $txt['login'], '
    ', $error, '
    ', $txt['username'], ':
    ', $txt['password'], ':
    —', $txt['or'], '—
    ', $txt['openid'], ':
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    +
    '; +} + +function template_imode_pm() +{ + global $context, $settings, $options, $scripturl, $txt, $user_info; + + if ($_REQUEST['action'] == 'findmember') + { + echo ' +
    + + + + + '; + if (!empty($context['last_search'])) + { + echo ' + '; + if (empty($context['results'])) + echo ' + '; + else + { + echo ' + '; + $count = 0; + foreach ($context['results'] as $result) + { + $count++; + echo ' + '; + } + } + } + echo ' + + '; + if (!empty($context['results'])) + echo empty($context['links']['next']) ? '' : ' + ', empty($context['links']['prev']) ? '' : ' + '; + echo ' +
    ', $txt['wireless_pm_search_member'], '
    ', $txt['find_members'], '
    + ', $txt['wireless_pm_search_name'], ': + ', empty($_REQUEST['u']) ? '' : ' + ', ' +
    ', $txt['find_results'], '
    [-] ', $txt['find_no_results'], '
    ', empty($context['links']['prev']) ? '' : '<< < ', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', empty($context['links']['next']) ? '' : ' > >> ', '
    + ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $result['name'], ' +
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    [#] ' . $txt['wireless_navigation_next'] . '
    [*] ' . $txt['wireless_navigation_prev'] . '
    +
    '; + } + elseif (!empty($_GET['sa'])) + { + echo ' + '; + if ($_GET['sa'] == 'addbuddy') + { + echo ' + + '; + $count = 0; + foreach ($context['buddies'] as $buddy) + { + $count++; + if ($buddy['selected']) + echo ' + '; + else + echo ' + '; + } + echo ' + + +
    ', $txt['wireless_pm_add_buddy'], '
    ', $txt['wireless_pm_select_buddy'], '
    [-] ', $buddy['name'], '
    + ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $buddy['name'], ' +
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    '; + } + if ($_GET['sa'] == 'send' || $_GET['sa'] == 'send2') + { + echo ' +
    + + ', empty($context['post_error']['messages']) ? '' : ' + ', ' + + + + '; + if ($context['reply']) + echo ' + + + '; + echo ' + + +
    ', $txt['new_message'], '
    ' . implode('
    ', $context['post_error']['messages']) . '
    + ', $txt['pm_to'], ': '; + if (empty($context['recipients']['to'])) + echo $txt['wireless_pm_no_recipients']; + else + { + $to_names = array(); + $ids = array(); + foreach ($context['recipients']['to'] as $to) + { + $ids[] = $to['id']; + $to_names[] = $to['name']; + } + echo implode(', ', $to_names); + $ids = implode(',', $ids); + } + echo ' + ', empty($ids) ? '' : '', '
    + ', $txt['wireless_pm_search_member'], '', empty($user_info['buddies']) ? '' : '
    + ' . $txt['wireless_pm_add_buddy'] . '', ' +
    + ', $txt['subject'], ': +
    + ', $txt['message'], ':
    + +
    + + + + + + + + +
    ', $txt['wireless_pm_reply_to'], '
    ', $context['quoted_message']['subject'], '
    ', $context['quoted_message']['body'], '
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    +
    '; + } + } + elseif (empty($_GET['pmsg'])) + { + echo ' + + + '; + $count = 0; + while ($message = $context['get_pmessage']()) + { + $count++; + echo ' + '; + } + + if ($context['currently_using_labels']) + { + $labels = array(); + ksort($context['labels']); + foreach ($context['labels'] as $label) + $labels[] = '' . $label['name'] . '' . (!empty($label['unread_messages']) ? ' (' . $label['unread_messages'] . ')' : ''); + echo ' + + '; + } + echo ' + + ', empty($context['links']['next']) ? '' : ' + ', empty($context['links']['prev']) ? '' : ' + ', $context['can_send_pm'] ? ' + ' : '', ' +
    ', $context['current_label_id'] == -1 ? $txt['wireless_pm_inbox'] : $txt['pm_current_label'] . ': ' . $context['current_label'], '
    ', empty($context['links']['prev']) ? '' : '<< < ', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', empty($context['links']['next']) ? '' : ' > >> ', '
    + ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $message['subject'], ' ', $txt['wireless_pm_by'], ' ', $message['member']['name'], '', $message['is_unread'] ? ' [' . $txt['new'] . ']' : '', ' +
    ', $txt['pm_labels'], '
    + ', implode(', ', $labels), ' +
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    [#] ' . $txt['wireless_navigation_next'] . '
    [*] ' . $txt['wireless_navigation_prev'] . '
    ' . $txt['new_message'] . '
    '; + } + else + { + $message = $context['get_pmessage'](); + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  8. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo ' + + + + + + '; + if ($context['can_send_pm']) + echo ' + '; + + if ($context['can_send_pm'] && $message['number_recipients'] > 1) + echo ' + '; + + echo ' +
    ', $message['subject'], '
    + ', $txt['wireless_pm_by'], ': ', $message['member']['name'], '
    + ', $txt['on'], ': ', $message['time'], ' +
    + ', $message['body'], ' +
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    ', $txt['wireless_pm_reply'], '
    ', $txt['wireless_pm_reply_all'], '
    '; + } +} + +function template_imode_recent() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + + '; + + $count = 0; + if (empty($context['topics'])) + echo ' + '; + else + { + echo ' + '; + foreach ($context['topics'] as $topic) + { + $count++; + echo ' + '; + } + } + echo ' + + ', !empty($context['links']['next']) ? ' + ' : '', !empty($context['links']['prev']) ? ' + ' : '', ' +
    ', $_REQUEST['action'] == 'unread' ? $txt['wireless_recent_unread_posts'] : $txt['wireless_recent_unread_replies'], '
    ', $txt['old_posts'], '
    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '
    ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $topic['first_post']['subject'], '
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    [#] ' . $txt['wireless_navigation_next'] . '
    [*] ' . $txt['wireless_navigation_prev'] . '
    '; +} + +function template_imode_error() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' + + + + +
    ', $context['error_title'], '
    ', $context['error_message'], '
    [0] ', $txt['wireless_error_home'], '
    '; +} + +function template_imode_profile() +{ + global $context, $settings, $options, $scripturl, $board, $txt; + + echo ' + + + + + '; + + if (!empty($context['member']['bans'])) + { + echo ' + '; + } + + echo ' + + '; + + if (!$context['user']['is_owner'] && $context['can_send_pm']) + echo ' + '; + + if (!$context['user']['is_owner'] && !empty($context['can_edit_ban'])) + echo ' + '; + + echo ' + '; + + echo ' +
    ', $txt['summary'], ' - ', $context['member']['name'], '
    + ', $txt['name'], ': ', $context['member']['name'], ' +
    + ', $txt['position'], ': ', (!empty($context['member']['group']) ? $context['member']['group'] : $context['member']['post_group']), ' +
    + ', $txt['lastLoggedIn'], ': ', $context['member']['last_login'], ' +
    + ', $txt['user_banned_by_following'], ':'; + + foreach ($context['member']['bans'] as $ban) + echo ' +
    ', $ban['explanation'], ''; + + echo ' +
    ', $txt['additional_info'], '
    ', $txt['wireless_profile_pm'], '.
    ', $txt['profileBanUser'], '.
    ', $txt['wireless_error_home'], '.
    '; +} + +function template_imode_ban_edit() +{ + global $context, $settings, $options, $scripturl, $board, $txt, $modSettings; + + echo ' +
    + + + + + + + '; + + if (!empty($context['ban_suggestions'])) + { + echo ' + + '; + + if (empty($modSettings['disableHostnameLookup'])) + echo ' + '; + + echo ' + + '; + } + + echo ' + + + '; + + echo ' +
    ', $context['ban']['is_new'] ? $txt['ban_add_new'] : $txt['ban_edit'] . ' \'' . $context['ban']['name'] . '\'', '
    + ', $txt['ban_name'], ': + +
    + ', $txt['ban_expiration'], ':
    + ', $txt['never'], '
    + ', $txt['ban_will_expire_within'], ' ', $txt['ban_days'], '
    + ', $txt['ban_expired'], '
    +
    + ', $txt['ban_reason'], ': + +
    + ', $txt['ban_notes'], ':
    + +
    + ', $txt['ban_restriction'], ':
    + ', $txt['ban_full_ban'], '
    + ', $txt['ban_cannot_post'], '
    + ', $txt['ban_cannot_register'], '
    + ', $txt['ban_cannot_login'], ' +
    ', $txt['ban_triggers'], '
    + ', $txt['wireless_ban_ip'], ':
    +      +
    + ', $txt['wireless_ban_hostname'], ':
    +      +
    + ', $txt['wireless_ban_email'], ':
    +      +
    + ', $txt['ban_on_username'], ':
    '; + + if (empty($context['ban_suggestions']['member']['id'])) + echo ' +     '; + else + echo ' +     ', $context['ban_suggestions']['member']['name'], ' + '; + + echo ' +
    ', $txt['wireless_additional_info'], '
    ', $txt['wireless_error_home'], '.
    + + + +
    '; +} + +function template_imode_below() +{ + global $context, $settings, $options, $txt; + + echo ' +
    ', $txt['wireless_go_to_full_version'], ' + +'; +} + +// XHTMLMP (XHTML Mobile Profile) templates used for WAP 2.0 start here +function template_wap2_above() +{ + global $context, $settings, $options, $user_info; + + echo ' + + + + ', $context['page_title'], ''; + + // Present a canonical url for search engines to prevent duplicate content in their indices. + if ($user_info['is_guest'] && !empty($context['canonical_url'])) + echo ' + '; + + echo ' + + + '; +} + +function template_wap2_boardindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +

    ', $context['forum_name_html_safe'], '

    '; + + $count = 0; + foreach ($context['categories'] as $category) + { + if (!empty($category['boards']) || $category['is_collapsed']) + echo ' +

    ', $category['can_collapse'] ? '' : '', $category['name'], $category['can_collapse'] ? '' : '', '

    '; + + foreach ($category['boards'] as $board) + { + $count++; + echo ' +

    ', $board['new'] ? '' : '', $count < 10 ? '[' . $count . '' : '[-', $board['children_new'] && !$board['new'] ? '' : '', '] ', $board['new'] || $board['children_new'] ? '' : '', '', $board['name'], '

    '; + } + } + + echo ' +

    ', $txt['wireless_options'], '

    '; + if ($context['user']['is_guest']) + echo ' +

    ', $txt['wireless_options_login'], '

    '; + else + { + if ($context['allow_pm']) + echo ' +

    ', empty($context['user']['unread_messages']) ? $txt['wireless_pm_inbox'] : sprintf($txt['wireless_pm_inbox_new'], $context['user']['unread_messages']), '

    '; + echo ' +

    ', $txt['wireless_recent_unread_posts'], '

    +

    ', $txt['wireless_recent_unread_replies'], '

    +

    ', $txt['wireless_options_logout'], '

    '; + } +} + +function template_wap2_messageindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +

    ', $context['name'], '

    '; + + if (!empty($context['boards'])) + { + echo ' +

    ', $txt['parent_boards'], '

    '; + foreach ($context['boards'] as $board) + echo ' +

    ', $board['new'] ? '[-] ' : ($board['children_new'] ? '[-] ' : '[-] '), '', $board['name'], '

    '; + } + + $count = 0; + if (!empty($context['topics'])) + { + echo ' +

    ', $txt['topics'], '

    +

    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + foreach ($context['topics'] as $topic) + { + $count++; + echo ' +

    ', $count < 10 ? '[' . $count . '] ' : '', '', $topic['first_post']['subject'], '', (!$topic['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), $topic['new'] && $context['user']['is_logged'] ? ' [' . $txt['new'] . ']' : '', '

    '; + } + } + + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    ', !empty($context['links']['next']) ? ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ' : '', !empty($context['links']['prev']) ? ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    ' : '', $context['can_post_new'] ? ' +

    ' . $txt['start_new_topic'] . '

    ' : ''; +} + +function template_wap2_display() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +

    ' . $context['linktree'][1]['name'] . ' > ' . $context['linktree'][count($context['linktree']) - 2]['name'] . '

    +

    ', $context['subject'], '

    +

    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + $alternate = true; + while ($message = $context['get_message']()) + { + // This is a special modification to the post so it will work on phones: + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  9. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo $message['first_new'] ? ' + ' : '', ' +

    + ', $context['wireless_moderate'] && $message['member']['id'] ? '' . $message['member']['name'] . '' : '' . $message['member']['name'] . '', ': + ', ((empty($context['wireless_more']) && $message['can_modify']) || !empty($context['wireless_moderate']) ? '[' . $txt['wireless_display_edit'] . ']' : ''), (!$message['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), '
    + ', $message['body'], ' +

    '; + $alternate = !$alternate; + } + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_index'], '

    ', $context['user']['is_logged'] ? ' +

    [1] ' . $txt['mark_unread'] . '

    ' : '', !empty($context['links']['next']) ? ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ' : '', !empty($context['links']['prev']) ? ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    ' : '', $context['can_reply'] ? ' +

    ' . $txt['reply'] . '

    ' : ''; + + if (!empty($context['wireless_more']) && empty($context['wireless_moderate'])) + echo ' +

    ', $txt['wireless_display_moderate'], '

    '; + elseif (!empty($context['wireless_moderate'])) + { + if ($context['can_sticky']) + echo ' +

    ', $txt['wireless_display_' . ($context['is_sticky'] ? 'unsticky' : 'sticky')], '

    '; + if ($context['can_lock']) + echo ' +

    ', $txt['wireless_display_' . ($context['is_locked'] ? 'unlock' : 'lock')], '

    '; + } +} + +function template_wap2_login() +{ + global $context, $modSettings, $scripturl, $txt; + + echo ' +
    +

    ', $txt['login'], '

    '; + + if (isset($context['login_errors'])) + foreach ($context['login_errors'] as $error) + echo ' +

    ', $error, '

    '; + + echo ' +

    ', $txt['username'], ':

    +

    +

    ', $txt['password'], ':

    +

    '; + + // Open ID? + if (!empty($modSettings['enableOpenID'])) + echo ' +

    —', $txt['or'], '—

    +

    ', $txt['openid'], ':

    +

    '; + + echo ' +

    +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    +
    '; +} + +function template_wap2_post() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +

    ', $context['page_title'], '

    '; + + if (!$context['becomes_approved']) + echo ' +

    + ' . $txt['wait_for_approval'] . ' + +

    '; + + if ($context['locked']) + echo ' +

    + ' . $txt['topic_locked_no_reply'] . ' +

    '; + + if (isset($context['name']) && isset($context['email'])) + { + echo ' +

    + ' . $txt['username'] . ': +

    '; + + if (empty($modSettings['guest_post_no_email'])) + echo ' +

    + ' . $txt['email'] . ': +

    '; + } + + if ($context['require_verification']) + echo ' +

    + ' . $txt['verification'] . ': ', template_control_verification($context['visual_verification_id'], 'all'), ' +

    '; + + echo ' +

    + ', $txt['subject'], ': +

    +

    + ', $txt['message'], ':
    + +

    +

    + + + + + ', isset($context['current_topic']) ? ' + ' : '', ' + +

    +

    [0] ', !empty($context['current_topic']) ? '' . $txt['wireless_navigation_topic'] . '' : '' . $txt['wireless_navigation_index'] . '', '

    +
    '; +} + +function template_wap2_pm() +{ + global $context, $settings, $options, $scripturl, $txt, $user_info; + + if ($_REQUEST['action'] == 'findmember') + { + echo ' +
    +

    ', $txt['wireless_pm_search_member'], '

    +

    ', $txt['find_members'], '

    +

    + ', $txt['wireless_pm_search_name'], ': + ', empty($_REQUEST['u']) ? '' : ' + ', ' +

    +

    +
    '; + if (!empty($context['last_search'])) + { + echo ' +

    ', $txt['find_results'], '

    '; + if (empty($context['results'])) + echo ' +

    [-] ', $txt['find_no_results'], '

    '; + else + { + echo ' +

    ', empty($context['links']['prev']) ? '' : '<< < ', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', empty($context['links']['next']) ? '' : ' > >> ', '

    '; + $count = 0; + foreach ($context['results'] as $result) + { + $count++; + echo ' +

    + [', $count < 10 ? $count : '-', '] ', $result['name'], ' +

    '; + } + } + } + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    '; + if (!empty($context['results'])) + echo empty($context['links']['next']) ? '' : ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ', empty($context['links']['prev']) ? '' : ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    '; + } + elseif (!empty($_GET['sa'])) + { + if ($_GET['sa'] == 'addbuddy') + { + echo ' +

    ', $txt['wireless_pm_add_buddy'], '

    +

    ', $txt['wireless_pm_select_buddy'], '

    '; + $count = 0; + foreach ($context['buddies'] as $buddy) + { + $count++; + if ($buddy['selected']) + echo ' +

    [-] ', $buddy['name'], '

    '; + else + echo ' +

    + [', $count < 10 ? $count : '-', '] ', $buddy['name'], ' +

    '; + } + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    '; + } + if ($_GET['sa'] == 'send' || $_GET['sa'] == 'send2') + { + echo ' +
    +

    ', $txt['new_message'], '

    ', empty($context['post_error']['messages']) ? '' : ' +

    ' . implode('
    ', $context['post_error']['messages']) . '

    ', ' +

    + ', $txt['pm_to'], ': '; + if (empty($context['recipients']['to'])) + echo $txt['wireless_pm_no_recipients']; + else + { + $to_names = array(); + $ids = array(); + foreach ($context['recipients']['to'] as $to) + { + $ids[] = $to['id']; + $to_names[] = $to['name']; + } + echo implode(', ', $to_names); + $ids = implode(',', $ids); + } + echo ' + ', empty($ids) ? '' : '', '
    + ', $txt['wireless_pm_search_member'], '', empty($user_info['buddies']) ? '' : '
    + ' . $txt['wireless_pm_add_buddy'] . '', ' +

    +

    + ', $txt['subject'], ': +

    +

    + ', $txt['message'], ':
    + +

    +

    + + + + + + + + +

    '; + if ($context['reply']) + echo ' +

    ', $txt['wireless_pm_reply_to'], '

    +

    ', $context['quoted_message']['subject'], '

    +

    ', $context['quoted_message']['body'], '

    '; + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    +
    '; + } + } + elseif (empty($_GET['pmsg'])) + { + echo ' +

    ', $context['current_label_id'] == -1 ? $txt['wireless_pm_inbox'] : $txt['pm_current_label'] . ': ' . $context['current_label'], '

    +

    ', empty($context['links']['prev']) ? '' : '<< < ', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', empty($context['links']['next']) ? '' : ' > >> ', '

    '; + $count = 0; + while ($message = $context['get_pmessage']()) + { + $count++; + echo ' +

    + [', $count < 10 ? $count : '-', '] ', $message['subject'], ' ', $txt['wireless_pm_by'], ' ', $message['member']['name'], '', $message['is_unread'] ? ' [' . $txt['new'] . ']' : '', ' +

    '; + } + + if ($context['currently_using_labels']) + { + $labels = array(); + ksort($context['labels']); + foreach ($context['labels'] as $label) + $labels[] = '' . $label['name'] . '' . (!empty($label['unread_messages']) ? ' (' . $label['unread_messages'] . ')' : ''); + echo ' +

    + ', $txt['pm_labels'], ' +

    +

    + ', implode(', ', $labels), ' +

    '; + } + + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    ', empty($context['links']['next']) ? '' : ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ', empty($context['links']['prev']) ? '' : ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    ', $context['can_send_pm'] ? ' +

    ' . $txt['new_message'] . '

    ' : ''; + } + else + { + $message = $context['get_pmessage'](); + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  10. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo ' +

    ', $message['subject'], '

    +

    + ', $txt['wireless_pm_by'], ': ', $message['member']['name'], '
    + ', $txt['on'], ': ', $message['time'], ' +

    +

    + ', $message['body'], ' +

    +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    '; + if ($context['can_send_pm']) + echo ' +

    ', $txt['wireless_pm_reply'], '

    '; + + if ($context['can_send_pm'] && $message['number_recipients'] > 1) + echo ' +

    ', $txt['wireless_pm_reply_all'], '

    '; + + } +} + +function template_wap2_recent() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +

    ', $_REQUEST['action'] == 'unread' ? $txt['wireless_recent_unread_posts'] : $txt['wireless_recent_unread_replies'], '

    '; + + $count = 0; + if (empty($context['topics'])) + echo ' +

    ', $txt['old_posts'], '

    '; + else + { + echo ' +

    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + foreach ($context['topics'] as $topic) + { + $count++; + echo ' +

    ', ($count < 10 ? '[' . $count . '] ' : ''), '', $topic['first_post']['subject'], '

    '; + } + } + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    ', !empty($context['links']['next']) ? ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ' : '', !empty($context['links']['prev']) ? ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    ' : ''; +} + +function template_wap2_error() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +

    ', $context['error_title'], '

    +

    ', $context['error_message'], '

    +

    [0] ', $txt['wireless_error_home'], '

    '; +} + +function template_wap2_profile() +{ + global $context, $settings, $options, $scripturl, $board, $txt; + + echo ' +

    ', $txt['summary'], ' - ', $context['member']['name'], '

    +

    ', $txt['name'], ': ', $context['member']['name'], '

    +

    ', $txt['position'], ': ', (!empty($context['member']['group']) ? $context['member']['group'] : $context['member']['post_group']), '

    +

    ', $txt['lastLoggedIn'], ': ', $context['member']['last_login'], '

    '; + + if (!empty($context['member']['bans'])) + { + echo ' +

    ', $txt['user_banned_by_following'], ':

    '; + + foreach ($context['member']['bans'] as $ban) + echo ' +

    ', $ban['explanation'], '

    '; + + } + + echo ' + +

    ', $txt['additional_info'], '

    '; + + if (!$context['user']['is_owner'] && $context['can_send_pm']) + echo ' +

    ', $txt['wireless_profile_pm'], '.

    '; + + if (!$context['user']['is_owner'] && !empty($context['can_edit_ban'])) + echo ' +

    ', $txt['profileBanUser'], '.

    '; + + echo ' +

    ', $txt['wireless_error_home'], '.

    '; + +} + +function template_wap2_ban_edit() +{ + global $context, $settings, $options, $scripturl, $board, $txt, $modSettings; + + echo ' +
    +

    ', $context['ban']['is_new'] ? $txt['ban_add_new'] : $txt['ban_edit'] . ' \'' . $context['ban']['name'] . '\'', '

    +

    + ', $txt['ban_name'], ': + +

    +

    + ', $txt['ban_expiration'], ':
    + ', $txt['never'], '
    + ', $txt['ban_will_expire_within'], ' ', $txt['ban_days'], '
    + ', $txt['ban_expired'], '
    +

    +

    + ', $txt['ban_reason'], ': + +

    +

    + ', $txt['ban_notes'], ':
    + +

    +

    + ', $txt['ban_restriction'], ':
    + ', $txt['ban_full_ban'], '
    + ', $txt['ban_cannot_post'], '
    + ', $txt['ban_cannot_register'], '
    + ', $txt['ban_cannot_login'], ' +

    '; + + if (!empty($context['ban_suggestions'])) + { + echo ' +

    ', $txt['ban_triggers'], '

    +

    + ', $txt['wireless_ban_ip'], ':
    +      +

    '; + + if (empty($modSettings['disableHostnameLookup'])) + echo ' +

    + ', $txt['wireless_ban_hostname'], ':
    +      +

    '; + + echo ' +

    + ', $txt['wireless_ban_email'], ':
    +      +

    +

    + ', $txt['ban_on_username'], ':
    '; + + if (empty($context['ban_suggestions']['member']['id'])) + echo ' +     '; + else + echo ' +     ', $context['ban_suggestions']['member']['name'], ' + '; + + echo ' +

    '; + } + + echo ' + +

    +

    ', $txt['wireless_additional_info'], '

    +

    ', $txt['wireless_error_home'], '.

    '; + + echo ' + + + +
    '; +} + +function template_wap2_below() +{ + global $context, $settings, $options, $txt; + + echo ' + ', $txt['wireless_go_to_full_version'], ' + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/Xml.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/Xml.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,389 @@ + + + ', cleanXml($context['message']), ' +'; +} + +function template_quotefast() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + ', cleanXml($context['quote']['xml']), ' +'; +} + +function template_modifyfast() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + + +'; + +} + +function template_modifydone() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + '; + if (empty($context['message']['errors'])) + { + echo ' + ' . $txt['last_edit'] . ': ' . $context['message']['modified']['time'] . ' ' . $txt['by'] . ' ' . $context['message']['modified']['name'] . ' »'), ']]> + + '; + } + else + echo ' + ', $context['message']['errors']), ']]>'; + echo ' + +'; +} + +function template_modifytopicdone() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + '; + if (empty($context['message']['errors'])) + { + echo ' + ' . $txt['last_edit'] . ': ' . $context['message']['modified']['time'] . ' ' . $txt['by'] . ' ' . $context['message']['modified']['name'] . ' »'), ']]>'; + if (!empty($context['message']['subject'])) + echo ' + '; + } + else + echo ' + ', $context['message']['errors'])), ']]>'; + echo ' + +'; +} + +function template_post() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + + + + + '; + if (!empty($context['post_error']['messages'])) + foreach ($context['post_error']['messages'] as $message) + echo ' + '; + echo ' + + + + + ', isset($context['post_error']['no_message']) || isset($context['post_error']['long_message']) ? ' + ' : '', ' + + ', isset($context['topic_last_message']) ? $context['topic_last_message'] : '0', ''; + + if (!empty($context['previous_posts'])) + { + echo ' + '; + foreach ($context['previous_posts'] as $post) + echo ' + + + + + ', $post['is_ignored'] ? '1' : '0', ' + '; + echo ' + '; + } + + echo ' +'; +} + +function template_stats() +{ + global $context, $settings, $options, $txt, $modSettings; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> +'; + foreach ($context['yearly'] as $year) + foreach ($year['months'] as $month); + { + echo ' + '; + foreach ($month['days'] as $day) + echo ' + '; + echo ' + '; + } + echo ' +'; +} + +function template_split() +{ + global $context, $settings, $options; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + + '; + foreach ($context['changes'] as $change) + { + if ($change['type'] == 'remove') + echo ' + '; + else + echo ' + + + + + + '; + } + echo ' +'; +} + +// This is just to hold off some errors if people are stupid. +if (!function_exists('template_button_strip')) +{ + function template_button_strip($button_strip, $direction = 'top', $strip_options = array()) + { + } + function template_menu() + { + } + function theme_linktree() + { + } +} + +function template_results() +{ + global $context, $settings, $options, $txt; + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> +'; + + if (empty($context['topics'])) + echo ' + ', $txt['search_no_results'], ''; + else + { + echo ' + '; + + while ($topic = $context['get_topics']()) + { + echo ' + + ', $topic['id'], ' + ', $topic['relevance'], ' + + ', $topic['board']['id'], ' + ', cleanXml($topic['board']['name']), ' + ', $topic['board']['href'], ' + + + ', $topic['category']['id'], ' + ', cleanXml($topic['category']['name']), ' + ', $topic['category']['href'], ' + + '; + foreach ($topic['matches'] as $message) + { + echo ' + + ', $message['id'], ' + + + + ', $message['timestamp'], ' + ', $message['start'], ' + + + ', $message['member']['id'], ' + ', cleanXml($message['member']['name']), ' + ', $message['member']['href'], ' + + '; + } + echo ' + + '; + } + + echo ' + '; + } + + echo ' +'; +} + +function template_jump_to() +{ + global $context, $settings, $options; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> +'; + foreach ($context['jump_to'] as $category) + { + echo ' + '; + foreach ($category['boards'] as $board) + echo ' + '; + } + echo ' +'; +} + +function template_message_icons() +{ + global $context, $settings, $options; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> +'; + foreach ($context['icons'] as $icon) + echo ' + '; + echo ' +'; +} + +function template_check_username() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + ', cleanXml($context['checked_username']), ' +'; +} + +// This prints XML in it's most generic form. +function template_generic_xml() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '>'; + + // Show the data. + template_generic_xml_recursive($context['xml_data'], 'smf', '', -1); +} + +// Recursive function for displaying generic XML data. +function template_generic_xml_recursive($xml_data, $parent_ident, $child_ident, $level) +{ + // This is simply for neat indentation. + $level++; + + echo "\n" . str_repeat("\t", $level), '<', $parent_ident, '>'; + + foreach ($xml_data as $key => $data) + { + // A group? + if (is_array($data) && isset($data['identifier'])) + template_generic_xml_recursive($data['children'], $key, $data['identifier'], $level); + // An item... + elseif (is_array($data) && isset($data['value'])) + { + echo "\n", str_repeat("\t", $level), '<', $child_ident; + + if (!empty($data['attributes'])) + foreach ($data['attributes'] as $k => $v) + echo ' ' . $k . '="' . $v . '"'; + echo '>'; + } + + } + + echo "\n", str_repeat("\t", $level), ''; +} + +function template_webslice_header_above() +{ + global $settings; + + echo ' + '; +} + +function template_webslice_header_below() +{ +} + +// This shows a webslice of the recent posts. +function template_webslice_recent_posts() +{ + global $context, $scripturl, $txt; + + echo ' +
    +
    + ', cleanXml($txt['recent_posts']), ' +
    '; + + $alternate = 0; + foreach ($context['recent_posts_data'] as $item) + { + echo ' +
    + ', cleanXml($item['subject']), ' ', cleanXml($txt['by']), ' ', cleanXml(!empty($item['poster']['link']) ? '' . $item['poster']['name'] . '' : $item['poster']['name']), ' +
    '; + $alternate = !$alternate; + } + + echo ' +
    +
    +
    '; + + if ($context['user']['is_guest']) + echo ' + ', $txt['login'], ''; + else + echo ' + ', cleanXml($context['user']['name']), ', ', cleanXml($txt['msg_alert_you_have']), ' ', cleanXml($context['user']['messages']), ' ', cleanXml($context['user']['messages'] != 1 ? $txt['msg_alert_messages'] : $txt['message_lowercase']), '', cleanXml($txt['newmessages4'] . ' ' . $context['user']['unread_messages']), ' ', cleanXml($context['user']['unread_messages'] == 1 ? $txt['newmessages0'] : $txt['newmessages1']); + + echo ' +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/admin.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/admin.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,627 @@ +/* Styles for the admin quick search. +------------------------------------------------------- */ + +#quick_search form, h3.catbg #quick_search form +{ + padding: 7px; + line-height: 0.9em; + font-size: 0.8em !important; +} + +ol.search_results +{ + margin-top: 0; + padding-top: 0; +} +ol.search_results li +{ + padding-top: 1em; + border-bottom: 1px solid #ccc; +} + +/* Styles for the core features screen. +------------------------------------------------------- */ +.features +{ + padding: 0 1em !important; + overflow: auto; +} +.features_image +{ + float: left; + margin: 0 2em 0.5em 1em; +} +.features_switch +{ + margin: 0.2em 1em 1em 1em; + float: right; +} +.features h4 +{ + padding: 1em 0 0.5em 0.5em; + margin: 0; + font-size: 1.1em; +} +.features p +{ + padding: 0 1em; + margin: 0; +} + +/* Styles for the admin home screen bar. +------------------------------------------------------- */ +#admin_main_section +{ + overflow: hidden; + margin: 1em 0; +} +#admincenter .content +{ + padding: 1em; +} + +#live_news +{ + width: 64%; + font-size: 0.85em; +} +#live_news div.content +{ + padding: 0; +} +#live_news div.content dl +{ + padding: 0.5em 0 0 0.5em; +} + +#supportVersionsTable +{ + width: 34%; +} +#version_details +{ + overflow: auto; + height: 9.5em; +} +#smfAnnouncements +{ + height: 13.5em; + padding: 0 0.5em; + overflow: auto; +} +#smfAnnouncements dt +{ + border-bottom: 1px dashed #000; +} +#smfAnnouncements dd +{ + padding: 0; + margin: 0 0 1em 1.5em; +} +#update_section +{ + margin: 0.5em 0 0; +} + +#quick_tasks, #quick_tasks ul +{ + margin: 0; + padding: 0; +} +#quick_tasks li +{ + float: left; + list-style-type: none; + margin: 0; + padding: 0.5em 0; + width: 49.5%; + height: 4.5em; +} +.quick_task +{ + display: block; + width: 100%; + margin: 0 1em; + padding: 0; +} +.home_image +{ + float: left; + margin: 0 1em 1em 1em; +} + +/* Common admin center classes. +------------------------------------------------------- */ +hr.hrcolor +{ + margin: 10px 0; +} +h3.titlebg form +{ + font-size: 80%; +} +.windowbg.nopadding +{ + margin: 0.3em 0 0 0; + padding: 0; +} +.windowbg ol +{ + margin-top: 0; + margin-bottom: 0; +} + +.table_caption, tr.table_caption td +{ + color: #000; + font-size: 10px; + font-weight: bold; +} +.additional_row div.floatleft +{ + padding: 0 0.8em; +} +fieldset +{ + margin-bottom: 0.5em; + border: 1px solid #cacdd3; + padding: 0.5em; +} +fieldset dl +{ + margin: 0; +} +legend +{ + font-weight: bold; + color: #000; +} +.information a +{ + font-weight: bold; +} + +/* Styles for the package manager. +------------------------------------------------- */ +#package_list .tborder +{ + margin: .25em 0 .25em 26px; +} +#package_list ol, #package_list ol li +{ + list-style: decimal; + margin-left: 50px; + border: none; +} +#package_list ol ul, #package_list ol ul li +{ + margin-left: 0; + list-style: none; +} +#package_list +{ + list-style-type: none; +} +#package_list li +{ + border: 1px solid #cacdd3; + padding: 0.2em; + margin: 1px; +} +.description +{ + max-height: 15em; + overflow: auto; + padding-bottom: .5em; +} +.information +{ + max-height: 15em; + overflow: auto; + padding-bottom: .5em; +} +.package_section +{ + border: 1px solid #cacdd3; +} +ul.packages li +{ + border: none !important; + list-style-type: none; +} +code#find_code, code#replace_code +{ + display: block; + font-family: "dejavu sans mono", "monaco", "lucida console", "courier new", monospace; + font-size: x-small; + background: #eef; + line-height: 1.5em; + padding: 3px 1em; + overflow: auto; + white-space: pre; + /* Show a scrollbar after about 24 lines. */ + max-height: 24em; +} +span.package_server +{ + padding: 0 3em; +} +ul.package_servers +{ + margin: 0; + padding: 0; +} +ul.package_servers li +{ + list-style-type: none; +} +pre.file_content +{ + overflow: auto; + width: 100%; + padding-bottom: 1em; +} +.operation +{ + padding: 0 1em; +} + +/* Styles for the file permissions section. +------------------------------------------------- */ +.filepermissions +{ + font-size: 0.8em; + white-space: nowrap; +} +.fperm +{ + display: block; + width: 35%; + text-align: center; +} +.perm_read +{ + background-color: #d1f7bf; +} +.perm_write +{ + background-color: #ffbbbb; +} +.perm_execute +{ + background-color: #fdd7af; +} +.perm_custom +{ + background-color: #c2c6c0; +} +.perm_nochange +{ + background-color: #eee; +} + +/* Styles for the BBC permissions +------------------------------------------------- */ +.list_bbc +{ + width: 33%; +} + +/* Styles for the manage boards section. +------------------------------------------------- */ +#manage_boards ul +{ + padding: 0; + margin: 0 0 0.6em 0; + max-height: 30em; + overflow: auto; +} +#manage_boards li +{ + list-style-type: none; + border: 1px solid #cacdd3; + padding: 0.2em; + margin: 1px; + clear: right; +} +#manage_boards li img +{ + vertical-align: middle; + padding-bottom: 3px; +} +#manage_boards li#recycle_board +{ + background-color: #dee; +} +.move_links +{ + padding: 0 13px 0 0; +} +.modify_boards +{ + padding: 0 0.5em; +} +#manage_boards span.post_group, #manage_boards span.regular_members +{ + border-bottom: 1px dotted #000; + cursor: help; +} + +/* Styles for the manage members section. +------------------------------------------------- */ +.msearch_details +{ + display: block; + width: 49%; +} +dl.right dt +{ + padding-right: 10px; +} + +/* Styles for the manage maintenance section. +------------------------------------------------- */ +.maintenance_finished, #task_completed +{ + margin: 1ex; + padding: 1ex 2ex; + border: 1px dashed green; + color: green; + background: #efe; +} +/* Styles for the manage calendar section. +------------------------------------------------- */ +dl.settings dt.small_caption +{ + width: 20%; +} +dl.settings dd.small_caption +{ + width: 79%; +} +/* Styles for the manage permissions section. +------------------------------------------------- */ +dl.admin_permissions dt +{ + width: 35%; +} +dl.admin_permissions dd +{ + width: 64%; +} + +/* Styles for the manage search section. +------------------------------------------------- */ +dl.settings dt.large_caption +{ + width: 70%; +} +dl.settings dd.large_caption +{ + width: 29%; +} +span.search_weight +{ + width: 40px; + padding: 0 0.5em; + text-align: right; + display: inline-block; +} +.search_settings +{ + width: 47%; +} + +/* Styles for the manage bans section. +------------------------------------------------- */ +.ban_restriction +{ + margin: 0.2em 0 0.2em 2.2em; +} +.ban_settings +{ + width: 46%; +} +#manage_bans dl +{ + margin-bottom: 1em; +} +#manage_bans fieldset dl.settings +{ + margin-bottom: 0; +} + +/* Styles for the manage subscriptions section. +------------------------------------------------- */ +#fixed_area +{ + width: 97%; +} +ul.pending_payments +{ + margin: 0; + padding: 0; +} +ul.pending_payments li +{ + list-style-type: none; +} + +/* Styles for the manage permissions section. +------------------------------------------------- */ +.perm_name, .perm_profile, .perm_board +{ + display: block; + width: 40%; +} +.perm_boards +{ + padding: 0; + margin: 0 0 0.6em 0; +} +.perm_boards li +{ + list-style-type: none; + border: 1px solid #cacdd3; + padding: 0.2em; + margin: 1px; +} +.perm_groups +{ + background-color: #fff; +} +.perm_classic +{ + margin: 0.2em; +} +.permission_groups +{ + padding: 0; + margin: 0; +} +.permission_groups li +{ + list-style-type: none; + padding: 0.2em; + margin: 1px; +} +.perms +{ + width: 20px; + display: inline-block; + text-align: center; +} + +/* Styles for the themes section. +------------------------------------------------- */ +ul.theme_options +{ + padding: 0; + margin: 0; +} +ul.theme_options li +{ + list-style: none; + padding: 0.4em; +} +.is_directory +{ + padding-left: 18px; + background: url(../images/admin/boards.gif) no-repeat; +} +.edit_file +{ + width: 96%; + font-family: monospace; + margin-top: 1ex; + white-space: pre; +} + +dl.themes_list +{ + margin: 0; +} +dl.themes_list dt +{ + margin-bottom: 3px; +} +dl.themes_list dd +{ + font-style: italic; + white-space: nowrap; +} + +/* Styles for the registration center. +------------------------------------------------- */ +.agreement, .reserved_names +{ + padding: 0; +} +#agreement, #reserved +{ + width: 99%; +} + +/* Styles for the moderation center. +------------------------------------------------- */ +#modcenter +{ + display: block; + width: 100%; +} +.modblock_left +{ + width: 49%; + float: left; + clear: right; + margin: 0 0 1em 0; +} +.modblock_right +{ + width: 49%; + float: right; + margin: 0 0 1em 0; +} + +.modbox +{ + height: 150px; + overflow: auto; +} +/* Moderation Notes */ +ul.moderation_notes +{ + margin: 0; + padding: 0; + list-style: none; + overflow: auto; + height: 8.5em; +} +ul.moderation_notes li +{ + padding: 4px 0 4px 4px; + border-bottom: 1px solid #cccccc; +} +.notes +{ + margin: 0.5em 0; +} +.post_note +{ + width: 85%; +} + +/* Styles for the error log. +------------------------------------------------- */ + +h3.grid_header +{ + height: 25px; +} +#error_log +{ + width: 100%; +} +#error_log tr.windowbg td, #error_log tr.windowbg2 td +{ + padding: 8px; + line-height: 160%; +} +#error_log td.half_width +{ + width: 50%; +} +#error_log td.checkbox_column +{ + width: 15px; + vertical-align: top; + text-align: center; +} +#error_log td div.marginleft +{ + margin: 0 0 0 1ex; +} +#manage_boards span.botslice, #manage_maintenance span.botslice, #manage_mail span.botslice +{ + margin-bottom: 4px; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/compat.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/compat.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2416 @@ +/************************************************************************************************** + This file will *attempt* to make themes designed for older versions of SMF usable with SMF 2.0. + Unfortunately, the end result will be far from perfect, in most cases. Therefore, we encourage + theme designers to rebase their themes on either the default or core theme. +**************************************************************************************************/ + +/* Styles for the general looks of things +------------------------------------------ */ + +/* Help popups require a different styling of the body element. */ +body#help_popup +{ + width: auto; + padding: 1em; + min-width: 0; +} + +/* The main content area. +------------------------------------------------------- */ +.content, .roundframe +{ + padding: 0.5em 1.2em; + margin: 0; + border: 1px solid #adadad; + color: #000; + background-color: #ecedf3; +} +.content p, .roundframe p +{ + margin: 0 0 0.5em 0; +} +.content fieldset +{ + border: 2px groove #fff; + padding: 1em; + margin: 0 0 0.3em 0; +} + +/* Reset header margins. */ +h1, h2, h3, h4, h5, h6 +{ + font-size: 1em; + margin: 0; + padding: 0; +} + +/* Alternative for u tag */ +.underline +{ + text-decoration: underline; +} + +/* Common classes for easy styling. +------------------------------------------------------- */ + +.floatright +{ + float: right; +} +.floatleft +{ + float: left; +} + +.flow_auto +{ + overflow: auto; +} +.flow_hidden +{ + overflow: hidden; +} +.clear +{ + clear: both; +} +.clear_left +{ + clear: left; +} +.clear_right +{ + clear: right; +} + +/* Default font sizes: small (8pt), normal (10pt), and large (14pt). */ +.smalltext, tr.smalltext th +{ + font-size: 0.85em; + font-family: verdana, sans-serif; +} +.middletext +{ + font-size: 0.9em; + font-family: verdana, sans-serif; +} +.normaltext +{ + font-size: 1em; + line-height: 1.2em; +} +.largetext +{ + font-size: 1.4em; +} +.centertext +{ + margin: 0 auto; + text-align: center; +} +.righttext +{ + margin-left: auto; + margin-right: 0; + text-align: right; +} +.lefttext +{ + margin-left: 0; + margin-right: auto; + text-align: left; +} +/* some common padding styles */ +.padding +{ + padding: 0.7em; +} +.main_section, .lower_padding +{ + padding-bottom: 0.5em; +} +/* a quick reset list class. */ +ul.reset, ul.reset li +{ + padding: 0; + margin: 0; + list-style: none; +} + +/* the page navigation area */ +.pagesection +{ + font-size: 0.9em; + padding: 0.5em; + overflow: hidden; +} +.pagesection .pagelinks +{ + padding: 0.5em 0; +} + +/* GenericList */ +table.table_grid thead tr.catbg th.smalltext +{ + white-space: nowrap; +} + +.custom_fields_above_signature +{ + clear: right; + padding: 1em 0 3px 0; + width: 98%; + border-top: 1px solid #666; + line-height: 1.4em; + font-size: 0.85em; +} + +/* Semantic classes introduced per RC2, used as alternatives for .windowbg and .windowbg2 +------------------------------------------------------------------------------------------ */ +.description +{ + padding: 1em; + font-size: 0.9em; + line-height: 1.5em; + border: 1px solid #bbb; + background: #f5f5f0; + margin: 0 0 1em 0; +} +.information +{ + padding: 0.5em 1em; + font-size: 0.9em; + line-height: 1.5em; + border: 1px solid #bbb; + background: #f0f6f0; + margin: 0 0 1em 0; +} +.information p +{ + padding: 1em; + margin: 0; +} + +/* Lists with settings use these a lot. +------------------------------------------------------- */ +dl.settings +{ + clear: right; + overflow: auto; + margin: 0 0 10px 0; + padding: 0; +} +dl.settings dt +{ + width: 48%; + float: left; + margin: 0 0 10px 0; + padding: 0; + clear: both; +} +dl.settings dt.settings_title +{ + width: 100%; + float: none; + margin: 0 0 10px 0; + padding: 5px 0 0 0; + font-weight: bold; + clear: both; +} +dl.settings dt.windowbg +{ + width: 98%; + float: left; + margin: 0 0 3px 0; + padding: 0 0 5px 0; + clear: both; +} +dl.settings dd +{ + width: 48%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} +dl.settings img +{ + margin: 0 10px 0 0; +} + +/* Styles for a very basic dropdown menu implementation. +------------------------------------------------------- */ +div#admin_menu +{ + margin: 1em 0 0 0; +} + +ul.dropmenu, ul.dropmenu li ul +{ + margin: 0; + padding: 0; + list-style: none; +} +ul.dropmenu +{ + margin: 0 0 0 15px; +} +ul.dropmenu li +{ + position: relative; + float: left; + padding-right: 4px; + text-transform: uppercase; +} +ul.dropmenu li a.firstlevel +{ + margin: 0; + padding: 5px; + cursor: default; + font-size: x-small; + color: #000; + background: #f0f0f0; + border: 1px solid #818181; + text-decoration: none; +} +ul.dropmenu li a.active +{ + padding-left: 3px; +} +ul.dropmenu li a.active.firstlevel +{ + background: #819db5; + color: #fff; +} +ul.dropmenu li ul li +{ + background: none; + width: 14em; + float: none; + margin: 0; + padding: 0; +} +ul.dropmenu li ul +{ + margin: 5px 0 0 0; + z-index: 90; + display: none; + position: absolute; + top: 100%; + border: 1px solid #808080; + background: #f8f8fb; +} +ul.dropmenu li ul li ul, ul.dropmenu li ul li.over ul +{ + display: none; + position: absolute; + left: -999em; + top: 0; + border: 1px solid #a0a0a0; + background: #fff; +} +ul.dropmenu li ul li a +{ + display: block; + padding: 5px; + font-size: x-small; + text-decoration: none; + background: none; + text-transform: none; + color: #000; +} +ul.dropmenu li ul li a.active +{ + font-weight: bold; +} +ul.dropmenu li ul li a:hover, #dropmenu ul li ul li:hover +{ + background: #c8e2fb; +} +ul.dropmenu li:hover ul, ul.dropmenu li.over ul +{ + display: block; +} +ul.dropmenu li ul li:hover ul, ul.dropmenu li ul li.over ul +{ + display: block; + left: 13em; +} + +/* The dropdown menu toggle image */ +#menu_toggle +{ + float: right; + margin-right: 10px; + padding-top: 3px; +} +#menu_toggle span +{ + position: relative; + right: 5000px; +} + +.generic_tab_strip +{ + margin: 0 1em 2em; +} +.generic_tab_strip .buttonlist +{ + float: left !important; +} + + +/* The linktree. +----------------- */ +ul.linktree +{ + clear: both; + list-style: none; + margin: 1.5em 0.5em 0.5em 0.5em; + padding: 0; +} +ul.linktree li +{ + margin: 0; + padding: 0; + display: inline; + font-size: 0.8em; +} +ul.linktree li a +{ + color: #000; +} +ul.linktree li a:hover +{ + color: #cc3333; +} +ul.linktree li span +{ + font-weight: bold; +} + +/* Styles for a typical table. +------------------------------------------------------- */ +table.table_list +{ + width: 100%; +} +table.table_list p +{ + padding: 0; + margin: 0; +} +table.table_list td,table.table_list th +{ + padding: 5px; +} +table.table_list tbody.header td +{ + padding: 0; +} +table.table_list tbody.content td.stats +{ + font-size: 90%; + width: 15%; + text-align: center; +} +table.table_list tbody.content td.lastpost +{ + line-height: 1.2em; + font-size: 85%; + width: 24%; +} +table.table_list tbody.content td.icon +{ + text-align: center; + width: 6%; +} + +/* Styles for headers used in Curve templates. +------------------------------------------------------- */ +h3.catbg, h3.catbg2, h3.titlebg, h4.titlebg, h4.catbg, div.titlebg, .table_list tbody.header td +{ + overflow: hidden; + line-height: 2em; + font-weight: bold; +} +h3.titlebg, h4.titlebg, h3.catbg, h4.catbg +{ + border-left: 1px solid #adadad; + border-right: 1px solid #adadad; +} +h3.titlebg, h4.catbg +{ + padding: 0 0 0 0.5em; +} +h3.catbg img.icon, div.titlebg img.icon, h3.catbg img +{ + float: left; + margin: 5px 8px 0 0; +} +h4.catbg a.toggle img +{ + vertical-align: middle; + margin: -2px 5px 0 5px; +} + +/* Styles for the board index. +------------------------------------------------- */ + +p#stats +{ + text-align: right; +} +h3#newsfader +{ + font-size: 1em; +} +#smfNewsFader +{ + font-weight: bold; + line-height: 1.4em; + padding: 1em; + font-size: 1em; + text-align: center; +} +#upshrink_ic +{ + margin-right: 2ex; + text-align: right; +} +.categoryframe +{ + margin-top: 0.4em; +} +.categoryframe h3 +{ + margin: 0; +} +table.boardsframe +{ + width: 100%; +} +table.boardsframe td.icon +{ + text-align: center; + padding: 0.5em; + width: 6%; +} +table.boardsframe td.info +{ + width: 60%; + padding: 0; +} +table.boardsframe td.info h4 +{ + padding: 0.4em 0.4em 0 0.4em; + margin: 0; +} +table.boardsframe td.info p +{ + padding: 0 0.4em 0.5em 0.4em; + margin: 0; +} +table.boardsframe td.info p.moderators +{ + font-size: 0.8em; + font-family: verdana, sans-serif; +} +table.boardsframe td.stats +{ + width: 8%; + vertical-align: middle; + text-align: center; +} +table.boardsframe td.lastpost +{ + width: 20%; + vertical-align: top; + padding: 0.5em; +} +#posticons +{ + clear: both; + width: 100%; +} +#posticons .buttonlist +{ + margin-right: 1em; + float: right; +} + +/* the newsfader */ +#smfFadeScroller +{ + text-align: center; + overflow: auto; + color: #000000; /* shouldn't be shorthand style due to JS bug in IE! */ +} + +/* Styles for the info center on the board index. +---------------------------------------------------- */ + +#infocenterframe +{ + margin-top: 2em; + clear: both; +} +/* each section in infocenter has this class */ +.infocenter_section +{ + clear: both; +} +.infocenter_section p.section +{ + display: block; + margin: 0; + width: 30px; + text-align: center; + float: left; + padding: 0.5em 0 0 0; +} +.infocenter_section div.sectionbody +{ + margin-left: 30px; + padding: 0.3em; + border-left: 1px solid #a0a0a0; + min-height: 25px; + height: auto !important; +} +/* recent posts - or just one recent post */ +dl#infocenter_recentposts +{ + float: left; + width: 100%; + padding: 0; + margin: 0; +} +dl#infocenter_recentposts dt +{ + clear: left; + float: left; + padding: 0.1em; + width: 68%; + white-space: nowrap; + overflow: hidden; +} +dl#infocenter_recentposts dd +{ + clear: right; + float: right; + padding: 0.1em; + width: 25%; + text-align: right; + white-space: nowrap; + overflow: hidden; +} +/* login form */ +form#infocenter_login ul.horizlist label +{ + white-space: nowrap; + font-size: 90%; + font-weight: bold; +} + +/* Styles for the message (topic) index. +---------------------------------------------------- */ + +#childboards table +{ + width: 100%; +} +.modbuttons +{ + clear: both; + width: 100%; +} +.buttonlist, .buttonlist_bottom +{ + margin-right: 1em; + float: right; +} +#messageindex td.icon1, #messageindex td.icon2 +{ + text-align: center; + padding: 0.5em; + width: 5%; +} +#messageindex td.subject +{ + padding: 0.5em; +} +#messageindex td.starter +{ + text-align: center; + padding: 0.5em; + width: 14%; +} +#messageindex td.replies +{ + text-align: center; + padding: 0.5em; + width: 4%; +} +#messageindex td.views +{ + text-align: center; + padding: 0.5em; + width: 4%; +} +#messageindex td.lastpost +{ + padding: 0.5em; + width: 22%; +} +#messageindex td.moderation +{ + text-align: center; + padding: 0.5em; + width: 4%; +} +#topic_icons p +{ + display: block; + padding: 0.5em 0.5em 0.1em 0.5em; + margin: 0; + border-bottom: none; + font-weight: normal !important; +} +#topic_icons ul +{ + display: block; + padding: 0.5em 1em 0.1em 1em; + margin: 0; + border-bottom: none; + font-weight: normal !important; +} +#message_index_jump_to +{ + margin: 2em 4em 0 2em; +} +.lastpost img +{ + float: right; +} + +/* Styles for the display template (topic view). +---------------------------------------------------- */ +.linked_events +{ + clear: both; + margin: 1em 0; +} +.linked_events .edit_event +{ + color: #f00; +} +#moderationbuttons +{ + margin-left: 0.5em; +} +#postbuttons .nav, #postbuttons_lower .nav +{ + margin: 0.5em 0.5em 0 0; + text-align: right; +} +#postbuttons_lower .nav +{ + margin: 0 0.5em 0.5em 0; +} +#postbuttons, #postbuttons_lower +{ + text-align: right; +} + +/* Poll question */ +h4#pollquestion +{ + padding: 1em 0 1em 2em; +} + +/* Poll vote options */ +#poll_options ul.options +{ + border-top: 1px solid #696969; + padding: 1em 2.5em 0 2em; + margin: 0 0 1em 0; +} +#poll_options div.submitbutton +{ + clear: both; + padding: 0 0 1em 2em; +} + +#poll_options div.submitbutton.border +{ + border-bottom: 1px solid #696969; + margin: 0 0 1em 0; +} + +/* Poll results */ +#poll_options dl.options +{ + border: solid #696969; + border-width: 1px 0; + padding: 1em 2.5em 0 2em; + margin: 0 0 1em 0; +} +#poll_options dl.options dt.voted +{ + font-weight: bold; +} +#poll_options dl.options dd +{ + margin: 0.5em 0 1em 0; +} + +/* Poll notices */ +#poll_options p +{ + margin: 0 1.5em 0.2em 1.5em; + padding: 0 0.5em 0.5em 0.5em; +} + +div#pollmoderation +{ + margin: -1em 0 0 2em; + padding: 0; +} + +.approve_post +{ + margin: 2ex; + padding: 1ex; + border: 2px dashed #cc3344; + color: #000; + font-weight: bold; +} +#forumposts h3.catbg3 +{ + font-weight: normal; + padding: 0.4em; + overflow: hidden; +} +#forumposts h3.catbg3 img +{ + float: left; + vertical-align: middle; +} +#forumposts h3.catbg3 span +{ + float: left; + padding-left: 2%; +} +#forumposts h3.catbg3 span#top_subject +{ + padding-left: 9.5em; +} +.poster +{ + width: 15em; + float: left; +} +.post +{ + clear: right; +} +.postarea +{ + margin-left: 16em; +} +.messageicon +{ + float: left; + margin: 0 0.5em 0.5em 0; +} +.messageicon img +{ + padding: 6px 3px; +} +.keyinfo +{ + float: left; + clear: none; + width: 50%; + min-height: 3em; +} +ul.postingbuttons +{ + float: right; + padding: 0 0.5em 0 0; +} +ul.postingbuttons li +{ + float: left; + margin: 0 0.5em 0 0; +} +.modifybutton +{ + float: right; + margin: 0 0.5em 0.5em 0; +} +.attachments hr +{ + clear: both; + margin: 1em 0 1em 0; +} +.attachments +{ + padding-top: 1em; +} +.postfooter +{ + margin-left: 16em; +} +.topborder +{ + border-top: 1px solid #bbb; +} +.moderatorbar +{ + clear: right; + margin: 1em 0 0 16em; +} +#pollmoderation, #moderationbuttons_strip +{ + float: left; +} + +/* Styles for the quick reply area. +---------------------------------------------------- */ + +#quickReplyOptions #quickReplyWarning +{ + border: none; + text-align: left; + margin: 0; + width: 25%; + float: left; +} +#quickReplyOptions #quickReplyContent +{ + text-align: right; + float: left; + width: 67.5%; + padding: 1em; + border-left: 1px solid #aaa; +} + +#quickReplyOptions #quickReplyContent textarea, #quickReplyOptions #quickReplyContent input +{ + margin-bottom: .5em; +} + +#quickReplyWarning +{ + width: 20%; + float: left; + padding: 0.5em 1em; +} +#quickReplyContent +{ + width: 75%; + float: right; + padding: 0.5em 0; +} +#quickReplyOptions .roundframe +{ + overflow: hidden; +} + +/* The jump to box */ +#display_jump_to +{ + clear: both; + padding: 5px; +} + +/* Separator of posts. More useful in the print stylesheet. */ +#forumposts .post_separator +{ + display: none; +} + +/* Styles for edit post section +---------------------------------------------------- */ +form#postmodify .roundframe +{ + padding: 0 12%; +} +#post_header +{ + margin-bottom: 0.5em; + border-bottom: 1px solid #666; + padding: 0.5em; + overflow: hidden; +} +#post_header dt +{ + float: left; + margin: 0; + padding: 0; + width: 15%; + margin: .3em 0; + font-weight: bold; +} +#post_header dd +{ + float: left; + margin: 0; + padding: 0; + width: 83%; + margin: .3em 0; +} +#post_header img +{ + vertical-align: middle; +} +ul.post_options +{ + margin: 0 0 0 1em; + padding: 0; + list-style: none; + overflow: hidden; +} +ul.post_options li +{ + margin: 0.2em 0; + width: 49%; + float: left; +} +#postAdditionalOptionsHeader +{ + margin-top: 1em; +} +#postMoreOptions +{ + border-bottom: 1px solid #666; + padding: 0.5em; +} +#postAttachment, #postAttachment2 +{ + overflow: hidden; + margin: .5em 0; + padding: 0; + border-bottom: 1px solid #666; + padding: 0.5em; +} +#postAttachment dd, #postAttachment2 dd +{ + margin: .3em 0 .3em 1em; +} +#postAttachment dt, #postAttachment2 dt +{ + font-weight: bold; +} +#postAttachment3 +{ + margin-left: 1em; +} +#post_confirm_strip, #shortcuts +{ + padding: 1em 0 0 0; +} +.post_verification +{ + margin-top: .5em; +} +.post_verification #verification_control +{ + margin: .3em 0 .3em 1em; +} +/* The BBC buttons */ +#bbcBox_message +{ + margin: 1em 0 0.5em 0; +} +#bbcBox_message div +{ + margin: 0.2em 0; + vertical-align: top; +} +#bbcBox_message div img +{ + margin: 0 1px 0 0; + vertical-align: top; +} +#bbcBox_message select +{ + margin: 0 2px; +} +/* The smiley strip */ +#smileyBox_message +{ + margin: 0.75em 0 0.5em 0; +} + +/* Styles for edit event section +---------------------------------------------------- */ +#post_event .roundframe +{ + padding: 1% 12%; +} +#post_event fieldset +{ + margin-bottom: 0.5em; + border: none; + border-bottom: 1px solid #666; + padding: 0.5em; + clear: both; +} +#post_event legend +{ + font-weight: bold; + color: #000; +} +#post_event div.event_options +{ + width: 49%; + float: left; +} +#post_event ul.event_main, ul.event_options +{ + padding: 0; + overflow: hidden; +} +#post_event ul.event_main li +{ + list-style-type: none; + margin: 0.2em 0; + width: 49%; + float: left; +} +#post_event ul.event_options +{ + margin: 0; + padding: 0 0 .7em .7em; +} +#post_event ul.event_options li +{ + list-style-type: none; + margin: 0.3em 0 0 0; +} + +/* Styles for edit poll section. +---------------------------------------------------- */ + +#edit_poll fieldset +{ + margin-bottom: 0.5em; + border: none; + border-bottom: 1px solid #666; + padding: 0.5em; + clear: both; +} +#edit_poll legend +{ + font-weight: bold; + color: #000; +} +#edit_poll ul.poll_main, dl.poll_options +{ + overflow: hidden; + padding: 0 0 0 .7em; + list-style: none; +} +#edit_poll ul.poll_main li +{ + margin: 0.2em 0; +} +#edit_poll dl.poll_options dt +{ + width: 35%; +} +#edit_poll dl.poll_options dd +{ + width: 63%; +} + +/* Styles for the recent messages section. +---------------------------------------------------- */ + +.readbuttons +{ + clear: both; + width: 100%; +} +.buttonlist, .buttonlist_bottom +{ + margin-right: 1em; + float: right; +} + +/* Styles for the move topic section. +---------------------------------------------------- */ + +#move_topic dl +{ + margin-bottom: 0; +} +.move_topic +{ + width: 710px; + margin: auto; + text-align: left; +} +div.move_topic fieldset +{ + margin: 0.5em 0; + border: 1px solid #cacdd3; + padding: 0.5em; +} + +/* Styles for the send topic section. +---------------------------------------------------- */ + +fieldset.send_topic +{ + margin-bottom: 0.5em; + border: none; + padding: 0.5em; +} +dl.send_topic +{ + margin-bottom: 0; +} +dl.send_mail dt +{ + width: 35%; +} +dl.send_mail dd +{ + width: 64%; +} + +/* Styles for the split topic section. +---------------------------------------------------- */ + +div#selected, div#not_selected +{ + width: 49%; +} +ul.split_messages li.windowbg, ul.split_messages li.windowbg2 +{ + border: 1px solid #adadad; + padding: 1em; + margin: 1px; +} +ul.split_messages li a.split_icon +{ + padding: 0 0.5em; +} +ul.split_messages div.post +{ + padding: 1em 0 0 0; + border-top: 1px solid #fff; +} + +/* Styles for the merge topic section. +---------------------------------------------------- */ + +ul.merge_topics li +{ + list-style-type: none; +} +dl.merge_topic dt +{ + width: 25%; +} +dl.merge_topic dd +{ + width: 74%; +} +fieldset.merge_options +{ + margin-bottom: 0.5em; +} +fieldset.merge_options legend +{ + font-weight: bold; +} +.custom_subject +{ + margin: 0.5em 0; +} + +/* Styles for the login areas. +------------------------------------------------------- */ +.login +{ + width: 540px; + margin: 0 auto; +} +.login dl +{ + overflow: auto; + clear: right; +} +.login dt, .login dd +{ + margin: 0 0 0.4em 0; + width: 44%; + padding: 0.1em; +} +.login dt +{ + float: left; + clear: both; + text-align: right; + font-weight: bold; +} +.login dd +{ + width: 54%; + float: right; + text-align: left; +} +.login p +{ + text-align: center; +} +.login h3 img +{ + float: left; + margin: 4px 0.5em 0 0; +} + +/* Styles for the registration section. +------------------------------------------------------- */ +.register_error +{ + border: 1px dashed red; + padding: 5px; + margin: 0 1ex 1ex 1ex; +} +.register_error span +{ + text-decoration: underline; +} + +/* Additional profile fields */ +dl.register_form +{ + margin: 0; + clear: right; + overflow: auto; +} + +dl.register_form dt +{ + font-weight: normal; + float: left; + clear: both; + width: 50%; + margin: 0.5em 0 0 0; +} + +dl.register_form dt strong +{ + font-weight: bold; +} + +dl.register_form dt span +{ + display: block; +} + +dl.register_form dd +{ + float: left; + width: 49%; + margin: 0.5em 0 0 0; +} + +#confirm_buttons +{ + text-align: center; + padding: 1em 0; +} + +.coppa_contact +{ + padding: 4px; + width: 32ex; + background-color: #fff; + color: #000; + margin-left: 5ex; + border: 1px solid #000; +} + +/* Styles for maintenance mode. +------------------------------------------------------- */ +#maintenance_mode +{ + width: 75%; + min-width: 520px; + text-align: left; +} +#maintenance_mode img.floatleft +{ + margin-right: 1em; +} + +/* common for all admin sections */ +h3.titlebg img +{ + vertical-align: middle; + margin-right: 0.5em; +} +tr.titlebg td +{ + padding-left: 0.7em; +} +#admin_menu +{ + min-height: 2em; + padding-left: 0; +} +#admin_content +{ + clear: left; +} +#admin_login .centertext +{ + padding: 1em; +} +#admin_login .centertext .error +{ + padding: 0 0 1em 0; +} + +/* Styles for sidebar menus. +------------------------------------------------------- */ +.left_admmenu, .left_admmenu ul, .left_admmenu li +{ + padding: 0; + margin: 0; + list-style: none; +} +#left_admsection +{ + background-color: #ecedf3; + padding: 1px; + border: 1px solid #ADADAD; + width: 160px; + float: left; + margin-right: 10px; +} +.adm_section h4.titlebg +{ + font-size: 95%; + margin-bottom: 5px; +} +.left_admmenu li +{ + padding: 0 0 0 0.5em; +} +.left_admmenu +{ + margin-bottom: 1.1em; +} +#main_admsection +{ + margin-left: 174px; +} + +tr.windowbg td, tr.windowbg2 td +{ + padding: 0.3em 0.7em; +} +#credits p +{ + padding: 0; + font-style: italic; + margin: 0; +} + +/* Styles for generic tables. +------------------------------------------------------- */ +.topic_table table +{ + width: 100%; +} +.topic_table .icon1, .topic_table .icon2, .topic_table .stats +{ + text-align: center; +} +#topic_icons +{ + margin-top: 1em; +} +#topic_icons .description +{ + margin: 0; +} +.topic_table table thead +{ + border-bottom: 1px solid #fff; +} +/* the subject column */ +.topic_table td +{ + font-size: 1em; +} +.topic_table td.subject +{ + padding: 4px; +} +.topic_table td.subject p, .topic_table td.stats, .topic_table td.lastpost +{ + font-size: 0.85em; + padding: 0; + margin: 0; +} +.topic_table td.lastpost, .topic_table td.lastpost +{ + font-size: 0.9em; + line-height: 100%; + padding: 4px; +} +.topic_table td.stickybg2 +{ + background-image: url(../images/icons/quick_sticky.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lockedbg2 +{ + background-image: url(../images/icons/quick_lock.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lastpost +{ + background-image: none; +} + +/* Styles for (fatal) errors. +------------------------------------------------- */ + +#fatal_error +{ + border: 1px solid #aaa; +} + +.errorbox +{ + padding: 1em; + border: 1px solid #cc3344; + color: #000; + background-color: #ffe4e9; + margin: 1em 0; +} +.errorbox h3 +{ + padding: 0; + margin: 0; + font-size: 1.1em; + text-decoration: underline; +} +.errorbox p +{ + margin: 1em 0 0 0; +} +.errorbox p.alert +{ + padding: 0; + margin: 0; + float: left; + width: 1em; + font-size: 1.5em; +} + +/* Styles for the profile section. +------------------------------------------------- */ + +dl +{ + overflow: auto; + margin: 0; + padding: 0; +} + +/* Fixes for the core theme */ +#profileview +{ + padding: 1px; + border: 1px solid #696969; + background-color: #ecedf3; +} +#profileview .content +{ + border: none; +} +#basicinfo .content +{ + padding: 1em; +} +#detailedinfo .content +{ + padding: 0.7em 1.2em; + border-left: 1px solid #aaa; +} + +/* The basic user info on the left */ +#basicinfo +{ + width: 20%; + float: left; +} +#detailedinfo +{ + width: 78%; + float: right; +} +#basicinfo h4 +{ + font-size: 135%; + font-weight: 100; + line-height: 105%; + white-space: pre-wrap; /* css-2.1 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + overflow: hidden; +} +#basicinfo h4 span.position +{ + font-size: 80%; + font-weight: 100; + display: block; +} +#basicinfo img.avatar +{ + display: block; + margin: 10px 0 0 0; +} +#basicinfo ul +{ + list-style-type: none; + margin: 10px 0 0 0; +} +#basicinfo ul li +{ + display: block; + float: left; + margin-right: 5px; + height: 20px; +} +#basicinfo span#userstatus +{ + display: block; + clear: both; +} +#basicinfo span#userstatus img +{ + vertical-align: middle; +} +#detailedinfo div.content dl, #tracking div.content dl +{ + clear: right; + overflow: auto; + margin: 0 0 18px 0; + padding: 0 0 15px 0; + border-bottom: 1px solid #ccc; +} +#detailedinfo div.content dt, #tracking div.content dt +{ + width: 30%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#detailedinfo div.content dd, #tracking div.content dd +{ + width: 70%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} +#detailedinfo div.content dl.noborder +{ + border-bottom: 0; +} +#detailedinfo div.content dt.clear +{ + width: 100%; +} +.signature, .custom_fields_above_signature, .attachments +{ + width: 98%; + overflow: auto; + clear: right; + border-top: 1px solid #666; +} +.signature h5 +{ + font-size: 100%; + margin-bottom: 10px; +} +#personal_picture +{ + display: block; + margin-bottom: 0.3em; +} +#avatar_server_stored div +{ + float: left; +} + +#main_admsection #basicinfo, #main_admsection #detailedinfo +{ + width: 100%; +} +#main_admsection #detailedinfo .content +{ + border: none !important; +} +#main_admsection #basicinfo +{ + border-bottom: 1px solid #ccc; +} +#main_admsection #basicinfo h4 +{ + float: left; +} +#main_admsection #basicinfo img.avatar +{ + float: right; + vertical-align: top; +} +#main_admsection #basicinfo ul +{ + clear: left; + padding-top: 10px; +} +#main_admsection #basicinfo span#userstatus +{ + clear: left; +} +#main_admsection #basicinfo p#infolinks +{ + display: none; + clear: both; +} +#main_admsection #basicinfo .botslice +{ + clear: both; +} + +/* Simple feedback messages */ +div#profile_error, div#profile_success +{ + margin: 0 0 1em 0; + padding: 1em 2em; + border: 1px solid; +} +div#profile_error +{ + border-color: red; + color: red; + background: #fee; +} + +div#profile_error span +{ + text-decoration: underline; +} + +div#profile_success +{ + border-color: green; + color: green; + background: #efe; +} + +/* Profile statistics */ +#generalstats div.content dt +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#generalstats div.content dd +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +/* Activity by time */ +.activity_stats +{ + margin: 0; + padding: 0; + list-style: none; +} +.activity_stats li +{ + width: 4.16%; + float: left; +} +.activity_stats li span +{ + display: block; + border: solid #000; + border-width: 1px 1px 0 0; + text-align: center; +} +.activity_stats li.last span +{ + border-right: none; +} +.activity_stats li div.bar +{ + margin: 0 auto; + width: 15px; +} +.activity_stats li div.bar div +{ + background: #6294CE; +} +.activity_stats li div.bar span +{ + position: absolute; + top: -1000em; + left: -1000em; +} + +/* Most popular boards by posts and activity */ +#popularposts +{ + width: 50%; + float: left; +} +#popularactivity +{ + width: 50%; + float: right; +} + +#popularposts div.content dt, #popularactivity div.content dt +{ + width: 65%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#popularposts div.content dd, #popularactivity div.content dd +{ + width: 35%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +.profile_pie +{ + background-image: url(../images/stats_pie.png); + float: left; + height: 20px; + width: 20px; + margin: 0 1em 0 0; + padding: 0; + text-indent: -1000em; +} + +/* View posts */ +.time +{ + float: right; +} +.counter +{ + margin: 0 0 0 0; + padding: 0.2em 0.5em 0.1em 0.2em; + font-size: 2.2em; + font-weight: bold; + color: #354c5f; + float: left; +} +.list_posts +{ + border-top: 1px solid #adadad; + padding-top: 1em; + margin-top: 0.5em; +} +div.core_posts +{ + border: 1px solid #adadad; + margin-bottom: 3px; +} +div.core_posts div.content +{ + background: none; + border: none; +} +.topic h4 +{ + margin: 3px 0; +} + +.mod_icons +{ + text-align: right; + margin-right: 1em; +} + +#permissions dt +{ + width: 48%; + float: left; + line-height: 1.2em; + margin: 0; + padding: 1%; + clear: both; + border-top: 1px solid #fff; +} + +#permissions dd +{ + width: 48%; + float: left; + margin: 0; + padding: 1%; + border-top: 1px solid #fff; +} + +#tracking div.content dl +{ + border-bottom: 0; + margin: 0; + padding: 0; +} + +#creator dl +{ + margin: 0; +} +#creator dt +{ + width: 40%; + float: left; + clear: both; + margin: 0 0 10px 0; +} +#creator dd +{ + float: left; + width: 60%; + margin: 0 0 10px 0; +} + +.ignoreboards +{ + margin: 0; + padding: 0; + width: 49%; + overflow: auto; +} +.ignoreboards a +{ + text-decoration: underline; +} +.ignoreboards ul +{ + overflow: auto; + margin: 0 0 0 1em; + padding: 0; +} +.ignoreboards li +{ + list-style: none; + float: left; + clear: both; +} + +#theme_settings +{ + overflow: auto; + margin: 0; + padding: 0; +} + +#theme_settings li +{ + list-style: none; + margin: 10px 0; + padding: 0; +} +/*Paid Subscriptions*/ +#paid_subscription +{ + width: 100%; +} +#paid_subscription dl.settings +{ + margin-bottom: 0; +} +#paid_subscription dl.settings dd, #paid_subscription dl.settings dt +{ + margin-bottom: 4px; +} +/*pick theme*/ +#pick_theme +{ + width: 100%; + float: left; +} + +/* Styles for the statistics center. +------------------------------------------------- */ +#statistics +{ + padding-bottom: 0.5em; +} +#statistics h4.titlebg +{ + text-align: center; + margin-bottom: 5px; +} +#stats_left, #top_posters, #top_topics_replies, #top_topics_starter +{ + float: left; + width: 49.5%; +} +#stats_right, #top_boards, #top_topics_views, #most_online +{ + float: right; + width: 49.5%; +} +dl.stats +{ + clear: both; + overflow: hidden; + margin: 0; + padding: 0; +} +dl.stats dt +{ + width: 49%; + float: left; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; + clear: both; + font-size: 1em; +} +dl.stats dd +{ + text-align: right; + width: 50%; + font-size: 1em; + float: right; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; +} +.stats_bar +{ + float: left; + background-image: url(../images/bar_stats.png); + height: 16px; + font-size: 0.9em; + display: block; + text-align: left; + color: #fff; + font-weight: bold; + background-position: top center; +} +.stats_bar span +{ + padding-left: 2px; +} + +/* Styles for the personal messages section. +------------------------------------------------- */ + +#personal_messages +{ + padding: 1px; +} +#personal_messages #top_subject +{ + padding-left: 11.75em !important; +} +#personal_messages div.labels +{ + padding: 0 1em 0 0; +} +#personal_messages .capacity_bar +{ + background: #fff; + border: 1px solid #000; + height: 7px; + width: 75%; + margin: 0 auto; +} +#personal_messages .capacity_bar div +{ + border: none; + height: 7px; +} +#personal_messages .capacity_bar div.empty +{ + background: #468008; +} +#personal_messages .capacity_bar div.filled +{ + background: #EEA800; +} +#personal_messages .capacity_bar div.full +{ + background: #A53D05; +} +#personal_messages .reportlinks +{ + padding: 0.5em 1.3em; +} + +/* Styles for the calendar section. +------------------------------------------------- */ +.calendar_table +{ + margin-bottom: 0.7em; +} + +/* Used to indicate the current day in the grid. */ +.calendar_today +{ + background-color: #fff; +} + +#month_grid +{ + width: 200px; + text-align: center; + float: left; +} + +#month_grid table +{ + width: 200px; + border-collapse: collapse; + border: 1px solid #adadad; +} + +#month_grid table td, #month_grid table th +{ + border: 1px solid #adadad; +} + +#main_grid table +{ + width: 100%; + padding-bottom: 4px; + border-collapse: collapse; + border: 1px solid #adadad; +} + +#main_grid table td, #main_grid table th +{ + border: 1px solid #adadad; +} + +#main_grid table h3.catbg +{ + text-align: center; + + border-top: 1px solid #adadad; + border-bottom: none; +} + +#main_grid table h4 +{ + border: none; +} + +#main_grid table.weeklist td.windowbg +{ + text-align: center; + height: 49px; + width: 25px; + font-size: large; + padding: 0 7px; + border-bottom: 1px solid #adadad; +} + +#main_grid table.weeklist td.weekdays +{ + height: 49px; + width: 100%; + padding: 4px; + text-align: left; + vertical-align: middle; + border-right: 1px solid #adadad; + border-bottom: 1px solid #adadad; +} + +#main_grid h3.weekly +{ + text-align: center; + padding-left: 0; + font-size: large; + height: 29px; +} + +#main_grid h3 span.floatleft, #main_grid h3 span.floatright +{ + display: block; + +} + +#main_grid table th.days +{ + width: 14%; +} + +#main_grid table td.weeks +{ + vertical-align: middle; + text-align: center; +} + +#main_grid table td.days +{ + vertical-align: top; + +} + +a.modify_event +{ + color: red; +} + +span.hidelink +{ + font-style: italic; +} + +#calendar_navigation +{ + text-align: center; +} + +#calendar .buttonlist_bottom +{ + border-bottom: 1px solid #adadad; + padding: 0 0 0 1ex; + margin: 0 0 1ex 0; +} + +/* Styles for the memberlist section. +------------------------------------------------- */ +#mlist_search +{ + margin: auto; + width: 500px; +} +span.memberstatsbar, span.memberstatsbar span +{ + height: 1.1em; + display: block; +} +span.memberstatsbar +{ + background: #fff; + border: 1px solid #888; +} +span.memberstatsbar span +{ + background: #fe9540; +} + +/* Styles for the basic search section. +------------------------------------------------- */ +#simple_search p +{ + padding: 0.5em; +} +#simple_search, #simple_search p, #advanced_search +{ + text-align: center !important; + margin: 0; +} +#search_error +{ + font-style: italic; + padding: 0.3em 1em; +} +#search_term_input +{ + font-size: 115%; + margin: 0 0 1em; +} + +/* Styles for the advanced search section. +------------------------------------------------- */ +#searchform fieldset +{ + text-align: left; + padding: 0; + margin: 0.5em 0; + border: none; +} +#advanced_search dl#search_options +{ + margin: 0 auto; + width: 600px; + padding-top: 1em; + overflow: hidden; +} +#advanced_search dt +{ + clear: both; + float: left; + padding: 0.2em; + text-align: right; + width: 20%; +} +#advanced_search dd +{ + width: 75%; + float: left; + padding: 0.2em; + margin: 0 0 0 0.5em; + text-align: left; +} +#searchform p.clear +{ + clear: both; +} + +/* Boards picker */ +#searchform fieldset div#searchBoardsExpand ul +{ + overflow: auto; + margin: 0 0 0 1em; + padding: 0; + width: 48%; +} +#searchform fieldset div#searchBoardsExpand ul ul +{ + width: auto; +} +#searchform fieldset div#searchBoardsExpand a +{ + text-decoration: underline; +} +#searchform fieldset div#searchBoardsExpand li +{ + list-style: none; + float: left; + clear: both; +} +#searchform fieldset p +{ + padding: 4px; + text-align: left; + margin-top: 5px; +} + +/* Styles for the search results page. +------------------------------------------------- */ +.pagelinks +{ + padding: 0.5em; +} +.topic_table td blockquote, .topic_table td .quoteheader +{ + margin: 0.5em; +} +.search_results_posts +{ + overflow: hidden; +} +.search_results_posts .inner +{ + padding: 0.5em 1em; + overflow: hidden; +} +.search_results_posts .windowbg2 +{ + margin-top: 4px; +} +.search_results_posts .buttons +{ + padding: 5px 1em 0 0; +} + +/* Styles for the help section. +------------------------------------------------- */ + +#helpmain +{ + padding: 1em; + border: 1px solid #696969; +} + +/* Samples should be easily distinguishable. */ +#helpmain .help_sample +{ + border: 1px solid #99a; + background: #fff; + padding: 1em; + overflow: auto; + margin-bottom: 1em; +} +#helpmain .help_sample .linktree +{ + font-weight: bold; +} + +/* We need some air between the lines */ +#helpmain p +{ + margin: 0 0 1.5em 0; + line-height: 1.5em; +} + +#helpmain ol +{ + font-weight: bold; + list-style-type: disc; + margin-bottom: 1em; + margin-top: 1em; + line-height: 1.5em; +} +#helpmain ol.la +{ + font-weight: normal; + list-style-type: circle; + margin: 0.5em 0 1em 0; + padding-left: 1.5em; +} + +ul.basic_helplist +{ + padding: 0.8em 1.5em; + line-height: 1.5em; +} +#helpmain .boardsframe p +{ + margin: 0; +} +#helpmain #messageindex +{ + clear: right; +} + +/* ...but leave the tab strips alone! */ +#helpmain .buttonlist_bottom ul, #helpmain .buttonlist ul +{ + margin: 0 !important; + padding: 0 0 0 1em !important; +} + +#helpmain .buttonlist_bottom ul li, #helpmain .buttonlist ul li +{ + margin: 0 0.2em 0 0 !important; + padding: 0 !important; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/editor.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/editor.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,32 @@ +/* This is the editor's playground (textarea for non-wysiwyg, iframe for wysiwyg). */ +.editor +{ + width: 100%; + max-width: 100%; + min-width: 100%; +} + +.editor, .rich_editor_frame +{ + border: 1px solid #808080; + padding: 2px !important; + margin: 0; +} + +.rich_editor_frame +{ + background: #fff; +} + +/* The resize handle. */ +.richedit_resize +{ + height: 5px; + font-size: 0; + background: #eee url(../images/bbc/resize-handle.gif) no-repeat 50% 1px; + border: 1px solid #ddd; + border-top-width: 0; + cursor: s-resize; + width: 100%; + padding: 0 2px; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/editor_ie.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/editor_ie.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,45 @@ +/* This is the editor's playground (textarea for non-wysiwyg, iframe for wysiwyg). */ +.editor +{ + width: 635px; + max-width: 100%; + min-width: 100%; +} + +/* This is the IFRAME that holds the editor. */ +.rich_editor_frame +{ + border: 1px solid #808080; +} + +/* This is the WYSIWYG editor */ +.rich_editor +{ + background-color: #fff; + color: #000; + font-family: verdana; + font-size: x-small; + border: none; +} + +.rich_editor p +{ + margin: 0; +} + +.rich_editor a img +{ + border: 0; +} + +/* The resize handle. */ +.richedit_resize +{ + height: 5px; + font-size: 0; + background: #eee url(../images/bbc/resize-handle.gif) no-repeat 50% 1px; + border: 1px solid #ddd; + border-top-width: 0; + cursor: s-resize; + width: 100%; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/ie6.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/ie6.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,208 @@ +.codeheader, code.bbc_code +{ + width: 96%; + margin: 0 auto; +} +code.bbc_code +{ + white-space: normal; +} +h3.catbg input.input_check +{ + margin: 0 4px; +} +h3.catbg img.icon, h4.titlebg img.icon +{ + margin: 1px 3px 0 0; +} +h3.catbg span.ie6_header, h4.catbg span.ie6_header, h3.titlebg span.ie6_header, h4.titlebg span.ie6_header +{ + padding: 6px 0; +} +#statistics h4.titlebg span.ie6_header +{ + padding: 0; +} +#statistics h4.titlebg span.ie6_header img.icon +{ + padding: 5px 0; +} +/* The dropdown menus +------------------------------------------------------- */ + +.dropmenu li +{ + width: 1px; +} +.dropmenu li a span +{ + white-space: nowrap; +} +.dropmenu li a:hover +{ + text-decoration: none; +} +.dropmenu li.iehover +{ + z-index: 120; +} + +/* the page section */ +.pagesection +{ + overflow: auto; +} +/* the user section needs some attention */ +#main_menu +{ + width: 98%; +} +#top_section +{ + height: 65px; +} + +/* the tabled definition lists */ +/* I commented the following out. Not sure why it was there. +/* Changing float: left; to float: right; sorts the settings dd class in index.css*/ +/* All the others seem fine too.*/ +/*dl.settings dd, #creator dd, dl.stats dd, dl.register_form dd, #poll_options dl.options dd, .login dd +{ + float: none !important; + width: auto; +}*/ +/* generic lists header */ +/* Side paddings must NOT be defined here.*/ +.table_grid thead th +{ + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +/* overflow: hidden doesn't help in IE6. */ +h3.titlebg a, h3.titlebg, h4.titlebg, h4.titlebg a +{ + display: inline-block; +} + +#upper_section +{ + display: inline-block; +} + +/* Overrides for the message index template +------------------------------------------------------- */ +#messageindex table +{ + margin-top: 5px; +} +#messageindex table th +{ + border-bottom: 1px solid #fff; +} +#topic_icons .description +{ + padding: 2em 1em 1em 1em; + overflow: auto; +} + +/* Overrides for the display template +------------------------------------------------------- */ +#forumposts .postarea +{ + margin-left: 0; + margin-right: 0; + float: right; +} +.keyinfo +{ + padding-bottom: 6px; +} +.inner +{ + clear: both; +} +.post +{ + word-wrap: break-word; +} +.buttonlist ul li +{ + width: 1%; + white-space: nowrap; +} +#forumposts h3.catbg +{ + clear: both; +} +#quickReplyOptions form textarea +{ + width: 98%; +} + +/* Styles for the statistics center. +------------------------------------------------- */ +#statistics div.content +{ + height: 210px; + overflow: hidden; +} +#statistics div.top_row +{ + height: 150px; +} + +/* Overrides for the admin template +------------------------------------------------------- */ +#main_admsection +{ + height: 100%; +} +#main_admsection table +{ + width: 99%; +} + +/* Overrides for the profile template +------------------------------------------------------- */ +#basicinfo h4 +{ + word-wrap: break-word; +} +.ignoreboards +{ + margin: 0 1%; + padding: 0; + width: 45%; +} + +/* Overrides for the personal messages template +------------------------------------------------------- */ +#personal_messages .postarea +{ + margin-left: 0; + margin-right: 0; + float: right; +} + +/* Overrides for the admin section of the register template +------------------------------------------------------- */ +#registration_agreement +{ + width: 99.5%; + margin: 0 auto; +} + +#edit_poll ul.poll_main li +{ + padding-left: 0; + margin: 0 -2em; +} +#postmodify div.roundframe { margin-right: 0;} + +/* Overrides for the recent posts template +------------------------------------------------------- */ +.list_posts +{ + word-wrap: break-word; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/ie7.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/ie7.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,103 @@ +code.bbc_code +{ + white-space: normal; +} +h3.catbg input.input_check +{ + margin: 0 4px; +} + +/* The dropdown menus +------------------------------------------------------- */ +/* the dropmenu - RTL tweak */ +.dropmenu li ul +{ + margin: 0 -50px 0 0; +} +/* the hover effects */ +.dropmenu li.iehover +{ + z-index: 120; +} +/* the tabled definition lists +/* I commented the following out. Not sure why it was there. +/* Changing float: left; to float: right; sorts the settings dd class in index.css*/ +/* All the others seem fine too.*/ +/*dl.settings dd, #creator dd, dl.stats dd, dl.register_form dd, #poll_options dl.options dd, .login dd +{ + float: none !important; + width: auto; +}*/ +/* generic lists header */ +/* Side paddings must NOT be defined here.*/ +.table_grid thead th +{ + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +/* Overrides for the messageindex template +------------------------------------------------------- */ +#messageindex table +{ + margin-top: 5px; +} +#messageindex table th +{ + border-bottom: 1px solid #fff; +} +#topic_icons .description +{ + padding: 2em 1em 1em 1em; + overflow: auto; +} + +/* Overrides for the display template +------------------------------------------------------- */ +.post +{ + padding-top: 1em; + float: none; + word-wrap: break-word; +} +#content_section #forumposts div.cat_bar +{ + margin-top: 8px; + clear: both; +} +#content_section .pagesection +{ + height: 1%; +} +#quickReplyOptions form textarea +{ + width: 98%; +} + +/* Overrides for the profile template +------------------------------------------------------- */ +#basicinfo h4 +{ + word-wrap: break-word; +} + +/* Overrides for the calendar template +------------------------------------------------- */ +#main_grid table.weeklist h4.titlebg +{ + margin: 2px 0 -4px 0; +} + +/* Overrides for the personal messages template +------------------------------------------------------- */ +#postmodify dl #pm_to, #postmodify dl #bcc_div2, #postmodify dl #pm_subject +{ + clear:both !important; +} + +/* Overrides for the recent posts template +------------------------------------------------------- */ +.list_posts +{ + word-wrap: break-word; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/index.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/index.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,3620 @@ +/* Styles for the general looks for the Curve theme. +------------------------------------------------------- */ + +/* Normal, standard links. */ +a:link, a:visited +{ + color: #346; + text-decoration: none; +} +a:hover +{ + text-decoration: underline; + cursor: pointer; +} + +/* Links that open in a new window. */ +a.new_win:link, a.new_win:visited +{ + color: #346; + text-decoration: none; +} +a.new_win:hover +{ + text-decoration: underline; +} + +/* Tables should show empty cells. */ +table +{ + empty-cells: show; +} + +/* Set a fontsize that will look the same in all browsers. */ +body +{ + background: #E9EEF2 url(../images/theme/backdrop.png) repeat-x; + font: 78%/130% "Verdana", "Arial", "Helvetica", sans-serif; + margin: 0 auto; + padding: 15px 0; +} + +/* Help popups require a different styling of the body element. */ +body#help_popup +{ + padding: 1em; +} + +/* use dark grey for the text, leaving #000 for headers etc */ +body, td, th, tr +{ + color: #444; +} + +/* This division wraps the entire forum when a forum width is set. */ +div#wrapper +{ + margin: 0 auto; + min-width: 764px; + max-width: 2300px; +} + +/* lets give all forms zero padding/margins */ +form +{ + padding: 0; + margin: 0; +} + +/* We can style the different types of input buttons to be uniform throughout different browsers and their color themes. + .button_submit - covers input[type=submit], input[type=button], button[type=submit] and button[type=button] in all browsers + .button_reset - covers input[type=reset] and button[type=reset] throughout all browsers + .input_check - covers input[type=checkbox] throughout all browsers + .input_radio - covers input[type=radio] throughout all browsers + .input_text - covers input[type=text] throughout all browsers + .input_file - covers input[type=file] throughout all browsers +*/ + +input, button, select, textarea +{ + font: 95%/115% verdana, Helvetica, sans-serif; + color: #000; + background: #fff; + border: 1px solid #7f9db9; + padding: 2px; +} + +/* Select elements look horrible with the extra padding, so leave them unpadded. */ +select +{ + padding: 0; +} + +/* Add some padding to the options instead. */ +select option +{ + padding: 1px; +} + +/* The font size of textareas should be just a little bit larger. */ +textarea +{ + font: 100%/130% verdana, Helvetica, sans-serif; +} + +/* Buttons should be styled a bit differently, in order to make them look more button'ish. */ +.button_submit, .button_reset +{ + background: #cde7ff url(../images/theme/submit_bg.png) no-repeat; + border: 1px solid #aaa; + cursor: pointer; + font-weight: normal; +} +input:hover, textarea:hover, button:hover, select:hover +{ + border: 1px solid #454545; +} +.button_submit:hover, .button_reset:hover +{ + border: 1px solid #aaa; + background: url(../images/theme/submit_bg.png) no-repeat 0 -140px #cde7ff; +} +input:focus, textarea:focus, button:focus, select:focus +{ + border: 1px solid #454545; +} + +/* All input elements that are checkboxes or radio buttons shouldn't have a border around them. */ +input.input_check, input.input_radio +{ + border: none; + background: none; +} +h3.catbg input.input_check +{ + margin: 9px 7px 0 7px; +} + +/* Give disabled text input elements a different background color. */ +input[disabled].input_text +{ + background-color: #eee; +} + +/* Standard horizontal rule.. ([hr], etc.) */ +hr, .hrcolor +{ + height: 1px; + border: 0; + color: #ccc; + background-color: #ccc; +} + +/* By default set the color on these tags as #000. */ +h1, h2, h3, h4, h5, h6 +{ + color: #000; + font-size: 1em; + margin: 0; + padding: 0; +} + +/* Fieldsets are used to group elements. */ +fieldset +{ + border: 1px solid #c4c4c4; + padding: 1em; + margin: 0 0 0.5em 0; +} +fieldset legend +{ + font-weight: bold; + color: #444; +} +/* No image should have a border when linked. */ +a img +{ + border: 0; +} + +/* Define strong as bold, and em as italics */ +strong +{ + font-weight: bold; +} + +em +{ + font-style: italic; +} +/* Alternative for u tag */ +.underline +{ + text-decoration: underline; +} + +/* Common classes to easy styling. +------------------------------------------------------- */ + +.floatright +{ + float: right; +} +.floatleft +{ + float: left; +} + +.flow_auto +{ + overflow: auto; +} +.flow_hidden +{ + overflow: hidden; +} +.flow_hidden .windowbg, .flow_hidden .windowbg2 +{ + margin-top: 2px; +} +.clear +{ + clear: both; +} +.clear_left +{ + clear: left; +} +.clear_right +{ + clear: right; +} + +/* Default font sizes: small (8pt), normal (10pt), and large (14pt). */ +.smalltext, tr.smalltext th +{ + font-size: 0.85em; + font-family: verdana, sans-serif; +} +.middletext +{ + font-size: 0.9em; + line-height: 1em; + font-family: verdana, sans-serif; +} +.normaltext +{ + font-size: 1em; + line-height: 1.2em; +} +.largetext +{ + font-size: 1.4em; +} +.centertext +{ + margin: 0 auto; + text-align: center; +} +.righttext +{ + margin-left: auto; + margin-right: 0; + text-align: right; +} +.lefttext +{ + margin-left: 0; + margin-right: auto; + text-align: left; +} +.double_height +{ + line-height: 2em; +} +/* some common padding styles */ +.padding +{ + padding: 0.7em; +} +.main_section, .lower_padding +{ + padding-bottom: 0.5em; +} +/* a quick reset list class. */ +ul.reset, ul.reset li +{ + padding: 0; + margin: 0; + list-style: none; +} + +/* Some BBC related styles. +------------------------------------------------------- */ + +/* A quote, perhaps from another post. */ +blockquote.bbc_standard_quote, blockquote.bbc_alternate_quote +{ + font-size: x-small; + color: #000; + line-height: 1.4em; + background: url(../images/theme/quote.png) 0.1em 0.1em no-repeat; + border-top: 2px solid #99A; + border-bottom: 2px solid #99A; + padding: 1.1em 1.4em; + margin: 0.1em 0 0.3em 0; + overflow: auto; +} + +/* Alterate blockquote stylings */ +blockquote.bbc_standard_quote +{ + background-color: #d7daec; +} +blockquote.bbc_alternate_quote +{ + background-color: #e7eafc; +} + +/* A code block - maybe PHP ;). */ +code.bbc_code +{ + display: block; + font-family: "dejavu sans mono", "monaco", "lucida console", "courier new", monospace; + font-size: x-small; + background: #eef; + border-top: 2px solid #999; + border-bottom: 2px solid #999; + line-height: 1.5em; + padding: 3px 1em; + overflow: auto; + white-space: nowrap; + /* Show a scrollbar after about 24 lines. */ + max-height: 24em; +} + +/* The "Quote:" and "Code:" header parts... */ +.codeheader, .quoteheader +{ + color: #666; + font-size: x-small; + font-weight: bold; + padding: 0 0.3em; +} + +/* For links to change the code stuff... */ +.codeoperation +{ + font-weight: normal; +} + +/* Styling for BBC tags */ +.bbc_link:link, .bbc_link:visited +{ + border-bottom: 1px solid #A8B6CF; +} +.bbc_link:hover +{ + text-decoration: none; + border-bottom: 1px solid #346; +} +.bbc_size +{ + line-height: 1.4em; +} +.bbc_color a +{ + color: inherit; +} +.bbc_img +{ + border: 0; +} +.bbc_table +{ + font: inherit; + color: inherit; +} +.bbc_table td +{ + font: inherit; + color: inherit; + vertical-align: top; +} +.bbc_u +{ + text-decoration: underline; +} +.bbc_list +{ + text-align: left; +} +.bbc_tt +{ + font-family: "dejavu sans mono", "monaco", "lucida console", "courier new", monospace; +} + +/* Generally, those [?] icons. This makes your cursor a help icon. */ +.help +{ + cursor: help; +} + +/* /me uses this a lot. (emote, try typing /me in a post.) */ +.meaction +{ + color: red; +} + +/* Highlighted text - such as search results. */ +.highlight +{ + font-weight: bold; + color: #ff7200 !important; + font-size: 1.1em; +} + +/* A more discreet highlight color, for selected membergroups etc. */ +.highlight2 +{ + background-color: #D1E1EF; + color: #000 !important; +} + +/* Generic, mostly color-related, classes. +------------------------------------------------------- */ + +.titlebg, .titlebg2, tr.titlebg th, tr.titlebg td, tr.titlebg2 td +{ + color: #222; + font-family: arial, helvetica, sans-serif; + font-size: 1.1em; + font-weight: bold; + background: #e3e9ef url(../images/theme/main_block.png) no-repeat -10px -380px; +} +.catbg, .catbg2, tr.catbg td, tr.catbg2 td, tr.catbg th, tr.catbg2 th +{ + color: #fff; + font-family: arial, helvetica, sans-serif; + font-size: 1.1em; + font-weight: bold; + background: #a7b9cd url(../images/theme/main_block.png) no-repeat -10px -280px; +} + +/* adjust the table versions of headers */ +tr.titlebg th, tr.titlebg2 th, td.titlebg, td.titlebg2, tr.catbg th, tr.catbg2 th, td.catbg, td.catbg2 +{ + padding: 0 6px; +} +tr.titlebg th a:link, tr.titlebg th a:visited, tr.titlebg2 td a:link, tr.titlebg2 td a:visited +{ + color: #222; +} +tr.catbg th a:link, tr.catbg th a:visited, tr.catbg2 td a:link, tr.catbg2 td a:visited +{ + color: #fff; +} +.catbg select +{ + height: 1.5em; + font-size: 0.85em; +} + +/* Alternating backgrounds for posts, and several other sections of the forum. */ +.windowbg, #preview_body +{ + color: #000; + background-color: #e7eaef; +} +.windowbg2 +{ + color: #000; + background-color: #f0f4f7; +} +.windowbg3 +{ + color: #000; + background-color: #cacdd3; +} + +/* the page navigation area */ +.pagesection +{ + font-size: 0.9em; + padding: 0.2em; + overflow: hidden; + margin-bottom: 1px; +} +div.pagesection div.floatright input +{ + margin-top: 3px; +} + +.pagelinks +{ + padding: 0.6em 0 0.4em 0; +} + +/* Colors for background of posts requiring approval */ +.approvebg +{ + color: #000; + background-color: #ffeaea; +} +.approvebg2 +{ + color: #000; + background-color: #fff2f2; +} + +/* Color for background of *topics* requiring approval */ +.approvetbg +{ + color: #000; + background-color: #e4a17c; +} +.approvetbg2 +{ + color: #000; + background-color: #f3bd9f; +} + +/* Sticky topics get a different background */ +.stickybg +{ + background: #e8d8cf; +} +.stickybg2 +{ + background: #f2e3d9; +} + +/* Locked posts get a different shade, too! */ +.lockedbg +{ + background: #d4dce2; + font-style: italic; +} +.lockedbg2 +{ + background: #d8e1e7; + font-style: italic; +} + +/* Posts and personal messages displayed throughout the forum. */ +.post, .personalmessage +{ + overflow: auto; + line-height: 1.4em; + padding: 0.1em 0; +} + +/* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ +.signature, .attachments +{ + width: 98%; + overflow: auto; + clear: right; + padding: 1em 0 3px 0; + border-top: 1px solid #aaa; + line-height: 1.4em; + font-size: 0.85em; +} +.custom_fields_above_signature +{ + width: 98%; + clear: right; + padding: 1em 0 3px 0; + border-top: 1px solid #aaa; + line-height: 1.4em; + font-size: 0.85em; +} + +/* Sometimes there will be an error when you post */ +.error +{ + color: red; +} + +/* Messages that somehow need to attract the attention. */ +.alert +{ + color: red; +} + +/* Calendar colors for birthdays, events and holidays */ +.birthday +{ + color: #920ac4; +} + +.event +{ + color: #078907; +} + +.holiday +{ + color: #000080; +} + +/* Colors for warnings */ +.warn_mute +{ + color: red; +} + +.warn_moderate +{ + color: #ffa500; +} + +.warn_watch, .success +{ + color: green; +} + +a.moderation_link, a.moderation_link:visited +{ + color: red; + font-weight: bold; +} + +.openid_login +{ + background: white url(../images/openid.gif) no-repeat; + padding-left: 18px; +} + +/* a descriptive style */ +.description, .description_board, .plainbox +{ + padding: 0.5em 1em; + font-size: 0.9em; + line-height: 1.4em; + border: 1px solid #bbb; + background: #f5f5f0; + margin: 0.2em 1px 1em 1px; +} +.description_board +{ + margin: 1em 1px 0 1px; +} + +/* an informative style */ +.information +{ + padding: 0.5em 1em; + font-size: 0.9em; + line-height: 1.3em; + border: 1px solid #bbb; + background: #f0f6f0; + margin: 0.2em 1px 1em 1px; +} +.information p +{ + padding: 1em; + margin: 0; +} +p.para2 +{ + padding: 1em 0 3.5em 0; + margin: 0; +} +/* AJAX notification bar +------------------------------------------------------- */ +#ajax_in_progress +{ + background: url(../images/theme/loadingbar.png) repeat-x; + color: #f96f00; + text-align: center; + font-size: 16pt; + padding: 8px; + width: 100%; + height: 66px; + line-height: 25px; + position: fixed; + top: 0; + left: 0; +} + +#ajax_in_progress a +{ + color: orange; + text-decoration: underline; + font-size: smaller; + float: right; + margin-right: 20px; +} + +/* Lists with settings use these a lot. +------------------------------------------------------- */ +dl.settings +{ + clear: right; + overflow: auto; + margin: 0 0 10px 0; + padding: 0; +} +dl.settings dt +{ + width: 40%; + float: left; + margin: 0 0 10px 0; + padding: 0; + clear: both; +} +dl.settings dt.settings_title +{ + width: 100%; + float: none; + margin: 0 0 10px 0; + padding: 5px 0 0 0; + font-weight: bold; + clear: both; +} +dl.settings dt.windowbg +{ + width: 98%; + float: left; + margin: 0 0 3px 0; + padding: 0 0 5px 0; + clear: both; +} +dl.settings dd +{ + width: 56%; + float: right; + overflow: auto; + margin: 0 0 3px 0; + padding: 0; +} +dl.settings img +{ + margin: 0 10px 0 0; +} +/* help icons */ +dl.settings dt a img +{ + position: relative; + top: 2px; +} + +/* Styles for rounded headers. +------------------------------------------------------- */ +h3.catbg, h3.catbg2, h3.titlebg, h4.titlebg, h4.catbg +{ + overflow: hidden; + height: 31px; + line-height: 31px; + font-size: 1.2em; + font-weight: bold; +} +h3.catbg a:link, h3.catbg a:visited, h4.catbg a:link, h4.catbg a:visited, h3.catbg, .table_list tbody.header td, .table_list tbody.header td a +{ + color: #fff; +} +h3.catbg2 a, h3.catbg2 +{ + color: #feb; +} +h3.catbg a:hover, h4.catbg a:hover, .table_list tbody.header td a:hover +{ + color: #fd9; + text-decoration: none; +} +h3.catbg2 a:hover +{ + color: #fff; + text-decoration: none; +} +h3.titlebg a, h3.titlebg, h4.titlebg, h4.titlebg a +{ + color: #222; +} +h3.titlebg a:hover, h4.titlebg a:hover +{ + color: #53616f; + text-decoration: none; +} +h3.catbg img.icon, h4.titlebg img.icon +{ + vertical-align: middle; + margin: -2px 5px 0 0; +} +h4.catbg a.toggle img +{ + vertical-align: middle; + margin: -2px 5px 0 5px; +} +h4.catbg, h4.catbg2 , h3.catbg , h3.catbg2 , .table_list tbody.header td.catbg +{ + background: url(../images/theme/main_block.png) no-repeat 100% -160px; + padding-right: 9px; +} +h4.titlebg, h3.titlebg +{ + background: url(../images/theme/main_block.png) no-repeat 100% -200px; + padding-right: 9px; +} +h4.titlebg img.icon +{ + float: left; + margin: 5px 8px 0 0; +} +div.cat_bar +{ + background: #99abbf url(../images/theme/main_block.png) no-repeat 0 -160px; + padding-left: 9px; + height: 31px; + overflow: hidden; + margin-bottom: 1px; +} +div.title_bar +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 0 -200px; + padding-left: 9px; + height: 31px; + overflow: hidden; + margin-bottom: 1px; +} + +/* rounded bars needs a different background here */ + +div.roundframe div.cat_bar +{ + background: #99abbf url(../images/theme/main_block.png) no-repeat 0 -240px; + margin-bottom: 0; +} +div.roundframe div.cat_bar h3.catbg +{ + background: url(../images/theme/main_block.png) no-repeat 100% -240px; +} +div.title_barIC +{ + background: #dadfe6 url(../images/theme/main_block.png) no-repeat 0 -120px; + padding-left: 9px; + height: 31px; + overflow: hidden; + margin-bottom: 1px; +} +div.title_barIC h4.titlebg +{ + background: url(../images/theme/main_block.png) no-repeat 100% -120px; +} +#upshrinkHeaderIC p.pminfo +{ + margin: 0; + padding: 0.5em; +} +img#upshrink_ic, img#newsupshrink +{ + float: right; + margin: 10px 5px 0 0; +} +table.table_list a.unreadlink, table.table_list a.collapse +{ + float: right; +} +table.table_list a.collapse +{ + margin: 10px 5px 0 1em; + height: 31px; + line-height: 31px; +} + +/* The half-round header bars for some tables. */ +.table_grid tr.catbg, .table_grid tr.titlebg +{ + font-size: 0.95em; + border-bottom: 1px solid #fff; +} +.table_grid tr.catbg th, .table_grid tr.titlebg th +{ + height: 28px; + line-height: 28px; +} +tr.catbg th.first_th +{ + background: #a7b9cd url(../images/theme/main_block.png) no-repeat 0 -280px; +} +tr.catbg th.last_th +{ + background: #a7b9cd url(../images/theme/main_block.png) no-repeat 100% -280px; +} +tr.titlebg th.first_th +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 0 -380px; +} +tr.titlebg th.last_th +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 100% -380px; +} +.table_grid th.last_th input +{ + margin: 0 2px; +} +.table_grid th.lefttext +{ + padding: 0 0.7em; +} + +/* a general table class */ +table.table_grid +{ + border-collapse: collapse; + margin-top: 0.1em; +} +table.table_grid td +{ + padding: 3px; + border-bottom: 1px solid #fff; + border-right: 1px solid #fff; +} + +/* GenericList */ +.additional_row +{ + padding: 0.5em 0 0.5em 0; +} +table.table_grid thead tr.catbg th +{ + white-space: nowrap; +} + +/* table_grid styles for Profile > Show Permissions. */ +#permissions table.table_grid td +{ + padding: 0.4em 0.8em; + cursor: default; +} + +/* Common styles used to add corners to divisions. +------------------------------------------------------- */ +.windowbg span.topslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -30px no-repeat; +} +.windowbg span.topslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -30px no-repeat; + height: 11px; +} +.windowbg span.botslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -40px no-repeat; + font-size: 5px; + line-height: 5px; + margin-bottom: 0.2em; +} +.windowbg span.botslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -40px no-repeat; + height: 11px; +} + +.windowbg2 span.topslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -60px no-repeat; +} +.windowbg2 span.topslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -60px no-repeat; + height: 11px; +} +.windowbg2 span.botslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -71px no-repeat; + font-size: 5px; + line-height: 5px; + margin-bottom: 0.2em; +} +.windowbg2 span.botslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -71px no-repeat; + height: 11px; +} +.approvebg span.topslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 0 no-repeat; +} +.approvebg span.topslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% 0 no-repeat; + height: 11px; +} +.approvebg span.botslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -11px no-repeat; + margin-bottom: 0.2em; +} +.approvebg span.botslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -11px no-repeat; + height: 11px; +} +.postbg +{ + border-left: 1px solid #7f7f7f; + border-right: 1px solid #7f7f7f; +} + +/* Used for sections that need somewhat larger corners. +----------------------------------------------------------- */ +.roundframe +{ + padding: 0 10px; + background: #f5f5f5; + border-left: 1px solid #c5c5c5; + border-right: 1px solid #c5c5c5; +} +.roundframe dl, .roundframe dt, .roundframe p +{ + margin: 0; +} +.roundframe p +{ + padding: 0.5em; +} +span.upperframe +{ + padding: 0; + display: block; + background: url(../images/theme/main_block.png) 0 -90px no-repeat; + padding-left: 20px; +} +span.upperframe span +{ + padding: 0; + height: 12px; + display: block; + background: url(../images/theme/main_block.png) 100% -90px no-repeat; +} +span.lowerframe +{ + padding: 0; + display: block; + background: url(../images/theme/main_block.png) 0 -102px no-repeat; + padding-left: 20px; +} +span.lowerframe span +{ + padding: 0; + height: 12px; + display: block; + background: url(../images/theme/main_block.png) 100% -102px no-repeat; +} + +/* The main content area. +------------------------------------------------------- */ +.content +{ + padding: 0.5em 1.2em; + margin: 0; + border: none; +} +.content p +{ + margin: 0 0 0.5em 0; +} + +/* Styles used by the auto suggest control. +------------------------------------------------------- */ +.auto_suggest_div +{ + border: 1px solid #000; + position: absolute; + visibility: hidden; +} +.auto_suggest_item +{ + background-color: #ddd; +} +.auto_suggest_item_hover +{ + background-color: #888; + cursor: pointer; + color: #eee; +} + +/* Styles for the standard dropdown menus. +------------------------------------------------------- */ +#main_menu +{ + padding: 0 0.5em; + float: left; + margin: 0; + width: 98%; +} + +.dropmenu, .dropmenu ul +{ + list-style: none; + line-height: 1em; + padding: 0; + margin: 0; +} +.dropmenu +{ + padding: 0 0.5em; +} +.dropmenu a +{ + display: block; + color: #000; + text-decoration: none; +} +.dropmenu a span +{ + display: block; + padding: 0 0 0 5px; + font-size: 0.9em; +} +/* the background's first level only */ +.dropmenu li a.firstlevel +{ + margin-right: 8px; +} +.dropmenu li a.firstlevel span.firstlevel +{ + display: block; + position: relative; + left: -5px; + padding-left: 5px; + height: 22px; + line-height: 19px; + white-space: pre; +} +.dropmenu li +{ + float: left; + padding: 0; + margin: 0; + position: relative; +} +.dropmenu li ul +{ + z-index: 90; + display: none; + position: absolute; + width: 19.2em; + font-weight: normal; + border-bottom: 1px solid #999; + background: url(../images/theme/menu_gfx.png) 0 -130px no-repeat; + padding: 7px 0 0 0; +} +.dropmenu li li +{ + width: 19em; + margin: 0; + border-left: 1px solid #999; + border-right: 1px solid #999; +} +.dropmenu li li a span +{ + display: block; + padding: 8px; +} +.dropmenu li ul ul +{ + margin: -1.8em 0 0 13em; +} + +/* the active button */ +.dropmenu li a.active +{ + background: url(../images/theme/menu_gfx.png) no-repeat 100% 0; + color: #fff; + font-weight: bold; +} +.dropmenu li a.active span.firstlevel +{ + background: url(../images/theme/menu_gfx.png) no-repeat 0 0; +} +/* the hover effects */ +.dropmenu li a.firstlevel:hover, .dropmenu li:hover a.firstlevel +{ + background: url(../images/theme/menu_gfx.png) no-repeat 100% -30px; + color: #fff; + cursor: pointer; + text-decoration: none; +} +.dropmenu li a.firstlevel:hover span.firstlevel, .dropmenu li:hover a.firstlevel span.firstlevel +{ + background: url(../images/theme/menu_gfx.png) no-repeat 0 -30px; +} +/* the hover effects on level2 and 3 */ +.dropmenu li li a:hover, .dropmenu li li:hover>a +{ + background: #d4dbe4; + color: #000; + text-decoration: none; +} +.dropmenu li:hover ul ul, .dropmenu li:hover ul ul ul +{ + top: -999em; +} +.dropmenu li li:hover ul +{ + top: auto; +} +.dropmenu li:hover ul +{ + display: block; +} +.dropmenu li li.additional_items +{ + background-color: #fff; +} + +/* The dropdown menu toggle image */ +#menu_toggle +{ + float: right; + margin-right: 10px; + padding-top: 3px; +} +#menu_toggle span +{ + position: relative; + right: 5000px; +} + +/* Styles for the standard button lists. +------------------------------------------------------- */ + +.buttonlist ul +{ + z-index: 100; + padding: 5px; + margin: 0 0.2em 5px 0; +} +.buttonlist ul li +{ + margin: 0; + padding: 0; + list-style: none; + float: left; +} +.buttonlist ul li a +{ + display: block; + font-size: 0.8em; + color: #000; + background: #e8e8e8 url(../images/theme/menu_gfx.png) no-repeat 0 -60px; + padding: 0 0 0 8px; + margin-left: 12px; + text-transform: uppercase; + cursor: pointer; +} +.buttonlist ul li a:hover +{ + background: url(../images/theme/menu_gfx.png) no-repeat 0 0; + color: #fff; + text-decoration: none; +} +.buttonlist ul li a span +{ + background: url(../images/theme/menu_gfx.png) no-repeat 100% -60px; + display: block; + height: 19px; + line-height: 19px; + padding: 0 8px 0 0; +} +.buttonlist ul li a:hover span +{ + background: #fff url(../images/theme/menu_gfx.png) no-repeat 100% 0; +} +/* the active one */ +.buttonlist ul li a.active +{ + background: #5a6c85 url(../images/theme/menu_gfx.png) no-repeat 0 -90px; + color: #fff; + font-weight: bold; +} +.buttonlist ul li a.active span +{ + background: url(../images/theme/menu_gfx.png) no-repeat 100% -90px; +} +.buttonlist ul li a.active +{ + font-weight: bold; +} +.buttonlist ul li a.active:hover +{ + color: #ddf; +} +.align_top ul li a, .align_bottom ul li a +{ + margin: 0 12px 0 0; +} + +/* the navigation list */ +ul#navigation +{ + margin: 0; + font-size: 0.9em; + padding: 1em 0.4em; +} +ul#navigation li +{ + float: none; + font-size: 0.95em; + display: inline; +} + +#adm_submenus +{ + padding-left: 2em; + overflow: hidden; +} + +/* Styles for the general looks for the Curve theme. +------------------------------------------------------- */ + +/* the framing graphics */ +#header +{ + background: url(../images/theme/main_block.png) #fefefe no-repeat 0 -480px; + padding-left: 20px; +} +#header div.frame +{ + background: url(../images/theme/main_block.png) no-repeat 100% -480px; + display: block; + padding: 5px 20px 1em 0; +} +/* the content section */ +#content_section +{ + background: #FFFFFF url(../images/theme/frame_repeat.png) repeat-y top left; + padding-left: 20px; +} +#content_section div.frame +{ + background: url(../images/theme/frame_repeat.png) repeat-y top right; + display: block; + padding: 0 20px 0 0; +} +#main_content_section +{ + width: 100%; + min-height: 200px; +} + +/* the main title, always stay at 45 pixels in height! */ +h1.forumtitle +{ + line-height: 45px; + font-size: 1.8em; + font-family: Geneva, verdana, sans-serif; + margin: 0; + padding: 0; + float: left; +} +/* float these items to the right */ +#siteslogan, img#smflogo +{ + margin: 0; + padding: 0; + float: right; + line-height: 3em; +} +h3, h4 +{ + padding-bottom: 3px; +} +/* the upshrink image needs some tweaking */ +img#upshrink +{ + float: right; + margin: 1em; +} +/* ..so does the SMF logo */ +img#smflogo +{ + margin-left: 1em; +} +/* the upper_section, float the two each way */ +#upper_section +{ + padding: 5px; + margin-bottom: 1.5em; +} +#upper_section ul li.greeting +{ + font-size: 1.3em; + font-weight: bold; + line-height: 1.5em; +} +#upper_section div.news +{ + width: 50%; + float: right; + text-align: right; +} +#guest_form +{ + overflow: hidden; +} +#guest_form .info +{ + padding: 4px 0 ; + line-height: 1.3em; +} +div#upper_section div.user +{ + width: 50%; + float: left; + overflow: auto; +} +div#upper_section div.user p +{ + float: left; + margin: 0 1em 1em 0; + padding: 0; +} +div#upper_section div.user ul +{ + margin: 0; + padding-left: 10px; +} +div#upper_section div.user ul li +{ + margin-bottom: 2px; +} +div#upper_section div.news p +{ + display: inline; +} +div#upper_section div.news form +{ + padding-bottom: 10px; +} +/* clearing the floats */ +#top_section +{ + min-height: 65px; + overflow: hidden; + margin-bottom: 3px; +} +#upper_section +{ + overflow: hidden; +} + +/* The navigation list (i.e. linktree) */ +.navigate_section +{ + padding: 0.5em; + margin: 0 0 0 0; +} +.navigate_section ul +{ + display: block; + margin: 0; + font-size: 0.9em; + padding: 1em 0 0.5em 0; + border-top: 1px solid #ccc; + overflow: hidden; + list-style: none; + clear: both; + width: 100%; +} +.navigate_section ul li +{ + float: left; + padding: 0 0.5em 0 0; + font-size: 0.95em; +} +.navigate_section ul li a +{ + white-space: pre; +} + +/* The footer wih copyright links etc. */ +#footer_section +{ + text-align: center; + background: url(../images/theme/main_block.png) no-repeat 0 -820px; + padding-left: 20px; +} +#footer_section span.smalltext +{ + font-size: 100%; +} +#footer_section div.frame +{ + background: url(../images/theme/main_block.png) no-repeat 100% -820px; + display: block; + padding: 60px 0 0 0; +} +#footer_section ul li, #footer_section p +{ + font-size: 0.8em; +} +#footer_section ul li +{ + display: inline; + padding-right: 5px; +} +#footer_section ul li.copyright +{ + display: block; +} +select.qaction, input.qaction +{ + font-size: 0.85em; + padding: 0; +} +#mlist table tbody td.windowbg2 +{ + text-align: center; +} + +/* Styles for a typical table. +------------------------------------------------------- */ +table.table_list +{ + width: 100%; +} +table.table_list p +{ + padding: 0; + margin: 0; +} +table.table_list td, table.table_list th +{ + padding: 5px; +} +table.table_list tbody.header td +{ + padding: 0; +} +table.table_list tbody.content td.stats +{ + font-size: 90%; + width: 15%; + text-align: center; +} +table.table_list tbody.content td.lastpost +{ + line-height: 1.3em; + font-size: 85%; + width: 24%; +} +table.table_list tbody.content td.icon +{ + text-align: center; + width: 6%; +} + +/* Styles for the board index. +------------------------------------------------- */ + +/* the board title! */ +.table_list tbody.content td.info a.subject +{ + font-weight: bold; + font-size: 110%; + color: #d97b33; +} +.table_list tbody.content td.children +{ + color: #555; + font-size: 85%; +} +p.moderators +{ + font-size: 0.8em; + font-family: verdana, sans-serif; +} +/* hide the table header/footer parts - but its here for those needing to style it */ +#boardindex_table .table_list thead, #boardindex_table .table_list tfoot +{ + display: none; +} + +/* the posting icons */ +#posting_icons +{ + padding: 0 1em 0.5em 1em; + margin: 0 0 1em 0; + line-height: 1em; +} +#posting_icons ul +{ + font-size: 0.8em; +} +#posting_icons img +{ + vertical-align: middle; + margin: 0 0 0 4ex; +} +#postbuttons_upper ul li a span +{ + line-height: 19px; + padding: 0 0 0 6px; +} +.nextlinks +{ + text-align: right; + margin-top: -1px; +} +.nextlinks_bottom +{ + clear: right; + text-align: right; +} +.mark_read +{ + padding: 0 0.5em; +} + +/* the newsfader */ +#newsfader +{ + margin: 0 2px; +} +#smfFadeScroller +{ + text-align: center; + padding: 0 2em; + overflow: auto; + margin: 1em 0; + color: #575757; /* shouldn't be shorthand style due to a JS bug in IE! */ +} + +/* Styles for the info center on the board index. +---------------------------------------------------- */ + +#upshrinkHeaderIC +{ + margin-top: 4px; +} +dl#ic_recentposts +{ + margin: 0 0 0.5em 0; + padding: 0.5em; + line-height: 1.3em; +} +dl#ic_recentposts dt +{ + float: left; +} +dl#ic_recentposts dd +{ + text-align: right; +} +#upshrinkHeaderIC p +{ + margin: 0 0 0.5em 0; + padding: 0.5em; +} +#upshrinkHeaderIC p.last +{ + margin: 0; + padding: 0.5em; + border-top: 2px dotted #bbb; +} +#upshrinkHeaderIC p.inline +{ + border: none; + margin: 0; + padding: 0.2em 0.5em 0.2em 0.5em; +} +#upshrinkHeaderIC p.stats +{ + font-size: 1.1em; + padding-top: 8px; +} +form#ic_login +{ + padding: 0.5em; + height: 2em; +} +form#ic_login ul li +{ + margin: 0; + padding: 0; + float: left; + width: 20%; + text-align: center; +} +form#ic_login ul li label +{ + display: block; +} + +/* the small stats */ +#index_common_stats +{ + display: block; + margin: 0 0 0.5em 0; + text-align: right; + font-size: 0.9em; + position: relative; + top: -20px; + line-height: 1px; +} + +img.new_posts +{ + padding: 0 0.1em; +} +/* Styles for the message (topic) index. +---------------------------------------------------- */ +div.table_frame .table_list +{ + border-collapse: collapse; + margin: 2px 0; +} +.table_frame .table_list td.icon, .table_frame .table_list td.info, .table_frame .table_list td.stats +{ + border-right: 2px solid white; +} +#messageindex +{ + clear: both; +} +/* the page navigation area */ +.childboards +{ + margin-bottom: 0.2em; +} +#childboards h3 +{ + padding-bottom: 0; +} +#childboards .table_list thead +{ + display: none; +} +#childboards .table_list +{ + margin-bottom: 1em; +} +.lastpost img +{ + float: right; + padding: 4px; +} + +/* Styles for the display template (topic view). +---------------------------------------------------- */ + +#postbuttons div.buttons +{ + padding: 0.5em; + width: 40%; + float: right; +} +#postbuttons div.middletext +{ + width: 60%; +} +#postbuttons span +{ + display: block; + text-align: right; +} +#postbuttons span.lower +{ + clear: right; +} +#postbuttons .buttonlist +{ + float: right; +} +#postbuttons #pagelinks +{ + padding-top: 1em; +} +#moderationbuttons +{ + overflow: hidden; +} +/* Events */ +.linked_events +{ + padding: 1em 0; +} +.edit_event +{ + margin: 0 1em; + vertical-align: middle; +} +/* Poll question */ +#poll +{ + overflow: hidden; +} +#poll .content +{ + padding: 0 1em; +} +h4#pollquestion +{ + padding: 0 0 0.5em 2em; +} + +/* Poll vote options */ +#poll_options ul.options +{ + border-top: 1px solid #9999aa; + padding: 1em 2.5em 0 2em; + margin: 0 0 1em 0; +} +#poll_options div.submitbutton +{ + border-bottom: 1px solid #9999aa; + clear: both; + padding: 0 0 1em 2em; + margin: 0 0 1em 0; +} + +/* Poll results */ +#poll_options dl.options +{ + border: solid #9999aa; + border-width: 1px 0; + padding: 1em 2.5em 1em 2em; + margin: 0 1em 1em 0; + line-height: 1.1em !important; +} + +#poll_options dl.options dt +{ + padding: 0.3em 0; + width: 30%; + float: left; + margin: 0; + clear: left; +} + +#poll_options dl.options .voted +{ + font-weight: bold; +} + +#poll_options dl.options dd +{ + margin: 0 0 0 2em; + padding: 0.1em 0 0 0; + width: 60%; + max-width: 450px; + float: left; +} + +#poll_options dl.options .percentage +{ + display: block; + float: right; + padding: 0.2em 0 0.3em 0; +} + +/* Poll notices */ +#poll_options p +{ + margin: 0 1.5em 0.2em 1.5em; + padding: 0 0.5em 0.5em 0.5em; +} + +div#pollmoderation +{ + margin: 0; + padding: 0; + overflow: auto; +} + +/* onto the posts */ +#forumposts +{ + clear: both; +} +#forumposts .cat_bar +{ + margin: 0 0 2px 0; +} +/* author and topic information */ +#forumposts h3 span#author +{ + margin: 0 7.7em 0 0; +} +#forumposts h3 img +{ + float: left; + margin: 4px 0.5em 0 0; +} +#forumposts h3.catbg +{ + margin-bottom: 3px; +} +p#whoisviewing +{ + margin: 0; + padding: 0.5em; +} +/* poster and postarea + moderation area underneath */ +.post_wrapper +{ + float:left; + width:100%; +} +.poster +{ + float: left; + width: 15em; +} +.postarea, .moderatorbar +{ + margin: 0 0 0 16em; +} +.postarea div.flow_hidden +{ + width: 100%; +} + +.moderatorbar +{ + clear: right; +} +/* poster details and list of items */ +.poster h4, .poster ul +{ + padding: 0; + margin: 0 1em 0 1.5em; +} +.poster h4 +{ + margin: 0.2em 0 0.4em 1.1em; + font-size: 120%; +} +.poster h4, .poster h4 a +{ + color: #c06002; +} +.poster ul ul +{ + margin: 0.3em 1em 0 0; + padding: 0; +} +.poster ul ul li +{ + display: inline; +} +.poster li.stars, .poster li.avatar, .poster li.blurb, li.postcount, li.im_icons ul +{ + margin-top: 0.5em; +} +.poster li.avatar +{ + overflow: hidden; +} +.poster li.warning +{ + line-height: 1.2em; + padding-top: 1em; +} +.poster li.warning a img +{ + vertical-align: bottom; + padding: 0 0.2em; +} +.messageicon +{ + float: left; + margin: 0 0.5em 0 0; +} +.messageicon img +{ + padding: 6px 3px; +} +.keyinfo +{ + float: left; + width: 50%; +} +.modifybutton +{ + clear: right; + float: right; + margin: 6px 20px 10px 0; + text-align: right; + font: bold 0.85em arial, sans-serif; + color: #334466; +} + +/* The quick buttons */ +div.quickbuttons_wrap +{ + padding: 0.2em 0; + width: 100%; + float: left; +} + +ul.quickbuttons +{ + margin: 0.9em 11px 0 0; + clear: right; + float: right; + text-align: right; + font: bold 0.85em arial, sans-serif; +} +ul.quickbuttons li +{ + float: left; + display: inline; + margin: 0 0 0 11px; +} +ul.quickbuttons li a +{ + padding: 0 0 0 20px; + display: block; + height: 20px; + line-height: 18px; + float: left; +} +ul.quickbuttons a:hover +{ + color: #a70; +} +ul.quickbuttons li.quote_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 0; +} +ul.quickbuttons li.remove_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -30px; +} +ul.quickbuttons li.modify_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -60px; +} +ul.quickbuttons li.approve_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -90px; +} +ul.quickbuttons li.restore_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -120px; +} +ul.quickbuttons li.split_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -150px; +} +ul.quickbuttons li.reply_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -180px; +} +ul.quickbuttons li.reply_all_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -180px; +} +ul.quickbuttons li.notify_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -210px; +} +ul.quickbuttons li.inline_mod_check +{ + margin: 0 0 0 5px; +} + +.post +{ + margin-top: 0.5em; + clear: right; +} +.inner +{ + padding: 1em 1em 2px 0; + margin: 0 1em 0 0; + border-top: 1px solid #99a; +} +img.smiley +{ + vertical-align: bottom; +} +#forumposts .modified +{ + float: left; +} +#forumposts .reportlinks +{ + margin-right: 1.5em; + text-align: right; + clear: right; +} +#forumposts .signature, .post .signature +{ + margin: 1em 0 0 0; +} +#forumposts span.botslice +{ + clear: both; +} +.attachments hr +{ + clear: both; + margin: 1em 0 1em 0; +} +.attachments +{ + padding: 1em 0 2em 0; +} +.attachments div +{ + padding: 0 0.5em; +} + +/* Styles for the quick reply area. +---------------------------------------------------- */ + +#quickreplybox +{ + padding-bottom: 1px; +} +#quickReplyOptions .roundframe +{ + padding: 0 10%; +} +#quickReplyOptions form textarea +{ + height: 100px; + width: 635px; + max-width: 100%; + min-width: 100%; + margin: 0.25em 0 1em 0; +} +/* The jump to box */ +#display_jump_to +{ + clear: both; + padding: 5px; + margin-top: 6px; + text-align: right; +} + +/* Separator of posts. More useful in the print stylesheet. */ +#forumposts .post_separator +{ + display: none; +} + +/* Styles for edit post section +---------------------------------------------------- */ +form#postmodify .roundframe +{ + padding: 0 12%; +} +#post_header, .postbox +{ + padding: 0.5em; + overflow: hidden; +} +#post_header dt, .postbox dt +{ + float: left; + padding: 0; + width: 15%; + margin: .5em 0 0 0; + font-weight: bold; +} +#post_header dd, .postbox dd +{ + float: left; + padding: 0; + width: 83%; + margin: .3em 0; +} +#post_header img +{ + vertical-align: middle; +} +ul.post_options +{ + margin: 0 0 0 1em; + padding: 0; + list-style: none; + overflow: hidden; +} +ul.post_options li +{ + margin: 0.2em 0; + width: 49%; + float: left; +} +#postAdditionalOptionsHeader +{ + margin-top: 1em; +} +#postMoreOptions +{ + border-bottom: 1px solid #cacdd3; + padding: 0.5em; +} +#postAttachment, #postAttachment2 +{ + overflow: hidden; + margin: .5em 0; + padding: 0; + border-bottom: 1px solid #cacdd3; + padding: 0.5em; +} +#postAttachment dd, #postAttachment2 dd +{ + margin: .3em 0 .3em 1em; +} +#postAttachment dt, #postAttachment2 dt +{ + font-weight: bold; +} +#postAttachment3 +{ + margin-left: 1em; +} +#post_confirm_strip, #shortcuts +{ + padding: 1em 0 0 0; +} +.post_verification +{ + margin-top: .5em; +} +.post_verification #verification_control +{ + margin: .3em 0 .3em 1em; +} +/* The BBC buttons */ +#bbcBox_message +{ + margin: 0.75em 0.5em; +} +#bbcBox_message div +{ + margin: 0.2em 0; + vertical-align: top; +} +#bbcBox_message div img +{ + margin: 0 1px 0 0; + vertical-align: top; +} +#bbcBox_message select +{ + margin: 0 2px; +} +/* The smiley strip */ +#smileyBox_message +{ + margin: 0.5em; +} + +/* Styles for edit event section +---------------------------------------------------- */ +#post_event .roundframe +{ + padding: 0 12%; +} +#post_event fieldset +{ + padding: 0.5em; + clear: both; +} +#post_event #event_main input +{ + margin: 0 0 1em 0; + float: left; +} +#post_event #event_main div.smalltext +{ + width: 33em; + float: right; +} +#post_event div.event_options +{ + float: right; +} +#post_event ul.event_main, ul.event_options +{ + padding: 0; + overflow: hidden; +} +#post_event ul.event_main li +{ + list-style-type: none; + margin: 0.2em 0; + width: 49%; + float: left; +} +#post_event ul.event_options +{ + margin: 0; + padding: 0 0 .7em .7em; +} +#post_event ul.event_options li +{ + list-style-type: none; + margin: 0; + float: left; +} +#post_event #event_main select, #post_event ul.event_options li select, #post_event ul.event_options li .input_check +{ + margin: 0 1em 0 0; +} + +/* Styles for edit poll section. +---------------------------------------------------- */ + +#edit_poll +{ + overflow: hidden; +} +#edit_poll fieldset +{ + padding: 0.5em; + clear: both; + overflow: hidden; +} +#edit_poll fieldset input +{ + margin-left: 8.1em; +} +#edit_poll ul.poll_main li +{ + padding-left: 1em; +} +#edit_poll ul.poll_main input +{ + margin-left: 1em; +} +#edit_poll ul.poll_main, dl.poll_options +{ + overflow: hidden; + padding: 0 0 .7em .7em; + list-style: none; +} +#edit_poll ul.poll_main li +{ + margin: 0.2em 0; +} +#edit_poll dl.poll_options dt +{ + width: 33%; + padding: 0 0 0 1em; +} +#edit_poll dl.poll_options dd +{ + width: 65%; +} +#edit_poll dl.poll_options dd input +{ + margin-left: 0; +} + +/* Styles for the recent messages section. +---------------------------------------------------- */ + +#readbuttons_top .pagelinks, #readbuttons .pagelinks +{ + padding-bottom: 1em; + width: 60%; +} +#readbuttons .pagelinks +{ + padding-top: 1em; +} +#recent +{ + clear: both; +} + +/* Styles for the move topic section. +---------------------------------------------------- */ + +#move_topic dl +{ + margin-bottom: 0; +} +#move_topic dl.settings dt +{ + width: 40%; +} +#move_topic dl.settings dd +{ + width: 59%; +} +.move_topic +{ + width: 710px; + margin: auto; + text-align: left; +} +div.move_topic fieldset +{ + padding: 0.5em; +} + +/* Styles for the send topic section. +---------------------------------------------------- */ + +fieldset.send_topic +{ + border: none; + padding: 0.5em; +} +dl.send_topic +{ + margin-bottom: 0; +} +dl.send_mail dt +{ + width: 35%; +} +dl.send_mail dd +{ + width: 64%; +} + +/* Styles for the report topic section. +---------------------------------------------------- */ + +#report_topic dl +{ + margin-bottom: 0; +} +#report_topic dl.settings dt +{ + width: 20%; +} +#report_topic dl.settings dd +{ + width: 79%; +} + +/* Styles for the split topic section. +---------------------------------------------------- */ + +div#selected, div#not_selected +{ + width: 49%; +} +ul.split_messages li.windowbg, ul.split_messages li.windowbg2 +{ + margin: 1px; +} +ul.split_messages li a.split_icon +{ + padding: 0 0.5em; +} +ul.split_messages div.post +{ + padding: 1em 0 0 0; + border-top: 1px solid #fff; +} + +/* Styles for the merge topic section. +---------------------------------------------------- */ +ul.merge_topics li +{ + list-style-type: none; +} +dl.merge_topic dt +{ + width: 25%; +} +dl.merge_topic dd +{ + width: 74%; +} +fieldset.merge_options +{ + clear: both; +} +.custom_subject +{ + margin: 0.5em 0; +} + +/* Styles for the login areas. +------------------------------------------------------- */ +.login +{ + width: 540px; + margin: 0 auto; +} +.login dl +{ + overflow: auto; + clear: right; +} +.login dt, .login dd +{ + margin: 0 0 0.4em 0; + width: 44%; + padding: 0.1em; +} +.login dt +{ + float: left; + clear: both; + text-align: right; + font-weight: bold; +} +.login dd +{ + width: 54%; + float: right; + text-align: left; +} +.login p +{ + text-align: center; +} + +/* Styles for the registration section. +------------------------------------------------------- */ +.register_error +{ + border: 1px dashed red; + padding: 5px; + margin: 0 1ex 1ex 1ex; +} +.register_error span +{ + text-decoration: underline; +} + +/* Additional profile fields */ +dl.register_form +{ + margin: 0; + clear: right; +} + +dl.register_form dt +{ + font-weight: normal; + float: left; + clear: both; + width: 50%; + margin: 0.5em 0 0 0; +} + +dl.register_form dt strong +{ + font-weight: bold; +} + +dl.register_form dt span +{ + display: block; +} + +dl.register_form dd +{ + float: left; + width: 49%; + margin: 0.5em 0 0 0; +} + +#confirm_buttons +{ + text-align: center; + padding: 1em 0; +} + +.coppa_contact +{ + padding: 4px; + width: 32ex; + background-color: #fff; + color: #000; + margin-left: 5ex; + border: 1px solid #000; +} + +.valid_input +{ + background-color: #f5fff0; +} +.invalid_input +{ + background-color: #fff0f0; +} + +/* Styles for maintenance mode. +------------------------------------------------------- */ +#maintenance_mode +{ + width: 75%; + min-width: 520px; + text-align: left; +} +#maintenance_mode img.floatleft +{ + margin-right: 1em; +} + +/* common for all admin sections */ +h3.titlebg img +{ + vertical-align: middle; + margin-right: 0.5em; + margin-top: -1px; +} +tr.titlebg td +{ + padding-left: 0.7em; +} +#admin_menu +{ + min-height: 2em; + padding-left: 0; +} +#admin_content +{ + clear: left; + padding-top: 0.5em; +} +/* Custom profile fields like to play with us some times. */ +#admin_content .custom_field +{ + margin-bottom: 15px; +} +#admin_login .centertext +{ + padding: 1em; +} +#admin_login .centertext .error +{ + padding: 0 0 1em 0; +} + +/* Styles for sidebar menus. +------------------------------------------------------- */ +.left_admmenu, .left_admmenu ul, .left_admmenu li +{ + padding: 0; + margin: 0; + list-style: none; +} +#left_admsection +{ + width: 160px; + float: left; + padding-right: 10px; +} +.adm_section h4.titlebg +{ + font-size: 95%; + margin-bottom: 5px; +} +#main_container +{ + position: relative; +} +.left_admmenu li +{ + padding: 0 0 0 0.5em; +} +.left_admmenu +{ + margin-bottom: 0.5em; +} +#main_admsection +{ + position: relative; + left: 0; + right: 0; + overflow: hidden; +} + +tr.windowbg td, tr.windowbg2 td, tr.approvebg td, tr.highlight2 td +{ + padding: 0.3em 0.7em; +} +#credits p +{ + padding: 0; + font-style: italic; + margin: 0; +} + +/* Styles for generic tables. +------------------------------------------------------- */ +.topic_table table +{ + width: 100%; +} +.topic_table .icon1, .topic_table .icon2, .topic_table .stats +{ + text-align: center; +} +#topic_icons +{ + margin: 1em 0 0 0; +} +#topic_icons .description +{ + margin: 0; +} +.topic_table table thead +{ + border-bottom: 1px solid #fff; +} +/* the subject column */ +.topic_table td +{ + font-size: 1em; +} +.topic_table td.subject p, .topic_table td.stats +{ + font-size: 0.85em; + padding: 0; + margin: 0; +} +.topic_table td.lastpost +{ + font-size: 0.85em; + line-height: 1.3em; + padding: 4px; +} +.topic_table td.stickybg2 +{ + background-image: url(../images/icons/quick_sticky.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lockedbg2 +{ + background-image: url(../images/icons/quick_lock.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.locked_sticky2 +{ + background-image: url(../images/icons/quick_sticky_lock.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lastpost +{ + background-image: none; +} + +/* Styles for (fatal) errors. +------------------------------------------------- */ + +#fatal_error +{ + width: 80%; + margin: auto; +} + +.errorbox +{ + padding: 1em; + border: 1px solid #cc3344; + color: #000; + background-color: #ffe4e9; + margin-bottom: 1em; +} +.errorbox h3 +{ + padding: 0; + margin: 0; + font-size: 1.1em; + text-decoration: underline; +} +.errorbox p +{ + margin: 1em 0 0 0; +} +.errorbox p.alert +{ + padding: 0; + margin: 0; + float: left; + width: 1em; + font-size: 1.5em; +} + +/* Styles for the profile section. +------------------------------------------------- */ + +dl +{ + overflow: auto; + margin: 0; + padding: 0; +} + +/* The basic user info on the left */ +#basicinfo +{ + width: 20%; + float: left; +} +#basicinfo .windowbg .content +{ + padding-left: 20px; +} +#detailedinfo +{ + width: 79.5%; + float: right; +} +#basicinfo h4 +{ + font-size: 135%; + font-weight: 100; + line-height: 105%; + white-space: pre-wrap; + overflow: hidden; +} +#basicinfo h4 span.position +{ + font-size: 80%; + font-weight: 100; + display: block; +} +#basicinfo img.avatar +{ + display: block; + margin: 10px 0 0 0; +} +#basicinfo ul +{ + list-style-type: none; + margin: 10px 0 0 0; +} +#basicinfo ul li +{ + display: block; + float: left; + margin-right: 5px; + height: 20px; +} +#basicinfo span#userstatus +{ + display: block; + clear: both; +} +#basicinfo span#userstatus img +{ + vertical-align: middle; +} +#detailedinfo div.content dl, #tracking div.content dl +{ + clear: right; + overflow: auto; + margin: 0 0 18px 0; + padding: 0 0 15px 0; + border-bottom: 1px #ccc solid; +} +#detailedinfo div.content dt, #tracking div.content dt +{ + width: 35%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#detailedinfo div.content dd, #tracking div.content dd +{ + width: 65%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} +#detailedinfo div.content dl.noborder +{ + border-bottom: 0; +} +#detailedinfo div.content dt.clear +{ + width: 100%; +} +.signature, .custom_fields_above_signature +{ + border-top: 1px #ccc solid; +} +.signature h5 +{ + font-size: 0.85em; + margin-bottom: 10px; +} +#personal_picture +{ + display: block; + margin-bottom: 0.3em; +} +#avatar_server_stored div +{ + float: left; +} +#avatar_upload +{ + overflow: auto; +} +#main_admsection #basicinfo, #main_admsection #detailedinfo +{ + width: 100%; +} +#main_admsection #basicinfo h4 +{ + float: left; + width: 35%; +} +#main_admsection #basicinfo img.avatar +{ + float: right; + vertical-align: top; +} +#main_admsection #basicinfo ul +{ + clear: left; +} +#main_admsection #basicinfo span#userstatus +{ + clear: left; +} +#main_admsection #basicinfo p#infolinks +{ + display: none; + clear: both; +} +#main_admsection #basicinfo .botslice +{ + clear: both; +} + +/* Simple feedback messages */ +div#profile_error, div#profile_success +{ + margin: 0 0 1em 0; + padding: 1em 2em; + border: 1px solid; +} +div#profile_error +{ + border-color: red; + color: red; + background: #fee; +} + +div#profile_error span +{ + text-decoration: underline; +} + +div#profile_success +{ + border-color: green; + color: green; + background: #efe; +} + +/* Profile statistics */ +#generalstats div.content dt +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#generalstats div.content dd +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +/* Activity by time */ +#activitytime +{ + margin: 6px 0; +} +.activity_stats +{ + margin: 0; + padding: 0; + list-style: none; +} +.activity_stats li +{ + margin: 0; + padding: 0; + width: 4.16%; + float: left; +} +.activity_stats li span +{ + display: block; + border: solid #000; + border-width: 1px 1px 0 0; + text-align: center; +} +.activity_stats li.last span +{ + border-right: none; +} +.activity_stats li div.bar +{ + margin: 0 auto; + width: 15px; +} +.activity_stats li div.bar div +{ + background: #6294CE; +} +.activity_stats li div.bar span +{ + position: absolute; + top: -1000em; + left: -1000em; +} + +/* Most popular boards by posts and activity */ +#popularposts +{ + width: 49.5%; + float: left; +} +#popularactivity +{ + width: 49.5%; + float: right; +} + +#popularposts div.content dt, #popularactivity div.content dt +{ + width: 65%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#popularposts div.content dd, #popularactivity div.content dd +{ + width: 35%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +.profile_pie +{ + background-image: url(../images/stats_pie.png); + float: left; + height: 20px; + width: 20px; + margin: 0 1em 0 0; + padding: 0; + text-indent: -1000em; +} + +/* View posts */ +.topic .time +{ + float: right; +} + +.counter +{ + margin: 0 0 0 0; + padding: 0.2em 0.5em 0.1em 0.2em; + font-size: 2.2em; + font-weight: bold; + color: #3f3f3f; + float: left; +} +.list_posts +{ + border-top: 2px solid #b3b3bf; + padding-top: 12px; + margin-top: 6px; + overflow: auto; +} + +.core_posts +{ + margin-bottom: 3px; +} + +.topic h4 +{ + margin: 3px 0; +} + +.topic .post +{ + margin: 0 1em; + min-height: 80px; + height: auto !important; + height: 80px; +} + +.topic .mod_icons +{ + text-align: right; + margin-right: 1em; +} + +#tracking div.content dl +{ + border-bottom: 0; + margin: 0; + padding: 0; +} + +#creator dl +{ + margin: 0; +} +#creator dt +{ + width: 40%; + float: left; + clear: both; + margin: 0 0 10px 0; +} +#creator dd +{ + float: right; + width: 55%; + margin: 0 0 10px 2px; + overflow: auto; +} + +.ignoreboards +{ + margin: 0 2%; + padding: 0; + width: 45%; +} +.ignoreboards a +{ + font-weight: bold; + border-bottom: 1px solid #c4c4c4; + padding: 0.1em 0; +} +.ignoreboards a:hover +{ + text-decoration: none; + border-bottom: 1px solid #334466; +} +.ignoreboards ul +{ + margin: 0; + padding: 0; +} +.ignoreboards li +{ + list-style: none; + float: left; + clear: both; +} +.ignoreboards li.category +{ + margin: 0.7em 0 0 0; + width: 100%; +} +.ignoreboards li ul +{ + margin: 0.2em 0 0 0; +} +.ignoreboards li.category ul li.board +{ + width: 93%; +} + +#theme_settings +{ + overflow: auto; + margin: 0; + padding: 0; +} + +#theme_settings li +{ + list-style: none; + margin: 10px 0; + padding: 0; +} +/* Paid Subscriptions */ +#paid_subscription +{ + width: 100%; +} +#paid_subscription dl.settings +{ + margin-bottom: 0; +} +#paid_subscription dl.settings dd, #paid_subscription dl.settings dt +{ + margin-bottom: 4px; +} +/* Pick theme */ +#pick_theme +{ + width: 100%; + float: left; +} +/*Issue a warning*/ +#warn_body{ + width: 80%; + font-size: 0.9em; +} + +/* Styles for the statistics center. +------------------------------------------------- */ +#statistics +{ + padding: 0.5em 0; +} +#statistics div.title_bar +{ + margin: 4px 0 -2px 0; +} +#statistics h3.catbg +{ + text-align: center; +} +#statistics div.content +{ + min-height: 210px; +} +#statistics div.top_row +{ + min-height: 150px; +} +#stats_left, #top_posters, #top_topics_replies, #top_topics_starter +{ + float: left; + width: 49.5%; +} +#stats_right, #top_boards, #top_topics_views, #most_online +{ + float: right; + width: 49.5%; +} +dl.stats +{ + clear: both; + overflow: hidden; + margin: 0; + padding: 0; +} +dl.stats dt +{ + width: 49%; + float: left; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; + clear: both; + font-size: 1em; +} +dl.stats dd +{ + text-align: right; + width: 50%; + font-size: 1em; + float: right; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; +} +.statsbar div.bar +{ + float: left; + background: url(../images/bar_stats.png) no-repeat; + display: block; + margin: 0 4px; + height: 16px; +} +.statsbar div.bar div +{ + position: relative; + right: -4px; + padding: 0 4px 0 0; + background: url(../images/bar_stats.png) no-repeat 100%; + height: 16px; +} +tr.windowbg2 th.stats_month +{ + width: 25%; + padding: 0 2em; + text-align: left; +} +tr.windowbg2 td.stats_day +{ + padding: 0 3.5em; + text-align: left; +} + +/* Styles for the personal messages section. +------------------------------------------------- */ + +#personal_messages h3 span#author, #personal_messages h3 span#topic_title +{ + float: left; +} +#personal_messages h3 span#author +{ + margin: 0 0 0 0.5em; +} +#personal_messages h3 span#topic_title +{ + margin: 0 0 0 9em; +} +#personal_messages div.labels +{ + padding: 0 1em 0 0; +} +#personal_messages .capacity_bar +{ + background: #f0f4f7; + display: block; + margin: 0.5em 0 0 1em; + height: 1em; + border: 1px solid #adadad; + width: 10em; +} +#personal_messages .capacity_bar span +{ + border-right: 1px solid #adadad; + display: block; + height: 1em; +} +#personal_messages .capacity_bar span.empty +{ + background: #a6d69d; +} +#personal_messages .capacity_bar span.filled +{ + background: #eea800; +} +#personal_messages .capacity_bar span.full +{ + background: #f10909; +} +#personal_messages .reportlinks +{ + padding: 0.5em 1.3em; +} +#searchLabelsExpand li +{ + padding: 0.3em 0.5em; +} +#manrules div.righttext +{ + padding: 0.3em 0.1em; +} +dl.addrules dt.floatleft +{ + width: 15em; + color: #333; + padding: 0 1.25em 0.5em 1.25em; +} +#addrule fieldset +{ + clear: both; +} + +/* Styles for the calendar section. +------------------------------------------------- */ +.calendar_table +{ + margin-bottom: 0.7em; +} + +/* Used to indicate the current day in the grid. */ +.calendar_today +{ + background-color: #fff; +} + +#month_grid +{ + width: 200px; + text-align: center; + float: left; +} +#month_grid div.cat_bar +{ + height: 25px; +} +#month_grid h3.catbg +{ + height: 25px; + line-height: 27px; +} +#month_grid table +{ + width: 200px; +} +#main_grid table +{ + width: 100%; + padding-bottom: 4px; +} +#main_grid table h3.catbg +{ + text-align: center; + height: 29px; + border-top: 2px solid #fff; + border-bottom: none; +} +#main_grid table.weeklist td.windowbg +{ + text-align: center; + height: 49px; + width: 25px; + font-size: large; + padding: 0 7px; + border-bottom: 2px solid #fff; +} +#main_grid table.weeklist td.weekdays +{ + height: 49px; + width: 100%; + padding: 4px; + text-align: left; + vertical-align: middle; + border-bottom: 2px solid #fff; +} +#main_grid h3.weekly +{ + text-align: center; + padding-left: 0; + font-size: large; + height: 29px; +} +#main_grid h3 span.floatleft, #main_grid h3 span.floatright +{ + display: block; + font-weight: bold; +} +#main_grid table th.days +{ + width: 14%; + padding: 4px 0; +} +#main_grid table.weeklist h4.titlebg +{ + margin: 0 0 0 0; + height: 23px; + line-height: 27px; +} +#main_grid table td.weeks +{ + vertical-align: middle; + text-align: center; + font-weight: bold; + font-size: large; +} +#main_grid table td.days +{ + vertical-align: top; + text-align: center; +} + +a.modify_event +{ + color: red; +} + +span.hidelink +{ + font-style: italic; +} + +#calendar_navigation +{ + text-align: center; +} + +/* Styles for the memberlist section. +------------------------------------------------- */ +#mlist_search +{ + margin: auto; + width: 500px; +} + +/* Styles for the basic search section. +------------------------------------------------- */ +#searchform, #simple_search p +{ + padding: 0.5em; + margin: 0; +} +#simple_search, #simple_search p, #advanced_search +{ + text-align: center !important; + margin: 0; +} +#search_error +{ + font-style: italic; + padding: 0.3em 1em; +} +#search_term_input +{ + font-size: 115%; + margin: 0 0 1em; +} + +/* Styles for the advanced search section. +------------------------------------------------- */ +#searchform fieldset +{ + text-align: left; + padding: 0; + border: none; +} +#advanced_search dl#search_options +{ + margin: 0 auto; + width: 600px; + padding-top: 1em; + overflow: hidden; +} +#advanced_search dt +{ + clear: both; + float: left; + padding: 0.2em; + text-align: right; + width: 20%; +} +#advanced_search dd +{ + width: 75%; + float: left; + padding: 0.2em; + margin: 0 0 0 0.5em; + text-align: left; +} +#searchform p.clear +{ + clear: both; +} + +/* Styles for the search results page. +------------------------------------------------- */ +.topic_table td blockquote, .topic_table td .quoteheader +{ + margin: 0.5em; +} +.search_results_posts +{ + overflow: hidden; +} +.search_results_posts .buttons +{ + padding: 5px 1em 0 0; +} + +/* Styles for the help section. +------------------------------------------------- */ + +#help_container +{ + margin: 4px 0 0 0; + padding: 0 0 8px 0; +} +#helpmain +{ + padding: 0 1em; +} +#helpmain p +{ + margin: 0 0 1.5em 0; + line-height: 1.5em; +} +#helpmain ul +{ + line-height: 1.5em; +} + +/* Styles for print media. +------------------------------------------------------- */ +@media print +{ + #headerarea + { + display: none; + } + + .tborder + { + border: none; + } +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/install.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/install.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,91 @@ +body +{ + width: 90%; +} +#top_section +{ + height: 70px; + min-height: 65px; +} +#upper_section +{ + margin-bottom: 0; + padding: 0; +} +#upper_section .user +{ + height: 4em; +} +#upper_section .news +{ + height: 80px; +} +#main_screen +{ + padding: 0 40px; +} +#main_screen h2 +{ + font-size: 1.5em; + border-bottom: 1px solid #d05800; + line-height: 1.5em; + margin: 0 0 0.5em 0; + color: #d05800; +} +#main-steps +{ + float: right; + width: 50%; + margin-top: -60px; +} +#main-steps h2 +{ + font-size: 1.1em; + border-bottom: 1px solid #d05800; + line-height: 1.1em; + margin: 0 0 0.5em 0; + color: #d05800; + margin-right: 40px; +} +#main-steps ul +{ + list-style: none; + padding-left: 0; + margin: 0; +} +#main-steps ul li +{ + padding: 1px 0; + font-size: 0.9em; +} +#main-steps ul li.stepdone +{ + color: #aaa; +} +#main-steps ul li.stepcurrent +{ + color: #000; + font-weight: bold; +} +#main-steps ul li.stepwaiting +{ + color: #666; +} +.panel +{ + font-weight: normal; +} +a:link, a:hover, a:visited +{ + text-decoration: underline; +} +.progress +{ + position: relative; + margin: -16px 3px 0 3px; +} +.overall_progress +{ + position: relative; + margin: -25px 3px 0 3px; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/report.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/report.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,59 @@ +body +{ + color: #000; + background-color: #fff; + zoom: 1; +} +body, td, .normaltext +{ + font-family: Verdana, arial, helvetica, serif; + font-size: small; +} +*, a:link, a:visited, a:hover, a:active +{ + color: #000 !important; +} +.smalltext, .quoteheader, .codeheader +{ + font-size: x-small; +} +.largetext +{ + font-size: large; +} +hr +{ + height: 1px; + border: 0; + color: #000; + background-color: #000; +} +.catbg +{ + background-color: #d6d6d6; + font-weight: bold; +} +.titlebg, tr.titlebg td, .titlebg a:link, .titlebg a:visited +{ + font-style: normal; + background-color: #f0f4f7; +} +.bordercolor +{ + background-color: #333; +} +.windowbg +{ + color: #000; + background-color: #fff; +} +.windowbg2 +{ + color: #000; + background-color: #f1f1f1; +} +.copyright +{ + font-size: x-small; + text-align: center; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/rtl.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/rtl.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1082 @@ +/* Common classes to ease styling. +------------------------------------------------------- */ + +.floatright +{ + float: left; +} +.floatleft +{ + float: right; +} +.clear_left +{ + clear: right; +} +.clear_right +{ + clear: left; +} +.righttext +{ + margin-left: auto; + margin-right: 0; + text-align: left; +} +.lefttext +{ + margin-left: 0; + margin-right: auto; + text-align: right; +} + +/* Styling for BBC tags */ +.bbc_list +{ + text-align: right; +} + +/* GenericList */ +.additional_row input +{ + margin-left: 0; + margin-right: 1em; +} +/* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ +.signature, .attachments +{ + clear: left; +} +.custom_fields_above_signature +{ + clear: left; +} +.openid_login +{ + padding-right: 18px; + padding-left: 0; +} + +/* Lists with settings use these a lot. +------------------------------------------------------- */ +dl.settings +{ + clear: left; +} +dl.settings dt +{ + float: right; + clear: both; +} +dl.settings dt.windowbg +{ + float: right; +} +dl.settings dd +{ + float: left; +} +dl.settings img +{ + margin: 0 0 0 10px; +} + +/* Styles for rounded headers. +------------------------------------------------------- */ + +h3.catbg img.icon, h4.titlebg img.icon +{ + vertical-align: middle; + margin: -2px 0 0 5px; +} +h4.titlebg, h3.titlebg +{ + padding-right: 9px; + padding-left: 0; +} +h4.titlebg img.icon +{ + float: right; + margin: 5px 0 0 8px; +} + +table.table_list a.unreadlink, table.table_list a.collapse +{ + float: left; +} +table.table_list a.collapse +{ + margin: 10px 1em 0 5px; +} +.table_grid th.first_th, tr.catbg th.first_th +{ + background: #a8bace url(../images/theme/main_block.png) no-repeat 100% -240px; +} +.table_grid th.last_th, tr.catbg th.last_th +{ + background: #a8bace url(../images/theme/main_block.png) no-repeat 0 -240px; +} +tr.titlebg th.first_th +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 100% -340px; +} +tr.titlebg th.last_th +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 0 -340px; +} + +/* Styles for the standard dropdown menus. +------------------------------------------------------- */ +#main_menu +{ + padding: 0 0.5em; + float: right; + text-align: right; +} +.dropmenu li +{ + float: right; + margin: 0 0 0 8px; +} +.dropmenu li ul ul +{ + right: 15em; +} +.dropmenu li ul +{ + background: url(../images/theme/menu_gfx.png) 100% -130px no-repeat; + right: 5px; +} + +/* The dropdown menu toggle image */ +#menu_toggle +{ + float: left; + margin-right: 0; + margin-left: 10px; + padding-top: 3px; +} +#menu_toggle span +{ + position: relative; + left: 0; +} + +/* Styles for the standard button lists. +------------------------------------------------------- */ +.buttonlist ul +{ + margin: 0 0 0 0.2em; +} +.buttonlist ul li a +{ + margin-left: 0; + margin-right: 12px; +} +.buttonlist ul li a span +{ + left: 8px; +} +.align_top ul li a, .align_bottom ul li a +{ + margin: 0 0 0 12px; +} +#adm_submenus +{ + padding-left: 0; + padding-right: 2em; +} +/* the main title, always stay at 45 pixels in height! */ +h1.forumtitle +{ + float: right; +} +/* float these items to the left */ +#siteslogan, img#smflogo +{ + float: left; +} +/* the upshrink image needs some tweaking */ +img#upshrink +{ + float: left; +} +/* ..so does the SMF logo */ +img#smflogo +{ + margin-right: 1em; +} +#upper_section div.news +{ + float: left; + text-align: left; +} +div#upper_section div.user +{ + float: right; +} +div#upper_section div.user p +{ + float: right; + margin: 0 0 1em 1em; +} +div#upper_section div.user ul +{ + padding-left: 0; + padding-right: 10px; +} + +/* The navigation list (i.e. linktree) */ +.navigate_section ul li +{ + float: right; + padding: 0 0 0 0.5em; +} + +/* Styles for the board index. +------------------------------------------------- */ + +/* the posting icons */ +#posting_icons +{ + padding: 0 1em 0.5em 1em; +} +#posting_icons img +{ + margin: 0 4ex 0 0; +} +#posting_icons .buttonlist +{ + float: left; +} +#postbuttons_upper ul li a span +{ + line-height: 19px; + padding: 0 6px 0 0; +} + +dl#ic_recentposts dt +{ + float: right; +} +dl#ic_recentposts dd +{ + text-align: left; +} +form#ic_login ul li +{ + float: right; + width: 20%; +} + +/* the small stats */ +#index_common_stats +{ + text-align: left; +} +img#upshrink_ic, img#newsupshrink +{ + float: right; + margin: 10px 0 0 5px; +} + +/* Styles for the message (topic) index. +---------------------------------------------------- */ +.table_frame .table_list td.icon, .table_frame .table_list td.info, .table_frame .table_list td.stats +{ + border-right: none; + border-left: 2px solid white; +} +.lastpost img +{ + float: left; +} + +/* Styles for the display template (topic view). +---------------------------------------------------- */ +#postbuttons div.buttons +{ + float: right; +} +#postbuttons span +{ + text-align: left; +} +#postbuttons span.lower +{ + clear: left; +} +#postbuttons .buttonlist +{ + float: left; +} + +h4#pollquestion +{ + padding: 0.5em 2em 0.5em 0; +} +/* Poll vote options */ +#poll_options ul.options +{ + padding: 1em 2em 0 2.5em; + margin: 0 0 1em 0; +} +#poll_options div.submitbutton +{ + clear: both; + padding: 0 2em 1em 0; + margin: 0 0 1em 0; +} + +/* Poll results */ +#poll_options dl.options +{ + padding: 1em 2em 1em 2.5em; + margin: 0 0 1em 1em; +} +#poll_options dl.options dt +{ + float: right; + clear: right; +} +#poll_options dl.options dd +{ + margin: 0 2em 0 0; + float: right; +} +span.percent +{ + float: left; +} + +/* author and topic information */ +#forumposts h3 span#author +{ + margin: 0 0 0 7.7em; +} +#forumposts h3 img +{ + float: right; + margin: 4px 0 0 0.5em; +} +/* poster and postarea + moderation area underneath */ +.poster +{ + float: right; + width: 15em; +} +.postarea, .moderatorbar +{ + margin: 0 16em 0 0; +} +.moderatorbar +{ + clear: left; +} +/* poster details and list of items */ +.poster h4, .poster ul +{ + padding: 0; + margin: 0 1.5em 0 1em; +} +.poster h4 +{ + margin: 0.2em 1.1em 0.4em 0; +} +.poster ul ul +{ + margin: 0.3em 0 0 1em; +} +.messageicon +{ + float: right; + margin: 0 0 0 0.5em; +} + +.keyinfo +{ + float: right; +} +.modifybutton +{ + clear: left; + float: left; + margin: 8px 0 10px 20px; + text-align: left; +} + +/* The quick buttons */ +ul.quickbuttons +{ + margin: 0.9em 0 0 11px; + clear: left; + float: left; + text-align: left; +} +ul.quickbuttons li +{ + float: left; + margin: 0 11px 0 0; +} +ul.quickbuttons li a +{ + padding: 0 20px 0 0; + float: left; +} +ul.quickbuttons li.quote_button +{ + background-position: 100% 0; +} +ul.quickbuttons li.remove_button +{ + background-position: 100% -30px; +} +ul.quickbuttons li.modify_button +{ + background-position: 100% -60px; +} +ul.quickbuttons li.approve_button +{ + background-position: 100% -90px; +} +ul.quickbuttons li.restore_button +{ + background-position: 100% -120px; +} +ul.quickbuttons li.split_button +{ + background-position: 100% -150px; +} +ul.quickbuttons li.reply_button +{ + background-position: 100% -180px; +} +ul.quickbuttons li.reply_all_button +{ + background-position: 100% -180px; +} +ul.quickbuttons li.notify_button +{ + background-position: 100% -210px; +} +ul.quickbuttons li.inline_mod_check +{ + margin: 0 5px 0 0; +} +.post +{ + clear: left; +} +.inner +{ + padding: 1em 0 0 1em; + margin: 0 0 0 1em; +} +#forumposts .modified +{ + float: right; +} +#forumposts .reportlinks +{ + margin-left: 1.5em; + text-align: left; + clear: left; +} + +#moderationbuttons_strip +{ + float: right; +} +#moderationbuttons_strip ul +{ + margin: 0 0.2em 0 0; + padding: 0 1em 0 0; +} +/* The jump to box */ +#display_jump_to +{ + text-align: left; +} + +/* Styles for edit post section +---------------------------------------------------- */ +#post_header dt +{ + float: right; +} +#post_header dd +{ + float: right; +} +ul.post_options +{ + margin: 0 1em 0 0; +} +ul.post_options li +{ + float: right; +} +#postAttachment dd, #postAttachment2 dd +{ + margin: .3em 1em .3em 0; +} +#postAttachment dt, #postAttachment2 dt +{ + font-weight: bold; +} +#postAttachment3 +{ + margin-left: 0; + margin-left: 1em; +} +.post_verification #verification_control +{ + margin: .3em 1em .3em 0; +} + +/* Styles for edit event section +---------------------------------------------------- */ +#post_event div.event_options +{ + float: left; +} +#post_event #event_main input +{ + margin: 0 0 1em 0; + float: right; +} +#post_event #event_main div.smalltext +{ + float: left; +} +#post_event ul.event_main li +{ + float: left; +} +#post_event ul.event_options +{ + padding: 0 .7em .7em 0; +} +#post_event #event_main select, #post_event ul.event_options li select, #post_event ul.event_options li .input_check +{ + margin: 0 0 0 1em; +} + +/* Styles for edit poll section. +---------------------------------------------------- */ + +#edit_poll fieldset input +{ + margin-right: 7em; +} +#edit_poll ul.poll_main li +{ + padding-right: 1em; +} +#edit_poll ul.poll_main input +{ + margin-right: 1em; +} +#edit_poll div.poll_options +{ + float: right; +} +#edit_poll ul.poll_main, dl.poll_options +{ + padding: 0 .7em 0 0; +} +#edit_poll dl.poll_options dt +{ + padding: 0 1em 0 0; +} +#edit_poll dl.poll_options dd input +{ + margin-right: 0; +} + +/* Styles for the personal messages section. +------------------------------------------------- */ + +#personal_messages h3 span#author, #personal_messages h3 span#topic_title +{ + float: right; +} +#personal_messages h3 span#author +{ + margin: 0 0.5em 0 0; +} +#personal_messages h3 span#topic_title +{ + margin: 0 9em 0 0; +} +#personal_messages .labels +{ + padding: 0 0 0 1em; +} + +/* Styles for the move topic section. +---------------------------------------------------- */ +.move_topic +{ + text-align: right; +} +/* Styles for the login areas. +------------------------------------------------------- */ +.login dt +{ + float: right; +} +.login dd +{ + float: right; + text-align: right; +} +.login h3 img +{ + margin: 0 0 0.5em; +} + +/* Additional profile fields */ +dl.register_form +{ + clear: left; +} + +dl.register_form dt +{ + float: right; +} +/* Styles for maintenance mode. +------------------------------------------------------- */ +#maintenance_mode +{ + text-align: right; +} +#maintenance_mode img.floatleft +{ + margin-left: 1em; +} +/* common for all admin sections */ +h3.titlebg img +{ + margin-left: 0.5em; +} +tr.titlebg td +{ + padding-right: 0.7em; +} +#admin_menu +{ + padding-right: 0; +} +#admin_content +{ + clear: right; +} +/* Styles for sidebar menus. +------------------------------------------------------- */ +#left_admsection +{ + float: right; + padding-right: 0; + padding-left: 10px; +} +.left_admmenu li +{ + padding: 0 0.5em 0 0; +} +/* Styles for generic tables. +------------------------------------------------------- */ +.topic_table td.stickybg2 +{ + background-image: url(../images/icons/quick_sticky.gif); + background-repeat: no-repeat; + background-position: 2% 4px; +} +.topic_table td.lockedbg2 +{ + background-image: url(../images/icons/quick_lock.gif); + background-repeat: no-repeat; + background-position: 2% 4px; +} +.topic_table td.locked_sticky2 +{ + background-image: url(../images/icons/quick_sticky_lock.gif); + background-repeat: no-repeat; + background-position: 2% 4px; +} +.topic_table td.lastpost +{ + background-image: none; +} +/* Styles for (fatal) errors. +------------------------------------------------- */ +.errorbox p.alert +{ + float: right; +} +/* Styles for the profile section. +------------------------------------------------- */ +#basicinfo +{ + float: right; +} +#detailedinfo +{ + float: left; +} +#basicinfo ul li +{ + float: right; + margin-right: 0; + margin-left: 5px; +} +#detailedinfo div.content dl, #tracking div.content dl +{ + clear: left; +} +#detailedinfo div.content dt, #tracking div.content dt +{ + float: right; +} +#detailedinfo div.content dd, #tracking div.content dd +{ + float: right; +} +#avatar_server_stored div +{ + float: right; +} + +#main_admsection #basicinfo h4 +{ + float: right; +} +#main_admsection #basicinfo img.avatar +{ + float: left; +} +#main_admsection #basicinfo ul +{ + clear: right; +} +#main_admsection #basicinfo span#userstatus +{ + clear: right; +} + +/* Profile statistics */ +#generalstats div.content dt +{ + float: right; +} +#generalstats div.content dd +{ + float: right; +} + +/* Activity by time */ +#activitytime +{ + clear: right; +} +.activity_stats li +{ + float: right; +} +.activity_stats li span +{ + border-width: 1px 0 0 1px; +} +.activity_stats li.last span +{ + border-left: none; +} + +/* Most popular boards by posts and activity */ +#popularposts +{ + float: right; +} +#popularactivity +{ + float: left; +} + +#popularposts div.content dt, #popularactivity div.content dt +{ + float: right; +} +#popularposts div.content dd, #popularactivity div.content dd +{ + float: right; +} + +.profile_pie +{ + background-image: url(../images/stats_pie_rtl.png); + float: right; + margin-right: 0; + margin-left: 1em; +} + +/* View posts */ +.topic .time +{ + float: left; +} +.counter +{ + padding: 0.2em 0.2em 0.1em 0.5em; + float: right; +} +.topic .mod_icons +{ + text-align: left; + margin-right: 0; + margin-left: 1em; +} +#permissions div.permission_name +{ + margin: 0 0 0 1%; +} + +#ip_list li.header, #ip_list li.ip +{ + float: right; +} +#creator dt +{ + float: right; +} +#creator dd +{ + float: right; +} + +.ignoreboards ul +{ + margin: 0 1em 0 0; +} +.ignoreboards li +{ + float: right; +} + +#pick_theme +{ + float: right; +} +/* Styles for the statistics center. +------------------------------------------------- */ +#stats_left, #top_posters, #top_topics_replies, #top_topics_starter +{ + float: right; +} +#stats_right, #top_boards, #top_topics_views, #most_online +{ + float: left; +} +dl.stats dt +{ + float: right; +} +dl.stats dd +{ + text-align: left; +} +.statsbar div.bar +{ + float: right; +} +.statsbar div.bar div +{ + right: -6px; + padding: 0 0 0 6px; +} +tr.windowbg2 th.stats_month, tr.windowbg2 td.stats_day +{ + text-align: right; +} + +/* Styles for the calendar section. +------------------------------------------------- */ +#month_grid +{ + float: right; +} + +#main_grid table.weeklist td.windowbg +{ + + border-left: 2px solid #fff; + border-bottom: 2px solid #fff; +} + +#main_grid table.weeklist td.weekdays +{ + text-align: left; + vertical-align: middle; + border-right: 2px solid #fff; + border-bottom: 2px solid #fff; +} + +/* Styles for the advanced search section. +------------------------------------------------- */ +#searchform fieldset +{ + text-align: right; +} +#advanced_search dt +{ + float: right; + text-align: left; +} +#advanced_search dd +{ + float: right; + margin: 0 0.5em 0 0; + text-align: right; +} +/* Boards picker */ +#searchform fieldset div#searchBoardsExpand ul +{ + margin: 0 1em 0 0; +} +#searchform fieldset div#searchBoardsExpand li +{ + float: right; +} +#searchform fieldset p +{ + text-align: right; +} + +.search_results_posts .buttons +{ + padding: 5px 0 0 1em; +} + +/* Styles for the help section. +------------------------------------------------- */ +#helpmain h3.section +{ + padding: 0 0.5em 0.5em 0; +} +/* put back the bullets please */ +#helpmain ul +{ + margin: 0 2em 1em 0; + padding-left: 0; + padding-right: 1em; +} +#helpmain #messageindex +{ + clear: left; +} + +/* Styles for the admincenter (reverse admin.css). +------------------------------------------------- */ +#quick_search +{ + margin-left: 5px; +} +.features_image +{ + float: right; + margin: 0 1em 0.5em 2em; +} +.features_switch +{ + float: left; +} +.features h4 +{ + padding: 1em 0.5em 0.5em 0; +} +/* admin home */ +#live_news div.content dl +{ + padding: 0.5em 0.5em 0 0; +} +#smfAnnouncements dd +{ + padding: 0; + margin: 0 1.5em 1em 0; +} +#quick_tasks li +{ + float: right; + list-style-type: none; +} +.home_image +{ + float: right; +} +/* common admin classes */ +.additional_row input +{ + margin-left: 0; + margin-right: 2em; +} +#error_log td div.marginleft +{ + margin: 0 1ex 0 0 !important; +} + +/* Styles for the package manager. +------------------------------------------------- */ +#package_list .tborder +{ + margin: .25em 26px .25em 0; +} +#package_list ol, #package_list ol li +{ + margin-left: 0; + margin-right: 50px; +} +/* ManageBoards */ +#manage_boards ul +{ + overflow: hidden; +} +#manage_boards li +{ + overflow: hidden; +} +.move_links +{ + padding: 0 0 0 13px; +} + +span.search_weight +{ + text-align: left; +} +/* Manage Bans */ +.ban_restriction +{ + margin: 0.2em 2.2em 0.2em 0; +} +/* Themes */ +.is_directory +{ + padding-right: 18px; + background: url(../images/admin/boards.gif) no-repeat; + background-position: 100% 0; +} +/* Styles for the moderation center. +------------------------------------------------- */ +.modblock_left +{ + float: right; + clear: left; +} +.modblock_right +{ + float: left; +} +ul.moderation_notes li +{ + padding: 4px 4px 4px 0; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/webkit.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/webkit.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,10 @@ +/* + Special styles for Safari (and other Webkit-based browsers like Chrome) + Webkit needs this otherwise the post goes off to the right. + Causes issues in IE browsers, and breaks cached search engines pages. +*/ + +table.table_list tbody.header td div.cat_bar +{ + margin-bottom: -1px; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/css/wireless.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/css/wireless.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,35 @@ +.catbg, tr.catbg td +{ + background-color: #6d92aa; + color: #fff; +} +.titlebg, .titlebg a, .titlebg a:link, .titlebg a:visited +{ + background-color: #b6dbff; + color: #000; + text-decoration: none; +} +.windowbg, tr.windowbg td +{ + background-color: #fff; + color: #000; +} +.windowbg2, tr.windowbg2 td +{ + background-color: #c0c0c0; + color: #000; +} +.new, a:link.new, a:visited.new +{ + background-color: #2f2fc0; + color: #fff; +} +.updated +{ + color: red; +} +/* Resize our post area as needed */ +#message +{ + width: 98%; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fader.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/fader.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,118 @@ +// smfFadeIndex: the current item in smfFadeContent. +var smfFadeIndex = -1; +// smfFadePercent: percent of fade. (-64 to 510.) +var smfFadePercent = 510 +// smfFadeSwitch: direction. (in or out) +var smfFadeSwitch = false; +// smfFadeScroller: the actual div to mess with. +var smfFadeScroller = document.getElementById('smfFadeScroller'); +// The ranges to fade from for R, G, and B. (how far apart they are.) +var smfFadeRange = { + 'r': smfFadeFrom.r - smfFadeTo.r, + 'g': smfFadeFrom.g - smfFadeTo.g, + 'b': smfFadeFrom.b - smfFadeTo.b +}; + +// Divide by 20 because we are doing it 20 times per one ms. +smfFadeDelay /= 20; + +// Start the fader! +window.setTimeout('smfFader();', 20); + +// Main fading function... called 50 times every second. +function smfFader() +{ + if (smfFadeContent.length <= 1) + return; + + // A fix for Internet Explorer 4: wait until the document is loaded so we can use setInnerHTML(). + if (typeof(window.document.readyState) != "undefined" && window.document.readyState != "complete") + { + window.setTimeout('smfFader();', 20); + return; + } + + // Starting out? Set up the first item. + if (smfFadeIndex == -1) + { + setInnerHTML(smfFadeScroller, smfFadeBefore + smfFadeContent[0] + smfFadeAfter); + smfFadeIndex = 1; + + // In Mozilla, text jumps around from this when 1 or 0.5, etc... + if (typeof(smfFadeScroller.style.MozOpacity) != "undefined") + smfFadeScroller.style.MozOpacity = "0.90"; + else if (typeof(smfFadeScroller.style.opacity) != "undefined") + smfFadeScroller.style.opacity = "0.90"; + // In Internet Explorer, we have to define this to use it. + else if (typeof(smfFadeScroller.style.filter) != "undefined") + smfFadeScroller.style.filter = "alpha(opacity=100)"; + } + + // Are we already done fading in? If so, fade out. + if (smfFadePercent >= 510) + smfFadeSwitch = !smfFadeSwitch; + // All the way faded out? + else if (smfFadePercent <= -64) + { + smfFadeSwitch = !smfFadeSwitch; + + // Go to the next item, or first if we're out of items. + setInnerHTML(smfFadeScroller, smfFadeBefore + smfFadeContent[smfFadeIndex++] + smfFadeAfter); + if (smfFadeIndex >= smfFadeContent.length) + smfFadeIndex = 0; + } + + // Increment or decrement the fade percentage. + if (smfFadeSwitch) + smfFadePercent -= 255 / smfFadeDelay * 2; + else + smfFadePercent += 255 / smfFadeDelay * 2; + + // If it's not outside 0 and 256... (otherwise it's just delay time.) + if (smfFadePercent < 256 && smfFadePercent > 0) + { + // Easier... also faster... + var tempPercent = smfFadePercent / 255, rounded; + + if (typeof(smfFadeScroller.style.MozOpacity) != "undefined") + { + rounded = Math.round(tempPercent * 100) / 100; + smfFadeScroller.style.MozOpacity = rounded == 1 ? "0.99" : rounded; + } + else if (typeof(smfFadeScroller.style.opacity) != "undefined") + { + rounded = Math.round(tempPercent * 100) / 100; + smfFadeScroller.style.opacity = rounded == 1 ? "0.99" : rounded; + } + else + { + var done = false; + if (typeof(smfFadeScroller.filters.alpha) != "undefined") + { + // Internet Explorer 4 just can't handle "try". + eval("try\ + {\ + smfFadeScroller.filters.alpha.opacity = Math.round(tempPercent * 100);\ + done = true;\ + }\ + catch (err)\ + {\ + }"); + } + + if (!done) + { + // Get the new R, G, and B. (it should be bottom + (range of color * percent)...) + var r = Math.ceil(smfFadeTo.r + smfFadeRange.r * tempPercent); + var g = Math.ceil(smfFadeTo.g + smfFadeRange.g * tempPercent); + var b = Math.ceil(smfFadeTo.b + smfFadeRange.b * tempPercent); + + // Set the color in the style, thereby fading it. + smfFadeScroller.style.color = 'rgb(' + r + ', ' + g + ', ' + b + ')'; + } + } + } + + // Keep going. + window.setTimeout('smfFader();', 20); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts-compat.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/fonts-compat.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,16 @@ +body, td, .normaltext, .windowbg, .windowbg2, .titlebg, .bordercolor, .tborder, .catbg, .catbg2, .windowbg td, .windowbg2 td, .titlebg td +{ + font-size: x-small; +} +.smalltext, td.smalltext, i.smalltext, div.smalltext, .smalltext td, .quote, .quoteheader, .codeheader , .middletext +{ + font-size: xx-small; +} +.code +{ + font-size: xx-small; +} +.largetext +{ + font-size: medium; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice.gdf Binary file forum/Themes/Vamp/fonts/Candice.gdf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/a.gif Binary file forum/Themes/Vamp/fonts/Candice/a.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/b.gif Binary file forum/Themes/Vamp/fonts/Candice/b.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/c.gif Binary file forum/Themes/Vamp/fonts/Candice/c.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/d.gif Binary file forum/Themes/Vamp/fonts/Candice/d.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/e.gif Binary file forum/Themes/Vamp/fonts/Candice/e.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/f.gif Binary file forum/Themes/Vamp/fonts/Candice/f.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/g.gif Binary file forum/Themes/Vamp/fonts/Candice/g.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/h.gif Binary file forum/Themes/Vamp/fonts/Candice/h.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/i.gif Binary file forum/Themes/Vamp/fonts/Candice/i.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/fonts/Candice/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/j.gif Binary file forum/Themes/Vamp/fonts/Candice/j.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/k.gif Binary file forum/Themes/Vamp/fonts/Candice/k.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/l.gif Binary file forum/Themes/Vamp/fonts/Candice/l.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/m.gif Binary file forum/Themes/Vamp/fonts/Candice/m.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/n.gif Binary file forum/Themes/Vamp/fonts/Candice/n.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/o.gif Binary file forum/Themes/Vamp/fonts/Candice/o.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/p.gif Binary file forum/Themes/Vamp/fonts/Candice/p.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/q.gif Binary file forum/Themes/Vamp/fonts/Candice/q.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/r.gif Binary file forum/Themes/Vamp/fonts/Candice/r.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/s.gif Binary file forum/Themes/Vamp/fonts/Candice/s.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/t.gif Binary file forum/Themes/Vamp/fonts/Candice/t.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/u.gif Binary file forum/Themes/Vamp/fonts/Candice/u.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/v.gif Binary file forum/Themes/Vamp/fonts/Candice/v.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/w.gif Binary file forum/Themes/Vamp/fonts/Candice/w.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/x.gif Binary file forum/Themes/Vamp/fonts/Candice/x.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/y.gif Binary file forum/Themes/Vamp/fonts/Candice/y.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Candice/z.gif Binary file forum/Themes/Vamp/fonts/Candice/z.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Forgottb.ttf Binary file forum/Themes/Vamp/fonts/Forgottb.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie.gdf Binary file forum/Themes/Vamp/fonts/Hootie.gdf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/a.gif Binary file forum/Themes/Vamp/fonts/Hootie/a.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/b.gif Binary file forum/Themes/Vamp/fonts/Hootie/b.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/c.gif Binary file forum/Themes/Vamp/fonts/Hootie/c.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/d.gif Binary file forum/Themes/Vamp/fonts/Hootie/d.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/e.gif Binary file forum/Themes/Vamp/fonts/Hootie/e.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/f.gif Binary file forum/Themes/Vamp/fonts/Hootie/f.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/g.gif Binary file forum/Themes/Vamp/fonts/Hootie/g.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/h.gif Binary file forum/Themes/Vamp/fonts/Hootie/h.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/i.gif Binary file forum/Themes/Vamp/fonts/Hootie/i.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/fonts/Hootie/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/j.gif Binary file forum/Themes/Vamp/fonts/Hootie/j.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/k.gif Binary file forum/Themes/Vamp/fonts/Hootie/k.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/l.gif Binary file forum/Themes/Vamp/fonts/Hootie/l.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/m.gif Binary file forum/Themes/Vamp/fonts/Hootie/m.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/n.gif Binary file forum/Themes/Vamp/fonts/Hootie/n.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/o.gif Binary file forum/Themes/Vamp/fonts/Hootie/o.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/p.gif Binary file forum/Themes/Vamp/fonts/Hootie/p.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/q.gif Binary file forum/Themes/Vamp/fonts/Hootie/q.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/r.gif Binary file forum/Themes/Vamp/fonts/Hootie/r.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/s.gif Binary file forum/Themes/Vamp/fonts/Hootie/s.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/t.gif Binary file forum/Themes/Vamp/fonts/Hootie/t.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/u.gif Binary file forum/Themes/Vamp/fonts/Hootie/u.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/v.gif Binary file forum/Themes/Vamp/fonts/Hootie/v.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/w.gif Binary file forum/Themes/Vamp/fonts/Hootie/w.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/x.gif Binary file forum/Themes/Vamp/fonts/Hootie/x.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/y.gif Binary file forum/Themes/Vamp/fonts/Hootie/y.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Hootie/z.gif Binary file forum/Themes/Vamp/fonts/Hootie/z.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Kimbalt.ttf Binary file forum/Themes/Vamp/fonts/Kimbalt.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President.gdf Binary file forum/Themes/Vamp/fonts/President.gdf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/a.gif Binary file forum/Themes/Vamp/fonts/President/a.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/b.gif Binary file forum/Themes/Vamp/fonts/President/b.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/c.gif Binary file forum/Themes/Vamp/fonts/President/c.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/d.gif Binary file forum/Themes/Vamp/fonts/President/d.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/e.gif Binary file forum/Themes/Vamp/fonts/President/e.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/f.gif Binary file forum/Themes/Vamp/fonts/President/f.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/g.gif Binary file forum/Themes/Vamp/fonts/President/g.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/h.gif Binary file forum/Themes/Vamp/fonts/President/h.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/i.gif Binary file forum/Themes/Vamp/fonts/President/i.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/fonts/President/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/j.gif Binary file forum/Themes/Vamp/fonts/President/j.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/k.gif Binary file forum/Themes/Vamp/fonts/President/k.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/l.gif Binary file forum/Themes/Vamp/fonts/President/l.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/m.gif Binary file forum/Themes/Vamp/fonts/President/m.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/n.gif Binary file forum/Themes/Vamp/fonts/President/n.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/o.gif Binary file forum/Themes/Vamp/fonts/President/o.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/p.gif Binary file forum/Themes/Vamp/fonts/President/p.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/q.gif Binary file forum/Themes/Vamp/fonts/President/q.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/r.gif Binary file forum/Themes/Vamp/fonts/President/r.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/s.gif Binary file forum/Themes/Vamp/fonts/President/s.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/t.gif Binary file forum/Themes/Vamp/fonts/President/t.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/u.gif Binary file forum/Themes/Vamp/fonts/President/u.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/v.gif Binary file forum/Themes/Vamp/fonts/President/v.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/w.gif Binary file forum/Themes/Vamp/fonts/President/w.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/x.gif Binary file forum/Themes/Vamp/fonts/President/x.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/y.gif Binary file forum/Themes/Vamp/fonts/President/y.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/President/z.gif Binary file forum/Themes/Vamp/fonts/President/z.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Screenge.ttf Binary file forum/Themes/Vamp/fonts/Screenge.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Venusris.ttf Binary file forum/Themes/Vamp/fonts/Venusris.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/Walshes.ttf Binary file forum/Themes/Vamp/fonts/Walshes.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/fonts/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/a.english.wav Binary file forum/Themes/Vamp/fonts/sound/a.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/b.english.wav Binary file forum/Themes/Vamp/fonts/sound/b.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/c.english.wav Binary file forum/Themes/Vamp/fonts/sound/c.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/d.english.wav Binary file forum/Themes/Vamp/fonts/sound/d.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/e.english.wav Binary file forum/Themes/Vamp/fonts/sound/e.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/f.english.wav Binary file forum/Themes/Vamp/fonts/sound/f.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/g.english.wav Binary file forum/Themes/Vamp/fonts/sound/g.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/h.english.wav Binary file forum/Themes/Vamp/fonts/sound/h.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/i.english.wav Binary file forum/Themes/Vamp/fonts/sound/i.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/fonts/sound/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/j.english.wav Binary file forum/Themes/Vamp/fonts/sound/j.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/k.english.wav Binary file forum/Themes/Vamp/fonts/sound/k.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/l.english.wav Binary file forum/Themes/Vamp/fonts/sound/l.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/m.english.wav Binary file forum/Themes/Vamp/fonts/sound/m.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/n.english.wav Binary file forum/Themes/Vamp/fonts/sound/n.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/o.english.wav Binary file forum/Themes/Vamp/fonts/sound/o.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/p.english.wav Binary file forum/Themes/Vamp/fonts/sound/p.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/q.english.wav Binary file forum/Themes/Vamp/fonts/sound/q.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/r.english.wav Binary file forum/Themes/Vamp/fonts/sound/r.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/s.english.wav Binary file forum/Themes/Vamp/fonts/sound/s.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/t.english.wav Binary file forum/Themes/Vamp/fonts/sound/t.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/u.english.wav Binary file forum/Themes/Vamp/fonts/sound/u.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/v.english.wav Binary file forum/Themes/Vamp/fonts/sound/v.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/w.english.wav Binary file forum/Themes/Vamp/fonts/sound/w.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/x.english.wav Binary file forum/Themes/Vamp/fonts/sound/x.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/y.english.wav Binary file forum/Themes/Vamp/fonts/sound/y.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/fonts/sound/z.english.wav Binary file forum/Themes/Vamp/fonts/sound/z.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/help.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/help.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,69 @@ +h1 +{ + font-size: 18px; + margin: 1ex 1ex 1ex 1ex; +} +h2 +{ + font-size: 18px; + border-bottom: solid 1px; + padding-bottom: 1ex; +} +h3, h4, h5, h6 +{ + font-size: 16px; + margin: 0; +} +p +{ + margin: 0 0 1.5em 0; +} +.board, .board:link +{ + text-decoration: underline; +} +.board:visited, .board:hover +{ + text-decoration: underline; +} +div.footer +{ + text-align: center; + padding: 3pt; +} +#menu +{ + line-height: 1; +} +#menu p +{ + margin: 0 0 1em 0; + line-height: 2; +} +table#reference1, table#reference2 +{ + border: 1px solid; +} +table#reference1 td, table#reference2 td +{ + vertical-align: top; + border: 1px solid; + font-size: x-small; + } +table#reference1 th, table#reference2 th +{ + font-size: x-small; +} +ol +{ + font-weight: bold; + list-style-type: square; + margin-bottom: 2ex; + margin-top: 3ex; +} +ol.la +{ + font-weight: normal; + list-style-type: circle; + margin: 0 0 2ex 4ex; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/Female.gif Binary file forum/Themes/Vamp/images/Female.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/Male.gif Binary file forum/Themes/Vamp/images/Male.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/administration.gif Binary file forum/Themes/Vamp/images/admin/administration.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/attachment.gif Binary file forum/Themes/Vamp/images/admin/attachment.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/ban.gif Binary file forum/Themes/Vamp/images/admin/ban.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/boards.gif Binary file forum/Themes/Vamp/images/admin/boards.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/calendar.gif Binary file forum/Themes/Vamp/images/admin/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/change_menu.png Binary file forum/Themes/Vamp/images/admin/change_menu.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/change_menu2.png Binary file forum/Themes/Vamp/images/admin/change_menu2.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/corefeatures.gif Binary file forum/Themes/Vamp/images/admin/corefeatures.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/current_theme.gif Binary file forum/Themes/Vamp/images/admin/current_theme.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/engines.gif Binary file forum/Themes/Vamp/images/admin/engines.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/feature_cd.png Binary file forum/Themes/Vamp/images/admin/feature_cd.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/feature_cp.png Binary file forum/Themes/Vamp/images/admin/feature_cp.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/feature_k.png Binary file forum/Themes/Vamp/images/admin/feature_k.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/feature_ml.png Binary file forum/Themes/Vamp/images/admin/feature_ml.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/feature_pm.png Binary file forum/Themes/Vamp/images/admin/feature_pm.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/feature_ps.png Binary file forum/Themes/Vamp/images/admin/feature_ps.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/feature_rg.png Binary file forum/Themes/Vamp/images/admin/feature_rg.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/feature_sp.png Binary file forum/Themes/Vamp/images/admin/feature_sp.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/feature_w.png Binary file forum/Themes/Vamp/images/admin/feature_w.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/features.gif Binary file forum/Themes/Vamp/images/admin/features.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/features_and_options.png Binary file forum/Themes/Vamp/images/admin/features_and_options.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/forum_maintenance.png Binary file forum/Themes/Vamp/images/admin/forum_maintenance.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/ignore.gif Binary file forum/Themes/Vamp/images/admin/ignore.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/images/admin/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/languages.gif Binary file forum/Themes/Vamp/images/admin/languages.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/logs.gif Binary file forum/Themes/Vamp/images/admin/logs.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/mail.gif Binary file forum/Themes/Vamp/images/admin/mail.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/maintain.gif Binary file forum/Themes/Vamp/images/admin/maintain.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/membergroups.gif Binary file forum/Themes/Vamp/images/admin/membergroups.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/members.gif Binary file forum/Themes/Vamp/images/admin/members.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/members.png Binary file forum/Themes/Vamp/images/admin/members.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/modifications.gif Binary file forum/Themes/Vamp/images/admin/modifications.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/news.gif Binary file forum/Themes/Vamp/images/admin/news.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/package_ops.gif Binary file forum/Themes/Vamp/images/admin/package_ops.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/packages.gif Binary file forum/Themes/Vamp/images/admin/packages.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/packages.png Binary file forum/Themes/Vamp/images/admin/packages.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/paid.gif Binary file forum/Themes/Vamp/images/admin/paid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/permissions.gif Binary file forum/Themes/Vamp/images/admin/permissions.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/permissions.png Binary file forum/Themes/Vamp/images/admin/permissions.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/post_moderation_allow.gif Binary file forum/Themes/Vamp/images/admin/post_moderation_allow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/post_moderation_deny.gif Binary file forum/Themes/Vamp/images/admin/post_moderation_deny.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/post_moderation_moderate.gif Binary file forum/Themes/Vamp/images/admin/post_moderation_moderate.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/posts.gif Binary file forum/Themes/Vamp/images/admin/posts.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/regcenter.gif Binary file forum/Themes/Vamp/images/admin/regcenter.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/reports.gif Binary file forum/Themes/Vamp/images/admin/reports.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/scheduled.gif Binary file forum/Themes/Vamp/images/admin/scheduled.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/search.gif Binary file forum/Themes/Vamp/images/admin/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/security.gif Binary file forum/Themes/Vamp/images/admin/security.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/server.gif Binary file forum/Themes/Vamp/images/admin/server.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/smiley.gif Binary file forum/Themes/Vamp/images/admin/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/smilies_and_messageicons.png Binary file forum/Themes/Vamp/images/admin/smilies_and_messageicons.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/subsection.gif Binary file forum/Themes/Vamp/images/admin/subsection.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/support.gif Binary file forum/Themes/Vamp/images/admin/support.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/support_and_credits.png Binary file forum/Themes/Vamp/images/admin/support_and_credits.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/switch_off.png Binary file forum/Themes/Vamp/images/admin/switch_off.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/switch_on.png Binary file forum/Themes/Vamp/images/admin/switch_on.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/themes.gif Binary file forum/Themes/Vamp/images/admin/themes.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/admin/themes_and_layout.png Binary file forum/Themes/Vamp/images/admin/themes_and_layout.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/aim.gif Binary file forum/Themes/Vamp/images/aim.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bar.gif Binary file forum/Themes/Vamp/images/bar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bar_stats.png Binary file forum/Themes/Vamp/images/bar_stats.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/bbc_bg.gif Binary file forum/Themes/Vamp/images/bbc/bbc_bg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/bbc_hoverbg.gif Binary file forum/Themes/Vamp/images/bbc/bbc_hoverbg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/bold.gif Binary file forum/Themes/Vamp/images/bbc/bold.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/center.gif Binary file forum/Themes/Vamp/images/bbc/center.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/code.gif Binary file forum/Themes/Vamp/images/bbc/code.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/divider.gif Binary file forum/Themes/Vamp/images/bbc/divider.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/email.gif Binary file forum/Themes/Vamp/images/bbc/email.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/face.gif Binary file forum/Themes/Vamp/images/bbc/face.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/flash.gif Binary file forum/Themes/Vamp/images/bbc/flash.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/ftp.gif Binary file forum/Themes/Vamp/images/bbc/ftp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/glow.gif Binary file forum/Themes/Vamp/images/bbc/glow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/hr.gif Binary file forum/Themes/Vamp/images/bbc/hr.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/img.gif Binary file forum/Themes/Vamp/images/bbc/img.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/images/bbc/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/italicize.gif Binary file forum/Themes/Vamp/images/bbc/italicize.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/left.gif Binary file forum/Themes/Vamp/images/bbc/left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/list.gif Binary file forum/Themes/Vamp/images/bbc/list.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/move.gif Binary file forum/Themes/Vamp/images/bbc/move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/orderlist.gif Binary file forum/Themes/Vamp/images/bbc/orderlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/pre.gif Binary file forum/Themes/Vamp/images/bbc/pre.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/quote.gif Binary file forum/Themes/Vamp/images/bbc/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/resize-handle.gif Binary file forum/Themes/Vamp/images/bbc/resize-handle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/right.gif Binary file forum/Themes/Vamp/images/bbc/right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/shadow.gif Binary file forum/Themes/Vamp/images/bbc/shadow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/size.gif Binary file forum/Themes/Vamp/images/bbc/size.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/strike.gif Binary file forum/Themes/Vamp/images/bbc/strike.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/sub.gif Binary file forum/Themes/Vamp/images/bbc/sub.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/sup.gif Binary file forum/Themes/Vamp/images/bbc/sup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/table.gif Binary file forum/Themes/Vamp/images/bbc/table.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/td.gif Binary file forum/Themes/Vamp/images/bbc/td.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/tele.gif Binary file forum/Themes/Vamp/images/bbc/tele.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/toggle.gif Binary file forum/Themes/Vamp/images/bbc/toggle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/tr.gif Binary file forum/Themes/Vamp/images/bbc/tr.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/underline.gif Binary file forum/Themes/Vamp/images/bbc/underline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/unformat.gif Binary file forum/Themes/Vamp/images/bbc/unformat.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bbc/url.gif Binary file forum/Themes/Vamp/images/bbc/url.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/bdaycake.gif Binary file forum/Themes/Vamp/images/bdaycake.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/blank.gif Binary file forum/Themes/Vamp/images/blank.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/board.gif Binary file forum/Themes/Vamp/images/board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/board_select_spot.gif Binary file forum/Themes/Vamp/images/board_select_spot.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/board_select_spot_child.gif Binary file forum/Themes/Vamp/images/board_select_spot_child.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/box_bg.gif Binary file forum/Themes/Vamp/images/box_bg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buddy_useroff.gif Binary file forum/Themes/Vamp/images/buddy_useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buddy_useron.gif Binary file forum/Themes/Vamp/images/buddy_useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/approve.gif Binary file forum/Themes/Vamp/images/buttons/approve.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/calendarpe.gif Binary file forum/Themes/Vamp/images/buttons/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/close.gif Binary file forum/Themes/Vamp/images/buttons/close.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/delete.gif Binary file forum/Themes/Vamp/images/buttons/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/details.gif Binary file forum/Themes/Vamp/images/buttons/details.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/ignore.gif Binary file forum/Themes/Vamp/images/buttons/ignore.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/im_reply.gif Binary file forum/Themes/Vamp/images/buttons/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/im_reply_all.gif Binary file forum/Themes/Vamp/images/buttons/im_reply_all.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/images/buttons/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/merge.gif Binary file forum/Themes/Vamp/images/buttons/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/modify.gif Binary file forum/Themes/Vamp/images/buttons/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/notify_sm.gif Binary file forum/Themes/Vamp/images/buttons/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/quote.gif Binary file forum/Themes/Vamp/images/buttons/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/reply.gif Binary file forum/Themes/Vamp/images/buttons/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/reply_sm.gif Binary file forum/Themes/Vamp/images/buttons/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/restore_topic.gif Binary file forum/Themes/Vamp/images/buttons/restore_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/search.gif Binary file forum/Themes/Vamp/images/buttons/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/buttons/split.gif Binary file forum/Themes/Vamp/images/buttons/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/cake.png Binary file forum/Themes/Vamp/images/cake.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/catbg.jpg Binary file forum/Themes/Vamp/images/catbg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/catbg2.jpg Binary file forum/Themes/Vamp/images/catbg2.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/collapse.gif Binary file forum/Themes/Vamp/images/collapse.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/construction.gif Binary file forum/Themes/Vamp/images/construction.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/construction.png Binary file forum/Themes/Vamp/images/construction.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/addpoll.gif Binary file forum/Themes/Vamp/images/dutch/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/admin.gif Binary file forum/Themes/Vamp/images/dutch/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/admin_lock.gif Binary file forum/Themes/Vamp/images/dutch/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/admin_move.gif Binary file forum/Themes/Vamp/images/dutch/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/admin_rem.gif Binary file forum/Themes/Vamp/images/dutch/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/admin_remove_poll.gif Binary file forum/Themes/Vamp/images/dutch/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/admin_sticky.gif Binary file forum/Themes/Vamp/images/dutch/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/calendar.gif Binary file forum/Themes/Vamp/images/dutch/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/calendarpe.gif Binary file forum/Themes/Vamp/images/dutch/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/delete.gif Binary file forum/Themes/Vamp/images/dutch/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/delete_selected.gif Binary file forum/Themes/Vamp/images/dutch/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/go_down.gif Binary file forum/Themes/Vamp/images/dutch/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/go_up.gif Binary file forum/Themes/Vamp/images/dutch/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/help.gif Binary file forum/Themes/Vamp/images/dutch/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/home.gif Binary file forum/Themes/Vamp/images/dutch/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/im_delete.gif Binary file forum/Themes/Vamp/images/dutch/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/im_inbox.gif Binary file forum/Themes/Vamp/images/dutch/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/im_new.gif Binary file forum/Themes/Vamp/images/dutch/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/im_outbox.gif Binary file forum/Themes/Vamp/images/dutch/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/im_reload.gif Binary file forum/Themes/Vamp/images/dutch/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/im_reply.gif Binary file forum/Themes/Vamp/images/dutch/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/keystats.gif Binary file forum/Themes/Vamp/images/dutch/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/linktocal.gif Binary file forum/Themes/Vamp/images/dutch/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/login.gif Binary file forum/Themes/Vamp/images/dutch/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/logout.gif Binary file forum/Themes/Vamp/images/dutch/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/markread.gif Binary file forum/Themes/Vamp/images/dutch/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/markunread.gif Binary file forum/Themes/Vamp/images/dutch/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/memberlist.gif Binary file forum/Themes/Vamp/images/dutch/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/merge.gif Binary file forum/Themes/Vamp/images/dutch/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/modify.gif Binary file forum/Themes/Vamp/images/dutch/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/new.gif Binary file forum/Themes/Vamp/images/dutch/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/new_poll.gif Binary file forum/Themes/Vamp/images/dutch/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/new_topic.gif Binary file forum/Themes/Vamp/images/dutch/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/newsbox.gif Binary file forum/Themes/Vamp/images/dutch/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/notify.gif Binary file forum/Themes/Vamp/images/dutch/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/notify_sm.gif Binary file forum/Themes/Vamp/images/dutch/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/print.gif Binary file forum/Themes/Vamp/images/dutch/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/profile.gif Binary file forum/Themes/Vamp/images/dutch/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/quote.gif Binary file forum/Themes/Vamp/images/dutch/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/register.gif Binary file forum/Themes/Vamp/images/dutch/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/reply.gif Binary file forum/Themes/Vamp/images/dutch/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/reply_sm.gif Binary file forum/Themes/Vamp/images/dutch/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/search.gif Binary file forum/Themes/Vamp/images/dutch/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/sendtopic.gif Binary file forum/Themes/Vamp/images/dutch/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/split.gif Binary file forum/Themes/Vamp/images/dutch/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/userinfo.gif Binary file forum/Themes/Vamp/images/dutch/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/dutch/who.gif Binary file forum/Themes/Vamp/images/dutch/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/email_sm.gif Binary file forum/Themes/Vamp/images/email_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/addpoll.gif Binary file forum/Themes/Vamp/images/english/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/admin.gif Binary file forum/Themes/Vamp/images/english/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/admin_lock.gif Binary file forum/Themes/Vamp/images/english/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/admin_move.gif Binary file forum/Themes/Vamp/images/english/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/admin_rem.gif Binary file forum/Themes/Vamp/images/english/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/admin_remove_poll.gif Binary file forum/Themes/Vamp/images/english/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/admin_sticky.gif Binary file forum/Themes/Vamp/images/english/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/calendar.gif Binary file forum/Themes/Vamp/images/english/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/calendarpe.gif Binary file forum/Themes/Vamp/images/english/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/delete.gif Binary file forum/Themes/Vamp/images/english/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/delete_selected.gif Binary file forum/Themes/Vamp/images/english/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/go_down.gif Binary file forum/Themes/Vamp/images/english/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/go_up.gif Binary file forum/Themes/Vamp/images/english/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/help.gif Binary file forum/Themes/Vamp/images/english/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/home.gif Binary file forum/Themes/Vamp/images/english/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/im_delete.gif Binary file forum/Themes/Vamp/images/english/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/im_inbox.gif Binary file forum/Themes/Vamp/images/english/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/im_new.gif Binary file forum/Themes/Vamp/images/english/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/im_outbox.gif Binary file forum/Themes/Vamp/images/english/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/im_reload.gif Binary file forum/Themes/Vamp/images/english/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/im_reply.gif Binary file forum/Themes/Vamp/images/english/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/images/english/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/keystats.gif Binary file forum/Themes/Vamp/images/english/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/linktocal.gif Binary file forum/Themes/Vamp/images/english/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/login.gif Binary file forum/Themes/Vamp/images/english/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/logout.gif Binary file forum/Themes/Vamp/images/english/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/markread.gif Binary file forum/Themes/Vamp/images/english/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/markunread.gif Binary file forum/Themes/Vamp/images/english/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/memberlist.gif Binary file forum/Themes/Vamp/images/english/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/merge.gif Binary file forum/Themes/Vamp/images/english/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/modify.gif Binary file forum/Themes/Vamp/images/english/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/new.gif Binary file forum/Themes/Vamp/images/english/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/new_poll.gif Binary file forum/Themes/Vamp/images/english/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/new_topic.gif Binary file forum/Themes/Vamp/images/english/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/newsbox.gif Binary file forum/Themes/Vamp/images/english/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/notify.gif Binary file forum/Themes/Vamp/images/english/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/notify_sm.gif Binary file forum/Themes/Vamp/images/english/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/print.gif Binary file forum/Themes/Vamp/images/english/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/profile.gif Binary file forum/Themes/Vamp/images/english/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/quote.gif Binary file forum/Themes/Vamp/images/english/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/register.gif Binary file forum/Themes/Vamp/images/english/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/reply.gif Binary file forum/Themes/Vamp/images/english/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/reply_sm.gif Binary file forum/Themes/Vamp/images/english/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/search.gif Binary file forum/Themes/Vamp/images/english/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/sendtopic.gif Binary file forum/Themes/Vamp/images/english/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/split.gif Binary file forum/Themes/Vamp/images/english/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/userinfo.gif Binary file forum/Themes/Vamp/images/english/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/english/who.gif Binary file forum/Themes/Vamp/images/english/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/expand.gif Binary file forum/Themes/Vamp/images/expand.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/filter.gif Binary file forum/Themes/Vamp/images/filter.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/addpoll.gif Binary file forum/Themes/Vamp/images/german/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/admin.gif Binary file forum/Themes/Vamp/images/german/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/admin_lock.gif Binary file forum/Themes/Vamp/images/german/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/admin_move.gif Binary file forum/Themes/Vamp/images/german/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/admin_rem.gif Binary file forum/Themes/Vamp/images/german/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/admin_remove_poll.gif Binary file forum/Themes/Vamp/images/german/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/admin_sticky.gif Binary file forum/Themes/Vamp/images/german/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/calendar.gif Binary file forum/Themes/Vamp/images/german/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/calendarpe.gif Binary file forum/Themes/Vamp/images/german/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/delete.gif Binary file forum/Themes/Vamp/images/german/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/delete_selected.gif Binary file forum/Themes/Vamp/images/german/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/go_down.gif Binary file forum/Themes/Vamp/images/german/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/go_up.gif Binary file forum/Themes/Vamp/images/german/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/help.gif Binary file forum/Themes/Vamp/images/german/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/home.gif Binary file forum/Themes/Vamp/images/german/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/im_delete.gif Binary file forum/Themes/Vamp/images/german/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/im_inbox.gif Binary file forum/Themes/Vamp/images/german/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/im_new.gif Binary file forum/Themes/Vamp/images/german/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/im_outbox.gif Binary file forum/Themes/Vamp/images/german/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/im_reload.gif Binary file forum/Themes/Vamp/images/german/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/im_reply.gif Binary file forum/Themes/Vamp/images/german/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/keystats.gif Binary file forum/Themes/Vamp/images/german/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/linktocal.gif Binary file forum/Themes/Vamp/images/german/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/login.gif Binary file forum/Themes/Vamp/images/german/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/logout.gif Binary file forum/Themes/Vamp/images/german/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/markread.gif Binary file forum/Themes/Vamp/images/german/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/markunread.gif Binary file forum/Themes/Vamp/images/german/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/memberlist.gif Binary file forum/Themes/Vamp/images/german/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/merge.gif Binary file forum/Themes/Vamp/images/german/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/modify.gif Binary file forum/Themes/Vamp/images/german/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/new.gif Binary file forum/Themes/Vamp/images/german/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/new_poll.gif Binary file forum/Themes/Vamp/images/german/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/new_topic.gif Binary file forum/Themes/Vamp/images/german/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/newsbox.gif Binary file forum/Themes/Vamp/images/german/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/notify.gif Binary file forum/Themes/Vamp/images/german/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/notify_sm.gif Binary file forum/Themes/Vamp/images/german/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/print.gif Binary file forum/Themes/Vamp/images/german/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/profile.gif Binary file forum/Themes/Vamp/images/german/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/quote.gif Binary file forum/Themes/Vamp/images/german/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/register.gif Binary file forum/Themes/Vamp/images/german/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/reply.gif Binary file forum/Themes/Vamp/images/german/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/reply_sm.gif Binary file forum/Themes/Vamp/images/german/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/search.gif Binary file forum/Themes/Vamp/images/german/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/sendtopic.gif Binary file forum/Themes/Vamp/images/german/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/split.gif Binary file forum/Themes/Vamp/images/german/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/userinfo.gif Binary file forum/Themes/Vamp/images/german/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/german/who.gif Binary file forum/Themes/Vamp/images/german/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/h_powered-mysql.gif Binary file forum/Themes/Vamp/images/h_powered-mysql.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/h_powered-php.gif Binary file forum/Themes/Vamp/images/h_powered-php.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/h_valid-css.gif Binary file forum/Themes/Vamp/images/h_valid-css.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/h_valid-xhtml10.gif Binary file forum/Themes/Vamp/images/h_valid-xhtml10.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/helplogo.jpg Binary file forum/Themes/Vamp/images/helplogo.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/helptopics.gif Binary file forum/Themes/Vamp/images/helptopics.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/assist.gif Binary file forum/Themes/Vamp/images/icons/assist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/bullet_grin.gif Binary file forum/Themes/Vamp/images/icons/bullet_grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/calendar.gif Binary file forum/Themes/Vamp/images/icons/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/clip.gif Binary file forum/Themes/Vamp/images/icons/clip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/config_sm.gif Binary file forum/Themes/Vamp/images/icons/config_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/delete.gif Binary file forum/Themes/Vamp/images/icons/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/field_check.gif Binary file forum/Themes/Vamp/images/icons/field_check.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/field_invalid.gif Binary file forum/Themes/Vamp/images/icons/field_invalid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/field_valid.gif Binary file forum/Themes/Vamp/images/icons/field_valid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/folder_open.gif Binary file forum/Themes/Vamp/images/icons/folder_open.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/im_newmsg.gif Binary file forum/Themes/Vamp/images/icons/im_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/images/icons/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/info.gif Binary file forum/Themes/Vamp/images/icons/info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/last_post.gif Binary file forum/Themes/Vamp/images/icons/last_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/linktree_main.gif Binary file forum/Themes/Vamp/images/icons/linktree_main.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/linktree_side.gif Binary file forum/Themes/Vamp/images/icons/linktree_side.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/login.gif Binary file forum/Themes/Vamp/images/icons/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/login_sm.gif Binary file forum/Themes/Vamp/images/icons/login_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/members.gif Binary file forum/Themes/Vamp/images/icons/members.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/modify_inline.gif Binary file forum/Themes/Vamp/images/icons/modify_inline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/modify_small.gif Binary file forum/Themes/Vamp/images/icons/modify_small.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/notify_sm.gif Binary file forum/Themes/Vamp/images/icons/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/online.gif Binary file forum/Themes/Vamp/images/icons/online.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/package_installed.gif Binary file forum/Themes/Vamp/images/icons/package_installed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/package_old.gif Binary file forum/Themes/Vamp/images/icons/package_old.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/pm_read.gif Binary file forum/Themes/Vamp/images/icons/pm_read.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/pm_replied.gif Binary file forum/Themes/Vamp/images/icons/pm_replied.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/profile_sm.gif Binary file forum/Themes/Vamp/images/icons/profile_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/quick_lock.gif Binary file forum/Themes/Vamp/images/icons/quick_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/quick_move.gif Binary file forum/Themes/Vamp/images/icons/quick_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/quick_remove.gif Binary file forum/Themes/Vamp/images/icons/quick_remove.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/quick_sticky.gif Binary file forum/Themes/Vamp/images/icons/quick_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/quick_sticky_lock.gif Binary file forum/Themes/Vamp/images/icons/quick_sticky_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icons/show_sticky.gif Binary file forum/Themes/Vamp/images/icons/show_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/icq.gif Binary file forum/Themes/Vamp/images/icq.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/im_off.gif Binary file forum/Themes/Vamp/images/im_off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/im_on.gif Binary file forum/Themes/Vamp/images/im_on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/im_sm_newmsg.gif Binary file forum/Themes/Vamp/images/im_sm_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/im_sm_prefs.gif Binary file forum/Themes/Vamp/images/im_sm_prefs.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/im_switch.gif Binary file forum/Themes/Vamp/images/im_switch.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/images/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/ip.gif Binary file forum/Themes/Vamp/images/ip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/loading.gif Binary file forum/Themes/Vamp/images/loading.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/maintab_active_back.gif Binary file forum/Themes/Vamp/images/maintab_active_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/maintab_active_first.gif Binary file forum/Themes/Vamp/images/maintab_active_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/maintab_active_last.gif Binary file forum/Themes/Vamp/images/maintab_active_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/maintab_back.gif Binary file forum/Themes/Vamp/images/maintab_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/maintab_first.gif Binary file forum/Themes/Vamp/images/maintab_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/maintab_last.gif Binary file forum/Themes/Vamp/images/maintab_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/menubg.gif Binary file forum/Themes/Vamp/images/menubg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/message_sm.gif Binary file forum/Themes/Vamp/images/message_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/mirrortab_active_back.gif Binary file forum/Themes/Vamp/images/mirrortab_active_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/mirrortab_active_first.gif Binary file forum/Themes/Vamp/images/mirrortab_active_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/mirrortab_active_last.gif Binary file forum/Themes/Vamp/images/mirrortab_active_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/mirrortab_back.gif Binary file forum/Themes/Vamp/images/mirrortab_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/mirrortab_first.gif Binary file forum/Themes/Vamp/images/mirrortab_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/mirrortab_last.gif Binary file forum/Themes/Vamp/images/mirrortab_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/modtab_back.gif Binary file forum/Themes/Vamp/images/modtab_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/modtab_first.gif Binary file forum/Themes/Vamp/images/modtab_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/modtab_last.gif Binary file forum/Themes/Vamp/images/modtab_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/msntalk.gif Binary file forum/Themes/Vamp/images/msntalk.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/new_bar.gif Binary file forum/Themes/Vamp/images/new_bar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/new_none.gif Binary file forum/Themes/Vamp/images/new_none.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/new_none.png Binary file forum/Themes/Vamp/images/new_none.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/new_redirect.png Binary file forum/Themes/Vamp/images/new_redirect.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/new_some.gif Binary file forum/Themes/Vamp/images/new_some.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/new_some.png Binary file forum/Themes/Vamp/images/new_some.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/off.gif Binary file forum/Themes/Vamp/images/off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/off.png Binary file forum/Themes/Vamp/images/off.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/on.gif Binary file forum/Themes/Vamp/images/on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/on.png Binary file forum/Themes/Vamp/images/on.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/on2.gif Binary file forum/Themes/Vamp/images/on2.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/on2.png Binary file forum/Themes/Vamp/images/on2.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/openid.gif Binary file forum/Themes/Vamp/images/openid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/pm_recipient_delete.gif Binary file forum/Themes/Vamp/images/pm_recipient_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/poll_left.gif Binary file forum/Themes/Vamp/images/poll_left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/poll_middle.gif Binary file forum/Themes/Vamp/images/poll_middle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/poll_right.gif Binary file forum/Themes/Vamp/images/poll_right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/angry.gif Binary file forum/Themes/Vamp/images/post/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/cheesy.gif Binary file forum/Themes/Vamp/images/post/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/clip.gif Binary file forum/Themes/Vamp/images/post/clip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/exclamation.gif Binary file forum/Themes/Vamp/images/post/exclamation.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/grin.gif Binary file forum/Themes/Vamp/images/post/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/images/post/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/lamp.gif Binary file forum/Themes/Vamp/images/post/lamp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/last_post.gif Binary file forum/Themes/Vamp/images/post/last_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/moved.gif Binary file forum/Themes/Vamp/images/post/moved.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/question.gif Binary file forum/Themes/Vamp/images/post/question.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/recycled.gif Binary file forum/Themes/Vamp/images/post/recycled.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/sad.gif Binary file forum/Themes/Vamp/images/post/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/smiley.gif Binary file forum/Themes/Vamp/images/post/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/thumbdown.gif Binary file forum/Themes/Vamp/images/post/thumbdown.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/thumbup.gif Binary file forum/Themes/Vamp/images/post/thumbup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/wink.gif Binary file forum/Themes/Vamp/images/post/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/wireless.gif Binary file forum/Themes/Vamp/images/post/wireless.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/post/xx.gif Binary file forum/Themes/Vamp/images/post/xx.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/powered-mysql.gif Binary file forum/Themes/Vamp/images/powered-mysql.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/powered-php.gif Binary file forum/Themes/Vamp/images/powered-php.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/redirect.png Binary file forum/Themes/Vamp/images/redirect.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/selected.gif Binary file forum/Themes/Vamp/images/selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/smflogo.gif Binary file forum/Themes/Vamp/images/smflogo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/smflogo.png Binary file forum/Themes/Vamp/images/smflogo.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/smiley_select_spot.gif Binary file forum/Themes/Vamp/images/smiley_select_spot.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/sort_down.gif Binary file forum/Themes/Vamp/images/sort_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/sort_up.gif Binary file forum/Themes/Vamp/images/sort_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/addpoll.gif Binary file forum/Themes/Vamp/images/spanish/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/admin.gif Binary file forum/Themes/Vamp/images/spanish/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/admin_lock.gif Binary file forum/Themes/Vamp/images/spanish/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/admin_move.gif Binary file forum/Themes/Vamp/images/spanish/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/admin_rem.gif Binary file forum/Themes/Vamp/images/spanish/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/admin_remove_poll.gif Binary file forum/Themes/Vamp/images/spanish/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/admin_sticky.gif Binary file forum/Themes/Vamp/images/spanish/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/calendar.gif Binary file forum/Themes/Vamp/images/spanish/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/calendarpe.gif Binary file forum/Themes/Vamp/images/spanish/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/delete.gif Binary file forum/Themes/Vamp/images/spanish/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/delete_selected.gif Binary file forum/Themes/Vamp/images/spanish/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/go_down.gif Binary file forum/Themes/Vamp/images/spanish/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/go_up.gif Binary file forum/Themes/Vamp/images/spanish/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/help.gif Binary file forum/Themes/Vamp/images/spanish/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/home.gif Binary file forum/Themes/Vamp/images/spanish/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/im_delete.gif Binary file forum/Themes/Vamp/images/spanish/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/im_inbox.gif Binary file forum/Themes/Vamp/images/spanish/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/im_new.gif Binary file forum/Themes/Vamp/images/spanish/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/im_outbox.gif Binary file forum/Themes/Vamp/images/spanish/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/im_reload.gif Binary file forum/Themes/Vamp/images/spanish/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/im_reply.gif Binary file forum/Themes/Vamp/images/spanish/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/keystats.gif Binary file forum/Themes/Vamp/images/spanish/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/linktocal.gif Binary file forum/Themes/Vamp/images/spanish/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/login.gif Binary file forum/Themes/Vamp/images/spanish/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/logout.gif Binary file forum/Themes/Vamp/images/spanish/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/markread.gif Binary file forum/Themes/Vamp/images/spanish/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/markunread.gif Binary file forum/Themes/Vamp/images/spanish/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/memberlist.gif Binary file forum/Themes/Vamp/images/spanish/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/merge.gif Binary file forum/Themes/Vamp/images/spanish/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/modify.gif Binary file forum/Themes/Vamp/images/spanish/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/new.gif Binary file forum/Themes/Vamp/images/spanish/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/new_poll.gif Binary file forum/Themes/Vamp/images/spanish/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/new_topic.gif Binary file forum/Themes/Vamp/images/spanish/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/newsbox.gif Binary file forum/Themes/Vamp/images/spanish/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/notify.gif Binary file forum/Themes/Vamp/images/spanish/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/notify_sm.gif Binary file forum/Themes/Vamp/images/spanish/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/print.gif Binary file forum/Themes/Vamp/images/spanish/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/profile.gif Binary file forum/Themes/Vamp/images/spanish/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/quote.gif Binary file forum/Themes/Vamp/images/spanish/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/register.gif Binary file forum/Themes/Vamp/images/spanish/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/reply.gif Binary file forum/Themes/Vamp/images/spanish/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/reply_sm.gif Binary file forum/Themes/Vamp/images/spanish/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/search.gif Binary file forum/Themes/Vamp/images/spanish/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/sendtopic.gif Binary file forum/Themes/Vamp/images/spanish/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/split.gif Binary file forum/Themes/Vamp/images/spanish/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/userinfo.gif Binary file forum/Themes/Vamp/images/spanish/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/spanish/who.gif Binary file forum/Themes/Vamp/images/spanish/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/split_deselect.gif Binary file forum/Themes/Vamp/images/split_deselect.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/split_select.gif Binary file forum/Themes/Vamp/images/split_select.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/star.gif Binary file forum/Themes/Vamp/images/star.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/staradmin.gif Binary file forum/Themes/Vamp/images/staradmin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stargmod.gif Binary file forum/Themes/Vamp/images/stargmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/starmod.gif Binary file forum/Themes/Vamp/images/starmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stats_board.gif Binary file forum/Themes/Vamp/images/stats_board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stats_boards.gif Binary file forum/Themes/Vamp/images/stats_boards.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stats_history.gif Binary file forum/Themes/Vamp/images/stats_history.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stats_info.gif Binary file forum/Themes/Vamp/images/stats_info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stats_pie.png Binary file forum/Themes/Vamp/images/stats_pie.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stats_pie_rtl.png Binary file forum/Themes/Vamp/images/stats_pie_rtl.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stats_posters.gif Binary file forum/Themes/Vamp/images/stats_posters.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stats_replies.gif Binary file forum/Themes/Vamp/images/stats_replies.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/stats_views.gif Binary file forum/Themes/Vamp/images/stats_views.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/theme/backdrop.png Binary file forum/Themes/Vamp/images/theme/backdrop.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/theme/frame_repeat.png Binary file forum/Themes/Vamp/images/theme/frame_repeat.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/theme/loadingbar.png Binary file forum/Themes/Vamp/images/theme/loadingbar.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/theme/main_block.png Binary file forum/Themes/Vamp/images/theme/main_block.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/theme/menu_gfx.png Binary file forum/Themes/Vamp/images/theme/menu_gfx.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/theme/quickbuttons.png Binary file forum/Themes/Vamp/images/theme/quickbuttons.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/theme/quote.png Binary file forum/Themes/Vamp/images/theme/quote.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/theme/submit_bg.png Binary file forum/Themes/Vamp/images/theme/submit_bg.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/thumbnail.gif Binary file forum/Themes/Vamp/images/thumbnail.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/titlebg.jpg Binary file forum/Themes/Vamp/images/titlebg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topbg.jpg Binary file forum/Themes/Vamp/images/topbg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/hot_poll.gif Binary file forum/Themes/Vamp/images/topic/hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/hot_poll_locked.gif Binary file forum/Themes/Vamp/images/topic/hot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/hot_poll_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/hot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/hot_poll_sticky.gif Binary file forum/Themes/Vamp/images/topic/hot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/hot_post.gif Binary file forum/Themes/Vamp/images/topic/hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/hot_post_locked.gif Binary file forum/Themes/Vamp/images/topic/hot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/hot_post_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/hot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/hot_post_sticky.gif Binary file forum/Themes/Vamp/images/topic/hot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/images/topic/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_hot_poll.gif Binary file forum/Themes/Vamp/images/topic/my_hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_hot_poll_locked.gif Binary file forum/Themes/Vamp/images/topic/my_hot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_hot_poll_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_hot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_hot_poll_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_hot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_hot_post.gif Binary file forum/Themes/Vamp/images/topic/my_hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_hot_post_locked.gif Binary file forum/Themes/Vamp/images/topic/my_hot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_hot_post_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_hot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_hot_post_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_hot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_normal_poll.gif Binary file forum/Themes/Vamp/images/topic/my_normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_normal_poll_locked.gif Binary file forum/Themes/Vamp/images/topic/my_normal_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_normal_poll_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_normal_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_normal_poll_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_normal_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_normal_post.gif Binary file forum/Themes/Vamp/images/topic/my_normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_normal_post_locked.gif Binary file forum/Themes/Vamp/images/topic/my_normal_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_normal_post_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_normal_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_normal_post_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_normal_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_veryhot_poll.gif Binary file forum/Themes/Vamp/images/topic/my_veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_veryhot_poll_locked.gif Binary file forum/Themes/Vamp/images/topic/my_veryhot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_veryhot_poll_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_veryhot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_veryhot_poll_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_veryhot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_veryhot_post.gif Binary file forum/Themes/Vamp/images/topic/my_veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_veryhot_post_locked.gif Binary file forum/Themes/Vamp/images/topic/my_veryhot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_veryhot_post_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_veryhot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/my_veryhot_post_sticky.gif Binary file forum/Themes/Vamp/images/topic/my_veryhot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/normal_poll.gif Binary file forum/Themes/Vamp/images/topic/normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/normal_poll_locked.gif Binary file forum/Themes/Vamp/images/topic/normal_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/normal_poll_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/normal_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/normal_poll_sticky.gif Binary file forum/Themes/Vamp/images/topic/normal_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/normal_post.gif Binary file forum/Themes/Vamp/images/topic/normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/normal_post_locked.gif Binary file forum/Themes/Vamp/images/topic/normal_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/normal_post_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/normal_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/normal_post_sticky.gif Binary file forum/Themes/Vamp/images/topic/normal_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/veryhot_poll.gif Binary file forum/Themes/Vamp/images/topic/veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/veryhot_poll_locked.gif Binary file forum/Themes/Vamp/images/topic/veryhot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/veryhot_poll_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/veryhot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/veryhot_poll_sticky.gif Binary file forum/Themes/Vamp/images/topic/veryhot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/veryhot_post.gif Binary file forum/Themes/Vamp/images/topic/veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/veryhot_post_locked.gif Binary file forum/Themes/Vamp/images/topic/veryhot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/veryhot_post_locked_sticky.gif Binary file forum/Themes/Vamp/images/topic/veryhot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/topic/veryhot_post_sticky.gif Binary file forum/Themes/Vamp/images/topic/veryhot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/upshrink.gif Binary file forum/Themes/Vamp/images/upshrink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/upshrink.png Binary file forum/Themes/Vamp/images/upshrink.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/upshrink2.gif Binary file forum/Themes/Vamp/images/upshrink2.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/upshrink2.png Binary file forum/Themes/Vamp/images/upshrink2.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/useroff.gif Binary file forum/Themes/Vamp/images/useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/useron.gif Binary file forum/Themes/Vamp/images/useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/valid-css.gif Binary file forum/Themes/Vamp/images/valid-css.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/valid-xhtml10.gif Binary file forum/Themes/Vamp/images/valid-xhtml10.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/warn.gif Binary file forum/Themes/Vamp/images/warn.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/warning_moderate.gif Binary file forum/Themes/Vamp/images/warning_moderate.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/warning_mute.gif Binary file forum/Themes/Vamp/images/warning_mute.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/warning_watch.gif Binary file forum/Themes/Vamp/images/warning_watch.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/www.gif Binary file forum/Themes/Vamp/images/www.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/www_sm.gif Binary file forum/Themes/Vamp/images/www_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/images/yim.gif Binary file forum/Themes/Vamp/images/yim.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/index.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/index.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,505 @@ + + +'; + + // The ?fin20 part of this link is just here to make sure browsers don't cache it wrongly. + echo ' + '; + + // Some browsers need an extra stylesheet due to bugs/compatibility issues. + foreach (array('ie7', 'ie6', 'webkit') as $cssfix) + if ($context['browser']['is_' . $cssfix]) + echo ' + '; + + // RTL languages require an additional stylesheet. + if ($context['right_to_left']) + echo ' + '; + + // Here comes the JavaScript bits! + echo ' + + + '; + + echo ' + + ', !empty($context['meta_keywords']) ? ' + ' : '', ' + ', $context['page_title_html_safe'], ''; + + // Please don't index these Mr Robot. + if (!empty($context['robot_no_index'])) + echo ' + '; + + // Present a canonical url for search engines to prevent duplicate content in their indices. + if (!empty($context['canonical_url'])) + echo ' + '; + + // Show all the relative links, such as help, search, contents, and the like. + echo ' + + + '; + + // If RSS feeds are enabled, advertise the presence of one. + if (!empty($modSettings['xmlnews_enable']) && (!empty($modSettings['allow_guestAccess']) || $context['user']['is_logged'])) + echo ' + '; + + // If we're viewing a topic, these should be the previous and next topics, respectively. + if (!empty($context['current_topic'])) + echo ' + + '; + + // If we're in a board, or a topic for that matter, the index will be the board's index. + if (!empty($context['current_board'])) + echo ' + '; + + // Output any remaining HTML headers. (from mods, maybe?) + echo $context['html_headers']; + + echo ' + +'; +} + +function template_body_above() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo !empty($settings['forum_width']) ? ' +
    ' : '', ' + '; + + // The main content should go here. + echo ' +
    +
    '; + + // Custom banners and shoutboxes should be placed here, before the linktree. + + // Show the navigation tree. + theme_linktree(); +} + +function template_body_below() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    '; + + // Show the "Powered by" and "Valid" logos, as well as the copyright. Remember, the copyright must be somewhere! + echo ' + ', !empty($settings['forum_width']) ? ' +
    ' : ''; +} + +function template_html_below() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +'; +} + +// Show a linktree. This is that thing that shows "My Community | General Category | General Discussion".. +function theme_linktree($force_show = false) +{ + global $context, $settings, $options, $shown_linktree; + + // If linktree is empty, just return - also allow an override. + if (empty($context['linktree']) || (!empty($context['dont_default_linktree']) && !$force_show)) + return; + + echo ' + '; + + $shown_linktree = true; +} + +// Show the menu up top. Something like [home] [help] [profile] [logout]... +function template_menu() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + '; +} + +// Generate a strip of buttons. +function template_button_strip($button_strip, $direction = 'top', $strip_options = array()) +{ + global $settings, $context, $txt, $scripturl; + + if (!is_array($strip_options)) + $strip_options = array(); + + // List the buttons in reverse order for RTL languages. + if ($context['right_to_left']) + $button_strip = array_reverse($button_strip, true); + + // Create the buttons... + $buttons = array(); + foreach ($button_strip as $key => $value) + { + if (!isset($value['test']) || !empty($context[$value['test']])) + $buttons[] = ' +
  11. ' . $txt[$value['text']] . '
  12. '; + } + + // No buttons? No button strip either. + if (empty($buttons)) + return; + + // Make the last one, as easy as possible. + $buttons[count($buttons) - 1] = str_replace('', '', $buttons[count($buttons) - 1]); + + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Admin.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Admin.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,619 @@ +Blank a box to remove that word.'; +$txt['admin_reserved_names'] = 'Reserved Names'; +$txt['admin_template_edit'] = 'Edit Your Forum Template'; +$txt['admin_modifications'] = 'Modification Settings'; +$txt['admin_security_moderation'] = 'Security and Moderation'; +$txt['admin_server_settings'] = 'Server Settings'; +$txt['admin_reserved_set'] = 'Set Reserved Names'; +$txt['admin_reserved_line'] = 'One reserved word per line.'; +$txt['admin_basic_settings'] = 'This page allows you to change the basic settings for your forum. Be very careful with these settings, as they may render the forum dysfunctional.'; +$txt['admin_maintain'] = 'Enable Maintenance Mode'; +$txt['admin_title'] = 'Forum Title'; +$txt['admin_url'] = 'Forum URL'; +$txt['cookie_name'] = 'Cookie Name'; +$txt['admin_webmaster_email'] = 'Webmaster Email Address'; +$txt['boarddir'] = 'SMF Directory'; +$txt['sourcesdir'] = 'Sources Directory'; +$txt['cachedir'] = 'Cache Directory'; +$txt['admin_news'] = 'Enable News'; +$txt['admin_guest_post'] = 'Enable Guest Posting'; +$txt['admin_manage_members'] = 'Members'; +$txt['admin_main'] = 'Main'; +$txt['admin_config'] = 'Configuration'; +$txt['admin_version_check'] = 'Detailed Version Check'; +$txt['admin_smffile'] = 'SMF File'; +$txt['admin_smfpackage'] = 'SMF Package'; +$txt['admin_maintenance'] = 'Maintenance'; +$txt['admin_image_text'] = 'Show buttons as images instead of text'; +$txt['admin_credits'] = 'Credits'; +$txt['admin_agreement'] = 'Show and require agreement letter when registering'; +$txt['admin_agreement_default'] = 'Default'; +$txt['admin_agreement_select_language'] = 'Language to edit'; +$txt['admin_agreement_select_language_change'] = 'Change'; +$txt['admin_delete_members'] = 'Delete Selected Members'; +$txt['admin_repair'] = 'Repair All Boards and Topics'; +$txt['admin_main_welcome'] = 'This is your "%1$s". From here, you can edit settings, maintain your forum, view logs, install packages, manage themes, and many other things.
    If you have any trouble, please look at the "Support & Credits" page. If the information there doesn\'t help you, feel free to look to us for help with the problem.
    You may also find answers to your questions or problems by clicking the %2$s symbols for more information on the related functions.'; +$txt['admin_news_desc'] = 'Please place one news item per box. BBC tags, such as [b], [i] and [u] are allowed in your news, as well as smileys. Clear a news item\'s text box to remove it.'; +$txt['administrators'] = 'Forum Administrators'; +$txt['admin_reserved_desc'] = 'Reserved names will keep members from registering certain usernames or using these words in their displayed names. Choose the options you wish to use from the bottom before submitting.'; +$txt['admin_activation_email'] = 'Send activation email to new members upon registration'; +$txt['admin_match_whole'] = 'Match whole name only. If unchecked, search within names.'; +$txt['admin_match_case'] = 'Match case. If unchecked, search will be case insensitive.'; +$txt['admin_check_user'] = 'Check username.'; +$txt['admin_check_display'] = 'Check display name.'; +$txt['admin_newsletter_send'] = 'You can email anyone from this page. The email addresses of the selected membergroups should appear below, but you may remove or add any email addresses you wish. Be sure that each address is separated in this fashion: \'address1; address2\'.'; +$txt['admin_fader_delay'] = 'Fading delay between items for the news fader'; +$txt['admin_bbc'] = 'Show BBC Buttons on Posting and PM Send Pages'; + +$txt['admin_backup_fail'] = 'Failed to make backup of Settings.php - make sure Settings_bak.php exists and is writable.'; +$txt['modSettings_info'] = 'Change or set options that control how this forum operates.'; +$txt['database_server'] = 'Database Server'; +$txt['database_user'] = 'Database Username'; +$txt['database_password'] = 'Database Password'; +$txt['database_name'] = 'Database Name'; +$txt['registration_agreement'] = 'Registration Agreement'; +$txt['registration_agreement_desc'] = 'This agreement is shown when a user registers an account on this forum and has to be accepted before users can continue registration.'; +$txt['database_prefix'] = 'Database Tables Prefix'; +$txt['errors_list'] = 'Listing of forum errors'; +$txt['errors_found'] = 'The following errors are fouling up your forum'; +$txt['errors_fix'] = 'Would you like to attempt to fix these errors?'; +$txt['errors_do_recount'] = 'All errors fixed - a salvage area has been created! Please click the button below to recount some key statistics.'; +$txt['errors_recount_now'] = 'Recount Statistics'; +$txt['errors_fixing'] = 'Fixing forum errors'; +$txt['errors_fixed'] = 'All errors fixed! Please check on any categories, boards, or topics created to decide what to do with them.'; +$txt['attachments_avatars'] = 'Attachments and Avatars'; +$txt['attachments_desc'] = 'From here you can administer the attached files on your system. You can delete attachments by size and by date from your system. Statistics on attachments are also displayed below.'; +$txt['attachment_stats'] = 'File Attachment Statistics'; +$txt['attachment_integrity_check'] = 'Attachment Integrity Check'; +$txt['attachment_integrity_check_desc'] = 'This function will check the integrity and sizes of attachments and filenames listed in the database and, if necessary, fix errors it encounters.'; +$txt['attachment_check_now'] = 'Run check now'; +$txt['attachment_pruning'] = 'Attachment Pruning'; +$txt['attachment_pruning_message'] = 'Message to add to post'; +$txt['attachment_pruning_warning'] = 'Are you sure you want to delete these attachments?\\nThis cannot be undone!'; +$txt['attachment_total'] = 'Total Attachments'; +$txt['attachmentdir_size'] = 'Total Size of Attachment Directory'; +$txt['attachmentdir_size_current'] = 'Total Size of Current Attachment Directory'; +$txt['attachment_space'] = 'Total Space Available in Attachment Directory'; +$txt['attachment_space_current'] = 'Total Space Available in Current Attachment Directory'; +$txt['attachment_options'] = 'File Attachment Options'; +$txt['attachment_log'] = 'Attachment Log'; +$txt['attachment_remove_old'] = 'Remove attachments older than'; +$txt['attachment_remove_size'] = 'Remove attachments larger than'; +$txt['attachment_name'] = 'Attachment Name'; +$txt['attachment_file_size'] = 'File Size'; +$txt['attachmentdir_size_not_set'] = 'No maximum directory size is currently set'; +$txt['attachment_delete_admin'] = '[attachment deleted by admin]'; +$txt['live'] = 'Live from Simple Machines...'; +$txt['remove_all'] = 'Remove All'; +$txt['approve_new_members'] = 'Admin must approve all new members'; +$txt['agreement_not_writable'] = 'Warning - agreement.txt is not writable, any changes you make will NOT be saved.'; + +$txt['version_check_desc'] = 'This shows you the versions of your installation\'s files versus those of the latest version. If any of these files are out of date, you should download and upgrade to the latest version at www.simplemachines.org.'; +$txt['version_check_more'] = '(more detailed)'; + +$txt['lfyi'] = 'You are unable to connect to simplemachines.org\'s latest news file.'; + +$txt['manage_calendar'] = 'Calendar'; +$txt['manage_search'] = 'Search'; + +$txt['smileys_manage'] = 'Smileys and Message Icons'; +$txt['smileys_manage_info'] = 'Install new smiley sets, add smileys to existing ones, or manage your message icons.'; +$txt['package_info'] = 'Install new features or modify existing ones with this interface.'; +$txt['theme_admin'] = 'Themes and Layout'; +$txt['theme_admin_info'] = 'Setup and manage your themes and set or reset theme options.'; +$txt['registration_center'] = 'Registration'; +$txt['member_center_info'] = 'View the member list, search for members and manage not-yet-approved members and members who haven\'t activated their account yet.'; + +$txt['viewmembers_name'] = 'Username (display name)'; +$txt['viewmembers_online'] = 'Last Online'; +$txt['viewmembers_today'] = 'Today'; +$txt['viewmembers_day_ago'] = 'day ago'; +$txt['viewmembers_days_ago'] = 'days ago'; + +$txt['display_name'] = 'Display name'; +$txt['email_address'] = 'Email Address'; +$txt['ip_address'] = 'IP address'; +$txt['member_id'] = 'ID'; + +$txt['unknown'] = 'unknown'; +$txt['security_wrong'] = 'Administration login attempt!' . "\n" . 'Referer: %1$s' . "\n" . 'User agent: %2$s' . "\n" . 'IP: %3$s'; + +$txt['email_as_html'] = 'Send in HTML format. (with this you can put normal HTML in the email.)'; +$txt['email_parsed_html'] = 'Add <br />s and &nbsp;s to this message.'; +$txt['email_variables'] = 'In this message you can use a few "variables". Click here for more information.'; +$txt['email_force'] = 'Send this to members even if they have chosen not to receive announcements.'; +$txt['email_as_pms'] = 'Send this to these groups using personal messages.'; +$txt['email_continue'] = 'Continue'; +$txt['email_done'] = 'done.'; + +$txt['ban_title'] = 'Ban List'; +$txt['ban_ip'] = 'IP banning: (e.g. 192.168.12.213 or 128.0.*.*) - one entry per line'; +$txt['ban_email'] = 'Email banning: (e.g. badguy@somewhere.com) - one entry per line'; +$txt['ban_username'] = 'User name banning: (e.g. l33tuser) - one entry per line'; + +$txt['ban_description'] = 'Here you can ban troublesome people either by IP, hostname, username, or email.'; +$txt['ban_add_new'] = 'Add New Ban'; +$txt['ban_banned_entity'] = 'Banned entity'; +$txt['ban_on_ip'] = 'Ban on IP (e.g. 192.168.10-20.*)'; +$txt['ban_on_hostname'] = 'Ban on Hostname (e.g. *.mil)'; +$txt['ban_on_email'] = 'Ban on Email Address (e.g. *@badsite.com)'; +$txt['ban_on_username'] = 'Ban on Username'; +$txt['ban_notes'] = 'Notes'; +$txt['ban_restriction'] = 'Restriction'; +$txt['ban_full_ban'] = 'Full ban'; +$txt['ban_partial_ban'] = 'Partial ban'; +$txt['ban_cannot_post'] = 'Cannot post'; +$txt['ban_cannot_register'] = 'Cannot register'; +$txt['ban_cannot_login'] = 'Cannot login'; +$txt['ban_add'] = 'Add'; +$txt['ban_edit_list'] = 'Ban List'; +$txt['ban_type'] = 'Ban Type'; +$txt['ban_days'] = 'day(s)'; +$txt['ban_will_expire_within'] = 'Ban will expire after'; +$txt['ban_added'] = 'Added'; +$txt['ban_expires'] = 'Expires'; +$txt['ban_hits'] = 'Hits'; +$txt['ban_actions'] = 'Actions'; +$txt['ban_expiration'] = 'Expiration'; +$txt['ban_reason_desc'] = 'Reason for ban, to be displayed to banned member.'; +$txt['ban_notes_desc'] = 'Notes that may assist other staff members.'; +$txt['ban_remove_selected'] = 'Remove selected'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['ban_remove_selected_confirm'] = 'Are you sure you want to remove the selected bans?'; +$txt['ban_modify'] = 'Modify'; +$txt['ban_name'] = 'Ban name'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['ban_edit'] = 'Edit ban'; +$txt['ban_add_notes'] = 'Note: after creating the above ban, you can add additional entries that trigger the ban, like IP addresses, hostnames and email addresses.'; +$txt['ban_expired'] = 'Expired / disabled'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['ban_restriction_empty'] = 'No restriction selected.'; + +$txt['ban_triggers'] = 'Triggers'; +$txt['ban_add_trigger'] = 'Add ban trigger'; +$txt['ban_add_trigger_submit'] = 'Add'; +$txt['ban_edit_trigger'] = 'Modify'; +$txt['ban_edit_trigger_title'] = 'Edit ban trigger'; +$txt['ban_edit_trigger_submit'] = 'Modify'; +$txt['ban_remove_selected_triggers'] = 'Remove selected ban triggers'; +$txt['ban_no_entries'] = 'There are currently no bans in effect.'; + +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['ban_remove_selected_triggers_confirm'] = 'Are you sure you want to remove the selected ban triggers?'; +$txt['ban_trigger_browse'] = 'Browse Ban Triggers'; +$txt['ban_trigger_browse_description'] = 'This screen shows all banned entities grouped by IP address, hostname, email address and username.'; + +$txt['ban_log'] = 'Ban Log'; +$txt['ban_log_description'] = 'The ban log shows all attempts to enter the forum by banned users (\'full ban\' and \'cannot register\' ban only).'; +$txt['ban_log_no_entries'] = 'There are currently no ban log entries.'; +$txt['ban_log_ip'] = 'IP'; +$txt['ban_log_email'] = 'Email address'; +$txt['ban_log_member'] = 'Member'; +$txt['ban_log_date'] = 'Date'; +$txt['ban_log_remove_all'] = 'Remove all'; +$txt['ban_log_remove_all_confirm'] = 'Are you sure you want to delete all ban log entries?'; +$txt['ban_log_remove_selected'] = 'Remove selected'; +$txt['ban_log_remove_selected_confirm'] = 'Are you sure you want to delete all selected ban log entries?'; +$txt['ban_no_triggers'] = 'There are currently no ban triggers.'; + +$txt['settings_not_writable'] = 'These settings cannot be changed because Settings.php is read only.'; + +$txt['maintain_title'] = 'Forum Maintenance'; +$txt['maintain_info'] = 'Optimize tables, make backups, check for errors, and prune boards with these tools.'; +$txt['maintain_sub_database'] = 'Database'; +$txt['maintain_sub_routine'] = 'Routine'; +$txt['maintain_sub_members'] = 'Members'; +$txt['maintain_sub_topics'] = 'Topics'; +$txt['maintain_done'] = 'The maintenance task \'%1$s\' was executed successfully.'; +$txt['maintain_no_errors'] = 'Congratulations, no errors found! Thanks for checking.'; + +$txt['maintain_tasks'] = 'Scheduled Tasks'; +$txt['maintain_tasks_desc'] = 'Manage all the tasks scheduled by SMF.'; + +$txt['scheduled_log'] = 'Task Log'; +$txt['scheduled_log_desc'] = 'Lists logs of the tasks that have be ran.'; +$txt['admin_log'] = 'Administration Log'; +$txt['admin_log_desc'] = 'Lists administrative tasks that have been performed by admins of your forum.'; +$txt['moderation_log'] = 'Moderation Log'; +$txt['moderation_log_desc'] = 'Lists moderation activities that have been performed by moderators on your forum.'; +$txt['spider_log_desc'] = 'Review the entries related to search engine spider activity on your forum.'; +$txt['pruning_log_desc'] = 'Use these tools to prune older entries in the various logs.'; + +$txt['mailqueue_title'] = 'Mail'; + +$txt['db_error_send'] = 'Send emails on database connection error'; +$txt['db_persist'] = 'Use a persistent connection'; +$txt['ssi_db_user'] = 'Database username to use in SSI mode'; +$txt['ssi_db_passwd'] = 'Database password to use in SSI mode'; + +$txt['default_language'] = 'Default Forum Language'; + +$txt['maintenance_subject'] = 'Subject for display'; +$txt['maintenance_message'] = 'Message for display'; + +$txt['errlog_desc'] = 'The error log tracks every error encountered by your forum. To delete any errors from the database, mark the checkbox, and click the %1$s button at the bottom of the page.'; +$txt['errlog_no_entries'] = 'There are currently no error log entries.'; + +$txt['theme_settings'] = 'Theme Settings'; +$txt['theme_current_settings'] = 'Current Theme'; + +$txt['dvc_your'] = 'Your Version'; +$txt['dvc_current'] = 'Current Version'; +$txt['dvc_sources'] = 'Sources'; +$txt['dvc_default'] = 'Default Templates'; +$txt['dvc_templates'] = 'Current Templates'; +$txt['dvc_languages'] = 'Language Files'; + +$txt['smileys_default_set_for_theme'] = 'Select default smiley set for this theme'; +$txt['smileys_no_default'] = '(use global default smiley set)'; + +$txt['censor_test'] = 'Test Censored Words'; +$txt['censor_test_save'] = 'Test'; +$txt['censor_case'] = 'Ignore case when censoring'; +$txt['censor_whole_words'] = 'Check only whole words'; + +$txt['admin_confirm_password'] = '(confirm)'; +$txt['admin_incorrect_password'] = 'Incorrect Password'; + +$txt['date_format'] = '(YYYY-MM-DD)'; +$txt['undefined_gender'] = 'Undefined'; +$txt['age'] = 'User age'; +$txt['activation_status'] = 'Activation Status'; +$txt['activated'] = 'Activated'; +$txt['not_activated'] = 'Not activated'; +$txt['primary'] = 'Primary'; +$txt['additional'] = 'Additional'; +$txt['messenger_address'] = 'Messenger Address'; +$txt['wild_cards_allowed'] = 'wildcard characters * and ? are allowed'; +$txt['search_for'] = 'Search for'; +$txt['member_part_of_these_membergroups'] = 'Member is part of these membergroups'; +$txt['membergroups'] = 'Membergroups'; +$txt['confirm_delete_members'] = 'Are you sure you want to delete the selected members?'; + +$txt['support_credits_title'] = 'Support and Credits'; +$txt['support_credits_info'] = 'Get support on common issues and version information to give if you have problems.'; +$txt['support_title'] = 'Support Information'; +$txt['support_versions_current'] = 'Current SMF version'; +$txt['support_versions_forum'] = 'Forum version'; +$txt['support_versions_php'] = 'PHP version'; +$txt['support_versions_db'] = '%1$s version'; +$txt['support_versions_server'] = 'Server version'; +$txt['support_versions_gd'] = 'GD version'; +$txt['support_versions'] = 'Version Information'; +$txt['support_resources'] = 'Support Resources'; +$txt['support_resources_p1'] = 'Our Online Manual provides the main documentation for SMF. The SMF Online Manual has many documents to help answer support questions and explain Features, Settings, Themes, Packages, etc. The Online Manual documents each area of SMF thoroughly and should answer most questions quickly.'; +$txt['support_resources_p2'] = 'If you can\'t find the answers to your questions in the Online Manual, you may want to search our Support Community or ask for assistance in either our English or one of our many international support boards. The SMF Support Community can be used for support, customization, and many other things such as discussing SMF, finding a host, and discussing administrative issues with other forum administrators.'; + +$txt['support_latest'] = 'Common Support & Issues'; +$txt['support_latest_fetch'] = 'Retrieving support information...'; + +$txt['edit_permissions_info'] = 'Change restrictions and available features globally or to specific boards.'; +$txt['membergroups_members'] = 'Regular Members'; +$txt['membergroups_guests'] = 'Guests'; +$txt['membergroups_guests_na'] = 'n/a'; +$txt['membergroups_add_group'] = 'Add group'; +$txt['membergroups_permissions'] = 'Permissions'; + +$txt['permitgroups_restrict'] = 'Restrictive'; +$txt['permitgroups_standard'] = 'Standard'; +$txt['permitgroups_moderator'] = 'Moderator'; +$txt['permitgroups_maintenance'] = 'Maintenance'; +$txt['permitgroups_inherit'] = 'Inherit'; + +$txt['confirm_delete_attachments_all'] = 'Are you sure you want to delete all attachments?'; +$txt['confirm_delete_attachments'] = 'Are you sure you want to delete the selected attachments?'; +$txt['attachment_manager_browse_files'] = 'Browse Files'; +$txt['attachment_manager_repair'] = 'Maintain'; +$txt['attachment_manager_avatars'] = 'Avatars'; +$txt['attachment_manager_attachments'] = 'Attachments'; +$txt['attachment_manager_thumbs'] = 'Thumbnails'; +$txt['attachment_manager_last_active'] = 'Last Active'; +$txt['attachment_manager_member'] = 'Member'; +$txt['attachment_manager_avatars_older'] = 'Remove avatars from members not active for more than'; +$txt['attachment_manager_total_avatars'] = 'Total Avatars'; + +$txt['attachment_manager_avatars_no_entries'] = 'There are currently no avatars.'; +$txt['attachment_manager_attachments_no_entries'] = 'There are currently no attachments.'; +$txt['attachment_manager_thumbs_no_entries'] = 'There are currently no thumbnails.'; + +$txt['attachment_manager_settings'] = 'Attachment Settings'; +$txt['attachment_manager_avatar_settings'] = 'Avatar Settings'; +$txt['attachment_manager_browse'] = 'Browse Files'; +$txt['attachment_manager_maintenance'] = 'File Maintenance'; +$txt['attachment_manager_save'] = 'Save'; + +$txt['attachmentEnable'] = 'Attachments mode'; +$txt['attachmentEnable_deactivate'] = 'Disable attachments'; +$txt['attachmentEnable_enable_all'] = 'Enable all attachments'; +$txt['attachmentEnable_disable_new'] = 'Disable new attachments'; +$txt['attachmentCheckExtensions'] = 'Check attachment\'s extension'; +$txt['attachmentExtensions'] = 'Allowed attachment extensions'; +$txt['attachmentRecodeLineEndings'] = 'Recode line endings in textual attachments'; +$txt['attachmentShowImages'] = 'Display image attachments as pictures under post'; +$txt['attachmentEncryptFilenames'] = 'Encrypt stored filenames'; +$txt['attachmentUploadDir'] = 'Attachments directory'; +$txt['attachmentUploadDir_multiple'] = 'Attachments directory'; +$txt['attachmentUploadDir_multiple_configure'] = '[Configure multiple attachment directories]'; +$txt['attachmentDirSizeLimit'] = 'Max attachment folder space
    (0 for no limit)
    '; +$txt['attachmentPostLimit'] = 'Max attachment size per post
    (0 for no limit)
    '; +$txt['attachmentSizeLimit'] = 'Max size per attachment
    (0 for no limit)
    '; +$txt['attachmentNumPerPostLimit'] = 'Max number of attachments per post
    (0 for no limit)
    '; +$txt['attachment_gd_warning'] = 'The GD module is currently not installed. Image re-encoding is not possible.'; +$txt['attachment_image_reencode'] = 'Re-encode potentially dangerous image attachments'; +$txt['attachment_image_reencode_note'] = '(requires GD module)'; +$txt['attachment_image_paranoid_warning'] = 'The extensive security checks can result in a large number of rejected attachments.'; +$txt['attachment_image_paranoid'] = 'Perform extensive security checks on uploaded image attachments'; +$txt['attachmentThumbnails'] = 'Resize images when showing under posts'; +$txt['attachment_thumb_png'] = 'Save thumbnails as PNG'; +$txt['attachmentThumbWidth'] = 'Maximum width of thumbnails'; +$txt['attachmentThumbHeight'] = 'Maximum height of thumbnails'; + +$txt['attach_dir_does_not_exist'] = 'Does Not Exist'; +$txt['attach_dir_not_writable'] = 'Not Writable'; +$txt['attach_dir_files_missing'] = 'Files Missing (Repair)'; +$txt['attach_dir_unused'] = 'Unused'; +$txt['attach_dir_ok'] = 'OK'; + +$txt['attach_path_manage'] = 'Manage Attachment Paths'; +$txt['attach_paths'] = 'Attachment Paths'; +$txt['attach_current_dir'] = 'Current Directory'; +$txt['attach_path'] = 'Path'; +$txt['attach_current_size'] = 'Current Size (KB)'; +$txt['attach_num_files'] = 'Files'; +$txt['attach_dir_status'] = 'Status'; +$txt['attach_add_path'] = 'Add Path'; +$txt['attach_path_current_bad'] = 'Invalid current attachment path.'; + +$txt['mods_cat_avatars'] = 'Avatars'; +$txt['avatar_directory'] = 'Avatars directory'; +$txt['avatar_url'] = 'Avatars URL'; +$txt['avatar_dimension_note'] = '(0 = no limit)'; +$txt['avatar_max_width_external'] = 'Maximum width of external avatar
    (0 for no limit)
    '; +$txt['avatar_max_height_external'] = 'Maximum height of external avatar
    (0 for no limit)
    '; +$txt['avatar_action_too_large'] = 'If the avatar is too large...'; +$txt['option_refuse'] = 'Refuse it'; +$txt['option_html_resize'] = 'Let the HTML resize it'; +$txt['option_js_resize'] = 'Resize it with JavaScript'; +$txt['option_download_and_resize'] = 'Download and resize it (requires GD module)'; +$txt['avatar_max_width_upload'] = 'Maximum width of uploaded avatar
    (0 for no limit)
    '; +$txt['avatar_max_height_upload'] = 'Maximum height of uploaded avatar
    (0 for no limit)
    '; +$txt['avatar_resize_upload'] = 'Resize oversized large avatars'; +$txt['avatar_resize_upload_note'] = '(requires GD module)'; +$txt['avatar_download_png'] = 'Use PNG for resized avatars'; +$txt['avatar_gd_warning'] = 'The GD module is currently not installed. Some avatar features are disabled.'; +$txt['avatar_external'] = 'External avatars'; +$txt['avatar_upload'] = 'Uploadable avatars'; +$txt['avatar_server_stored'] = 'Server-stored avatars'; +$txt['avatar_server_stored_groups'] = 'Membergroups allowed to select a server stored avatar'; +$txt['avatar_upload_groups'] = 'Membergroups allowed to upload an avatar to the server'; +$txt['avatar_external_url_groups'] = 'Membergroups allowed to select an external URL'; +$txt['avatar_select_permission'] = 'Select permissions for each group'; +$txt['avatar_download_external'] = 'Download avatar at given URL'; +$txt['custom_avatar_enabled'] = 'Upload avatars to...'; +$txt['option_attachment_dir'] = 'Attachment directory'; +$txt['option_specified_dir'] = 'Specific directory...'; +$txt['custom_avatar_dir'] = 'Upload directory'; +$txt['custom_avatar_dir_desc'] = 'This should not be the same as the server-stored directory.'; +$txt['custom_avatar_url'] = 'Upload URL'; +$txt['custom_avatar_check_empty'] = 'The custom avatar directory you have specified may be empty or invalid. Please ensure these settings are correct.'; +$txt['avatar_reencode'] = 'Re-encode potentially dangerous avatars'; +$txt['avatar_reencode_note'] = '(requires GD module)'; +$txt['avatar_paranoid_warning'] = 'The extensive security checks can result in a large number of rejected avatars.'; +$txt['avatar_paranoid'] = 'Perform extensive security checks on uploaded avatars'; + +$txt['repair_attachments'] = 'Maintain Attachments'; +$txt['repair_attachments_complete'] = 'Maintenance Complete'; +$txt['repair_attachments_complete_desc'] = 'All selected errors have now been corrected'; +$txt['repair_attachments_no_errors'] = 'No errors were found!'; +$txt['repair_attachments_error_desc'] = 'The follow errors were found during maintenance. Check the box next to the errors you wish to fix and hit continue.'; +$txt['repair_attachments_continue'] = 'Continue'; +$txt['repair_attachments_cancel'] = 'Cancel'; +$txt['attach_repair_missing_thumbnail_parent'] = '%1$d thumbnails are missing a parent attachment'; +$txt['attach_repair_parent_missing_thumbnail'] = '%1$d parents are flagged as having thumbnails but don\'t'; +$txt['attach_repair_file_missing_on_disk'] = '%1$d attachments/avatars have an entry but no longer exist on disk'; +$txt['attach_repair_file_wrong_size'] = '%1$d attachments/avatars are being reported as the wrong filesize'; +$txt['attach_repair_file_size_of_zero'] = '%1$d attachments/avatars have a size of zero on disk. (These will be deleted)'; +$txt['attach_repair_attachment_no_msg'] = '%1$d attachments no longer have a message associated with them'; +$txt['attach_repair_avatar_no_member'] = '%1$d avatars no longer have a member associated with them'; +$txt['attach_repair_wrong_folder'] = '%1$d attachments are in the wrong folder'; + +$txt['news_title'] = 'News and Newsletters'; +$txt['news_settings_desc'] = 'Here you can change the settings and permissions related to news and newsletters.'; +$txt['news_settings_submit'] = 'Save'; +$txt['news_mailing_desc'] = 'From this menu you can send messages to all members who\'ve registered and entered their email addresses. You may edit the distribution list, or send messages to all. Useful for important update/news information.'; +$txt['groups_edit_news'] = 'Groups allowed to edit news items'; +$txt['groups_send_mail'] = 'Groups allowed to send out forum newsletters'; +$txt['xmlnews_enable'] = 'Enable XML/RSS news'; +$txt['xmlnews_maxlen'] = 'Maximum message length:
    (0 to disable, bad idea.)
    '; +$txt['editnews_clickadd'] = 'Click here to add another item.'; +$txt['editnews_remove_selected'] = 'Remove selected'; +$txt['editnews_remove_confirm'] = 'Are you sure you want to delete the selected news items?'; +$txt['censor_clickadd'] = 'Click here to add another word.'; + +$txt['layout_controls'] = 'Forum'; +$txt['logs'] = 'Logs'; +$txt['generate_reports'] = 'Reports'; + +$txt['update_available'] = 'Update Available!'; +$txt['update_message'] = 'You\'re using an outdated version of SMF, which contains some bugs which have since been fixed. + It is recommended that you update your forum to the latest version as soon as possible. It only takes a minute!'; + +$txt['manageposts'] = 'Posts and Topics'; +$txt['manageposts_title'] = 'Manage Posts and Topics'; +$txt['manageposts_description'] = 'Here you can manage all settings related to topics and posts.'; + +$txt['manageposts_seconds'] = 'seconds'; +$txt['manageposts_minutes'] = 'minutes'; +$txt['manageposts_characters'] = 'characters'; +$txt['manageposts_days'] = 'days'; +$txt['manageposts_posts'] = 'posts'; +$txt['manageposts_topics'] = 'topics'; + +$txt['manageposts_settings'] = 'Post Settings'; +$txt['manageposts_settings_description'] = 'Here you can set everything related to posts and posting.'; +$txt['manageposts_settings_submit'] = 'Save'; + +$txt['manageposts_bbc_settings'] = 'Bulletin Board Code'; +$txt['manageposts_bbc_settings_description'] = 'Bulletin board code can be used to add markup to forum messages. For example, to highlight the word \'house\' you can type [b]house[/b]. All Bulletin board code tags are surrounded by square brackets (\'[\' and \']\').'; +$txt['manageposts_bbc_settings_title'] = 'Bulletin Board Code Settings'; +$txt['manageposts_bbc_settings_submit'] = 'Save'; + +$txt['manageposts_topic_settings'] = 'Topic Settings'; +$txt['manageposts_topic_settings_description'] = 'Here you can set all settings involving topics.'; +$txt['manageposts_topic_settings_submit'] = 'Save'; + +$txt['removeNestedQuotes'] = 'Remove nested quotes when quoting'; +$txt['enableEmbeddedFlash'] = 'Embed flash into posts'; +$txt['enableEmbeddedFlash_warning'] = 'may be a security risk!'; +$txt['enableSpellChecking'] = 'Enable spell checking'; +$txt['enableSpellChecking_warning'] = 'this does not work on all servers!'; +$txt['disable_wysiwyg'] = 'Disable WYSIWYG editor'; +$txt['max_messageLength'] = 'Maximum allowed post size'; +$txt['max_messageLength_zero'] = '0 for no max.'; +$txt['fixLongWords'] = 'Break up words with more letters than'; +$txt['fixLongWords_zero'] = '0 to disable.'; +$txt['fixLongWords_warning'] = 'this does not work on all servers!'; +$txt['topicSummaryPosts'] = 'Posts to show on topic summary'; +$txt['spamWaitTime'] = 'Time required between posts from the same IP'; +$txt['edit_wait_time'] = 'Courtesy edit wait time'; +$txt['edit_disable_time'] = 'Maximum time after posting to allow edit'; +$txt['edit_disable_time_zero'] = '0 to disable'; + +$txt['enableBBC'] = 'Enable bulletin board code (BBC)'; +$txt['enablePostHTML'] = 'Enable basic HTML in posts'; +$txt['autoLinkUrls'] = 'Automatically link posted URLs'; +$txt['disabledBBC'] = 'Enabled BBC tags'; +$txt['bbcTagsToUse'] = 'Enabled BBC tags'; +$txt['bbcTagsToUse_select'] = 'Select the tags allowed to be used'; +$txt['bbcTagsToUse_select_all'] = 'Select all tags'; + +$txt['enableStickyTopics'] = 'Enable sticky topics'; +$txt['enableParticipation'] = 'Enable participation icons'; +$txt['oldTopicDays'] = 'Time before topic is warned as old on reply'; +$txt['oldTopicDays_zero'] = '0 to disable'; +$txt['defaultMaxTopics'] = 'Number of topics per page in the message index'; +$txt['defaultMaxMessages'] = 'Number of posts per page in a topic page'; +$txt['hotTopicPosts'] = 'Number of posts for a hot topic'; +$txt['hotTopicVeryPosts'] = 'Number of posts for a very hot topic'; +$txt['enableAllMessages'] = 'Max topic size to show "All" posts'; +$txt['enableAllMessages_zero'] = '0 to never show "All"'; +$txt['disableCustomPerPage'] = 'Disable user defined topic/message count per page'; +$txt['enablePreviousNext'] = 'Enable previous/next topic links'; + +$txt['not_done_title'] = 'Not done yet!'; +$txt['not_done_reason'] = 'To avoid overloading your server, the process has been temporarily paused. It should automatically continue in a few seconds. If it doesn\'t, please click continue below.'; +$txt['not_done_continue'] = 'Continue'; + +$txt['general_settings'] = 'General'; +$txt['database_paths_settings'] = 'Database and Paths'; +$txt['cookies_sessions_settings'] = 'Cookies and Sessions'; +$txt['caching_settings'] = 'Caching'; +$txt['load_balancing_settings'] = 'Load Balancing'; + +$txt['language_configuration'] = 'Languages'; +$txt['language_description'] = 'This section allows you to edit languages installed on your forum, download new ones from the Simple Machines website. You may also edit language-related settings here.'; +$txt['language_edit'] = 'Edit Languages'; +$txt['language_add'] = 'Add Language'; +$txt['language_settings'] = 'Settings'; + +$txt['advanced'] = 'Advanced'; +$txt['simple'] = 'Simple'; + +$txt['admin_news_select_recipients'] = 'Please select who should receive a copy of the newsletter'; +$txt['admin_news_select_group'] = 'Membergroups'; +$txt['admin_news_select_group_desc'] = 'Select the groups to receive this newsletter.'; +$txt['admin_news_select_members'] = 'Members'; +$txt['admin_news_select_members_desc'] = 'Additional members to receive newsletter.'; +$txt['admin_news_select_excluded_members'] = 'Excluded Members'; +$txt['admin_news_select_excluded_members_desc'] = 'Members who should not receive newsletter.'; +$txt['admin_news_select_excluded_groups'] = 'Excluded Groups'; +$txt['admin_news_select_excluded_groups_desc'] = 'Select groups who should definitely not receive the newsletter.'; +$txt['admin_news_select_email'] = 'Email Addresses'; +$txt['admin_news_select_email_desc'] = 'A semi-colon separated list of email addresses which should be sent newsletter. (i.e. address1; address2)'; +$txt['admin_news_select_override_notify'] = 'Override Notification Settings'; +// Use entities in below. +$txt['admin_news_cannot_pm_emails_js'] = 'You cannot send a personal message to an email address. If you continue all entered email addresses will be ignored.\\n\\nAre you sure you wish to do this?'; + +$txt['mailqueue_browse'] = 'Browse Queue'; +$txt['mailqueue_settings'] = 'Settings'; + +$txt['admin_search'] = 'Quick Search'; +$txt['admin_search_type_internal'] = 'Task/Setting'; +$txt['admin_search_type_member'] = 'Member'; +$txt['admin_search_type_online'] = 'Online Manual'; +$txt['admin_search_go'] = 'Go'; +$txt['admin_search_results'] = 'Search Results'; +$txt['admin_search_results_desc'] = 'Results for search: "%1$s"'; +$txt['admin_search_results_again'] = 'Search again'; +$txt['admin_search_results_none'] = 'No results found!'; + +$txt['admin_search_section_sections'] = 'Section'; +$txt['admin_search_section_settings'] = 'Setting'; + +$txt['core_settings_title'] = 'Core Features'; +$txt['mods_cat_features'] = 'General'; +$txt['mods_cat_security_general'] = 'General'; +$txt['antispam_title'] = 'Anti-Spam'; +$txt['mods_cat_modifications_misc'] = 'Miscellaneous'; +$txt['mods_cat_layout'] = 'Layout'; +$txt['karma'] = 'Karma'; +$txt['moderation_settings_short'] = 'Moderation'; +$txt['signature_settings_short'] = 'Signatures'; +$txt['custom_profile_shorttitle'] = 'Profile Fields'; +$txt['pruning_title'] = 'Log Pruning'; + +$txt['boardsEdit'] = 'Modify Boards'; +$txt['mboards_new_cat'] = 'Create New Category'; +$txt['manage_holidays'] = 'Manage Holidays'; +$txt['calendar_settings'] = 'Calendar Settings'; +$txt['search_weights'] = 'Weights'; +$txt['search_method'] = 'Search Method'; + +$txt['smiley_sets'] = 'Smiley Sets'; +$txt['smileys_add'] = 'Add Smiley'; +$txt['smileys_edit'] = 'Edit Smileys'; +$txt['smileys_set_order'] = 'Set Smiley Order'; +$txt['icons_edit_message_icons'] = 'Edit Message Icons'; + +$txt['membergroups_new_group'] = 'Add Membergroup'; +$txt['membergroups_edit_groups'] = 'Edit Membergroups'; +$txt['permissions_groups'] = 'General Permissions'; +$txt['permissions_boards'] = 'Board Permissions'; +$txt['permissions_profiles'] = 'Edit Profiles'; +$txt['permissions_post_moderation'] = 'Post Moderation'; + +$txt['browse_packages'] = 'Browse Packages'; +$txt['download_packages'] = 'Download Packages'; +$txt['installed_packages'] = 'Installed Packages'; +$txt['package_file_perms'] = 'File Permissions'; +$txt['package_settings'] = 'Options'; +$txt['themeadmin_admin_title'] = 'Manage and Install'; +$txt['themeadmin_list_title'] = 'Theme Settings'; +$txt['themeadmin_reset_title'] = 'Member Options'; +$txt['themeadmin_edit_title'] = 'Modify Themes'; +$txt['admin_browse_register_new'] = 'Register New Member'; + +$txt['search_engines'] = 'Search Engines'; +$txt['spiders'] = 'Spiders'; +$txt['spider_logs'] = 'Spider Log'; +$txt['spider_stats'] = 'Stats'; + +$txt['paid_subscriptions'] = 'Paid Subscriptions'; +$txt['paid_subs_view'] = 'View Subscriptions'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/EmailTemplates.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/EmailTemplates.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1062 @@ + array( + /* + @additional_params: resend_activate_message + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + ACTIVATIONLINK: The url link to activate the member's account. + ACTIVATIONCODE: The code needed to activate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. If you forget your password, you can reset it by visiting {FORGOTPASSWORDLINK}. + +Before you can login, you must first activate your account by selecting the following link: + +{ACTIVATIONLINK} + +Should you have any problems with the activation, please visit {ACTIVATIONLINKWITHOUTCODE} and enter the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + + 'resend_pending_message' => array( + /* + @additional_params: resend_pending_message + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Your registration request at {FORUMNAME} has been received, {REALNAME}. + +The username you registered with was {USERNAME}. + +Before you can login and start using the forum, your request will be reviewed and approved. When this happens, you will receive another email from this address. + +{REGARDS}', + ), + 'mc_group_approve' => array( + /* + @additional_params: mc_group_approve + USERNAME: The user name for the member receiving the email. + GROUPNAME: The name of the membergroup that the user was accepted into. + @description: The request to join a particular membergroup has been accepted. + */ + 'subject' => 'Group Membership Approval', + 'body' => '{USERNAME}, + +We\'re pleased to notify you that your application to join the "{GROUPNAME}" group at {FORUMNAME} has been accepted, and your account has been updated to include this new membergroup. + +{REGARDS}', + ), + 'mc_group_reject' => array( + /* + @additional_params: mc_group_reject + USERNAME: The user name for the member receiving the email. + GROUPNAME: The name of the membergroup that the user was rejected from. + @description: The request to join a particular membergroup has been rejected. + */ + 'subject' => 'Group Membership Rejection', + 'body' => '{USERNAME}, + +We\'re sorry to notify you that your application to join the "{GROUPNAME}" group at {FORUMNAME} has been rejected. + +{REGARDS}', + ), + 'mc_group_reject_reason' => array( + /* + @additional_params: mc_group_reject_reason + USERNAME: The user name for the member receiving the email. + GROUPNAME: The name of the membergroup that the user was rejected from. + REASON: Reason for the rejection. + @description: The request to join a particular membergroup has been rejected with a reason given. + */ + 'subject' => 'Group Membership Rejection', + 'body' => '{USERNAME}, + +We\'re sorry to notify you that your application to join the "{GROUPNAME}" group at {FORUMNAME} has been rejected. + +This is due to the following reason: {REASON} + +{REGARDS}', + ), + 'admin_approve_accept' => array( + /* + @additional_params: admin_approve_accept + NAME: The display name of the member. + USERNAME: The user name for the member receiving the email. + PROFILELINK: The URL of the profile page. + FORGOTPASSWORDLINK: The URL of the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Welcome, {NAME}! + +Your account has been activated manually by the admin and you can now login and post. Your username is: {USERNAME}. If you forget your password, you can change it at {FORGOTPASSWORDLINK}. + +{REGARDS}', + ), + 'admin_approve_activation' => array( + /* + @additional_params: admin_approve_activation + USERNAME: The user name for the member receiving the email. + ACTIVATIONLINK: The url link to activate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The activation code. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Welcome, {USERNAME}! + +Your account on {FORUMNAME} has been approved by the forum administrator. Before you can login, you must first activate your account by selecting the following link: + +{ACTIVATIONLINK} + +Should you have any problems with the activation, please visit {ACTIVATIONLINKWITHOUTCODE} and enter the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'admin_approve_reject' => array( + /* + @additional_params: admin_approve_reject + USERNAME: The user name for the member receiving the email. + @description: + */ + 'subject' => 'Registration Rejected', + 'body' => '{USERNAME}, + +Regrettably, your application to join {FORUMNAME} has been rejected. + +{REGARDS}', + ), + 'admin_approve_delete' => array( + /* + @additional_params: admin_approve_delete + USERNAME: The user name for the member receiving the email. + @description: + */ + 'subject' => 'Account Deleted', + 'body' => '{USERNAME}, + +Your account on {FORUMNAME} has been deleted. This may be because you never activated your account, in which case you should be able to register again. + +{REGARDS}', + ), + 'admin_approve_remind' => array( + /* + @additional_params: admin_approve_remind + USERNAME: The user name for the member receiving the email. + ACTIVATIONLINK: The url link to activate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The activation code. + @description: + */ + 'subject' => 'Registration Reminder', + 'body' => '{USERNAME}, +You still have not activated your account at {FORUMNAME}. + +Please use the link below to activate your account: +{ACTIVATIONLINK} + +Should you have any problems with the activation, please visit {ACTIVATIONLINKWITHOUTCODE} and enter the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'admin_register_activate' => array( + /* + @additional_params: + USERNAME: The user name for the member receiving the email. + ACTIVATIONLINK: The url link to activate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The activation code. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME} and your password is {PASSWORD}. + +Before you can login, you must first activate your account by selecting the following link: + +{ACTIVATIONLINK} + +Should you have any problems with the activation, please visit {ACTIVATIONLINKWITHOUTCODE} and enter the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'admin_register_immediate' => array( + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME} and your password is {PASSWORD}. + +{REGARDS}', + ), + 'new_announcement' => array( + /* + @additional_params: new_announcement + TOPICSUBJECT: The subject of the topic being announced. + MESSAGE: The message body of the first post of the announced topic. + TOPICLINK: A link to the topic being announced. + @description: + + */ + 'subject' => 'New announcement: {TOPICSUBJECT}', + 'body' => '{MESSAGE} + +To unsubscribe from these announcements, login to the forum and uncheck "Receive forum announcements and important notifications by email." in your profile. + +You can view the full announcement by following this link: +{TOPICLINK} + +{REGARDS}', + ), + 'notify_boards_once_body' => array( + /* + @additional_params: notify_boards_once_body + TOPICSUBJECT: The subject of the topic causing the notification + TOPICLINK: A link to the topic. + MESSAGE: This is the body of the message. + UNSUBSCRIBELINK: Link to unsubscribe from notifications. + @description: + */ + 'subject' => 'New Topic: {TOPICSUBJECT}', + 'body' => 'A new topic, \'{TOPICSUBJECT}\', has been made on a board you are watching. + +You can see it at +{TOPICLINK} + +More topics may be posted, but you won\'t receive more email notifications until you return to the board and read some of them. + +The text of the topic is shown below: +{MESSAGE} + +Unsubscribe to new topics from this board by using this link: +{UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notify_boards_once' => array( + /* + @additional_params: notify_boards_once + TOPICSUBJECT: The subject of the topic causing the notification + TOPICLINK: A link to the topic. + UNSUBSCRIBELINK: Link to unsubscribe from notifications. + @description: + */ + 'subject' => 'New Topic: {TOPICSUBJECT}', + 'body' => 'A new topic, \'{TOPICSUBJECT}\', has been made on a board you are watching. + +You can see it at +{TOPICLINK} + +More topics may be posted, but you won\'t receive more email notifications until you return to the board and read some of them. + +Unsubscribe to new topics from this board by using this link: +{UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notify_boards_body' => array( + /* + @additional_params: notify_boards_body + TOPICSUBJECT: The subject of the topic causing the notification + TOPICLINK: A link to the topic. + MESSAGE: This is the body of the message. + UNSUBSCRIBELINK: Link to unsubscribe from notifications. + @description: + */ + 'subject' => 'New Topic: {TOPICSUBJECT}', + 'body' => 'A new topic, \'{TOPICSUBJECT}\', has been made on a board you are watching. + +You can see it at +{TOPICLINK} + +The text of the topic is shown below: +{MESSAGE} + +Unsubscribe to new topics from this board by using this link: +{UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notify_boards' => array( + /* + @additional_params: notify_boards + TOPICSUBJECT: The subject of the topic causing the notification + TOPICLINK: A link to the topic. + UNSUBSCRIBELINK: Link to unsubscribe from notifications. + @description: + */ + 'subject' => 'New Topic: {TOPICSUBJECT}', + 'body' => 'A new topic, \'{TOPICSUBJECT}\', has been made on a board you are watching. + +You can see it at +{TOPICLINK} + +Unsubscribe to new topics from this board by using this link: +{UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'request_membership' => array( + /* + @additional_params: request_membership + RECPNAME: The name of the person recieving the email + APPYNAME: The name of the person applying for group membership + GROUPNAME: The name of the group being applied to. + REASON: The reason given by the applicant for wanting to join the group. + MODLINK: Link to the group moderation page. + @description: + */ + 'subject' => 'New Group Application', + 'body' => '{RECPNAME}, + +{APPYNAME} has requested membership to the "{GROUPNAME}" group. The user has given the following reason: + +{REASON} + +You can approve or reject this application by clicking the link below: + +{MODLINK} + +{REGARDS}', + ), + 'paid_subscription_reminder' => array( + /* + @additional_params: scheduled_approval + REALNAME: The real (display) name of the person receiving the email. + PROFILE_LINK: Link to profile of member receiving email where can renew. + SUBSCRIPTION: Name of the subscription. + END_DATE: Date it expires. + @description: + */ + 'subject' => 'Subscription about to expire at {FORUMNAME}', + 'body' => '{REALNAME}, + +A subscription you are subscribed to at {FORUMNAME} is about to expire. If when you took out the subscription you selected to auto-renew you need take no action - otherwise you may wish to consider subscribing once more. Details are below: + +Subscription Name: {SUBSCRIPTION} +Expires: {END_DATE} + +To edit your subscriptions visit the following URL: +{PROFILE_LINK} + +{REGARDS}', + ), + 'activate_reactivate' => array( + /* + @additional_params: activate_reactivate + ACTIVATIONLINK: The url link to reactivate the member's account. + ACTIVATIONCODE: The code needed to reactivate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + @description: + */ + 'subject' => 'Welcome back to {FORUMNAME}', + 'body' => 'In order to re-validate your email address, your account has been deactivated. Click the following link to activate it again: +{ACTIVATIONLINK} + +Should you have any problems with activation, please visit {ACTIVATIONLINKWITHOUTCODE} and use the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'forgot_password' => array( + /* + @additional_params: forgot_password + REALNAME: The real (display) name of the person receiving the reminder. + REMINDLINK: The link to reset the password. + IP: The IP address of the requester. + MEMBERNAME: + @description: + */ + 'subject' => 'New password for {FORUMNAME}', + 'body' => 'Dear {REALNAME}, +This mail was sent because the \'forgot password\' function has been applied to your account. To set a new password, click the following link: +{REMINDLINK} + +IP: {IP} +Username: {MEMBERNAME} + +{REGARDS}', + ), + 'forgot_openid' => array( + /* + @additional_params: forgot_password + REALNAME: The real (display) name of the person receiving the reminder. + IP: The IP address of the requester. + OPENID: The members OpenID identity. + @description: + */ + 'subject' => 'OpenID reminder for {FORUMNAME}', + 'body' => 'Dear {REALNAME}, +This mail was sent because the \'forgot OpenID\' function has been applied to your account. Below is the OpenID that your account is associated with: +{OPENID} + +IP: {IP} +Username: {MEMBERNAME} + +{REGARDS}', + ), + 'scheduled_approval' => array( + /* + @additional_params: scheduled_approval + REALNAME: The real (display) name of the person receiving the email. + BODY: The generated body of the mail. + @description: + */ + 'subject' => 'Summary of posts awaiting approval at {FORUMNAME}', + 'body' => '{REALNAME}, + +This email contains a summary of all items awaiting approval at {FORUMNAME}. + +{BODY} + +Please log in to the forum to review these items. +{SCRIPTURL} + +{REGARDS}', + ), + 'send_topic' => array( + /* + @additional_params: send_topic + TOPICSUBJECT: The subject of the topic being sent. + SENDERNAME: The name of the member sending the topic. + RECPNAME: The name of the person receiving the email. + TOPICLINK: A link to the topic being sent. + @description: + */ + 'subject' => 'Topic: {TOPICSUBJECT} (From: {SENDERNAME})', + 'body' => 'Dear {RECPNAME}, +I want you to check out "{TOPICSUBJECT}" on {FORUMNAME}. To view it, please click this link: + +{TOPICLINK} + +Thanks, + +{SENDERNAME}', + ), + 'send_topic_comment' => array( + /* + @additional_params: send_topic_comment + TOPICSUBJECT: The subject of the topic being sent. + SENDERNAME: The name of the member sending the topic. + RECPNAME: The name of the person receiving the email. + TOPICLINK: A link to the topic being sent. + COMMENT: A comment left by the sender. + @description: + */ + 'subject' => 'Topic: {TOPICSUBJECT} (From: {SENDERNAME})', + 'body' => 'Dear {RECPNAME}, +I want you to check out "{TOPICSUBJECT}" on {FORUMNAME}. To view it, please click this link: + +{TOPICLINK} + +A comment has also been added regarding this topic: +{COMMENT} + +Thanks, + +{SENDERNAME}', + ), + 'send_email' => array( + /* + @additional_params: send_email + EMAILSUBJECT: The subject the user wants to email. + EMAILBODY: The body the user wants to email. + SENDERNAME: The name of the member sending the email. + RECPNAME: The name of the person receiving the email. + @description: + */ + 'subject' => '{EMAILSUBJECT}', + 'body' => '{EMAILBODY}', + ), + 'report_to_moderator' => array( + /* + @additional_params: report_to_moderator + TOPICSUBJECT: The subject of the reported post. + POSTERNAME: The report post's author's name. + REPORTERNAME: The name of the person reporting the post. + TOPICLINK: The url of the post that is being reported. + REPORTLINK: The url of the moderation center report. + COMMENT: The comment left by the reporter, hopefully to explain why they are reporting the post. + @description: When a user reports a post this email is sent out to moderators and admins of that board. + */ + 'subject' => 'Reported post: {TOPICSUBJECT} by {POSTERNAME}', + 'body' => 'The following post, "{TOPICSUBJECT}" by {POSTERNAME} has been reported by {REPORTERNAME} on a board you moderate: + +The topic: {TOPICLINK} +Moderation center: {REPORTLINK} + +The reporter has made the following comment: +{COMMENT} + +{REGARDS}', + ), + 'change_password' => array( + /* + @additional_params: change_password + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + @description: + */ + 'subject' => 'New Password Details', + 'body' => 'Hey, {USERNAME}! + +Your login details at {FORUMNAME} have been changed and your password reset. Below are your new login details. + +Your username is "{USERNAME}" and your password is "{PASSWORD}". + +You may change it after you login by going to the profile page, or by visiting this page after you login: +{SCRIPTURL}?action=profile + +{REGARDS}', + ), + 'register_activate' => array( + /* + @additional_params: register_activate + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + ACTIVATIONLINK: The url link to reactivate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The code needed to reactivate the member's account. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. If you forget your password, you can reset it by visiting {FORGOTPASSWORDLINK}. + +Before you can login, you first need to activate your account. To do so, please follow this link: + +{ACTIVATIONLINK} + +Should you have any problems with activation, please visit {ACTIVATIONLINKWITHOUTCODE} use the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'register_openid_activate' => array( + /* + @additional_params: register_activate + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + OPENID: The openID identity for the member. + ACTIVATIONLINK: The url link to reactivate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The code needed to reactivate the member's account. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. You have chosen to authenticate using the following OpenID identity: +{OPENID} + +Before you can login, you first need to activate your account. To do so, please follow this link: + +{ACTIVATIONLINK} + +Should you have any problems with activation, please visit {ACTIVATIONLINKWITHOUTCODE} and use the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'register_coppa' => array( + /* + @additional_params: register_coppa + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + COPPALINK: The url link to the coppa form. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. If you forget your password, you can change it at {FORGOTPASSWORDLINK} + +Before you can login, the admin requires consent from your parent/guardian for you to join the community. You can obtain more information at the link below: + +{COPPALINK} + +{REGARDS}', + ), + 'register_openid_coppa' => array( + /* + @additional_params: register_coppa + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + OPENID: The openID identity for the member. + COPPALINK: The url link to the coppa form. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. + +You have chosen to authenticate using the following OpenID identity: +{OPENID} + +Before you can login, the admin requires consent from your parent/guardian for you to join the community. You can obtain more information at the link below: + +{COPPALINK} + +{REGARDS}', + ), + 'register_immediate' => array( + /* + @additional_params: register_immediate + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. If you forget your password, you may change it at {FORGOTPASSWORDLINK}. + +{REGARDS}', + ), + 'register_openid_immediate' => array( + /* + @additional_params: register_immediate + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + OPENID: The openID identity for the member. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. + +You have chosen to authenticate using the following OpenID identity: +{OPENID} + +You may update your profile by visiting this page after you login: + +{SCRIPTURL}?action=profile + +{REGARDS}', + ), + 'register_pending' => array( + /* + @additional_params: register_pending + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Your registration request at {FORUMNAME} has been received, {REALNAME}. + +The username you registered with was {USERNAME}. If you forget your password, you can change it at {FORGOTPASSWORDLINK}. + +Before you can login and start using the forum, your request will be reviewed and approved. When this happens, you will receive another email from this address. + +{REGARDS}', + ), + 'register_openid_pending' => array( + /* + @additional_params: register_pending + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + OPENID: The openID identity for the member. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Your registration request at {FORUMNAME} has been received, {REALNAME}. + +The username you registered with was {USERNAME}. + +You have chosen to authenticate using the following OpenID identity: +{OPENID} + +Before you can login and start using the forum, your request will be reviewed and approved. When this happens, you will receive another email from this address. + +{REGARDS}', + ), + 'notification_reply' => array( + /* + @additional_params: notification_reply + TOPICSUBJECT: + POSTERNAME: + TOPICLINK: + UNSUBSCRIBELINK: + @description: + */ + 'subject' => 'Topic reply: {TOPICSUBJECT}', + 'body' => 'A reply has been posted to a topic you are watching by {POSTERNAME}. + +View the reply at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_reply_body' => array( + /* + @additional_params: notification_reply_body + TOPICSUBJECT: + POSTERNAME: + TOPICLINK: + UNSUBSCRIBELINK: + MESSAGE: + @description: + */ + 'subject' => 'Topic reply: {TOPICSUBJECT}', + 'body' => 'A reply has been posted to a topic you are watching by {POSTERNAME}. + +View the reply at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +The text of the reply is shown below: +{MESSAGE} + +{REGARDS}', + ), + 'notification_reply_once' => array( + /* + @additional_params: notification_reply_once + TOPICSUBJECT: + POSTERNAME: + TOPICLINK: + UNSUBSCRIBELINK: + @description: + */ + 'subject' => 'Topic reply: {TOPICSUBJECT}', + 'body' => 'A reply has been posted to a topic you are watching by {POSTERNAME}. + +View the reply at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +More replies may be posted, but you won\'t receive any more notifications until you read the topic. + +{REGARDS}', + ), + 'notification_reply_body_once' => array( + /* + @additional_params: notification_reply_body_once + TOPICSUBJECT: + POSTERNAME: + TOPICLINK: + UNSUBSCRIBELINK: + MESSAGE: + @description: + */ + 'subject' => 'Topic reply: {TOPICSUBJECT}', + 'body' => 'A reply has been posted to a topic you are watching by {POSTERNAME}. + +View the reply at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +The text of the reply is shown below: +{MESSAGE} + +More replies may be posted, but you won\'t receive any more notifications until you read the topic. + +{REGARDS}', + ), + 'notification_sticky' => array( + /* + @additional_params: notification_sticky + @description: + */ + 'subject' => 'Topic stickied: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been marked as a sticky topic by {POSTERNAME}. + +View the topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_lock' => array( + /* + @additional_params: notification_lock + @description: + */ + 'subject' => 'Topic locked: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been locked by {POSTERNAME}. + +View the topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_unlock' => array( + /* + @additional_params: notification_unlock + @description: + */ + 'subject' => 'Topic unlocked: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been unlocked by {POSTERNAME}. + +View the topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_remove' => array( + /* + @additional_params: notification_remove + @description: + */ + 'subject' => 'Topic removed: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been removed by {POSTERNAME}. + +{REGARDS}', + ), + 'notification_move' => array( + /* + @additional_params: notification_move + @description: + */ + 'subject' => 'Topic moved: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been moved to another board by {POSTERNAME}. + +View the topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_merge' => array( + /* + @additional_params: notification_merged + @description: + */ + 'subject' => 'Topic merged: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been merged with another topic by {POSTERNAME}. + +View the new merged topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_split' => array( + /* + @additional_params: notification_split + @description: + */ + 'subject' => 'Topic split: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been split into two or more topics by {POSTERNAME}. + +View what remains of this topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'admin_notify' => array( + /* + @additional_params: admin_notify + USERNAME: + PROFILELINK: + @description: + */ + 'subject' => 'A new member has joined', + 'body' => '{USERNAME} has just signed up as a new member of your forum. Click the link below to view their profile. +{PROFILELINK} + +{REGARDS}', + ), + 'admin_notify_approval' => array( + /* + @additional_params: admin_notify_approval + USERNAME: + PROFILELINK: + APPROVALLINK: + @description: + */ + 'subject' => 'A new member has joined', + 'body' => '{USERNAME} has just signed up as a new member of your forum. Click the link below to view their profile. +{PROFILELINK} + +Before this member can begin posting they must first have their account approved. Click the link below to go to the approval screen. +{APPROVALLINK} + +{REGARDS}', + ), + 'admin_attachments_full' => array( + /* + @additional_params: admin_attachments_full + REALNAME: + @description: + */ + 'subject' => 'Urgent! Attachments folder almost full', + 'body' => '{REALNAME}, + +The attachments folder at {FORUMNAME} is almost full. Please visit the forum to resolve this problem. + +Once the attachments folder reaches it\'s maximum permitted size users will not be able to continue to post attachments or upload custom avatars (If enabled). + +{REGARDS}', + ), + 'paid_subscription_refund' => array( + /* + @additional_params: paid_subscription_refund + NAME: Subscription title. + REALNAME: Recipients name + REFUNDUSER: Username who took out the subscription. + REFUNDNAME: User's display name who took out the subscription. + DATE: Today's date. + PROFILELINK: Link to members profile. + @description: + */ + 'subject' => 'Refunded Paid Subscription', + 'body' => '{REALNAME}, + +A member has received a refund on a paid subscription. Below are the details of this subscription: + + Subscription: {NAME} + User Name: {REFUNDNAME} ({REFUNDUSER}) + Date: {DATE} + +You can view this members profile by clicking the link below: +{PROFILELINK} + +{REGARDS}', + ), + 'paid_subscription_new' => array( + /* + @additional_params: paid_subscription_new + NAME: Subscription title. + REALNAME: Recipients name + SUBEMAIL: Email address of the user who took out the subscription + SUBUSER: Username who took out the subscription. + SUBNAME: User's display name who took out the subscription. + DATE: Today's date. + PROFILELINK: Link to members profile. + @description: + */ + 'subject' => 'New Paid Subscription', + 'body' => '{REALNAME}, + +A member has taken out a new paid subscription. Below are the details of this subscription: + + Subscription: {NAME} + User Name: {SUBNAME} ({SUBUSER}) + User Email: {SUBEMAIL} + Price: {PRICE} + Date: {DATE} + +You can view this members profile by clicking the link below: +{PROFILELINK} + +{REGARDS}', + ), + 'paid_subscription_error' => array( + /* + @additional_params: paid_subscription_error + ERROR: Error message. + REALNAME: Recipients name + @description: + */ + 'subject' => 'Paid Subscription Error Occurred', + 'body' => '{REALNAME}, + +The following error occurred when processing a paid subscription +--------------------------------------------------------------- +{ERROR} + +{REGARDS}', + ), +); + +/* + @additional_params: happy_birthday + REALNAME: The real (display) name of the person receiving the birthday message. + @description: A message sent to members on their birthday. +*/ +$birthdayEmails = array( + 'happy_birthday' => array( + 'subject' => 'Happy birthday from {FORUMNAME}.', + 'body' => 'Dear {REALNAME}, + +We here at {FORUMNAME} would like to wish you a happy birthday. May this day and the year to follow be full of joy. + +{REGARDS}', + 'author' => 'Thantos', + ), + 'karlbenson1' => array( + 'subject' => 'On your Birthday...', + 'body' => 'We could have sent you a birthday card. We could have sent you some flowers or a cake. + +But we didn\'t. + +We could have even sent you one of those automatically generated messages to wish you happy birthday where we don\'t even have to replace INSERT NAME. + +But we didn\'t + +We wrote this birthday greeting just for you. + +We would like to wish you a very special birthday. + +{REGARDS} + +//:: This message was automatically generated :://', + 'author' => 'karlbenson', + ), + 'nite0859' => array( + 'subject' => 'Happy Birthday!', + 'body' => 'Your friends at {FORUMNAME} would like to take a moment of your time to wish you a happy birthday, {REALNAME}. If you have not done so recently, please visit our community in order for others to have the opportunity to pass along their warm regards. + +Even though today is your birthday, {REALNAME}, we would like to remind you that your membership in our community has been the best gift to us thus far. + +Best Wishes, +The Staff of {FORUMNAME}', + 'author' => 'nite0859', + ), + 'zwaldowski' => array( + 'subject' => 'Birthday Wishes to {REALNAME}', + 'body' => 'Dear {REALNAME}, + +Another year in your life has passed. We at {FORUMNAME} hope it has been filled with happiness, and wish you luck in the coming one. + +{REGARDS}', + 'author' => 'zwaldowski', + ), + 'geezmo' => array( + 'subject' => 'Happy birthday, {REALNAME}!', + 'body' => 'Do you know who\'s having a birthday today, {REALNAME}? + +We know... YOU! + +Happy birthday! + +You\'re now a year older but we hope you\'re a lot happier than last year. + +Enjoy your day today, {REALNAME}! + +- From your {FORUMNAME} family', + 'author' => 'geezmo', + ), + 'karlbenson2' => array( + 'subject' => 'Your Birthday Greeting', + 'body' => 'We hope your birthday is the best ever cloudy, sunny or whatever the weather. +Have lots of birthday cake and fun, and tell us what you have done. + +We hope this message brought you cheer, and make it last, until same time same place, next year. + +{REGARDS}', + 'author' => 'karlbenson', + ), +); +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Errors.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Errors.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,407 @@ +register.'; +$txt['passwords_dont_match'] = 'Passwords aren\'t the same.'; +$txt['register_to_use'] = 'Sorry, you must register before using this feature.'; +$txt['password_invalid_character'] = 'Invalid character used in password.'; +$txt['name_invalid_character'] = 'Invalid character used in name.'; +$txt['email_invalid_character'] = 'Invalid character used in email.'; +$txt['username_reserved'] = 'The username you tried to use contains the reserved name \'%1$s\'. Please try another username.'; +$txt['numbers_one_to_nine'] = 'This field only accepts numbers from 0-9'; +$txt['not_a_user'] = 'The user whose profile you are trying to view does not exist.'; +$txt['not_a_topic'] = 'This topic doesn\'t exist on this board.'; +$txt['not_approved_topic'] = 'This topic has not been approved yet.'; +$txt['email_in_use'] = 'That email address (%1$s) is being used by a registered member already. If you feel this is a mistake, go to the login page and use the password reminder with that address.'; + +$txt['didnt_select_vote'] = 'You didn\'t select a vote option.'; +$txt['poll_error'] = 'Either that poll doesn\'t exist, the poll has been locked, or you tried to vote twice.'; +$txt['members_only'] = 'This option is only available to registered members.'; +$txt['locked_by_admin'] = 'This was locked by an administrator. You cannot unlock it.'; +$txt['not_enough_posts_karma'] = 'Sorry, you don\'t have enough posts to modify karma - you need at least %1$d.'; +$txt['cant_change_own_karma'] = 'Sorry, you are not permitted to modify your own karma.'; +$txt['karma_wait_time'] = 'Sorry, you can\'t repeat a karma action without waiting %1$s %2$s.'; +$txt['feature_disabled'] = 'Sorry, this feature is disabled.'; +$txt['cant_access_upload_path'] = 'Cannot access attachments upload path!'; +$txt['file_too_big'] = 'Your file is too large. The maximum attachment size allowed is %1$d KB.'; +$txt['attach_timeout'] = 'Your attachment couldn\'t be saved. This might happen because it took too long to upload or the file is bigger than the server will allow.

    Please consult your server administrator for more information.'; +$txt['filename_exists'] = 'Sorry! There is already an attachment with the same filename as the one you tried to upload. Please rename the file and try again.'; +$txt['bad_attachment'] = 'Your attachment has failed security checks and cannot be uploaded. Please consult the forum administrator.'; +$txt['ran_out_of_space'] = 'The upload folder is full. Please try a smaller file and/or contact an administrator.'; +$txt['couldnt_connect'] = 'Could not connect to server or could not find file'; +$txt['no_board'] = 'The board you specified doesn\'t exist'; +$txt['cant_split'] = 'You are not allowed to split topics'; +$txt['cant_merge'] = 'You are not allowed to merge topics'; +$txt['no_topic_id'] = 'You specified an invalid topic ID.'; +$txt['split_first_post'] = 'You cannot split a topic at the first post.'; +$txt['topic_one_post'] = 'This topic only contains one message and cannot be split.'; +$txt['no_posts_selected'] = 'No messages selected'; +$txt['selected_all_posts'] = 'Unable to split. You have selected every message.'; +$txt['cant_find_messages'] = 'Unable to find messages'; +$txt['cant_find_user_email'] = 'Unable to find user\'s email address.'; +$txt['cant_insert_topic'] = 'Unable to insert topic'; +$txt['already_a_mod'] = 'You have chosen a username of an already existing moderator. Please choose another username'; +$txt['session_timeout'] = 'Your session timed out while posting. Please go back and try again.'; +$txt['session_verify_fail'] = 'Session verification failed. Please try logging out and back in again, and then try again.'; +$txt['verify_url_fail'] = 'Unable to verify referring url. Please go back and try again.'; +$txt['guest_vote_disabled'] = 'Guests cannot vote in this poll.'; + +$txt['cannot_access_mod_center'] = 'You do not have permission to access the moderation center.'; +$txt['cannot_admin_forum'] = 'You are not allowed to administrate this forum.'; +$txt['cannot_announce_topic'] = 'You are not allowed to announce topics on this board.'; +$txt['cannot_approve_posts'] = 'You do not have permission to approve items.'; +$txt['cannot_post_unapproved_attachments'] = 'You do not have permission to post unapproved attachments.'; +$txt['cannot_post_unapproved_topics'] = 'You do not have permission to post unapproved topics.'; +$txt['cannot_post_unapproved_replies_own'] = 'You do not have permission to post unapproved replies to your topics.'; +$txt['cannot_post_unapproved_replies_any'] = 'You do not have permission to post unapproved replies to other users\' topics.'; +$txt['cannot_calendar_edit_any'] = 'You cannot edit calendar events.'; +$txt['cannot_calendar_edit_own'] = 'You don\'t have the privileges necessary to edit your own events.'; +$txt['cannot_calendar_post'] = 'Event posting isn\'t allowed - sorry.'; +$txt['cannot_calendar_view'] = 'Sorry, but you are not allowed to view the calendar.'; +$txt['cannot_remove_any'] = 'Sorry, but you don\'t have the privilege to remove just any topic. Check to make sure this topic wasn\'t just moved to another board.'; +$txt['cannot_remove_own'] = 'You cannot delete your own topics in this board. Check to make sure this topic wasn\'t just moved to another board.'; +$txt['cannot_edit_news'] = 'You are not allowed to edit news items on this forum.'; +$txt['cannot_pm_read'] = 'Sorry, you can\'t read your personal messages.'; +$txt['cannot_pm_send'] = 'You are not allowed to send personal messages.'; +$txt['cannot_karma_edit'] = 'You aren\'t permitted to modify other people\'s karma.'; +$txt['cannot_lock_any'] = 'You are not allowed to lock just any topic here.'; +$txt['cannot_lock_own'] = 'Apologies, but you cannot lock your own topics here.'; +$txt['cannot_make_sticky'] = 'You don\'t have permission to sticky this topic.'; +$txt['cannot_manage_attachments'] = 'You\'re not allowed to manage attachments or avatars.'; +$txt['cannot_manage_bans'] = 'You\'re not allowed to change the list of bans.'; +$txt['cannot_manage_boards'] = 'You are not allowed to manage boards and categories.'; +$txt['cannot_manage_membergroups'] = 'You don\'t have permission to modify or assign membergroups.'; +$txt['cannot_manage_permissions'] = 'You don\'t have permission to manage permissions.'; +$txt['cannot_manage_smileys'] = 'You\'re not allowed to manage smileys and message icons.'; +$txt['cannot_mark_any_notify'] = 'You don\'t have the permissions necessary to get notifications from this topic.'; +$txt['cannot_mark_notify'] = 'Sorry, but you are not permitted to request notifications from this board.'; +$txt['cannot_merge_any'] = 'You aren\'t allowed to merge topics on one of the selected board(s).'; +$txt['cannot_moderate_forum'] = 'You are not allowed to moderate this forum.'; +$txt['cannot_moderate_board'] = 'You are not allowed to moderate this board.'; +$txt['cannot_modify_any'] = 'You aren\'t allowed to modify just any post.'; +$txt['cannot_modify_own'] = 'Sorry, but you aren\'t allowed to edit your own posts.'; +$txt['cannot_modify_replies'] = 'Even though this post is a reply to your topic, you cannot edit it.'; +$txt['cannot_move_own'] = 'You are not allowed to move your own topics in this board.'; +$txt['cannot_move_any'] = 'You are not allowed to move topics in this board.'; +$txt['cannot_poll_add_own'] = 'Sorry, you aren\'t allowed to add polls to your own topics in this board.'; +$txt['cannot_poll_add_any'] = 'You don\'t have the access to add polls to this topic.'; +$txt['cannot_poll_edit_own'] = 'You cannot edit this poll, even though it is your own.'; +$txt['cannot_poll_edit_any'] = 'You have been denied access to editing polls in this board.'; +$txt['cannot_poll_lock_own'] = 'You are not allowed to lock your own polls in this board.'; +$txt['cannot_poll_lock_any'] = 'Sorry, but you aren\'t allowed to lock just any poll.'; +$txt['cannot_poll_post'] = 'You aren\'t allowed to post polls in the current board.'; +$txt['cannot_poll_remove_own'] = 'You are not permitted to remove this poll from your topic.'; +$txt['cannot_poll_remove_any'] = 'You cannot remove just any poll on this board.'; +$txt['cannot_poll_view'] = 'You are not allowed to view polls in this board.'; +$txt['cannot_poll_vote'] = 'Sorry, but you cannot vote in polls in this board.'; +$txt['cannot_post_attachment'] = 'You don\'t have permission to post attachments here.'; +$txt['cannot_post_new'] = 'Sorry, you cannot post new topics in this board.'; +$txt['cannot_post_reply_any'] = 'You are not permitted to post replies to topics on this board.'; +$txt['cannot_post_reply_own'] = 'You are not allowed to post replies even to your own topics in this board.'; +$txt['cannot_profile_remove_own'] = 'Sorry, but you aren\'t allowed to delete your own account.'; +$txt['cannot_profile_remove_any'] = 'You don\'t have the permissions to go about removing people\'s accounts!'; +$txt['cannot_profile_extra_any'] = 'You are not permitted to modify profile settings.'; +$txt['cannot_profile_identity_any'] = 'You aren\'t allowed to edit account settings.'; +$txt['cannot_profile_title_any'] = 'You cannot edit people\'s custom titles.'; +$txt['cannot_profile_extra_own'] = 'Sorry, but you don\'t have the necessary permissions to edit your profile data.'; +$txt['cannot_profile_identity_own'] = 'You can\'t change your identity at the current moment.'; +$txt['cannot_profile_title_own'] = 'You are not allowed to change your custom title.'; +$txt['cannot_profile_server_avatar'] = 'You are not permitted to use a server stored avatar.'; +$txt['cannot_profile_upload_avatar'] = 'You do not have permission to upload an avatar.'; +$txt['cannot_profile_remote_avatar'] = 'You don\'t have the privilege of using a remote avatar.'; +$txt['cannot_profile_view_own'] = 'Many apologies, but you can\'t view your own profile.'; +$txt['cannot_profile_view_any'] = 'Many apologies, but you can\'t view just any profile.'; +$txt['cannot_delete_own'] = 'You are not, on this board, allowed to delete your own posts.'; +$txt['cannot_delete_replies'] = 'Sorry, but you cannot remove these posts, even though they are replies to your topic.'; +$txt['cannot_delete_any'] = 'Deleting just any posts in this board is not allowed.'; +$txt['cannot_report_any'] = 'You are not allowed to report posts in this board.'; +$txt['cannot_search_posts'] = 'You are not allowed to search for posts in this forum.'; +$txt['cannot_send_mail'] = 'You don\'t have the privilege of sending out emails to everyone.'; +$txt['cannot_issue_warning'] = 'Sorry, you do not have permission to issue warnings to members.'; +$txt['cannot_send_topic'] = 'Sorry, but the administrator has disallowed sending topics on this board.'; +$txt['cannot_split_any'] = 'Splitting just any topic is not allowed in this board.'; +$txt['cannot_view_attachments'] = 'It seems that you are not allowed to download or view attachments on this board.'; +$txt['cannot_view_mlist'] = 'You can\'t view the memberlist because you don\'t have permission to.'; +$txt['cannot_view_stats'] = 'You aren\'t allowed to view the forum statistics.'; +$txt['cannot_who_view'] = 'Sorry - you don\'t have the proper permissions to view the Who\'s Online list.'; + +$txt['no_theme'] = 'That theme does not exist.'; +$txt['theme_dir_wrong'] = 'The default theme\'s directory is wrong, please correct it by clicking this text.'; +$txt['registration_disabled'] = 'Sorry, registration is currently disabled.'; +$txt['registration_no_secret_question'] = 'Sorry, there is no secret question set for this member.'; +$txt['poll_range_error'] = 'Sorry, the poll must run for more than 0 days.'; +$txt['delFirstPost'] = 'You are not allowed to delete the first post in a topic.

    If you want to delete this topic, click on the Remove Topic link, or ask a moderator/administrator to do it for you.

    '; +$txt['parent_error'] = 'Unable to create board!'; +$txt['login_cookie_error'] = 'You were unable to login. Please check your cookie settings.'; +$txt['incorrect_answer'] = 'Sorry, but you did not answer your question correctly. Please click back to try again, or click back twice to use the default method of obtaining your password.'; +$txt['no_mods'] = 'No moderators found!'; +$txt['parent_not_found'] = 'Board structure corrupt: unable to find parent board'; +$txt['modify_post_time_passed'] = 'You may not modify this post as the time limit for edits has passed.'; + +$txt['calendar_off'] = 'You cannot access the calendar right now because it is disabled.'; +$txt['invalid_month'] = 'Invalid month value.'; +$txt['invalid_year'] = 'Invalid year value.'; +$txt['invalid_day'] = 'Invalid day value.'; +$txt['event_month_missing'] = 'Event month is missing.'; +$txt['event_year_missing'] = 'Event year is missing.'; +$txt['event_day_missing'] = 'Event day is missing.'; +$txt['event_title_missing'] = 'Event title is missing.'; +$txt['invalid_date'] = 'Invalid date.'; +$txt['no_event_title'] = 'No event title was entered.'; +$txt['missing_event_id'] = 'Missing event ID.'; +$txt['cant_edit_event'] = 'You do not have permission to edit this event.'; +$txt['missing_board_id'] = 'Board ID is missing.'; +$txt['missing_topic_id'] = 'Topic ID is missing.'; +$txt['topic_doesnt_exist'] = 'Topic doesn\'t exist.'; +$txt['not_your_topic'] = 'You are not the owner of this topic.'; +$txt['board_doesnt_exist'] = 'The board does not exist.'; +$txt['no_span'] = 'The span feature is currently disabled.'; +$txt['invalid_days_numb'] = 'Invalid number of days to span.'; + +$txt['moveto_noboards'] = 'There are no boards to move this topic to!'; + +$txt['already_activated'] = 'Your account has already been activated.'; +$txt['still_awaiting_approval'] = 'Your account is still awaiting admin approval.'; + +$txt['invalid_email'] = 'Invalid email address / email address range.
    Example of a valid email address: evil.user@badsite.com.
    Example of a valid email address range: *@*.badsite.com'; +$txt['invalid_expiration_date'] = 'Expiration date is not valid'; +$txt['invalid_hostname'] = 'Invalid host name / host name range.
    Example of a valid host name: proxy4.badhost.com
    Example of a valid host name range: *.badhost.com'; +$txt['invalid_ip'] = 'Invalid IP / IP range.
    Example of a valid IP address: 127.0.0.1
    Example of a valid IP range: 127.0.0-20.*'; +$txt['invalid_tracking_ip'] = 'Invalid IP / IP range.
    Example of a valid IP address: 127.0.0.1
    Example of a valid IP range: 127.0.0.*'; +$txt['invalid_username'] = 'Member name not found'; +$txt['no_ban_admin'] = 'You may not ban an admin - You must demote them first!'; +$txt['no_bantype_selected'] = 'No ban type was selected'; +$txt['ban_not_found'] = 'Ban not found'; +$txt['ban_unknown_restriction_type'] = 'Restriction type unknown'; +$txt['ban_name_empty'] = 'The name of the ban was left empty'; +$txt['ban_name_exists'] = 'The name of this ban (%1$s) already exists. Please choose a different name.'; +$txt['ban_trigger_already_exists'] = 'This ban trigger (%1$s) already exists in %2$s.'; + +$txt['recycle_no_valid_board'] = 'No valid board selected for recycled topics'; + +$txt['login_threshold_fail'] = 'Sorry, you are out of login chances. Please come back and try again later.'; +$txt['login_threshold_brute_fail'] = 'Sorry, but you\'ve reached your login attempts threshold. Please wait 30 seconds and try again later.'; + +$txt['who_off'] = 'You cannot access Who\'s Online right now because it is disabled.'; + +$txt['merge_create_topic_failed'] = 'Error creating a new topic.'; +$txt['merge_need_more_topics'] = 'Merge topics require at least two topics to merge.'; + +$txt['postWaitTime_broken'] = 'The last posting from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['registerWaitTime_broken'] = 'You already registered just %1$d seconds ago!'; +$txt['loginWaitTime_broken'] = 'You will have to wait about %1$d seconds to login again, sorry.'; +$txt['pmWaitTime_broken'] = 'The last personal message from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['reporttmWaitTime_broken'] = 'The last topic report from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['sendtopcWaitTime_broken'] = 'The last topic sent from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['sendmailWaitTime_broken'] = 'The last email sent from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['searchWaitTime_broken'] = 'Your last search was less than %1$d seconds ago. Please try again later.'; + +$txt['email_missing_data'] = 'You must enter something in both the subject and message boxes.'; + +$txt['topic_gone'] = 'The topic or board you are looking for appears to be either missing or off limits to you.'; +$txt['theme_edit_missing'] = 'The file you are trying to edit... can\'t even be found!'; + +$txt['attachments_no_write'] = 'The attachments upload directory is not writable. Your attachment or avatar cannot be saved.'; +$txt['attachments_limit_per_post'] = 'You may not upload more than %1$d attachments per post'; + +$txt['no_dump_database'] = 'Only administrators can make database backups!'; +$txt['pm_not_yours'] = 'The personal message you\'re trying to quote is not your own or does not exist, please go back and try again.'; +$txt['mangled_post'] = 'Mangled form data - please go back and try again.'; +$txt['quoted_post_deleted'] = 'The post you are trying to quote either does not exist, was deleted, or is no longer viewable by you.'; +$txt['pm_too_many_per_hour'] = 'You have exceeded the limit of %1$d personal messages per hour.'; +$txt['labels_too_many'] = 'Sorry, %1$s messages already had the maximum amount of labels allowed!'; + +$txt['register_only_once'] = 'Sorry, but you\'re not allowed to register multiple accounts at the same time from the same computer.'; +$txt['admin_setting_coppa_require_contact'] = 'You must enter either a postal or fax contact if parent/guardian approval is required.'; + +$txt['error_long_name'] = 'The name you tried to use was too long.'; +$txt['error_no_name'] = 'No name was provided.'; +$txt['error_bad_name'] = 'The name you submitted cannot be used, because it is or contains a reserved name.'; +$txt['error_no_email'] = 'No email address was provided.'; +$txt['error_bad_email'] = 'An invalid email address was given.'; +$txt['error_no_event'] = 'No event name has been given.'; +$txt['error_no_subject'] = 'No subject was filled in.'; +$txt['error_no_question'] = 'No question was filled in for this poll.'; +$txt['error_no_message'] = 'The message body was left empty.'; +$txt['error_long_message'] = 'The message exceeds the maximum allowed length (%1$d characters).'; +$txt['error_no_comment'] = 'The comment field was left empty.'; +$txt['error_session_timeout'] = 'Your session timed out while posting. Please try to re-submit your message.'; +$txt['error_no_to'] = 'No recipients specified.'; +$txt['error_bad_to'] = 'One or more \'to\'-recipients could not be found.'; +$txt['error_bad_bcc'] = 'One or more \'bcc\'-recipients could not be found.'; +$txt['error_form_already_submitted'] = 'You already submitted this post! You might have accidentally double clicked or tried to refresh the page.'; +$txt['error_poll_few'] = 'You must have at least two choices!'; +$txt['error_need_qr_verification'] = 'Please complete the verification section below to complete your post.'; +$txt['error_wrong_verification_code'] = 'The letters you typed don\'t match the letters that were shown in the picture.'; +$txt['error_wrong_verification_answer'] = 'You did not answer the verification questions correctly.'; +$txt['error_need_verification_code'] = 'Please enter the verification code below to continue to the results.'; +$txt['error_bad_file'] = 'Sorry but the file specified could not be opened: %1$s'; +$txt['error_bad_line'] = 'The line you specified is invalid.'; + +$txt['smiley_not_found'] = 'Smiley not found.'; +$txt['smiley_has_no_code'] = 'No code for this smiley was given.'; +$txt['smiley_has_no_filename'] = 'No filename for this smiley was given.'; +$txt['smiley_not_unique'] = 'A smiley with that code already exists.'; +$txt['smiley_set_already_exists'] = 'A smiley set with that URL already exists'; +$txt['smiley_set_not_found'] = 'Smiley set not found'; +$txt['smiley_set_path_already_used'] = 'The URL of the smiley set is already being used by another smiley set.'; +$txt['smiley_set_unable_to_import'] = 'Unable to import smiley set. Either the directory is invalid or cannot be accessed.'; + +$txt['smileys_upload_error'] = 'Failed to upload file.'; +$txt['smileys_upload_error_blank'] = 'All smiley sets must have an image!'; +$txt['smileys_upload_error_name'] = 'All smileys must have the same filename!'; +$txt['smileys_upload_error_illegal'] = 'Illegal Type.'; + +$txt['search_invalid_weights'] = 'Search weights are not properly configured. At least one weight should be configure to be non-zero. Please report this error to an administrator.'; +$txt['unable_to_create_temporary'] = 'The search function was unable to create temporary tables. Please try again.'; + +$txt['package_no_file'] = 'Unable to find package file!'; +$txt['packageget_unable'] = 'Unable to connect to the server. Please try using this URL instead.'; +$txt['not_on_simplemachines'] = 'Sorry, packages can only be downloaded like this from the simplemachines.org server.'; +$txt['package_cant_uninstall'] = 'This package was either never installed or was already uninstalled - you can\'t uninstall it now.'; +$txt['package_cant_download'] = 'You cannot download or install new packages because the Packages directory or one of the files in it are not writable!'; +$txt['package_upload_error_nofile'] = 'You did not select a package to upload.'; +$txt['package_upload_error_failed'] = 'Could not upload package, please check directory permissions!'; +$txt['package_upload_error_exists'] = 'The file you are uploading already exists on the server. Please delete it first then try again.'; +$txt['package_upload_error_supports'] = 'The package manager currently allows only these file types: %1$s.'; +$txt['package_upload_error_broken'] = 'Package upload failed due to the following error:
    "%1$s"'; + +$txt['package_get_error_not_found'] = 'The package you are trying to install cannot be located. You may want to manually upload the package to your Packages directory.'; +$txt['package_get_error_missing_xml'] = 'The package you are attempting to install is missing the package-info.xml that must be in the root package directory.'; +$txt['package_get_error_is_zero'] = 'Although the package was downloaded to the server it appears to be empty. Please check the Packages directory, and the "temp" sub-directory are both writable. If you continue to experience this problem you should try extracting the package on your PC and uploading the extracted files into a subdirectory in your Packages directory and try again. For example, if the package was called shout.tar.gz you should:
    1) Download the package to your local PC and extract it into files.
    2) Using an FTP client create a new directory in your "Packages" folder, in this example you may call it "shout".
    3) Upload all the files from the extracted package to this directory.
    4) Go back to the package manager browse page and the package will be automatically found by SMF.'; +$txt['package_get_error_packageinfo_corrupt'] = 'SMF was unable to find any valid information within the package-info.xml file included within the Package. There may be an error with the modification, or the package may be corrupt.'; + +$txt['no_membergroup_selected'] = 'No membergroup selected'; +$txt['membergroup_does_not_exist'] = 'The membergroup doesn\'t exist or is invalid.'; + +$txt['at_least_one_admin'] = 'There must be at least one administrator on a forum!'; + +$txt['error_functionality_not_windows'] = 'Sorry, this functionality is currently not available for servers running Windows.'; + +// Don't use entities in the below string. +$txt['attachment_not_found'] = 'Attachment Not Found'; + +$txt['error_no_boards_selected'] = 'No valid boards were selected!'; +$txt['error_invalid_search_string'] = 'Did you forget to put something to search for?'; +$txt['error_invalid_search_string_blacklist'] = 'Your search query contained too trivial words. Please try again with a different query.'; +$txt['error_search_string_small_words'] = 'Each word must be at least two characters long.'; +$txt['error_query_not_specific_enough'] = 'Your search query didn\'t return any matches.'; +$txt['error_no_messages_in_time_frame'] = 'No messages found in selected time frame.'; +$txt['error_no_labels_selected'] = 'No labels were selected!'; +$txt['error_no_search_daemon'] = 'Unable to access the search daemon'; + +$txt['profile_errors_occurred'] = 'The following errors occurred when trying to save your profile'; +$txt['profile_error_bad_offset'] = 'The time offset is out of range'; +$txt['profile_error_no_name'] = 'The name field was left blank'; +$txt['profile_error_name_taken'] = 'The selected username/display name has already been taken'; +$txt['profile_error_name_too_long'] = 'The selected name is too long. It should be no greater than 60 characters long'; +$txt['profile_error_no_email'] = 'The email field was left blank'; +$txt['profile_error_bad_email'] = 'You have not entered a valid email address'; +$txt['profile_error_email_taken'] = 'Another user is already registered with that email address'; +$txt['profile_error_no_password'] = 'You did not enter your password'; +$txt['profile_error_bad_new_password'] = 'The new passwords you entered do not match'; +$txt['profile_error_bad_password'] = 'The password you entered was not correct'; +$txt['profile_error_bad_avatar'] = 'The avatar you have selected is either too large or not an avatar'; +$txt['profile_error_password_short'] = 'Your password must be at least ' . (empty($modSettings['password_strength']) ? 4 : 8) . ' characters long.'; +$txt['profile_error_password_restricted_words'] = 'Your password must not contain your username, email address or other commonly used words.'; +$txt['profile_error_password_chars'] = 'Your password must contain a mix of upper and lower case letters, as well as digits.'; +$txt['profile_error_already_requested_group'] = 'You already have an outstanding request for this group!'; +$txt['profile_error_openid_in_use'] = 'Another user is already using that OpenID authentication URL'; + +$txt['mysql_error_space'] = ' - check database storage space or contact the server administrator.'; + +$txt['icon_not_found'] = 'The icon image could not be found in the default theme - please ensure the image has been uploaded and try again.'; +$txt['icon_after_itself'] = 'The icon cannot be positioned after itself!'; +$txt['icon_name_too_long'] = 'Icon filenames cannot be more than 16 characters long'; + +$txt['name_censored'] = 'Sorry, the name you tried to use, %1$s, contains words which have been censored. Please try another name.'; + +$txt['poll_already_exists'] = 'A topic can only have one poll associated with it!'; +$txt['poll_not_found'] = 'There is no poll associated with this topic!'; + +$txt['error_while_adding_poll'] = 'The following error or errors occurred while adding this poll'; +$txt['error_while_editing_poll'] = 'The following error or errors occurred while editing this poll'; + +$txt['loadavg_search_disabled'] = 'Due to high stress on the server, the search function has been automatically and temporarily disabled. Please try again in a short while.'; +$txt['loadavg_generic_disabled'] = 'Sorry, because of high stress on the server, this feature is currently unavailable.'; +$txt['loadavg_allunread_disabled'] = 'The server\'s resources are temporarily under too high a demand to find all the topics you have not read.'; +$txt['loadavg_unreadreplies_disabled'] = 'The server is currently under high stress. Please try again shortly.'; +$txt['loadavg_show_posts_disabled'] = 'Please try again later. This member\'s posts are not currently available due to high load on the server.'; +$txt['loadavg_unread_disabled'] = 'The server\'s resources are temporarily under too high a demand to list out the topics you have not read.'; + +$txt['cannot_edit_permissions_inherited'] = 'You cannot edit inherited permissions directly, you must either edit the parent group or edit the membergroup inheritance.'; + +$txt['mc_no_modreport_specified'] = 'You need to specify which report you wish to view.'; +$txt['mc_no_modreport_found'] = 'The specified report either doesn\'t exist or is off limits to you'; + +$txt['st_cannot_retrieve_file'] = 'Could not retrieve the file %1$s.'; +$txt['admin_file_not_found'] = 'Could not load the requested file: %1$s.'; + +$txt['themes_none_selectable'] = 'At least one theme must be selectable.'; +$txt['themes_default_selectable'] = 'The overall forum default theme must be a selectable theme.'; +$txt['ignoreboards_disallowed'] = 'The option to ignore boards has not been enabled.'; + +$txt['mboards_delete_error'] = 'No category selected!'; +$txt['mboards_delete_board_error'] = 'No board selected!'; + +$txt['mboards_parent_own_child_error'] = 'Unable to make a parent its own child!'; +$txt['mboards_board_own_child_error'] = 'Unable to make a board its own child!'; + +$txt['smileys_upload_error_notwritable'] = 'The following smiley directories are not writable: %1$s'; +$txt['smileys_upload_error_types'] = 'Smiley images can only have the following extensions: %1$s.'; + +$txt['change_email_success'] = 'Your email address has been changed, and a new activation email has been sent to it.'; +$txt['resend_email_success'] = 'A new activation email has successfully been sent.'; + +$txt['custom_option_need_name'] = 'The profile option must have a name!'; +$txt['custom_option_not_unique'] = 'Field name is not unique!'; + +$txt['warning_no_reason'] = 'You must enter a reason for altering the warning state of a member'; +$txt['warning_notify_blank'] = 'You selected to notify the user but did not fill in the subject/message fields'; + +$txt['cannot_connect_doc_site'] = 'Could not connect to the Simple Machines Online Manual. Please check that your server configuration allows external internet connections and try again later.'; + +$txt['movetopic_no_reason'] = 'You must enter a reason for moving the topic, or uncheck the option to \'post a redirection topic\'.'; + +// OpenID error strings +$txt['openid_server_bad_response'] = 'The requested identifier did not return the proper information.'; +$txt['openid_return_no_mode'] = 'The identity provider did not respond with the OpenID mode.'; +$txt['openid_not_resolved'] = 'The identity provider did not approve your request.'; +$txt['openid_no_assoc'] = 'Could not find the requested association with the identity provider.'; +$txt['openid_sig_invalid'] = 'The signature from the identity provider is invalid.'; +$txt['openid_load_data'] = 'Could not load the data from your login request. Please try again.'; +$txt['openid_not_verified'] = 'The OpenID address given has not been verified yet. Please log in to verify.'; + +$txt['error_custom_field_too_long'] = 'The "%1$s" field cannot be greater than %2$d characters in length.'; +$txt['error_custom_field_invalid_email'] = 'The "%1$s" field must be a valid email address.'; +$txt['error_custom_field_not_number'] = 'The "%1$s" field must be numeric.'; +$txt['error_custom_field_inproper_format'] = 'The "%1$s" field is an invalid format.'; +$txt['error_custom_field_empty'] = 'The "%1$s" field cannot be left blank.'; + +$txt['email_no_template'] = 'The email template "%1$s" could not be found.'; + +$txt['search_api_missing'] = 'The search API could not be found! Please contact the admin to check they have uploaded the correct files.'; +$txt['search_api_not_compatible'] = 'The selected search API the forum is using is out of date - falling back to standard search. Please check file %1$s.'; + +// Restore topic/posts +$txt['cannot_restore_first_post'] = 'You cannot restore the first post in a topic.'; +$txt['parent_topic_missing'] = 'The parent topic of the post you are trying to restore has been deleted.'; +$txt['restored_disabled'] = 'The restoration of topics has been disabled.'; +$txt['restore_not_found'] = 'The following messages could not be restored; the original topic may have been removed:
      %1$s
    You will need to move these manually.'; + +$txt['error_invalid_dir'] = 'The directory you entered is invalid.'; + +$txt['error_sqlite_optimizing'] = 'Sqlite is optimizing the database, the forum can not be accessed until it has finished. Please try refreshing this page momentarily.'; +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Help.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Help.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,604 @@ +Edit Boards
    + In this menu you can create/reorder/remove boards, and the categories + above them. For example, if you had a wide-ranging + site that offered information on "Sports" and "Cars" and "Music", these + would be the top-level Categories you\'d create. Under each of these + categories you\'d likely want to create hierarchical "sub-categories", + or "Boards" for topics within each. It\'s a simple hierarchy, with this structure:
    +
      +
    • + Sports +  - A "category" +
    • +
        +
      • + Baseball +  - A board under the category of "Sports" +
      • +
          +
        • + Stats +  - A child board under the board of "Baseball" +
        • +
        +
      • Football +  - A board under the category of "Sports"
      • +
      +
    + Categories allow you to break down the board into broad topics ("Cars, + Sports"), and the "Boards" under them are the actual topics under which + members can post. A user interested in Pintos + would post a message under "Cars->Pinto". Categories allow people to + quickly find what their interests are: Instead of a "Store" you have + "Hardware" and "Clothing" stores you can go to. This simplifies your + search for "pipe joint compound" because you can go to the Hardware + Store "category" instead of the Clothing Store (where you\'re unlikely + to find pipe joint compound).
    + As noted above, a Board is a key topic underneath a broad category. + If you want to discuss "Pintos" you\'d go to the "Auto" category and + jump into the "Pinto" board to post your thoughts in that board.
    + Administrative functions for this menu item are to create new boards + under each category, to reorder them (put "Pinto" behind "Chevy"), or + to delete the board entirely.'; + +$helptxt['edit_news'] = ' +
      +
    • + News
      + This section allows you to set the text for news items displayed on the Board Index page. + Add any item you want (e.g., "Don\'t miss the conference this Tuesday"). Each news item is displayed randomly and should be placed in a separate box. +
    • +
    • + Newsletters
      + This section allows you to send out newsletters to the members of the forum via personal message or email. First select the groups that you want to receive the newsletter, and those you don\'t want to receive the newsletter. If you wish, you can add additional members and email addresses that will receive the newsletter. Finally, input the message you want to send and select whether you want it to be sent to members as a personal message or as an email. +
    • +
    • + Settings
      + This section contains a few settings that relate to news and newsletters, including selecting what groups can edit forum news or send newsletters. There is also an setting to configure whether you want news feeds enabled on the forum, as well as a setting to configure the length (how many characters are displayed) for each news post from a news feed. +
    • +
    '; + +$helptxt['view_members'] = ' +
      +
    • + View all Members
      + View all members in the board. You are presented with a hyperlinked list of member names. You may click + on any of the names to find details of the members (homepage, age, etc.), and as Administrator + you are able to modify these parameters. You have complete control over members, including the + ability to delete them from the forum.

      +
    • +
    • + Awaiting Approval
      + This section is only shown if you have enabled admin approval of all new registrations. Anyone who registers to join your + forum will only become a full member once they have been approved by an admin. The section lists all those members who + are still awaiting approval, along with their email and IP address. You can choose to either accept or reject (delete) + any member on the list by checking the box next to the member and choosing the action from the drop-down box at the bottom + of the screen. When rejecting a member you can choose to delete the member either with or without notifying them of your decision.

      +
    • +
    • + Awaiting Activation
      + This section will only be visible if you have activation of member accounts enabled on the forum. This section will list all + members who have still not activated their new accounts. From this screen you can choose to either accept, reject or remind + members with outstanding registrations. As above you can also choose to email the member to inform them of the + action you have taken.

      +
    • +
    '; + +$helptxt['ban_members'] = 'Ban Members
    + SMF provides the ability to "ban" users, to prevent people who have violated the trust of the board + by spamming, trolling, etc. This allows you to those users who are detrimental to your forum. As an admin, + when you view messages, you can see each user\'s IP address used to post at that time. In the ban list, + you simply type that IP address in, save, and they can no longer post from that location.
    You can also + ban people through their email address.'; + +$helptxt['featuresettings'] = 'Features and Options
    + There are several features in this section that can be changed to your preference.'; + +$helptxt['securitysettings'] = 'Security and Moderation
    + This section contains settings relating to the security and moderation of your forum.'; + +$helptxt['modsettings'] = 'Modification Settings
    + This section should contain any settings added by modifications installed on your forum.'; + +$helptxt['number_format'] = 'Number Format
    + You can use this setting to format the way in which numbers on your forum will be displayed to the user. The format of this setting is:
    +
    1,234.00

    + Where \',\' is the character used to split up groups of thousands, \'.\' is the character used as the decimal point and the number of zeros dictate the accuracy of rounding.'; + +$helptxt['time_format'] = 'Time Format
    + You have the power to adjust how the time and date look for yourself. There are a lot of little letters, but it\'s quite simple. + The conventions follow PHP\'s strftime function and are described as below (more details can be found at php.net).
    +
    + The following characters are recognized in the format string:
    + +   %a - abbreviated weekday name
    +   %A - full weekday name
    +   %b - abbreviated month name
    +   %B - full month name
    +   %d - day of the month (01 to 31)
    +   %D* - same as %m/%d/%y
    +   %e* - day of the month (1 to 31)
    +   %H - hour using a 24-hour clock (range 00 to 23)
    +   %I - hour using a 12-hour clock (range 01 to 12)
    +   %m - month as a number (01 to 12)
    +   %M - minute as a number
    +   %p - either "am" or "pm" according to the given time
    +   %R* - time in 24 hour notation
    +   %S - second as a decimal number
    +   %T* - current time, equal to %H:%M:%S
    +   %y - 2 digit year (00 to 99)
    +   %Y - 4 digit year
    +   %% - a literal \'%\' character
    +
    + * Does not work on Windows-based servers.
    '; + +$helptxt['live_news'] = 'Live announcements
    + This box shows recently updated announcements from www.simplemachines.org. + You should check here every now and then for updates, new releases, and important information from Simple Machines.'; + +$helptxt['registrations'] = 'Registration Management
    + This section contains all the functions that could be necessary to manage new registrations on the forum. It contains up to four + sections which are visible depending on your forum settings. These are:

    +
      +
    • + Register new member
      + From this screen you can choose to register accounts for new members on their behalf. This can be useful in forums where registration is closed + to new members, or in cases where the admin wishes to create a test account. If the option to require activation of the account + is selected the member will be emailed a activation link which must be clicked before they can use the account. Similarly you can + select to email the users new password to the stated email address.

      +
    • +
    • + Edit Registration Agreement
      + This allows you to set the text for the registration agreement displayed when members sign up for your forum. + You can add or remove anything from the default registration agreement, which is included in SMF.

      +
    • +
    • + Set Reserved Names
      + Using this interface you can specify words or names which may not be used by your users.

      +
    • +
    • + Settings
      + This section will only be visible if you have permission to administrate the forum. From this screen you can decide on the registration method + is use on your forum, as well as other registration related settings. +
    • +
    '; + +$helptxt['modlog'] = 'Moderation Log
    + This section allows members of the moderation team to track all the moderation actions that the forum moderators have performed. To ensure that + moderators cannot remove references to the actions they have performed, entries may not be deleted until 24 hours after the action was taken.'; +$helptxt['adminlog'] = 'Administration Log
    + This section allows members of the admin team to track some of the administrative actions that have occurred on the forum. To ensure that + admins cannot remove references to the actions they have performed, entries may not be deleted until 24 hours after the action was taken.'; +$helptxt['warning_enable'] = 'User Warning System
    + This feature enables members of the admin and moderation team to issue warnings to members - and to use a members warning level to determine the + actions available to them on the forum. Upon enabling this feature a permission will be available within the permissions section to define + which groups may assign warnings to members. Warning levels can be adjusted from a members profile. The following additional options are available: +
      +
    • + Warning Level for Member Watch
      + This setting defines the percentage warning level a member must reach to automatically assign a "watch" to the member. + Any member who is being "watched" will appear in the relevant area of the moderation center. +
    • +
    • + Warning Level for Post Moderation
      + Any member passing the value of this setting will find all their posts require moderator approval before they appear to the forum + community. This will override any local board permissions which may exist related to post moderation. +
    • +
    • + Warning Level for Member Muting
      + If this warning level is passed by a member they will find themselves under a post ban. The member will lose all posting rights. +
    • +
    • + Maximum Member Warning Point per Day
      + This setting limits the amount of points a moderator may add/remove to any particular member in a twenty four hour period. This will + can be used to limit what a moderator can do in a small period of time. This setting can be disabled by setting to a value of zero. Note that + any member with administrator permissions are not affected by this value. +
    • +
    '; +$helptxt['error_log'] = 'Error Log
    + The error log tracks logs every serious error encountered by users using your forum. It lists all of these errors by date which can be sorted + by clicking the black arrow next to each date. Additionally you can filter the errors by clicking the picture next to each error statistic. This + allows you to filter, for example, by member. When a filter is active the only results that will be displayed will be those that match that filter.'; +$helptxt['theme_settings'] = 'Theme Settings
    + The settings screen allows you to change settings specific to a theme. These settings include options such as the themes directory and URL information but + also options that affect the layout of a theme on your forum. Most themes will have a variety of user configurable options, allowing you to adapt a theme + to suit your individual forum needs.'; +$helptxt['smileys'] = 'Smiley Center
    + Here you can add and remove smileys, and smiley sets. Note importantly that if a smiley is in one set, it\'s in all sets - otherwise, it might + get confusing for your users using different sets.

    + + You are also able to edit message icons from here, if you have them enabled on the settings page.'; +$helptxt['calendar'] = 'Manage Calendar
    + Here you can modify the current calendar settings as well as add and remove holidays that appear on the calendar.'; + +$helptxt['serversettings'] = 'Server Settings
    + Here you can perform the core configuration for your forum. This section includes the database and url settings, as well as other + important configuration items such as mail settings and caching. Think carefully whenever editing these settings as an error may + render the forum inaccessible'; +$helptxt['manage_files'] = ' +
      +
    • + Browse Files
      + Browse through all the attachments, avatars and thumbnails stored by SMF.

      +
    • + Attachment Settings
      + Configure where attachments are stored and set restrictions on the types of attachments.

      +
    • + Avatar Settings
      + Configure where avatars are stored and manage resizing of avatars.

      +
    • + File Maintenance
      + Check and repair any error in the attachment directory and delete selected attachments.

      +
    • +
    '; + +$helptxt['topicSummaryPosts'] = 'This allows you to set the number of previous posts shown in the topic summary at the reply screen.'; +$helptxt['enableAllMessages'] = 'Set this to the maximum number of posts a topic can have to show the all link. Setting this lower than "Maximum messages to display in a topic page" will simply mean it never gets shown, and setting it too high could slow down your forum.'; +$helptxt['enableStickyTopics'] = 'Stickies are topics that remain on top of the topic list. They are mostly used for important + messages. Although you can change this with permissions, by default only moderators and administrators can make topics sticky.'; +$helptxt['allow_guestAccess'] = 'Unchecking this box will stop guests from doing anything but very basic actions - login, register, password reminder, etc. - on your forum. This is not the same as disallowing guest access to boards.'; +$helptxt['userLanguage'] = 'Turning this option on will allow users to select which language file they use. It will not affect the + default selection.'; +$helptxt['trackStats'] = 'Stats:
    This will allow users to see the latest posts and the most popular topics on your forum. + It will also show several statistics, like the most members online, new members and new topics.
    + Page views:
    Adds another column to the stats page with the number of pageviews on your forum.'; +$helptxt['titlesEnable'] = 'Switching Custom Titles on will allow members with the relevant permission to create a special title for themselves. + This will be shown underneath the name.
    For example:
    Jeff
    Cool Guy'; +$helptxt['topbottomEnable'] = 'This will add go up and go down buttons, so that member can go to the top and bottom of a page + without scrolling.'; +$helptxt['onlineEnable'] = 'This will show an image to indicate whether the member is online or offline'; +$helptxt['todayMod'] = 'This will show "Today" or "Yesterday" instead of the date.

    + Examples:

    +
    +
    Disabled
    +
    October 3, 2009 at 12:59:18 am
    +
    Only Today
    +
    Today at 12:59:18 am
    +
    Today & Yesterday
    +
    Yesterday at 09:36:55 pm
    + '; +$helptxt['disableCustomPerPage'] = 'Check this option to stop users from customizing the amount of messages and topics to display per page on the Message Index and Topic Display page respectively.'; +$helptxt['enablePreviousNext'] = 'This will show a link to the next and previous topic.'; +$helptxt['pollMode'] = 'This selects whether polls are enabled or not. If polls are disabled, any existing polls will be hidden + from the topic listing. You can choose to continue to show the regular topic without their polls by selecting + "Show Existing Polls as Topics".

    To choose who can post polls, view polls, and similar, you + can allow and disallow those permissions. Remember this if polls are not working.'; +$helptxt['enableVBStyleLogin'] = 'This will show a more compact login on every page of the forum for guests.'; +$helptxt['enableCompressedOutput'] = 'This option will compress output to lower bandwidth consumption, but it requires + zlib to be installed.'; +$helptxt['disableTemplateEval'] = 'By default, templates are evaluated instead of just included. This helps with showing more useful debug information in case a template contains an error.

    + On large forums however, this customised inclusion process may be significantly slower. Therefore, advanced users may wish to disable it.'; +$helptxt['databaseSession_enable'] = 'This option makes use of the database for session storage - it is best for load balanced servers, but helps with all timeout issues and can make the forum faster.'; +$helptxt['databaseSession_loose'] = 'Turning this on will decrease the bandwidth your forum uses, and make it so clicking back will not reload the page - the downside is that the (new) icons won\'t update, among other things. (unless you click to that page instead of going back to it.)'; +$helptxt['databaseSession_lifetime'] = 'This is the number of seconds for sessions to last after they haven\'t been accessed. If a session is not accessed for too long, it is said to have "timed out". Anything higher than 2400 is recommended.'; +$helptxt['enableErrorLogging'] = 'This will log any errors, like a failed login, so you can see what went wrong.'; +$helptxt['enableErrorQueryLogging'] = 'This will include the full query sent to the database in the error log. Requires error logging to be turned on.

    Note: This will affect the ability to filter the error log by the error message.'; +$helptxt['allow_disableAnnounce'] = 'This will allow users to opt out of notification of topics you announce by checking the "announce topic" checkbox when posting.'; +$helptxt['disallow_sendBody'] = 'This option removes the option to receive the text of replies and posts in notification emails.

    Often, members will reply to the notification email, which in most cases means the webmaster receives the reply.'; +$helptxt['compactTopicPagesEnable'] = 'This will just show a selection of the number of pages.
    Example: + "3" to display: 1 ... 4 [5] 6 ... 9
    + "5" to display: 1 ... 3 4 [5] 6 7 ... 9'; +$helptxt['timeLoadPageEnable'] = 'This will show the time in seconds SMF took to create that page at the bottom of the board.'; +$helptxt['removeNestedQuotes'] = 'This will strip nested quotes from a post when citing the post in question via a quote link.'; +$helptxt['simpleSearch'] = 'This will show a simple search form and a link to a more advanced form.'; +$helptxt['max_image_width'] = 'This allows you to set a maximum size for posted pictures. Pictures smaller than the maximum will not be affected.'; +$helptxt['mail_type'] = 'This setting allows you to choose either PHP\'s default settings, or to override those settings with SMTP. PHP doesn\'t support using authentication with SMTP (which many hosts require, now) so if you want that you should select SMTP. Please note that SMTP can be slower, and some servers will not take usernames and passwords.

    You don\'t need to fill in the SMTP settings if this is set to PHP\'s default.'; +$helptxt['attachment_manager_settings'] = 'Attachments are files that members can upload, and attach to a post.

    + Check attachment extension:
    Do you want to check the extension of the files?
    + Allowed attachment extensions:
    You can set the allowed extensions of attached files.
    + Attachments directory:
    The path to your attachment folder
    (example: /home/sites/yoursite/www/forum/attachments)
    + Max attachment folder space (in KB):
    Select how large the attachment folder can be, including all files within it.
    + Max attachment size per post (in KB):
    Select the maximum filesize of all attachments made per post. If this is lower than the per-attachment limit, this will be the limit.
    + Max size per attachment (in KB):
    Select the maximum filesize of each separate attachment.
    + Max number of attachments per post:
    Select the number of attachments a person can make, per post.
    + Display attachment as picture in posts:
    If the uploaded file is a picture, this will show it underneath the post.
    + Resize images when showing under posts:
    If the above option is selected, this will save a separate (smaller) attachment for the thumbnail to decrease bandwidth.
    + Maximum width and height of thumbnails:
    Only used with the "Resize images when showing under posts" option, the maximum width and height to resize attachments down from. They will be resized proportionally.'; +$helptxt['attachment_image_paranoid'] = 'Selecting this option will enable very strict security checks on image attachments. Warning! These extensive checks can fail on valid images too. It is strongly recommended to only use this option together with image re-encoding, in order to have SMF try to resample the images which fail the security checks: if successful, they will be sanitized and uploaded. Otherwise, if image re-encoding is not enabled, all attachments failing checks will be rejected.'; +$helptxt['attachment_image_reencode'] = 'Selecting this option will enable trying to re-encode the uploaded image attachments. Image re-encoding offers better security. Note however that image re-encoding also renders all animated images static.
    This feature is only possible if the GD module is installed on your server.'; +$helptxt['avatar_paranoid'] = 'Selecting this option will enable very strict security checks on avatars. Warning! These extensive checks can fail on valid images too. It is strongly recommended to only use this option together with avatars re-encoding, in order to have SMF try to resample the images which fail the security checks: if successful, they will be sanitized and uploaded. Otherwise, if re-encoding of avatars is not enabled, all avatars failing checks will be rejected.'; +$helptxt['avatar_reencode'] = 'Selecting this option will enable trying to re-encode the uploaded avatars. Image re-encoding offers better security. Note however that image re-encoding also renders all animated images static.
    This feature is only possible if the GD module is installed on your server.'; +$helptxt['karmaMode'] = 'Karma is a feature that shows the popularity of a member. Members, if allowed, can + \'applaud\' or \'smite\' other members, which is how their popularity is calculated. You can change the + number of posts needed to have a "karma", the time between smites or applauds, and if administrators + have to wait this time as well.

    Whether or not groups of members can smite others is controlled by + a permission. If you have trouble getting this feature to work for everyone, double check your permissions.'; +$helptxt['cal_enabled'] = 'The calendar can be used for showing birthdays, or for showing important moments happening in your community.

    + Show days as link to \'Post Event\':
    This will allow members to post events for that day, when they click on that date
    + Max days in advance on board index:
    If this is set to 7, the next week\'s worth of events will be shown.
    + Show holidays on board index:
    Show today\'s holidays in a calendar bar on the board index.
    + Show birthdays on board index:
    Show today\'s birthdays in a calendar bar on the board index.
    + Show events on board index:
    Show today\'s events in a calendar bar on the board index.
    + Default Board to Post In:
    What\'s the default board to post events in?
    + Allow events not linked to posts:
    Allow members to post events without requiring it to be linked with a post in a board.
    + Minimum year:
    Select the "first" year on the calendar list.
    + Maximum year:
    Select the "last" year on the calendar list
    + Allow events to span multiple days:
    Check to allow events to span multiple days.
    + Max number of days an event can span:
    Select the maximum days that an event can span.

    + Remember that usage of the calendar (posting events, viewing events, etc.) is controlled by permissions set on the permissions screen.'; +$helptxt['localCookies'] = 'SMF uses cookies to store login information on the client computer. + Cookies can be stored globally (myserver.com) or locally (myserver.com/path/to/forum).
    + Check this option if you\'re experiencing problems with users getting logged out automatically.
    + Globally stored cookies are less secure when used on a shared webserver (like Tripod).
    + Local cookies don\'t work outside the forum folder so, if your forum is stored at www.myserver.com/forum, pages like www.myserver.com/index.php cannot access the account information. + Especially when using SSI.php, global cookies are recommended.'; +$helptxt['enableBBC'] = 'Selecting this option will allow your members to use Bulletin Board Code (BBC) throughout the forum, allowing users to format their posts with images, type formatting and more.'; +$helptxt['time_offset'] = 'Not all forum administrators want their forum to use the same time zone as the server upon which it is hosted. Use this option to specify a time difference (in hours) from which the forum should operate from the server time. Negative and decimal values are permitted.'; +$helptxt['default_timezone'] = 'The server timezone tells PHP where your server is located. You should ensure this is set correctly, preferably to the country/city in which the city is located. You can find out more information on the PHP Site.'; +$helptxt['spamWaitTime'] = 'Here you can select the amount of time that must pass between postings. This can be used to stop people from "spamming" your forum by limiting how often they can post.'; + +$helptxt['enablePostHTML'] = 'This will allow the posting of some basic HTML tags: +
      +
    • <b>, <u>, <i>, <s>, <em>, <ins>, <del>
    • +
    • <a href="">
    • +
    • <img src="" alt="" />
    • +
    • <br />, <hr />
    • +
    • <pre>, <blockquote>
    • +
    '; + +$helptxt['themes'] = 'Here you can select whether the default theme can be chosen, what theme guests will use, + as well as other options. Click on a theme to the right to change the settings for it.'; +$helptxt['theme_install'] = 'This allows you to install new themes. You can do this from an already created directory, by uploading an archive for the theme, or by copying the default theme.

    Note that the archive or directory must have a theme_info.xml definition file.'; +$helptxt['enableEmbeddedFlash'] = 'This option will allow your users to use Flash directly inside their posts, + just like images. This could pose a security risk, although few have successfully exploited it. + USE AT YOUR OWN RISK!'; +// !!! Add more information about how to use them here. +$helptxt['xmlnews_enable'] = 'Allows people to link to Recent news + and similar data. It is also recommended that you limit the size of recent posts/news because, when rss data + is displayed in some clients, like Trillian, it is expected to be truncated.'; +$helptxt['hotTopicPosts'] = 'Change the number of posts for a topic to reach the state of a "hot" or + "very hot" topic.'; +$helptxt['globalCookies'] = 'Makes log in cookies available across subdomains. For example, if...
    + Your site is at http://www.simplemachines.org/,
    + And your forum is at http://forum.simplemachines.org/,
    + Using this option will allow you to access the forum\'s cookie on your site. Do not enable this if there are other subdomains (like hacker.simplemachines.org) not controlled by you.'; +$helptxt['secureCookies'] = 'Enabling this option will force the cookies created for users on your forum to be marked as secure. Only enable this option if you are using HTTPS throughout your site as it will break cookie handling otherwise!'; +$helptxt['securityDisable'] = 'This disables the additional password check for the administration section. This is not recommended!'; +$helptxt['securityDisable_why'] = 'This is your current password. (the same one you use to login.)

    Having to type this helps ensure that you want to do whatever administration you are doing, and that it is you doing it.'; +$helptxt['emailmembers'] = 'In this message you can use a few "variables". These are:
    + {$board_url} - The URL to your forum.
    + {$current_time} - The current time.
    + {$member.email} - The current member\'s email.
    + {$member.link} - The current member\'s link.
    + {$member.id} - The current member\'s id.
    + {$member.name} - The current member\'s name. (for personalization.)
    + {$latest_member.link} - The most recently registered member\'s link.
    + {$latest_member.id} - The most recently registered member\'s id.
    + {$latest_member.name} - The most recently registered member\'s name.'; +$helptxt['attachmentEncryptFilenames'] = 'Encrypting attachment filenames allows you to have more than one attachment of the + same name, to safely use .php files for attachments, and heightens security. It, however, could make it more + difficult to rebuild your database if something drastic happened.'; + +$helptxt['failed_login_threshold'] = 'Set the number of failed login attempts before directing the user to the password reminder screen.'; +$helptxt['oldTopicDays'] = 'If this option is enabled a warning will be displayed to the user when attempting to reply to a topic which has not had any new replies for the amount of time, in days, specified by this setting. Set this setting to 0 to disable the feature.'; +$helptxt['edit_wait_time'] = 'Number of seconds allowed for a post to be edited before logging the last edit date.'; +$helptxt['edit_disable_time'] = 'Number of minutes allowed to pass before a user can no longer edit a post they have made. Set to 0 disable.

    Note: This will not affect any user who has permission to edit other people\'s posts.'; +$helptxt['posts_require_captcha'] = 'This setting will force users to pass anti-spam bot verification each time they make a post to a board. Only users with a post count below the number set will need to enter the code - this should help combat automated spamming scripts.'; +$helptxt['enableSpellChecking'] = 'Enable spell checking. You MUST have the pspell library installed on your server and your PHP configuration set up to use the pspell library. Your server ' . (function_exists('pspell_new') ? 'DOES' : 'DOES NOT') . ' appear to have this set up.'; +$helptxt['disable_wysiwyg'] = 'This setting disallows all users from using the WYSIWYG ("What You See Is What You Get") editor on the post page.'; +$helptxt['lastActive'] = 'Set the number of minutes to show people are active in X number of minutes on the board index. Default is 15 minutes.'; + +$helptxt['customoptions'] = 'This section defines the options that a user may choose from a drop down list. There are a few key points to note in this section: +
      +
    • Default Option: Whichever option box has the "radio button" next to it selected will be the default selection for the user when they enter their profile.
    • +
    • Removing Options: To remove an option simply empty the text box for that option - all users with that selected will have their option cleared.
    • +
    • Reordering Options: You can reorder the options by moving text around between the boxes. However - an important note - you must make sure you do not change the text when reordering options as otherwise user data will be lost.
    • +
    '; + +$helptxt['autoOptDatabase'] = 'This option optimizes the database every so many days. Set it to 1 to make a daily optimization. You can also specify a maximum number of online users, so that you won\'t overload your server or inconvenience too many users.'; +$helptxt['autoFixDatabase'] = 'This will automatically fix broken tables and resume like nothing happened. This can be useful, because the only way to fix it is to REPAIR the table, and this way your forum won\'t be down until you notice. It does email you when this happens.'; + +$helptxt['enableParticipation'] = 'This shows a little icon on the topics the user has posted in.'; + +$helptxt['db_persist'] = 'Keeps the connection active to increase performance. If you aren\'t on a dedicated server, this may cause you problems with your host.'; +$helptxt['ssi_db_user'] = 'Optional setting to use a different database user and password when you are using SSI.php.'; + +$helptxt['queryless_urls'] = 'This changes the format of URLs a little so search engines will like them better. They will look like index.php/topic,1.0.html.

    This feature will ' . (isset($_SERVER['SERVER_SOFTWARE']) && (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false) ? '' : 'not') . ' work on your server.'; +$helptxt['countChildPosts'] = 'Checking this option will mean that posts and topics in a board\'s child board will count toward its totals on the index page.

    This will make things notably slower, but means that a parent with no posts in it won\'t show \'0\'.'; +$helptxt['fixLongWords'] = 'This option breaks words longer than a certain length into pieces so they don\'t disturb the forum\'s layout. (as much...) This option should not be set to a value under 40. This option will not work with forums using UTF-8 and PHP less than 4.4.0. This ' . (empty($GLOBALS['context']['utf8']) || version_compare(PHP_VERSION, '4.4.0') != -1 ? 'WILL' : 'WILL NOT') . ' work on your server'; +$helptxt['allow_ignore_boards'] = 'Checking this option will allow users to select boards they wish to ignore.'; + +$helptxt['who_enabled'] = 'This option allows you to turn on or off the ability for users to see who is browsing the forum and what they are doing.'; + +$helptxt['recycle_enable'] = '"Recycles" deleted topics and posts to the specified board.'; + +$helptxt['enableReportPM'] = 'This option allows your users to report personal messages they receive to the administration team. This may be useful in helping to track down any abuse of the personal messaging system.'; +$helptxt['max_pm_recipients'] = 'This option allows you to set the maximum amount of recipients allowed in a single personal message sent by a forum member. This may be used to help stop spam abuse of the PM system. Note that users with permission to send newsletters are exempt from this restriction. Set to zero for no limit.'; +$helptxt['pm_posts_verification'] = 'This setting will force users to enter a code shown on a verification image each time they are sending a personal message. Only users with a post count below the number set will need to enter the code - this should help combat automated spamming scripts.'; +$helptxt['pm_posts_per_hour'] = 'This will limit the number of personal messages which may be sent by a user in a one hour period. This does not affect admins or moderators.'; + +$helptxt['default_personal_text'] = 'Sets the default text a new user will have as their "personal text."'; + +$helptxt['modlog_enabled'] = 'Logs all moderation actions.'; + +$helptxt['guest_hideContacts'] = 'If selected this option will hide the email addresses and messenger contact details + of all members from any guests on your forum'; + +$helptxt['registration_method'] = 'This option determines what method of registration is used for people wishing to join your forum. You can select from:

    +
      +
    • + Registration Disabled
      + Disables the registration process, which means that no new members can register to join your forum.
      +
    • + Immediate Registration
      + New members can login and post immediately after registering on your forum.
      +
    • + Email Activation
      + When this option is enabled any members registering with the forum will have an activation link emailed to them which they must click before they can become full members.
      +
    • + Admin Approval
      + This option will make it so all new members registering to your forum will need to be approved by the admin before they become members. +
    • +
    '; +$helptxt['register_openid'] = 'Authenticate with OpenID
    + OpenID is a means of using one username across different websites, to simplify the online experience. To use OpenID you first need to create an OpenID account - a list of providers can be found on the OpenID Official Site

    + Once you have an OpenID account simply enter your unique identification URL into the OpenID input box and submit. You will then be taken to your providers site to verify your identity before being passed back to this site.

    + On your first visit to this site you will be asked to confirm a couple of details before you will be recognized, after which you can login to this site and change your profile settings using just your OpenID.

    + For more information please visit the OpenID Official Site'; + +$helptxt['send_validation_onChange'] = 'When this option is checked all members who change their email address in their profile will have to reactivate their account from an email sent to that address'; +$helptxt['send_welcomeEmail'] = 'When this option is enabled all new members will be sent an email welcoming them to your community'; +$helptxt['password_strength'] = 'This setting determines the strength required for passwords selected by your forum users. The stronger the password, the harder it should be to compromise member\'s accounts. + Its possible options are: +
      +
    • Low: The password must be at least four characters long.
    • +
    • Medium: The password must be at least eight characters long, and can not be part of a users name or email address.
    • +
    • High: As for medium, except the password must also contain a mixture of upper and lower case letters, and at least one number.
    • +
    '; + +$helptxt['coppaAge'] = 'The value specified in this box will determine the minimum age that new members must be to be granted immediate access to the forums. + On registration they will be prompted to confirm whether they are over this age, and if not will either have their application rejected or suspended awaiting parental approval - dependant on the type of restriction chosen. + If a value of 0 is chosen for this setting then all other age restriction settings shall be ignored.'; +$helptxt['coppaType'] = 'If age restrictions are enabled, then this setting will define that happens when a user below the minimum age attempts to register with your forum. There are two possible choices: +
      +
    • + Reject Their Registration:
      + Any new member below the minimum age will have their registration rejected immediately.
      +
    • + Require Parent/Guardian Approval
      + Any new member who attempts to register and is below the minimum permitted age will have their account marked as awaiting approval, and will be presented with a form upon which their parents must give permission to become a member of the forums. + They will also be presented with the forum contact details entered on the settings page, so they can send the form to the administrator by mail or fax. +
    • +
    '; +$helptxt['coppaPost'] = 'The contact boxes are required so that forms granting permission for underage registration can be sent to the forum administrator. These details will be shown to all new minors, and are required for parent/guardian approval. At the very least a postal address or fax number must be provided.'; + +$helptxt['allow_hideOnline'] = 'With this option enabled all members will be able to hide their online status from other users (except administrators). If disabled only users who can moderate the forum can hide their presence. Note that disabling this option will not change any existing member\'s status - it just stops them from hiding themselves in the future.'; +$helptxt['make_email_viewable'] = 'If this option is enabled instead of users email addresses being hidden to normal members and guests they will be publicly viewable on the forum. Enabling this will put your users at greater risk of being victims of spam as a result of email harvesters visiting your forum. Note this setting does not override the user setting for hiding their email address from users. Enabling this setting is not recommended.'; +$helptxt['meta_keywords'] = 'These keywords are sent in the output of every page to indicate to search engines (etc) the key content of your site. They should be a comma separated list of words, and should not use HTML.'; + +$helptxt['latest_support'] = 'This panel shows you some of the most common problems and questions on your server configuration. Don\'t worry, this information isn\'t logged or anything.

    If this stays as "Retrieving support information...", your computer probably cannot connect to www.simplemachines.org.'; +$helptxt['latest_packages'] = 'Here you can see some of the most popular and some random packages or mods, with quick and easy installations.

    If this section doesn\'t show up, your computer probably cannot connect to www.simplemachines.org.'; +$helptxt['latest_themes'] = 'This area shows a few of the latest and most popular themes from www.simplemachines.org. It may not show up properly if your computer can\'t find www.simplemachines.org, though.'; + +$helptxt['secret_why_blank'] = 'For your security, your password and the answer to your secret question are encrypted so that the SMF software will never tell you, or anyone else, what they are.'; +$helptxt['moderator_why_missing'] = 'Since moderation is done on a board-by-board basis, you have to make members moderators from the board management interface.'; + +$helptxt['permissions'] = 'Permissions are how you either allow groups to, or deny groups from, doing specific things.

    You can modify multiple boards at once with the checkboxes, or look at the permissions for a specific group by clicking \'Modify.\''; +$helptxt['permissions_board'] = 'If a board is set to \'Global,\' it means that the board will not have any special permissions. \'Local\' means it will have its own permissions - separate from the global ones. This allows you to have a board that has more or less permissions than another, without requiring you to set them for each and every board.'; +$helptxt['permissions_quickgroups'] = 'These allow you to use the "default" permission setups - standard means \'nothing special\', restrictive means \'like a guest\', moderator means \'what a moderator has\', and lastly \'maintenance\' means permissions very close to those of an administrator.'; +$helptxt['permissions_deny'] = 'Denying permissions can be useful when you want take away permission from certain members. You can add a membergroup with a \'deny\'-permission to the members you wish to deny a permission.

    Use with care, a denied permission will stay denied no matter what other membergroups the member is in.'; +$helptxt['permissions_postgroups'] = 'Enabling permissions for post count based groups will allow you to attribute permissions to members that have posted a certain amount of messages. The permissions of the post count based groups are added to the permissions of the regular membergroups.'; +$helptxt['membergroup_guests'] = 'The Guests membergroup are all users that are not logged in.'; +$helptxt['membergroup_regular_members'] = 'The Regular Members are all members that are logged in, but that have no primary membergroup assigned.'; +$helptxt['membergroup_administrator'] = 'The administrator can, per definition, do anything and see any board. There are no permission settings for the administrator.'; +$helptxt['membergroup_moderator'] = 'The Moderator membergroup is a special membergroup. Permissions and settings assigned to this group apply to moderators but only on the boards they moderate. Outside these boards they\'re just like any other member.'; +$helptxt['membergroups'] = 'In SMF there are two types of groups that your members can be part of. These are: +
      +
    • Regular Groups: A regular group is a group to which members are not automatically put into. To assign a member to be in a group simply go to their profile and click "Account Settings". From here you can assign them any number of regular groups to which they will be part.
    • +
    • Post Groups: Unlike regular groups post based groups cannot be assigned. Instead, members are automatically assigned to a post based group when they reach the minimum number of posts required to be in that group.
    • +
    '; + +$helptxt['calendar_how_edit'] = 'You can edit these events by clicking on the red asterisk (*) next to their names.'; + +$helptxt['maintenance_backup'] = 'This area allows you to save a copy of all the posts, settings, members, and other information in your forum to a very large file.

    It is recommended that you do this often, perhaps weekly, for safety and security.'; +$helptxt['maintenance_rot'] = 'This allows you to completely and irrevocably remove old topics. It is recommended that you try to make a backup first, just in case you remove something you didn\'t mean to.

    Use this option with care.'; +$helptxt['maintenance_members'] = 'This allows you to completely and irrevocably remove member accounts from your forum. It is highly recommended that you try to make a backup first, just in case you remove something you didn\'t mean to.

    Use this option with care.'; + +$helptxt['avatar_server_stored'] = 'This allows your members to pick from avatars stored on your server itself. They are, generally, in the same place as SMF under the avatars folder.
    As a tip, if you create directories in that folder, you can make "categories" of avatars.'; +$helptxt['avatar_external'] = 'With this enabled, your members can type in a URL to their own avatar. The downside of this is that, in some cases, they may use avatars that are overly large or portray images you don\'t want on your forum.'; +$helptxt['avatar_download_external'] = 'With this option enabled, the URL given by the user is accessed to download the avatar at that location. On success, the avatar will be treated as uploadable avatar.'; +$helptxt['avatar_upload'] = 'This option is much like "Allow members to select an external avatar", except that you have better control over the avatars, a better time resizing them, and your members do not have to have somewhere to put avatars.

    However, the downside is that it can take a lot of space on your server.'; +$helptxt['avatar_download_png'] = 'PNGs are larger, but offer better quality compression. If this is unchecked, JPEG will be used instead - which is often smaller, but also of lesser or blurry quality.'; + +$helptxt['disableHostnameLookup'] = 'This disables host name lookups, which on some servers are very slow. Note that this will make banning less effective.'; + +$helptxt['search_weight_frequency'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor counts the amount of matching messages and divides them by the total number of messages within a topic.'; +$helptxt['search_weight_age'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor rates the age of the last matching message within a topic. The more recent this message is, the higher the score.'; +$helptxt['search_weight_length'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor is based on the topic size. The more messages are within the topic, the higher the score.'; +$helptxt['search_weight_subject'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor looks whether a search term can be found within the subject of a topic.'; +$helptxt['search_weight_first_message'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor looks whether a match can be found in the first message of a topic.'; +$helptxt['search_weight_sticky'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor looks whether a topic is sticky and increases the relevancy score if it is.'; +$helptxt['search'] = 'Adjust all settings for the search function here.'; +$helptxt['search_why_use_index'] = 'A search index can greatly improve the performance of searches on your forum. Especially when the number of messages on a forum grows bigger, searching without an index can take a long time and increase the pressure on your database. If your forum is bigger than 50.000 messages, you might want to consider creating a search index to assure peak performance of your forum.

    Note that a search index can take up quite some space. A fulltext index is a built-in index of MySQL. It\'s relatively compact (approximately the same size as the message table), but a lot of words aren\'t indexed and it can, in some search queries, turn out to be very slow. The custom index is often bigger (depending on your configuration it can be up to 3 times the size of the messages table) but it\'s performance is better than fulltext and relatively stable.'; + +$helptxt['see_admin_ip'] = 'IP addresses are shown to administrators and moderators to facilitate moderation and to make it easier to track people up to no good. Remember that IP addresses may not always be identifying, and most people\'s IP addresses change periodically.

    Members are also allowed to see their own IPs.'; +$helptxt['see_member_ip'] = 'Your IP address is shown only to you and moderators. Remember that this information is not identifying, and that most IPs change periodically.

    You cannot see other members\' IP addresses, and they cannot see yours.'; +$helptxt['whytwoip'] = 'SMF uses various methods to detect user IP addresses. Usually these two methods result in the same address but in some cases more than one address may be detected. In this case SMF logs both addresses, and uses them both for ban checks (etc). You can click on either address to track that IP and ban if necessary.'; + +$helptxt['ban_cannot_post'] = 'The \'cannot post\' restriction turns the forum into read-only mode for the banned user. The user cannot create new topics, or reply to existing ones, send personal messages or vote in polls. The banned user can however still read personal messages and topics.

    A warning message is shown to the users that are banned this way.'; + +$helptxt['posts_and_topics'] = ' +
      +
    • + Post Settings
      + Modify the settings related to the posting of messages and the way messages are shown. You can also enable the spell check here. +
    • + Bulletin Board Code
      + Enable the code that shows forum messages in the right layout. Also adjust which codes are allowed and which aren\'t. +
    • + Censored Words + In order to keep the language on your forum under control, you can censor certain words. This function allows you to convert forbidden words into innocent versions. +
    • + Topic Settings + Modify the settings related to topics. The number of topics per page, whether sticky topics are enabled or not, the number of messages needed for a topic to be hot, etc. +
    • +
    '; +$helptxt['spider_group'] = 'By selecting a restrictive group, when a guest is detected as a search crawler it will automatically be assigned any "deny" deny permissions of this group in addition to the normal permissions of a guest. You can use this to provide lesser access to a search engine than you would a normal guest. You might for example wish to create a new group called "Spiders" and select that here. You could then deny permission for that group to view profiles to stop spiders indexing your members profiles.
    Note: Spider detection is not perfect and can be simulated by users so this feature is not guaranteed to restrict content only to those search engines you have added.'; +$helptxt['show_spider_online'] = 'This setting allows you to select whether spiders should be listed in the who\'s online list on the board index and "Who\'s Online" page. Options are: +
      +
    • + Not at All
      + Spiders will simply appear as guests to all users. +
    • + Show Spider Quantity
      + The Board Index will display the number of spiders currently visiting the forum. +
    • + Show Spider Names
      + Each spider name will be revealed, so users can see how many of each spider is currently visiting the forum - this takes effect in both the Board Index and Who\'s Online page. +
    • + Show Spider Names - Admin Only
      + As above except only Administrators can see spider status - to all other users spiders appear as guests. +
    • +
    '; + +$helptxt['birthday_email'] = 'Choose the index of the birthday email message to use. A preview will be shown in the Email Subject and Email Body fields.
    Note: Setting this option does not automatically enable birthday emails. To enable birthday emails use the Scheduled Tasks page and enable the birthday email task.'; +$helptxt['pm_bcc'] = 'When sending a personal message you can choose to add a recipient as BCC or "Blind Carbon Copy". BCC recipients do not have their identities revealed to other recipients of the message.'; + +$helptxt['move_topics_maintenance'] = 'This will allow you to move all the posts from one board to another board.'; +$helptxt['maintain_reattribute_posts'] = 'You can use this function to attribute guest posts on your board to a registered member. This is useful if, for example, a user deleted their account and changed their mind and wished to have their old posts associated with their account.'; +$helptxt['chmod_flags'] = 'You can manually set the permissions you wish to set the selected files to. To do this enter the chmod value as a numeric (octet) value. Note - these flags will have no effect on Microsoft Windows operating systems.'; + +$helptxt['postmod'] = 'This section allows members of the moderation team (with sufficient permissions) to approve any posts and topics before they are shown.'; + +$helptxt['field_show_enclosed'] = 'Encloses the user input between some text or html. This will allow you to add more instant message providers, images or an embed etc. For example:

    + <a href="http://website.com/{INPUT}"><img src="{DEFAULT_IMAGES_URL}/icon.gif" alt="{INPUT}" /></a>

    + Note that you can use the following variables:
    +
      +
    • {INPUT} - The input specified by the user.
    • +
    • {SCRIPTURL} - Web address of forum.
    • +
    • {IMAGES_URL} - Url to images folder in the users current theme.
    • +
    • {DEFAULT_IMAGES_URL} - Url to the images folder in the default theme.
    • +
    '; + +$helptxt['custom_mask'] = 'The input mask is important for your forum\'s security. Validating the input from a user can help ensure that data is not used in a way you do not expect. We have provided some simple regular expressions as hints.

    +
    + "[A-Za-z]+" - Match all upper and lower case alphabet characters.
    + "[0-9]+" - Match all numeric characters.
    + "[A-Za-z0-9]{7}" - Match all upper and lower case alphabet and numeric characters seven times.
    + "[^0-9]?" - Forbid any number from being matched.
    + "^([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$" - Only allow 3 or 6 character hexcodes.
    +


    + Additionally, special metacharacters ?+*^$ and {xx} can be defined. +
    + ? - None or one match of previous expression.
    + + - One or more of previous expression.
    + * - None or more of previous expression.
    + {xx} - An exact number from previous expression.
    + {xx,} - An exact number or more from previous expression.
    + {,xx} - An exact number or less from previous expression.
    + {xx,yy} - An exact match between the two numbers from previous expression.
    + ^ - Start of string.
    + $ - End of string.
    + \ - Escapes the next character.
    +


    + More information and advanced techniques may be found on the internet.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Install.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Install.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,235 @@ +help is available if you need it.'; +$txt['still_writable'] = 'Your installation directory is still writable. It\'s a good idea to chmod it so that it is not writable for security reasons.'; +$txt['delete_installer'] = 'Click here to delete this install.php file now.'; +$txt['delete_installer_maybe'] = '(doesn\'t work on all servers.)'; +$txt['go_to_your_forum'] = 'Now you can see your newly installed forum and begin to use it. You should first make sure you are logged in, after which you will be able to access the administration center.'; +$txt['good_luck'] = 'Good luck!
    Simple Machines'; + +$txt['install_welcome'] = 'Welcome'; +$txt['install_welcome_desc'] = 'Welcome to SMF. This script will guide you through the process for installing %1$s. We\'ll gather a few details about your forum over the next few steps, and after a couple of minutes your forum will be ready for use.'; +$txt['install_all_lovely'] = 'We\'ve completed some initial tests on your server and everything appears to be in order. Simply click the "Continue" button below to get started.'; + +$txt['user_refresh_install'] = 'Forum Refreshed'; +$txt['user_refresh_install_desc'] = 'While installing, the installer found that (with the details you provided) one or more of the tables this installer might create already existed.
    Any missing tables in your installation have been recreated with the default data, but no data was deleted from existing tables.'; + +$txt['default_topic_subject'] = 'Welcome to SMF!'; +$txt['default_topic_message'] = 'Welcome to Simple Machines Forum!

    We hope you enjoy using your forum.  If you have any problems, please feel free to [url=http://www.simplemachines.org/community/index.php]ask us for assistance[/url].

    Thanks!
    Simple Machines'; +$txt['default_board_name'] = 'General Discussion'; +$txt['default_board_description'] = 'Feel free to talk about anything and everything in this board.'; +$txt['default_category_name'] = 'General Category'; +$txt['default_time_format'] = '%B %d, %Y, %I:%M:%S %p'; +$txt['default_news'] = 'SMF - Just Installed!'; +$txt['default_karmaLabel'] = 'Karma:'; +$txt['default_karmaSmiteLabel'] = '[smite]'; +$txt['default_karmaApplaudLabel'] = '[applaud]'; +$txt['default_reserved_names'] = 'Admin\nWebmaster\nGuest\nroot'; +$txt['default_smileyset_name'] = 'Alienine\'s Set'; +$txt['default_aaron_smileyset_name'] = 'Aaron\'s Set'; +$txt['default_akyhne_smileyset_name'] = 'Akyhne\'s Set'; +$txt['default_theme_name'] = 'SMF Default Theme - Curve'; +$txt['default_core_theme_name'] = 'Core Theme'; +$txt['default_classic_theme_name'] = 'Classic YaBB SE Theme'; +$txt['default_babylon_theme_name'] = 'Babylon Theme'; + +$txt['default_administrator_group'] = 'Administrator'; +$txt['default_global_moderator_group'] = 'Global Moderator'; +$txt['default_moderator_group'] = 'Moderator'; +$txt['default_newbie_group'] = 'Newbie'; +$txt['default_junior_group'] = 'Jr. Member'; +$txt['default_full_group'] = 'Full Member'; +$txt['default_senior_group'] = 'Sr. Member'; +$txt['default_hero_group'] = 'Hero Member'; + +$txt['default_smiley_smiley'] = 'Smiley'; +$txt['default_wink_smiley'] = 'Wink'; +$txt['default_cheesy_smiley'] = 'Cheesy'; +$txt['default_grin_smiley'] = 'Grin'; +$txt['default_angry_smiley'] = 'Angry'; +$txt['default_sad_smiley'] = 'Sad'; +$txt['default_shocked_smiley'] = 'Shocked'; +$txt['default_cool_smiley'] = 'Cool'; +$txt['default_huh_smiley'] = 'Huh?'; +$txt['default_roll_eyes_smiley'] = 'Roll Eyes'; +$txt['default_tongue_smiley'] = 'Tongue'; +$txt['default_embarrassed_smiley'] = 'Embarrassed'; +$txt['default_lips_sealed_smiley'] = 'Lips Sealed'; +$txt['default_undecided_smiley'] = 'Undecided'; +$txt['default_kiss_smiley'] = 'Kiss'; +$txt['default_cry_smiley'] = 'Cry'; +$txt['default_evil_smiley'] = 'Evil'; +$txt['default_azn_smiley'] = 'Azn'; +$txt['default_afro_smiley'] = 'Afro'; +$txt['default_laugh_smiley'] = 'Laugh'; +$txt['default_police_smiley'] = 'Police'; +$txt['default_angel_smiley'] = 'Angel'; + +$txt['error_message_click'] = 'Click here'; +$txt['error_message_try_again'] = 'to try this step again.'; +$txt['error_message_bad_try_again'] = 'to try installing anyway, but note that this is strongly discouraged.'; + +$txt['install_settings'] = 'Forum Settings'; +$txt['install_settings_info'] = 'This page requires you to define a few key settings for your forum. SMF has automatically detected key settings for you.'; +$txt['install_settings_name'] = 'Forum name'; +$txt['install_settings_name_info'] = 'This is the name of your forum, ie. "The Testing Forum".'; +$txt['install_settings_name_default'] = 'My Community'; +$txt['install_settings_url'] = 'Forum URL'; +$txt['install_settings_url_info'] = 'This is the URL to your forum without the trailing \'/\'!.
    In most cases, you can leave the default value in this box alone - it is usually right.'; +$txt['install_settings_compress'] = 'Gzip Output'; +$txt['install_settings_compress_title'] = 'Compress output to save bandwidth.'; +// In this string, you can translate the word "PASS" to change what it says when the test passes. +$txt['install_settings_compress_info'] = 'This function does not work properly on all servers, but can save you a lot of bandwidth.
    Click here to test it. (it should just say "PASS".)'; +$txt['install_settings_dbsession'] = 'Database Sessions'; +$txt['install_settings_dbsession_title'] = 'Use the database for sessions instead of using files.'; +$txt['install_settings_dbsession_info1'] = 'This feature is almost always for the best, as it makes sessions more dependable.'; +$txt['install_settings_dbsession_info2'] = 'This feature is generally a good idea, but may not work properly on this server.'; +$txt['install_settings_utf8'] = 'UTF-8 Character Set'; +$txt['install_settings_utf8_title'] = 'Use UTF-8 as default character set'; +$txt['install_settings_utf8_info'] = 'This feature lets both the database and the forum use an international character set, UTF-8. This can be useful when working with multiple languages that use different character sets.'; +$txt['install_settings_stats'] = 'Allow Stat Collection'; +$txt['install_settings_stats_title'] = 'Allow Simple Machines to Collect Basic Stats Monthly'; +$txt['install_settings_stats_info'] = 'If enabled, this will allow Simple Machines to visit your site once a month to collect basic statistics. This will help us make decisions as to which configurations to optimize the software for. For more information please visit our info page.'; +$txt['install_settings_proceed'] = 'Proceed'; + +$txt['db_settings'] = 'Database Server Settings'; +$txt['db_settings_info'] = 'These are the settings to use for your database server. If you don\'t know the values, you should ask your host what they are.'; +$txt['db_settings_type'] = 'Database type'; +$txt['db_settings_type_info'] = 'Multiple supported database types were detected - which do you wish to use. Please note that running pre-SMF 2.0 RC3 along with newer SMF versions in the same PostgreSQL database is not supported. You need to upgrade your older installations in that case.'; +$txt['db_settings_server'] = 'Server name'; +$txt['db_settings_server_info'] = 'This is nearly always localhost - so if you don\'t know, try localhost.'; +$txt['db_settings_username'] = 'Username'; +$txt['db_settings_username_info'] = 'Fill in the username you need to connect to your database here.
    If you don\'t know what it is, try the username of your ftp account, most of the time they are the same.'; +$txt['db_settings_password'] = 'Password'; +$txt['db_settings_password_info'] = 'Here, put the password you need to connect to your database.
    If you don\'t know this, you should try the password to your ftp account.'; +$txt['db_settings_database'] = 'Database name'; +$txt['db_settings_database_info'] = 'Fill in the name of the database you want to use for SMF to store its data in.'; +$txt['db_settings_database_info_note'] = 'If this database does not exist, this installer will try to create it.'; +$txt['db_settings_database_file'] = 'Database filename'; +$txt['db_settings_database_file_info'] = 'This is the name of the file in which to store the SMF data. We recommend you use the randomly generated name for this and set the path of this file to be outside of the public area of your webserver.'; +$txt['db_settings_prefix'] = 'Table prefix'; +$txt['db_settings_prefix_info'] = 'The prefix for every table in the database. Do not install two forums with the same prefix!
    This value allows for multiple installations in one database.'; +$txt['db_sqlite_warning'] = 'Only recommended for small, low volume and/or intranet-type forums'; +$txt['db_populate'] = 'Populated Database'; +$txt['db_populate_info'] = 'Your settings have now been saved and the database has been populated with all the data required to get your forum up and running. Summary of population:'; +$txt['db_populate_info2'] = 'Click "Continue" to progress to the admin account creation page.'; +$txt['db_populate_inserts'] = 'Inserted %1$d rows.'; +$txt['db_populate_tables'] = 'Created %1$d tables.'; +$txt['db_populate_insert_dups'] = 'Ignored %1$d duplicated inserts.'; +$txt['db_populate_table_dups'] = 'Ignored %1$d duplicated tables.'; + +$txt['user_settings'] = 'Create Your Account'; +$txt['user_settings_info'] = 'The installer will now create a new administrator account for you.'; +$txt['user_settings_username'] = 'Your username'; +$txt['user_settings_username_info'] = 'Choose the name you want to login with.
    This can\'t be changed later, but your display name can be.'; +$txt['user_settings_password'] = 'Password'; +$txt['user_settings_password_info'] = 'Fill in your preferred password here, and remember it well!'; +$txt['user_settings_again'] = 'Password'; +$txt['user_settings_again_info'] = '(just for verification.)'; +$txt['user_settings_email'] = 'Email Address'; +$txt['user_settings_email_info'] = 'Provide your email address as well. This must be a valid email address.'; +$txt['user_settings_database'] = 'Database Password'; +$txt['user_settings_database_info'] = 'The installer requires that you supply the database password to create an administrator account, for security reasons.'; +$txt['user_settings_skip'] = 'Skip'; +$txt['user_settings_skip_sure'] = 'Are you sure you wish to skip admin account creation?'; +$txt['user_settings_proceed'] = 'Finish'; + +$txt['ftp_checking_writable'] = 'Checking Files are Writable'; +$txt['ftp_setup'] = 'FTP Connection Information'; +$txt['ftp_setup_info'] = 'This installer can connect via FTP to fix the files that need to be writable and are not. If this doesn\'t work for you, you will have to go in manually and make the files writable. Please note that this doesn\'t support SSL right now.'; +$txt['ftp_server'] = 'Server'; +$txt['ftp_server_info'] = 'This should be the server and port for your FTP server.'; +$txt['ftp_port'] = 'Port'; +$txt['ftp_username'] = 'Username'; +$txt['ftp_username_info'] = 'The username to login with. This will not be saved anywhere.'; +$txt['ftp_password'] = 'Password'; +$txt['ftp_password_info'] = 'The password to login with. This will not be saved anywhere.'; +$txt['ftp_path'] = 'Install Path'; +$txt['ftp_path_info'] = 'This is the relative path you use in your FTP server.'; +$txt['ftp_path_found_info'] = 'The path in the box above was automatically detected.'; +$txt['ftp_connect'] = 'Connect'; +$txt['ftp_setup_why'] = 'What is this step for?'; +$txt['ftp_setup_why_info'] = 'Some files need to be writable for SMF to work properly. This step allows you to let the installer make them writable for you. However, in some cases it won\'t work - in that case, please make the following files 777 (writable, 755 on some hosts):'; +$txt['ftp_setup_again'] = 'to test if these files are writable again.'; + +$txt['error_php_too_low'] = 'Warning! You do not appear to have a version of PHP installed on your webserver that meets SMF\'s minimum installations requirements.
    If you are not the host, you will need to ask your host to upgrade, or use a different host - otherwise, please upgrade PHP to a recent version.

    If you know for a fact that your PHP version is high enough you may continue, although this is strongly discouraged.'; +$txt['error_missing_files'] = 'Unable to find crucial installation files in the directory of this script!

    Please make sure you uploaded the entire installation package, including the sql file, and then try again.'; +$txt['error_session_save_path'] = 'Please inform your host that the session.save_path specified in php.ini is not valid! It needs to be changed to a directory that exists, and is writable by the user PHP is running under.
    '; +$txt['error_windows_chmod'] = 'You\'re on a windows server, and some crucial files are not writable. Please ask your host to give write permissions to the user PHP is running under for the files in your SMF installation. The following files or directories need to be writable:'; +$txt['error_ftp_no_connect'] = 'Unable to connect to FTP server with this combination of details.'; +$txt['error_db_file'] = 'Cannot find database source script! Please check file %1$s is within your forum source directory.'; +$txt['error_db_connect'] = 'Cannot connect to the database server with the supplied data.

    If you are not sure about what to type in, please contact your host.'; +$txt['error_db_too_low'] = 'The version of your database server is very old, and does not meet SMF\'s minimum requirements.

    Please ask your host to either upgrade it or supply a new one, and if they won\'t, please try a different host.'; +$txt['error_db_database'] = 'The installer was unable to access the "%1$s" database. With some hosts, you have to create the database in your administration panel before SMF can use it. Some also add prefixes - like your username - to your database names.'; +$txt['error_db_queries'] = 'Some of the queries were not executed properly. This could be caused by an unsupported (development or old) version of your database software.

    Technical information about the queries:'; +$txt['error_db_queries_line'] = 'Line #'; +$txt['error_db_missing'] = 'The installer was unable to detect any database support in PHP. Please ask your host to ensure that PHP was compiled with the desired database, or that the proper extension is being loaded.'; +$txt['error_db_script_missing'] = 'The installer could not find any install script files for the detected databases. Please check you have uploaded the necessary install script files to your forum directory, for example "%1$s"'; +$txt['error_session_missing'] = 'The installer was unable to detect sessions support in your server\'s installation of PHP. Please ask your host to ensure that PHP was compiled with session support (in fact, it has to be explicitly compiled without it.)'; +$txt['error_user_settings_again_match'] = 'You typed in two completely different passwords!'; +$txt['error_user_settings_no_password'] = 'Your password must be at least four characters long.'; +$txt['error_user_settings_taken'] = 'Sorry, a member is already registered with that username and/or email address.

    A new account has not been created.'; +$txt['error_user_settings_query'] = 'A database error occurred while trying to create an administrator. This error was:'; +$txt['error_subs_missing'] = 'Unable to find the Sources/Subs.php file. Please make sure it was uploaded properly, and then try again.'; +$txt['error_db_alter_priv'] = 'The database account you specified does not have permission to ALTER, CREATE, and/or DROP tables in the database; this is necessary for SMF to function properly.'; +$txt['error_versions_do_not_match'] = 'The installer has detected another version of SMF already installed with the specified information. If you are trying to upgrade, you should use the upgrader, not the installer.

    Otherwise, you may wish to use different information, or create a backup and then delete the data currently in the database.'; +$txt['error_mod_security'] = 'The installer has detected the mod_security module is installed on your web server. Mod_security will block submitted forms even before SMF gets a say in anything. SMF has a built-in security scanner that will work more effectively than mod_security and that won\'t block submitted forms.

    More information about disabling mod_security'; +$txt['error_mod_security_no_write'] = 'The installer has detected the mod_security module is installed on your web server. Mod_security will block submitted forms even before SMF gets a say in anything. SMF has a built-in security scanner that will work more effectively than mod_security and that won\'t block submitted forms.

    More information about disabling mod_security

    Alternatively, you may wish to use your ftp client to chmod .htaccess in the forum directory to be writable (777), and then refresh this page.'; +$txt['error_utf8_version'] = 'The current version of your database doesn\'t support the use of the UTF-8 character set. You can still install SMF without any problems, but only with UTF-8 support unchecked. If you would like to switch over to UTF-8 in the future (e.g. after the database server of your forum has been upgraded to version >= %1$s), you can convert your forum to UTF-8 through the admin panel.'; +$txt['error_valid_email_needed'] = 'You have not entered a valid email address.'; +$txt['error_already_installed'] = 'The installer has detected that you already have SMF installed. It is strongly advised that you do not try to overwrite an existing installation - continuing with installation may result in the loss or corruption of existing data.

    If you wish to upgrade please visit the Simple Machines Website and download the latest upgrade package.

    If you wish to overwrite your existing installation, including all data, it\'s recommended that you delete the existing database tables and replace Settings.php and try again.'; +$txt['error_warning_notice'] = 'Warning!'; +$txt['error_script_outdated'] = 'This install script is out of date! The current version of SMF is %1$s but this install script is for %2$s.

    + It is recommended that you visit the Simple Machines website to ensure you are installing the latest version.'; +$txt['error_db_filename'] = 'You must enter a name for the database file name for SQLite.'; +$txt['error_db_prefix_numeric'] = 'The selected database type does not support the use of numeric prefixes.'; +$txt['error_invalid_characters_username'] = 'Invalid character used in Username.'; +$txt['error_username_too_long'] = 'Username must be less than 25 characters long.'; +$txt['error_username_left_empty'] = 'Username field was left empty.'; +$txt['error_db_filename_exists'] = 'The database that you are trying to create exists. Please delete the current database file or enter another name.'; +$txt['error_db_prefix_reserved'] = 'The prefix that you entered is a reserved prefix. Please enter another prefix.'; + +$txt['upgrade_upgrade_utility'] = 'SMF Upgrade Utility'; +$txt['upgrade_warning'] = 'Warning!'; +$txt['upgrade_critical_error'] = 'Critical Error!'; +$txt['upgrade_continue'] = 'Continue'; +$txt['upgrade_skip'] = 'Skip'; +$txt['upgrade_note'] = 'Note!'; +$txt['upgrade_step'] = 'Step'; +$txt['upgrade_steps'] = 'Steps'; +$txt['upgrade_progress'] = 'Progress'; +$txt['upgrade_overall_progress'] = 'Overall Progress'; +$txt['upgrade_step_progress'] = 'Step Progress'; +$txt['upgrade_time_elapsed'] = 'Time Elapsed'; +$txt['upgrade_time_mins'] = 'mins'; +$txt['upgrade_time_secs'] = 'seconds'; + +$txt['upgrade_incomplete'] = 'Incomplete'; +$txt['upgrade_not_quite_done'] = 'Not quite done yet!'; +$txt['upgrade_paused_overload'] = 'This upgrade has been paused to avoid overloading your server. Don\'t worry, nothing\'s wrong - simply click the below to keep going.'; + +$txt['upgrade_ready_proceed'] = 'Thank you for choosing to upgrade to SMF %1$s. All files appear to be in place, and we\'re ready to proceed.'; + +$txt['upgrade_error_script_js'] = 'The upgrade script cannot find script.js or it is out of date. Make sure your theme paths are correct. You can download a setting checker tool from the Simple Machines Website'; + +$txt['upgrade_warning_lots_data'] = 'This upgrade script has detected that your forum contains a lot of data which needs upgrading. This process may take quite some time depending on your server and forum size, and for very large forums (~300,000 messages) may take several hours to complete.'; +$txt['upgrade_warning_out_of_date'] = 'This upgrade script is out of date! The current version of SMF is ?? but this upgrade script is for %1$s.

    It is recommended that you visit the Simple Machines website to ensure you are upgrading to the latest version.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Login.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Login.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,151 @@ +If you wish to restore your account, please check the "Reactivate my account" box, and login again.'; +$txt['undelete_account'] = 'Reactivate my account'; + +// Use numeric entities in the below three strings. +$txt['change_password'] = 'New Password Details'; +$txt['change_password_login'] = 'Your login details at'; +$txt['change_password_new'] = 'have been changed and your password reset. Below are your new login details.'; + +$txt['in_maintain_mode'] = 'This board is in Maintenance Mode.'; + +// These two are used as a javascript alert; please use international characters directly, not as entities. +$txt['register_agree'] = 'Please read and accept the agreement before registering.'; +$txt['register_passwords_differ_js'] = 'The two passwords you entered are not the same!'; + +$txt['approval_after_registration'] = 'Thank you for registering. The admin must approve your registration before you may begin to use your account, you will receive an email shortly advising you of the admins decision.'; + +$txt['admin_settings_desc'] = 'Here you can change a variety of settings related to registration of new members.'; + +$txt['setting_enableOpenID'] = 'Allow users to register using OpenID'; + +$txt['setting_registration_method'] = 'Method of registration employed for new members'; +$txt['setting_registration_disabled'] = 'Registration Disabled'; +$txt['setting_registration_standard'] = 'Immediate Registration'; +$txt['setting_registration_activate'] = 'Email Activation'; +$txt['setting_registration_approval'] = 'Admin Approval'; +$txt['setting_notify_new_registration'] = 'Notify administrators when a new member joins'; +$txt['setting_send_welcomeEmail'] = 'Send welcome email to new members'; + +$txt['setting_coppaAge'] = 'Age below which to apply registration restrictions'; +$txt['setting_coppaAge_desc'] = '(Set to 0 to disable)'; +$txt['setting_coppaType'] = 'Action to take when a user below minimum age registers'; +$txt['setting_coppaType_reject'] = 'Reject their registration'; +$txt['setting_coppaType_approval'] = 'Require parent/guardian approval'; +$txt['setting_coppaPost'] = 'Postal address to which approval forms should be sent'; +$txt['setting_coppaPost_desc'] = 'Only applies if age restriction is in place'; +$txt['setting_coppaFax'] = 'Fax number to which approval forms should be faxed'; +$txt['setting_coppaPhone'] = 'Contact number for parents to contact with age restriction queries'; + +$txt['admin_register'] = 'Registration of new member'; +$txt['admin_register_desc'] = 'From here you can register new members into the forum, and if desired, email them their details.'; +$txt['admin_register_username'] = 'New Username'; +$txt['admin_register_email'] = 'Email Address'; +$txt['admin_register_password'] = 'Password'; +$txt['admin_register_username_desc'] = 'Username for the new member'; +$txt['admin_register_email_desc'] = 'Email address of the member'; +$txt['admin_register_password_desc'] = 'Password for new member'; +$txt['admin_register_email_detail'] = 'Email new password to user'; +$txt['admin_register_email_detail_desc'] = 'Email address required even if unchecked'; +$txt['admin_register_email_activate'] = 'Require user to activate the account'; +$txt['admin_register_group'] = 'Primary Membergroup'; +$txt['admin_register_group_desc'] = 'Primary membergroup new member will belong to'; +$txt['admin_register_group_none'] = '(no primary membergroup)'; +$txt['admin_register_done'] = 'Member %1$s has been registered successfully!'; + +$txt['coppa_title'] = 'Age Restricted Forum'; +$txt['coppa_after_registration'] = 'Thank you for registering with ' . $context['forum_name_html_safe'] . '.

    Because you fall under the age of {MINIMUM_AGE}, it is a legal requirement + to obtain your parent or guardian\'s permission before you may begin to use your account. To arrange for account activation please print off the form below:'; +$txt['coppa_form_link_popup'] = 'Load Form In New Window'; +$txt['coppa_form_link_download'] = 'Download Form as Text File'; +$txt['coppa_send_to_one_option'] = 'Then arrange for your parent/guardian to send the completed form by:'; +$txt['coppa_send_to_two_options'] = 'Then arrange for your parent/guardian to send the completed form by either:'; +$txt['coppa_send_by_post'] = 'Post, to the following address:'; +$txt['coppa_send_by_fax'] = 'Fax, to the following number:'; +$txt['coppa_send_by_phone'] = 'Alternatively, arrange for them to phone the administrator at {PHONE_NUMBER}.'; + +$txt['coppa_form_title'] = 'Permission form for registration at ' . $context['forum_name_html_safe']; +$txt['coppa_form_address'] = 'Address'; +$txt['coppa_form_date'] = 'Date'; +$txt['coppa_form_body'] = 'I {PARENT_NAME},

    Give permission for {CHILD_NAME} (child name) to become a fully registered member of the forum: ' . $context['forum_name_html_safe'] . ', with the username: {USER_NAME}.

    I understand that certain personal information entered by {USER_NAME} may be shown to other users of the forum.

    Signed:
    {PARENT_NAME} (Parent/Guardian).'; + +$txt['visual_verification_sound_again'] = 'Play again'; +$txt['visual_verification_sound_close'] = 'Close window'; +$txt['visual_verification_sound_direct'] = 'Having problems hearing this? Try a direct link to it.'; + +// Use numeric entities in the below. +$txt['registration_username_available'] = 'Username is available'; +$txt['registration_username_unavailable'] = 'Username is not available'; +$txt['registration_username_check'] = 'Check if username is available'; +$txt['registration_password_short'] = 'Password is too short'; +$txt['registration_password_reserved'] = 'Password contains your username/email'; +$txt['registration_password_numbercase'] = 'Password must contain both upper and lower case, and numbers'; +$txt['registration_password_no_match'] = 'Passwords do not match'; +$txt['registration_password_valid'] = 'Password is valid'; + +$txt['registration_errors_occurred'] = 'The following errors were detected in your registration. Please correct them to continue:'; + +$txt['authenticate_label'] = 'Authentication Method'; +$txt['authenticate_password'] = 'Password'; +$txt['authenticate_openid'] = 'OpenID'; +$txt['authenticate_openid_url'] = 'OpenID Authentication URL'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManageBoards.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManageBoards.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,98 @@ +"username", "username". (these must be usernames not display names!)
    To create a new board, click the Add Board button. To make the new board a child of a current board, select "Child of..." from the Order drop down menu when creating the board.'; +$txt['parent_members_only'] = 'Regular Members'; +$txt['parent_guests_only'] = 'Guests'; +$txt['catConfirm'] = 'Do you really want to delete this category?'; +$txt['boardConfirm'] = 'Do you really want to delete this board?'; + +$txt['catEdit'] = 'Edit Category'; +$txt['collapse_enable'] = 'Collapsible'; +$txt['collapse_desc'] = 'Allow users to collapse this category'; +$txt['catModify'] = '(modify)'; + +$txt['mboards_order_after'] = 'After '; +$txt['mboards_order_inside'] = 'Inside '; +$txt['mboards_order_first'] = 'In first place'; + +$txt['mboards_new_board'] = 'Add Board'; +$txt['mboards_new_cat_name'] = 'New Category'; +$txt['mboards_add_cat_button'] = 'Add Category'; +$txt['mboards_new_board_name'] = 'New Board'; + +$txt['mboards_name'] = 'Name'; +$txt['mboards_modify'] = 'modify'; +$txt['mboards_permissions'] = 'permissions'; +// Don't use entities in the below string. +$txt['mboards_permissions_confirm'] = 'Are you sure you want to switch this board to use local permissions?'; + +$txt['mboards_delete_cat'] = 'Delete Category'; +$txt['mboards_delete_board'] = 'Delete Board'; + +$txt['mboards_delete_cat_contains'] = 'Deleting this category will also delete the below boards, including all topics, posts and attachments within each board'; +$txt['mboards_delete_option1'] = 'Delete category and all boards contained within.'; +$txt['mboards_delete_option2'] = 'Delete category and move all boards contained within to'; +$txt['mboards_delete_board_contains'] = 'Deleting this board will also move the child boards below, including all topics, posts and attachments within each board'; +$txt['mboards_delete_board_option1'] = 'Delete board and move child boards contained within to category level.'; +$txt['mboards_delete_board_option2'] = 'Delete board and move all child boards contained within to'; +$txt['mboards_delete_what_do'] = 'Please select what you would like to do with these boards'; +$txt['mboards_delete_confirm'] = 'Confirm'; +$txt['mboards_delete_cancel'] = 'Cancel'; + +$txt['mboards_category'] = 'Category'; +$txt['mboards_description'] = 'Description'; +$txt['mboards_description_desc'] = 'A short description of your board.'; +$txt['mboards_groups'] = 'Allowed Groups'; +$txt['mboards_groups_desc'] = 'Groups allowed to access this board.
    Note: if the member is in any group or post group checked, they will have access to this board.'; +$txt['mboards_groups_regular_members'] = 'This group contains all members that have no primary group set.'; +$txt['mboards_groups_post_group'] = 'This group is a post count based group.'; +$txt['mboards_moderators'] = 'Moderators'; +$txt['mboards_moderators_desc'] = 'Additional members to have moderation privileges on this board. Note that administrators don\'t have to be listed here.'; +$txt['mboards_count_posts'] = 'Count Posts'; +$txt['mboards_count_posts_desc'] = 'Makes new replies and topics raise members\' post counts.'; +$txt['mboards_unchanged'] = 'Unchanged'; +$txt['mboards_theme'] = 'Board Theme'; +$txt['mboards_theme_desc'] = 'This allows you to change the look of your forum inside only this board.'; +$txt['mboards_theme_default'] = '(overall forum default.)'; +$txt['mboards_override_theme'] = 'Override Member\'s Theme'; +$txt['mboards_override_theme_desc'] = 'Use this board\'s theme even if the member didn\'t choose to use the defaults.'; + +$txt['mboards_redirect'] = 'Redirect to a web address'; +$txt['mboards_redirect_desc'] = 'Enable this option to redirect anyone who clicks on this board to another web address.'; +$txt['mboards_redirect_url'] = 'Address to redirect users to'; +$txt['mboards_redirect_url_desc'] = 'For example: "http://www.simplemachines.org".'; +$txt['mboards_redirect_reset'] = 'Reset redirect count'; +$txt['mboards_redirect_reset_desc'] = 'Selecting this will reset the redirection count for this board to zero.'; +$txt['mboards_current_redirects'] = 'Currently: %1$s'; +$txt['mboards_redirect_disabled'] = 'Note: Board must be empty of topics to enable this option.'; +$txt['mboards_redirect_disabled_recycle'] = 'Note: You cannot set the recycle bin board to be a redirection board.'; + +$txt['mboards_order_before'] = 'Before'; +$txt['mboards_order_child_of'] = 'Child of'; +$txt['mboards_order_in_category'] = 'In category'; +$txt['mboards_current_position'] = 'Current Position'; +$txt['no_valid_parent'] = 'Board %1$s does not have a valid parent. Use the \'find and repair errors\' function to fix this.'; + +$txt['mboards_recycle_disabled_delete'] = 'Note: You must select an alternative recycle bin board or disable recycling before you can delete this board.'; + +$txt['mboards_settings_desc'] = 'Edit general board and category settings.'; +$txt['groups_manage_boards'] = 'Membergroups allowed to manage boards and categories'; +$txt['mboards_settings_submit'] = 'Save'; +$txt['recycle_enable'] = 'Enable recycling of deleted topics'; +$txt['recycle_board'] = 'Board for recycled topics'; +$txt['recycle_board_unselected_notice'] = 'You have enabled the recycling of topics without specifying a board to place them in. This feature will not be enabled until you specify a board to place recycled topics into.'; +$txt['countChildPosts'] = 'Count child\'s posts in parent\'s totals'; +$txt['allow_ignore_boards'] = 'Allow boards to be ignored'; + +$txt['mboards_select_destination'] = 'Select destination for board \'%1$s\''; +$txt['mboards_cancel_moving'] = 'Cancel moving'; +$txt['mboards_move'] = 'move'; + +$txt['mboards_no_cats'] = 'There are currently no categories or boards configured.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManageCalendar.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManageCalendar.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,45 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManageMail.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManageMail.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,50 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManageMaintenance.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManageMaintenance.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,214 @@ +Critical
    '; +$txt['errortype_critical_desc'] = 'Critical errors. These should be taken care of as quickly as possible. Ignoring these errors can result in your forum failing and possibly security issues'; +$txt['errortype_database'] = 'Database'; +$txt['errortype_database_desc'] = 'Errors caused by faulty queries. These should be looked at and reported to the SMF team.'; +$txt['errortype_undefined_vars'] = 'Undefined'; +$txt['errortype_undefined_vars_desc'] = 'Errors caused by the use of undefined variables, indexes, or offsets.'; +$txt['errortype_template'] = 'Template'; +$txt['errortype_template_desc'] = 'Errors related to the loading of templates.'; +$txt['errortype_user'] = 'User'; +$txt['errortype_user_desc'] = 'Errors resulting from user errors. Includes failed passwords, trying to login when banned, and trying to do an action for which they do not have permission.'; + +$txt['maintain_recount'] = 'Recount all forum totals and statistics'; +$txt['maintain_recount_info'] = 'Should the total replies of a topic or the number of PMs in your inbox be incorrect: this function will recount all saved counts and statistics for you.'; +$txt['maintain_errors'] = 'Find and repair any errors'; +$txt['maintain_errors_info'] = 'If, for example, posts or topics are missing after a server crash, this function may help finding them again.'; +$txt['maintain_logs'] = 'Empty out unimportant logs'; +$txt['maintain_logs_info'] = 'This function will empty out all unimportant logs. This should be avoided unless something\'s wrong, but it doesn\'t hurt anything.'; +$txt['maintain_cache'] = 'Empty the file cache'; +$txt['maintain_cache_info'] = 'This function will empty out the file cache should you need it to be cleared.'; +$txt['maintain_optimize'] = 'Optimize all tables'; +$txt['maintain_optimize_info'] = 'This task allows you to optimize all tables. This will get rid of overhead, effectively making the tables smaller in size and your forum faster!'; +$txt['maintain_version'] = 'Check all files against current versions'; +$txt['maintain_version_info'] = 'This maintenance task allows you to do a detailed version check of all forum files against the official list of latest versions.'; +$txt['maintain_run_now'] = 'Run task now'; +$txt['maintain_return'] = 'Back to Forum Maintenance'; + +$txt['maintain_backup'] = 'Backup Database'; +$txt['maintain_backup_info'] = 'Download a backup copy of your forums database in case of emergency.'; +$txt['maintain_backup_struct'] = 'Save the table structure.'; +$txt['maintain_backup_data'] = 'Save the table data (the important stuff).'; +$txt['maintain_backup_gz'] = 'Compress the file with gzip.'; +$txt['maintain_backup_save'] = 'Download'; + +$txt['maintain_old'] = 'Remove Old Posts'; +$txt['maintain_old_since_days1'] = 'Remove all topics not posted in for '; +$txt['maintain_old_since_days2'] = ' days, which are:'; +$txt['maintain_old_nothing_else'] = 'Any sort of topic.'; +$txt['maintain_old_are_moved'] = 'Moved topic notices.'; +$txt['maintain_old_are_locked'] = 'Locked.'; +$txt['maintain_old_are_not_stickied'] = 'But don\'t count stickied topics.'; +$txt['maintain_old_all'] = 'All Boards (click to select specific boards)'; +$txt['maintain_old_choose'] = 'Specific Boards (click to select all)'; +$txt['maintain_old_remove'] = 'Remove now'; +$txt['maintain_old_confirm'] = 'Are you really sure you want to delete old posts now?\\n\\nThis cannot be undone!'; + +$txt['maintain_members'] = 'Remove Inactive Members'; +$txt['maintain_members_ungrouped'] = 'Ungrouped Members (Members with no assigned groups)'; +$txt['maintain_members_since1'] = 'Remove all members who have not'; +$txt['maintain_members_since2'] = 'for'; +$txt['maintain_members_since3'] = 'days.'; +$txt['maintain_members_activated'] = 'activated their account'; +$txt['maintain_members_logged_in'] = 'logged in'; +$txt['maintain_members_all'] = 'All Membergroups'; +$txt['maintain_members_choose'] = 'Selected Groups'; +$txt['maintain_members_confirm'] = 'Are you sure you really want to delete these member accounts?\\n\\nThis cannot be undone!'; + +$txt['utf8_title'] = 'Convert the database and data to UTF-8'; +$txt['utf8_introduction'] = 'UTF-8 is an international character set covering nearly all languages around the world. Converting your database and data to UTF-8 can make it easier to support multiple languages on the same board. It also can enhance search and sorting capabilities for languages with non-latin characters.'; +$txt['utf8_warning'] = 'If you want to convert your data and database to UTF-8, be aware of the following: +
      +
    • Converting character sets might be harmful for your data! Make sure you have backed up your database before converting.
    • +
    • Because UTF-8 is a richer character set than most other character sets, there\'s no way back, unless by restoring your database to before the conversion.
    • +
    • After converting your data and database to UTF-8, you will need UTF-8 compatible language files.
    • +
    '; +$txt['utf8_charset_not_supported'] = 'Conversion from %1$s to UTF-8 is not supported.'; +$txt['utf8_detected_charset'] = 'Based on your default language file (\'%1$s\'), the character set of your data would most likely be \'%2$s\'.'; +$txt['utf8_already_utf8'] = 'Your database and data already seem to be configured as UTF-8 data. No conversion is needed.'; +$txt['utf8_source_charset'] = 'Data character set'; +$txt['utf8_proceed'] = 'Proceed'; +$txt['utf8_database_charset'] = 'Database character set'; +$txt['utf8_target_charset'] = 'Convert data and database to'; +$txt['utf8_utf8'] = 'UTF-8'; +$txt['utf8_db_version_too_low'] = 'The version of MySQL that your database server is using is not high enough to support UTF-8 properly. A minimum version of 4.1.2 is required.'; +$txt['utf8_cannot_convert_fulltext'] = 'Your messages table is using a fulltext index for use when searching. You cannot proceed in converting to UTF-8 until that index is removed. You can re-create it after the conversion has been completed.'; + +$txt['entity_convert_title'] = 'Convert HTML-entities to UTF-8 characters'; +$txt['entity_convert_only_utf8'] = 'The database needs to be in UTF-8 format before HTML-entities can be converted to UTF-8'; +$txt['entity_convert_introduction'] = 'This function will convert all characters that are stored in the database as HTML-entities to UTF-8 characters. This is especially useful when you have just converted your forum from a character set like ISO-8859-1 while non-latin characters were used on the forum. The browser then sends all characters as HTML-entities. For example, the HTML-entity &#945; represents the greek letter α (alpha). Converting entities to UTF-8 will improve searching and sorting of text and reduce storage size.'; +$txt['entity_convert_proceed'] = 'Proceed'; + +// Move topics out. +$txt['move_topics_maintenance'] = 'Move Topics'; +$txt['move_topics_select_board'] = 'Select Board'; +$txt['move_topics_from'] = 'Move topics from'; +$txt['move_topics_to'] = 'to'; +$txt['move_topics_now'] = 'Move now'; +$txt['move_topics_confirm'] = 'Are you sure you want to move ALL the topics from "%board_from%" to "%board_to%"?'; + +$txt['maintain_reattribute_posts'] = 'Reattribute User Posts'; +$txt['reattribute_guest_posts'] = 'Attribute guest posts made with'; +$txt['reattribute_email'] = 'Email address of'; +$txt['reattribute_username'] = 'Username of'; +$txt['reattribute_current_member'] = 'Attribute posts to member'; +$txt['reattribute_increase_posts'] = 'Add posts to users post count'; +$txt['reattribute'] = 'Reattribute'; +// Don't use entities in the below string. +$txt['reattribute_confirm'] = 'Are you sure you want to attribute all guest posts with %type% of "%find%" to member "%member_to%"?'; +$txt['reattribute_confirm_username'] = 'a username'; +$txt['reattribute_confirm_email'] = 'an email address'; +$txt['reattribute_cannot_find_member'] = 'Could not find member to attribute posts to.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManageMembers.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManageMembers.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,129 @@ +Note: normally, post groups don\'t need access because the group the member is in will give them access.'; +$txt['membergroups_new_as_inherit'] = 'inherit from'; +$txt['membergroups_new_as_type'] = 'by type'; +$txt['membergroups_new_as_copy'] = 'based off of'; +$txt['membergroups_new_copy_none'] = '(none)'; +$txt['membergroups_can_edit_later'] = 'You can edit them later.'; + +$txt['membergroups_edit_group'] = 'Edit Membergroup'; +$txt['membergroups_edit_name'] = 'Group name'; +$txt['membergroups_edit_inherit_permissions'] = 'Inherit Permissions'; +$txt['membergroups_edit_inherit_permissions_desc'] = 'Select "No" to enable group to have own permission set.'; +$txt['membergroups_edit_inherit_permissions_no'] = 'No - Use Unique Permissions'; +$txt['membergroups_edit_inherit_permissions_from'] = 'Inherit From'; +$txt['membergroups_edit_hidden'] = 'Visibility'; +$txt['membergroups_edit_hidden_no'] = 'Visible'; +$txt['membergroups_edit_hidden_boardindex'] = 'Visible - Except in Group Key'; +$txt['membergroups_edit_hidden_all'] = 'Invisible'; +// Do not use numeric entities in the below string. +$txt['membergroups_edit_hidden_warning'] = 'Are you sure you want to disallow assignment of this group as a users primary group?\\n\\nDoing so will restrict assignment to additional groups only, and will update all current "primary" members to have it as an additional group only.'; +$txt['membergroups_edit_desc'] = 'Group description'; +$txt['membergroups_edit_group_type'] = 'Group Type'; +$txt['membergroups_edit_select_group_type'] = 'Select Group Type'; +$txt['membergroups_group_type_private'] = 'Private (Membership must be assigned)'; +$txt['membergroups_group_type_protected'] = 'Protected (Only administrators can manage and assign)'; +$txt['membergroups_group_type_request'] = 'Requestable (User may request membership)'; +$txt['membergroups_group_type_free'] = 'Free (User may leave and join group at will)'; +$txt['membergroups_group_type_post'] = 'Post Based (Membership based on post count)'; +$txt['membergroups_min_posts'] = 'Required posts'; +$txt['membergroups_online_color'] = 'Color in online list'; +$txt['membergroups_star_count'] = 'Number of star images'; +$txt['membergroups_star_image'] = 'Star image filename'; +$txt['membergroups_star_image_note'] = 'you can use $language for the language of the user'; +$txt['membergroups_max_messages'] = 'Max personal messages'; +$txt['membergroups_max_messages_note'] = '0 = unlimited'; +$txt['membergroups_edit_save'] = 'Save'; +$txt['membergroups_delete'] = 'Delete'; +$txt['membergroups_confirm_delete'] = 'Are you sure you want to delete this group?!'; + +$txt['membergroups_members_title'] = 'Viewing Group'; +$txt['membergroups_members_group_members'] = 'Group Members'; +$txt['membergroups_members_no_members'] = 'This group is currently empty'; +$txt['membergroups_members_add_title'] = 'Add a member to this group'; +$txt['membergroups_members_add_desc'] = 'List of Members to Add'; +$txt['membergroups_members_add'] = 'Add Members'; +$txt['membergroups_members_remove'] = 'Remove from Group'; +$txt['membergroups_members_last_active'] = 'Last Active'; +$txt['membergroups_members_additional_only'] = 'Add as additional group only.'; +$txt['membergroups_members_group_moderators'] = 'Group Moderators'; +$txt['membergroups_members_description'] = 'Description'; +// Use javascript escaping in the below. +$txt['membergroups_members_deadmin_confirm'] = 'Are you sure you wish to remove yourself from the Administration group?'; + +$txt['membergroups_postgroups'] = 'Post groups'; +$txt['membergroups_settings'] = 'Membergroup Settings'; +$txt['groups_manage_membergroups'] = 'Groups allowed to change membergroups'; +$txt['membergroups_select_permission_type'] = 'Select permission profile'; +$txt['membergroups_images_url'] = '{theme URL}/images/'; +$txt['membergroups_select_visible_boards'] = 'Show boards'; +$txt['membergroups_members_top'] = 'Members'; +$txt['membergroups_name'] = 'Name'; +$txt['membergroups_stars'] = 'Stars'; + +$txt['admin_browse_approve'] = 'Members whose accounts are awaiting approval'; +$txt['admin_browse_approve_desc'] = 'From here you can manage all members who are waiting to have their accounts approved.'; +$txt['admin_browse_activate'] = 'Members whose accounts are awaiting activation'; +$txt['admin_browse_activate_desc'] = 'This screen lists all the members who have still not activated their accounts at your forum.'; +$txt['admin_browse_awaiting_approval'] = 'Awaiting Approval (%1$d)'; +$txt['admin_browse_awaiting_activate'] = 'Awaiting Activation (%1$d)'; + +$txt['admin_browse_username'] = 'Username'; +$txt['admin_browse_email'] = 'Email Address'; +$txt['admin_browse_ip'] = 'IP Address'; +$txt['admin_browse_registered'] = 'Registered'; +$txt['admin_browse_id'] = 'ID'; +$txt['admin_browse_with_selected'] = 'With Selected'; +$txt['admin_browse_no_members_approval'] = 'No members currently await approval.'; +$txt['admin_browse_no_members_activate'] = 'No members currently have not activated their accounts.'; + +// Don't use entities in the below strings, except the main ones. (lt, gt, quot.) +$txt['admin_browse_warn'] = 'all selected members?'; +$txt['admin_browse_outstanding_warn'] = 'all affected members?'; +$txt['admin_browse_w_approve'] = 'Approve'; +$txt['admin_browse_w_activate'] = 'Activate'; +$txt['admin_browse_w_delete'] = 'Delete'; +$txt['admin_browse_w_reject'] = 'Reject'; +$txt['admin_browse_w_remind'] = 'Remind'; +$txt['admin_browse_w_approve_deletion'] = 'Approve (Delete Accounts)'; +$txt['admin_browse_w_email'] = 'and send email'; +$txt['admin_browse_w_approve_require_activate'] = 'Approve and Require Activation'; + +$txt['admin_browse_filter_by'] = 'Filter By'; +$txt['admin_browse_filter_show'] = 'Displaying'; +$txt['admin_browse_filter_type_0'] = 'Unactivated New Accounts'; +$txt['admin_browse_filter_type_2'] = 'Unactivated Email Changes'; +$txt['admin_browse_filter_type_3'] = 'Unapproved New Accounts'; +$txt['admin_browse_filter_type_4'] = 'Unapproved Account Deletions'; +$txt['admin_browse_filter_type_5'] = 'Unapproved "Under Age" Accounts'; + +$txt['admin_browse_outstanding'] = 'Outstanding Members'; +$txt['admin_browse_outstanding_days_1'] = 'With all members who registered longer than'; +$txt['admin_browse_outstanding_days_2'] = 'days ago'; +$txt['admin_browse_outstanding_perform'] = 'Perform the following action'; +$txt['admin_browse_outstanding_go'] = 'Perform Action'; + +$txt['check_for_duplicate'] = 'Check for Duplicates'; +$txt['dont_check_for_duplicate'] = 'Don\'t Check for Duplicates'; +$txt['duplicates'] = 'Duplicates'; + +$txt['not_activated'] = 'Not activated'; +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManagePaid.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManagePaid.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,209 @@ +Note:
    For subscriptions to be automatically updated for your users, you + will need to setup a return URL for each of your payment methods. For all payment types, this return URL should be set as:

    +   •  ' . $boardurl . '/subscriptions.php

    + You can edit the link for paypal directly, by clicking here.
    + For the other gateways (If installed) you can normally find it in your customer panels, usually under the term "Return URL" or "Callback URL".'; + +// View subscription strings. +$txt['paid_name'] = 'Name'; +$txt['paid_status'] = 'Status'; +$txt['paid_cost'] = 'Cost'; +$txt['paid_duration'] = 'Duration'; +$txt['paid_active'] = 'Active'; +$txt['paid_pending'] = 'Pending Payment'; +$txt['paid_finished'] = 'Finished'; +$txt['paid_total'] = 'Total'; +$txt['paid_is_active'] = 'Activated'; +$txt['paid_none_yet'] = 'You haven\'t set up any subscriptions yet.'; +$txt['paid_payments_pending'] = 'Payments Pending'; +$txt['paid_order'] = 'Order'; + +$txt['yes'] = 'Yes'; +$txt['no'] = 'No'; + +// Add/Edit/Delete subscription. +$txt['paid_add_subscription'] = 'Add Subscription'; +$txt['paid_edit_subscription'] = 'Edit Subscription'; +$txt['paid_delete_subscription'] = 'Delete Subscription'; + +$txt['paid_mod_name'] = 'Subscription Name'; +$txt['paid_mod_desc'] = 'Description'; +$txt['paid_mod_reminder'] = 'Send Reminder Email'; +$txt['paid_mod_reminder_desc'] = 'Days before subscription is due to expire to send reminder. (In days, 0 to disable)'; +$txt['paid_mod_email'] = 'Email to Send upon Completion'; +$txt['paid_mod_email_desc'] = 'Where {NAME} is members name; {FORUM} is community name. Email subject should be on first line. Blank for no email notification.'; +$txt['paid_mod_cost_usd'] = 'Cost (USD)'; +$txt['paid_mod_cost_eur'] = 'Cost (EUR)'; +$txt['paid_mod_cost_gbp'] = 'Cost (GBP)'; +$txt['paid_mod_cost_blank'] = 'Leave this blank to not offer this currency.'; +$txt['paid_mod_span'] = 'Length of Subscription'; +$txt['paid_mod_span_days'] = 'Days'; +$txt['paid_mod_span_weeks'] = 'Weeks'; +$txt['paid_mod_span_months'] = 'Months'; +$txt['paid_mod_span_years'] = 'Years'; +$txt['paid_mod_active'] = 'Active'; +$txt['paid_mod_active_desc'] = 'A subscription must be active for new members to join.'; +$txt['paid_mod_prim_group'] = 'Primary Group upon Subscription'; +$txt['paid_mod_prim_group_desc'] = 'Primary group to put the user into when they subscribe.'; +$txt['paid_mod_add_groups'] = 'Additional Groups upon Subscription'; +$txt['paid_mod_add_groups_desc'] = 'Additional groups to add the user to after subscription.'; +$txt['paid_mod_no_group'] = 'Don\'t Change'; +$txt['paid_mod_edit_note'] = 'Note that as this group has existing subscribers the group settings cannot be changed!'; +$txt['paid_mod_delete_warning'] = 'WARNING

    If you delete this subscription all users currently subscribed will lose any access rights granted by the subscription. Unless you are sure you want to do this it is recommended that you simply deactivate a subscription rather than delete it.
    '; +$txt['paid_mod_repeatable'] = 'Allow user to auto-renew this subscription'; +$txt['paid_mod_allow_partial'] = 'Allow partial subscription'; +$txt['paid_mod_allow_partial_desc'] = 'If this option is enabled, in the case where the user pays less than required they will be granted a subscription for the percentage of the duration they have paid for.'; +$txt['paid_mod_fixed_price'] = 'Subscription for fixed price and period'; +$txt['paid_mod_flexible_price'] = 'Subscription price varies on duration ordered'; +$txt['paid_mod_price_breakdown'] = 'Flexible Price Breakdown'; +$txt['paid_mod_price_breakdown_desc'] = 'Define here how much the subscription should cost dependant on the period they subscribe for. For example, it could cost 12USD to subscribe for a month, but only 100USD for a year. If you don\'t want to define a price for a particular period of time leave it blank.'; +$txt['flexible'] = 'Flexible'; + +$txt['paid_per_day'] = 'Price Per Day'; +$txt['paid_per_week'] = 'Price Per Week'; +$txt['paid_per_month'] = 'Price Per Month'; +$txt['paid_per_year'] = 'Price Per Year'; +$txt['day'] = 'Day'; +$txt['week'] = 'Week'; +$txt['month'] = 'Month'; +$txt['year'] = 'Year'; + +// View subscribed users. +$txt['viewing_users_subscribed'] = 'Viewing Users'; +$txt['view_users_subscribed'] = 'Viewing users subscribed to: "%1$s"'; +$txt['no_subscribers'] = 'There are currently no subscribers to this subscription!'; +$txt['add_subscriber'] = 'Add New Subscriber'; +$txt['edit_subscriber'] = 'Edit Subscriber'; +$txt['delete_selected'] = 'Delete Selected'; +$txt['complete_selected'] = 'Complete Selected'; + +// !!! These strings are used in conjunction with JavaScript. Use numeric entities. +$txt['delete_are_sure'] = 'Are you sure you want to delete all records of the selected subscriptions?'; +$txt['complete_are_sure'] = 'Are you sure you want to complete the selected subscriptions?'; + +$txt['start_date'] = 'Start Date'; +$txt['end_date'] = 'End Date'; +$txt['start_date_and_time'] = 'Start Date and Time'; +$txt['end_date_and_time'] = 'End Date and Time'; +$txt['edit'] = 'EDIT'; +$txt['one_username'] = 'Please enter one username only.'; +$txt['hour'] = 'Hour'; +$txt['minute'] = 'Minute'; +$txt['error_member_not_found'] = 'The member entered could not be found'; +$txt['member_already_subscribed'] = 'This member is already subscribed to this subscription. Please edit their existing subscription.'; +$txt['search_sub'] = 'Find User'; + +// Make payment. +$txt['paid_confirm_payment'] = 'Confirm Payment'; +$txt['paid_confirm_desc'] = 'To continue through to payment please check the details below and hit "Order"'; +$txt['paypal'] = 'PayPal'; +$txt['paid_confirm_paypal'] = 'To pay using PayPal please click the button below. You will be directed to the PayPal site for payment.'; +$txt['paid_paypal_order'] = 'Order with PayPal'; +$txt['worldpay'] = 'WorldPay'; +$txt['paid_confirm_worldpay'] = 'To pay using WorldPay please click the button below. You will be directed to the WorldPay site for payment.'; +$txt['paid_worldpay_order'] = 'Order with WorldPay'; +$txt['nochex'] = 'Nochex'; +$txt['paid_confirm_nochex'] = 'To pay using Nochex please click the button below. You will be directed to the Nochex site for payment.'; +$txt['paid_nochex_order'] = 'Order with Nochex'; +$txt['authorize'] = 'Authorize.Net'; +$txt['paid_confirm_authorize'] = 'To pay using Authorize.Net please click the button below. You will be directed to the Authorize.Net site for payment.'; +$txt['paid_authorize_order'] = 'Order with Authorize.Net'; +$txt['2co'] = '2checkout'; +$txt['paid_confirm_2co'] = 'To pay using 2co.com please click the button below. You will be directed to the 2co.com site for payment.'; +$txt['paid_2co_order'] = 'Order with 2co.com'; +$txt['paid_done'] = 'Payment Complete'; +$txt['paid_done_desc'] = 'Thank you for your payment. Once the transaction has been verified the subscription will be activated.'; +$txt['paid_sub_return'] = 'Return to Subscriptions'; +$txt['paid_current_desc'] = 'Below is a list of all your current and previous subscriptions. To extend an existing subscription simply select it from the list above.'; +$txt['paid_admin_add'] = 'Add This Subscription'; + +$txt['paid_not_set_currency'] = 'You have not setup your currency yet. Please do so from the Settings section before continuing.'; +$txt['paid_no_cost_value'] = 'You must enter a cost and subscription length.'; +$txt['paid_all_freq_blank'] = 'You must enter a cost for at least one of the four durations.'; + +// Some error strings. +$txt['paid_no_data'] = 'No valid data was sent to the script.'; + +$txt['paypal_could_not_connect'] = 'Could not connect to PayPal server'; +$txt['paid_sub_not_active'] = 'That subscription is not taking any new users!'; +$txt['paid_disabled'] = 'Paid subscriptions are currently disabled!'; +$txt['paid_unknown_transaction_type'] = 'Unknown Paid Subscriptions transaction type.'; +$txt['paid_empty_member'] = 'Paid subscription handler could not recover member ID'; +$txt['paid_could_not_find_member'] = 'Paid subscription handler could not find member with ID: %1$d'; +$txt['paid_count_not_find_subscription'] = 'Paid subscription handler could not find subscription for member ID: %1$s, subscription ID: %2$s'; +$txt['paid_count_not_find_subscription_log'] = 'Paid subscription handler could not find subscription log entry for member ID: %1$s, subscription ID: %2$s'; +$txt['paid_count_not_find_outstanding_payment'] = 'Could not find outstanding payment entry for member ID: %1$s, subscription ID: %2$s so ignoring'; +$txt['paid_admin_not_setup_gateway'] = 'Sorry, the admin has not yet finished setting up paid subscriptions. Please check back later.'; +$txt['paid_make_recurring'] = 'Make this a recurring payment'; + +$txt['subscriptions'] = 'Subscriptions'; +$txt['subscription'] = 'Subscription'; +$txt['paid_subs_desc'] = 'Below is a list of all the subscriptions which are available on this forum.'; +$txt['paid_subs_none'] = 'There are currently no paid subscriptions available!'; + +$txt['paid_current'] = 'Existing Subscriptions'; +$txt['pending_payments'] = 'Pending Payments'; +$txt['pending_payments_desc'] = 'This member has attempted to make the following payments for this subscription but the confirmation has not been received by the forum. If you are sure the payment has been received click "accept" to action to subscription. Alternatively you can click "Remove" to remove all reference to the payment.'; +$txt['pending_payments_value'] = 'Value'; +$txt['pending_payments_accept'] = 'Accept'; +$txt['pending_payments_remove'] = 'Remove'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManagePermissions.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManagePermissions.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,345 @@ +Note: You cannot edit this permission profile as it is a predefined profile included within the forum software by default. If you wish to change the permissions of this profile you must first create a duplicate profile. You can carry out this task by clicking here.'; + +$txt['permissions_for_profile'] = 'Permissions for Profile'; +$txt['permissions_boards_desc'] = 'The list below shows which set of permissions has been assigned to each board on your forum. You may edit the assigned permission profile by either clicking the board name or select "edit all" from the bottom of the page. To edit the profile itself simply click the profile name.'; +$txt['permissions_board_all'] = 'Edit All'; +$txt['permission_profile'] = 'Permission Profile'; +$txt['permission_profile_desc'] = 'Which permission set the board should use.'; +$txt['permission_profile_inherit'] = 'Inherit from parent board'; + +$txt['permissions_profile'] = 'Profile'; +$txt['permissions_profiles_desc'] = 'Permission profiles are assigned to individual boards to allow you to easily manage your security settings. From this area you can create, edit and delete permission profiles.'; +$txt['permissions_profiles_change_for_board'] = 'Edit Permission Profile For: "%1$s"'; +$txt['permissions_profile_default'] = 'Default'; +$txt['permissions_profile_no_polls'] = 'No Polls'; +$txt['permissions_profile_reply_only'] = 'Reply Only'; +$txt['permissions_profile_read_only'] = 'Read Only'; + +$txt['permissions_profile_rename'] = 'Rename'; +$txt['permissions_profile_edit'] = 'Edit Profiles'; +$txt['permissions_profile_new'] = 'New Profile'; +$txt['permissions_profile_new_create'] = 'Create'; +$txt['permissions_profile_name'] = 'Profile Name'; +$txt['permissions_profile_used_by'] = 'Used By'; +$txt['permissions_profile_used_by_one'] = '1 Board'; +$txt['permissions_profile_used_by_many'] = '%1$d Boards'; +$txt['permissions_profile_used_by_none'] = 'No Boards'; +$txt['permissions_profile_do_edit'] = 'Edit'; +$txt['permissions_profile_do_delete'] = 'Delete'; +$txt['permissions_profile_copy_from'] = 'Copy Permissions From'; + +$txt['permissions_includes_inherited'] = 'Inherited Groups'; + +$txt['permissions_all'] = 'all'; +$txt['permissions_none'] = 'none'; +$txt['permissions_set_permissions'] = 'Set permissions'; + +$txt['permissions_advanced_options'] = 'Advanced Options'; +$txt['permissions_with_selection'] = 'With selection'; +$txt['permissions_apply_pre_defined'] = 'Apply pre-defined permission set'; +$txt['permissions_select_pre_defined'] = 'Select a pre-defined profile'; +$txt['permissions_copy_from_board'] = 'Copy permissions from this board'; +$txt['permissions_select_board'] = 'Select a board'; +$txt['permissions_like_group'] = 'Set permissions like this group'; +$txt['permissions_select_membergroup'] = 'Select a membergroup'; +$txt['permissions_add'] = 'Add permission'; +$txt['permissions_remove'] = 'Clear permission'; +$txt['permissions_deny'] = 'Deny permission'; +$txt['permissions_select_permission'] = 'Select a permission'; + +// All of the following block of strings should not use entities, instead use \\" for " etc. +$txt['permissions_only_one_option'] = 'You can only select one action to modify the permissions'; +$txt['permissions_no_action'] = 'No action selected'; +$txt['permissions_deny_dangerous'] = 'You are about to deny one or more permissions.\\nThis can be dangerous and cause unexpected results if you haven\'t made sure no one is \\"accidentally\\" in the group or groups you are denying permissions to.\\n\\nAre you sure you want to continue?'; + +$txt['permissions_modify_group'] = 'Modify Group'; +$txt['permissions_general'] = 'General Permissions'; +$txt['permissions_board'] = 'Default Board Profile Permissions'; +$txt['permissions_board_desc'] = 'Note: changing these board permissions will affect all boards currently assigned the "Default" permissions profile. Boards not using the "Default" profile will not be affected by changes to this page.'; +$txt['permissions_commit'] = 'Save changes'; +$txt['permissions_on'] = 'in profile'; +$txt['permissions_local_for'] = 'Permissions for group'; +$txt['permissions_option_on'] = 'A'; +$txt['permissions_option_off'] = 'X'; +$txt['permissions_option_deny'] = 'D'; +$txt['permissions_option_desc'] = 'For each permission you can pick either \'Allow\' (A), \'Disallow\' (X), or \'Deny\' (D).

    Remember that if you deny a permission, any member - whether moderator or otherwise - that is in that group will be denied that as well.
    For this reason, you should use deny carefully, only when necessary. Disallow, on the other hand, denies unless otherwise granted.'; +$txt['permissions_change_view'] = 'Change View'; +$txt['permissions_view_simple'] = 'Simple'; +$txt['permissions_view_classic'] = 'Classic'; + +$txt['permissiongroup_general'] = 'General'; +$txt['permissionname_view_stats'] = 'View forum statistics'; +$txt['permissionhelp_view_stats'] = 'The forum statistics is a page summarizing all statistics of the forum, like member count, daily number of posts, and several top 10 statistics. Enabling this permission adds a link to the bottom of the board index (\'[More Stats]\').'; +$txt['permissionname_view_mlist'] = 'View the memberlist and groups'; +$txt['permissionhelp_view_mlist'] = 'The memberlist shows all members that have registered on your forum. The list can be sorted and searched. The memberlist is linked from both the boardindex and the stats page, by clicking on the number of members. It also applies to the groups page which is a mini memberlist of people in that group.'; +$txt['permissionname_who_view'] = 'View Who\'s Online'; +$txt['permissionhelp_who_view'] = 'Who\'s online shows all members that are currently online and what they are doing at that moment. This permission will only work if you also have enabled it in \'Features and Options\'. You can access the \'Who\'s Online\' screen by clicking the link in the \'Users Online\' section of the board index. Even if this is denied, members will still be able to see who\'s online, just not where they are.'; +$txt['permissionname_search_posts'] = 'Search for posts and topics'; +$txt['permissionhelp_search_posts'] = 'The Search permission allows the user to search all boards he or she is allowed to access. When the search permission is enabled, a \'Search\' button will be added to the forum button bar.'; +$txt['permissionname_karma_edit'] = 'Change other people\'s karma'; +$txt['permissionhelp_karma_edit'] = 'Karma is a feature that shows the popularity of a member. In order to use this feature, you need to have it enabled in \'Features and Options\'. This permission will allow a membergroup to cast a vote. This permission has no effect on guests.'; + +$txt['permissiongroup_pm'] = 'Personal Messaging'; +$txt['permissionname_pm_read'] = 'Read personal messages'; +$txt['permissionhelp_pm_read'] = 'This permission allows users to access the Personal Messages section and read their Personal Messages. Without this permission a user is unable to send Personal Messages.'; +$txt['permissionname_pm_send'] = 'Send personal messages'; +$txt['permissionhelp_pm_send'] = 'Send personal messages to other registered members. Requires the \'Read personal messages\' permission.'; + +$txt['permissiongroup_calendar'] = 'Calendar'; +$txt['permissionname_calendar_view'] = 'View the calendar'; +$txt['permissionhelp_calendar_view'] = 'The calendar shows for each month the birthdays, events and holidays. This permission allows access to this calendar. When this permission is enabled, a button will be added to the top button bar and a list will be shown at the bottom of the board index with current and upcoming birthdays, events and holidays. The calendar needs be enabled from \'Configuration - Core Features\'.'; +$txt['permissionname_calendar_post'] = 'Create events in the calendar'; +$txt['permissionhelp_calendar_post'] = 'An Event is a topic linked to a certain date or date range. Creating events can be done from the calendar. An event can only be created if the user that creates the event is allowed to post new topics.'; +$txt['permissionname_calendar_edit'] = 'Edit events in the calendar'; +$txt['permissionhelp_calendar_edit'] = 'An Event is a topic linked to a certain date or date range. The event can be edited by clicking the red asterisk (*) next to the event in the calendar view. In order to be able to edit an event, a user must have sufficient permissions to edit the first message of the topic that is linked to the event.'; +$txt['permissionname_calendar_edit_own'] = 'Own events'; +$txt['permissionname_calendar_edit_any'] = 'Any events'; + +$txt['permissiongroup_maintenance'] = 'Forum administration'; +$txt['permissionname_admin_forum'] = 'Administrate forum and database'; +$txt['permissionhelp_admin_forum'] = 'This permission allows a user to:
    • change forum, database and theme settings
    • manage packages
    • use the forum and database maintenance tools
    • view the error and mod logs
    Use this permission with caution, as it is very powerful.'; +$txt['permissionname_manage_boards'] = 'Manage boards and categories'; +$txt['permissionhelp_manage_boards'] = 'This permission allows creation, editing and removal of boards and categories.'; +$txt['permissionname_manage_attachments'] = 'Manage attachments and avatars'; +$txt['permissionhelp_manage_attachments'] = 'This permission allows access to the attachment center, where all forum attachments and avatars are listed and can be removed.'; +$txt['permissionname_manage_smileys'] = 'Manage smileys and message icons'; +$txt['permissionhelp_manage_smileys'] = 'This allows access to the smiley center. In the smiley center you can add, edit and remove smileys and smiley sets. If you\'ve enabled customized message icons you are also able to add and edit message icons with this permission.'; +$txt['permissionname_edit_news'] = 'Edit news'; +$txt['permissionhelp_edit_news'] = 'The news function allows a random news line to appear on each screen. In order to use the news function, enabled it in the forum settings.'; +$txt['permissionname_access_mod_center'] = 'Access the moderation center'; +$txt['permissionhelp_access_mod_center'] = 'With this permission any members of this group can access the moderation center from where they will have access to functionality to ease moderation. Note that this does not in itself grant any moderation privileges.'; + +$txt['permissiongroup_member_admin'] = 'Member administration'; +$txt['permissionname_moderate_forum'] = 'Moderate forum members'; +$txt['permissionhelp_moderate_forum'] = 'This permission includes all important member moderation functions:
    • access to registration management
    • access to the view/delete members screen
    • extensive profile info, including track IP/user and (hidden) online status
    • activate accounts
    • get approval notifications and approve accounts
    • immune to ignore PM
    • several small things
    '; +$txt['permissionname_manage_membergroups'] = 'Manage and assign membergroups'; +$txt['permissionhelp_manage_membergroups'] = 'This permission allows a user to edit membergroups and assign membergroups to other members.'; +$txt['permissionname_manage_permissions'] = 'Manage permissions'; +$txt['permissionhelp_manage_permissions'] = 'This permission allows a user to edit all permissions of a membergroup, globally or for individual boards.'; +$txt['permissionname_manage_bans'] = 'Manage ban list'; +$txt['permissionhelp_manage_bans'] = 'This permission allows a user to add or remove usernames, IP addresses, hostnames and email addresses to a list of banned users. It also allows a user to view and remove log entries of banned users that attempted to login.'; +$txt['permissionname_send_mail'] = 'Send a forum email to members'; +$txt['permissionhelp_send_mail'] = 'Mass mail all forum members, or just a few membergroups by email or personal message (the latter requires \'Send Personal Message\' permission).'; +$txt['permissionname_issue_warning'] = 'Issue warnings to members'; +$txt['permissionhelp_issue_warning'] = 'Issue a warning to members of the forum and change that members\' warning level. Requires the warning system to be enabled.'; + +$txt['permissiongroup_profile'] = 'Member Profiles'; +$txt['permissionname_profile_view'] = 'View profile summary and stats'; +$txt['permissionhelp_profile_view'] = 'This permission allows users clicking on a username to see a summary of profile settings, some statistics and all posts of the user.'; +$txt['permissionname_profile_view_own'] = 'Own profile'; +$txt['permissionname_profile_view_any'] = 'Any profile'; +$txt['permissionname_profile_identity'] = 'Edit account settings'; +$txt['permissionhelp_profile_identity'] = 'Account settings are the basic settings of a profile, like password, email address, membergroup and preferred language.'; +$txt['permissionname_profile_identity_own'] = 'Own profile'; +$txt['permissionname_profile_identity_any'] = 'Any profile'; +$txt['permissionname_profile_extra'] = 'Edit additional profile settings'; +$txt['permissionhelp_profile_extra'] = 'Additional profile settings include settings for avatars, theme preferences, notifications and Personal Messages.'; +$txt['permissionname_profile_extra_own'] = 'Own profile'; +$txt['permissionname_profile_extra_any'] = 'Any profile'; +$txt['permissionname_profile_title'] = 'Edit custom title'; +$txt['permissionhelp_profile_title'] = 'The custom title is shown on the topic display page, under the profile of each user that has a custom title.'; +$txt['permissionname_profile_title_own'] = 'Own profile'; +$txt['permissionname_profile_title_any'] = 'Any profile'; +$txt['permissionname_profile_remove'] = 'Delete account'; +$txt['permissionhelp_profile_remove'] = 'This permission allows a user to delete his account, when set to \'Own Account\'.'; +$txt['permissionname_profile_remove_own'] = 'Own account'; +$txt['permissionname_profile_remove_any'] = 'Any account'; +$txt['permissionname_profile_server_avatar'] = 'Select an avatar from the server'; +$txt['permissionhelp_profile_server_avatar'] = 'If enabled this will allow a user to select an avatar from the avatar collections installed on the server.'; +$txt['permissionname_profile_upload_avatar'] = 'Upload an avatar to the server'; +$txt['permissionhelp_profile_upload_avatar'] = 'This permission will allow a user to upload their personal avatar to the server.'; +$txt['permissionname_profile_remote_avatar'] = 'Choose a remotely stored avatar'; +$txt['permissionhelp_profile_remote_avatar'] = 'Because avatars might influence the page creation time negatively, it is possible to disallow certain membergroups to use avatars from external servers.'; + +$txt['permissiongroup_general_board'] = 'General'; +$txt['permissionname_moderate_board'] = 'Moderate board'; +$txt['permissionhelp_moderate_board'] = 'The moderate board permission adds a few small permissions that make a moderator a real moderator. Permissions include replying to locked topics, changing the poll expire time and viewing poll results.'; + +$txt['permissiongroup_topic'] = 'Topics'; +$txt['permissionname_post_new'] = 'Post new topics'; +$txt['permissionhelp_post_new'] = 'This permission allows users to post new topics. It doesn\'t allow to post replies to topics.'; +$txt['permissionname_merge_any'] = 'Merge any topic'; +$txt['permissionhelp_merge_any'] = 'Merge two or more topic into one. The order of messages within the merged topic will be based on the time the messages were created. A user can only merge topics on those boards a user is allowed to merge. In order to merge multiple topics at once, a user has to enable quickmoderation in their profile settings.'; +$txt['permissionname_split_any'] = 'Split any topic'; +$txt['permissionhelp_split_any'] = 'Split a topic into two separate topics.'; +$txt['permissionname_send_topic'] = 'Send topics to friends'; +$txt['permissionhelp_send_topic'] = 'This permission allows a user to mail a topic to a friend, by entering their email address and allows adding a message.'; +$txt['permissionname_make_sticky'] = 'Make topics sticky'; +$txt['permissionhelp_make_sticky'] = 'Sticky topics are topics that always remain on top of a board. They can be useful for announcements or other important messages.'; +$txt['permissionname_move'] = 'Move topic'; +$txt['permissionhelp_move'] = 'Move a topic from one board to the other. Users can only select target boards they are allowed to access.'; +$txt['permissionname_move_own'] = 'Own topic'; +$txt['permissionname_move_any'] = 'Any topic'; +$txt['permissionname_lock'] = 'Lock topics'; +$txt['permissionhelp_lock'] = 'This permission allows a user to lock a topic. This can be done in order to make sure no one can reply to a topic. Only uses with a \'Moderate board\' permission can still post in locked topics.'; +$txt['permissionname_lock_own'] = 'Own topic'; +$txt['permissionname_lock_any'] = 'Any topic'; +$txt['permissionname_remove'] = 'Remove topics'; +$txt['permissionhelp_remove'] = 'Delete topics as a whole. Note that this permission doesn\'t allow to delete specific messages within the topic!'; +$txt['permissionname_remove_own'] = 'Own topic'; +$txt['permissionname_remove_any'] = 'Any topics'; +$txt['permissionname_post_reply'] = 'Post replies to topics'; +$txt['permissionhelp_post_reply'] = 'This permission allows replying to topics.'; +$txt['permissionname_post_reply_own'] = 'Own topic'; +$txt['permissionname_post_reply_any'] = 'Any topic'; +$txt['permissionname_modify_replies'] = 'Modify replies to own topics'; +$txt['permissionhelp_modify_replies'] = 'This permission allows a user that started a topic to modify all replies to their topic.'; +$txt['permissionname_delete_replies'] = 'Delete replies to own topics'; +$txt['permissionhelp_delete_replies'] = 'This permission allows a user that started a topic to remove all replies to their topic.'; +$txt['permissionname_announce_topic'] = 'Announce topic'; +$txt['permissionhelp_announce_topic'] = 'This allows a user to send an announcement e-mail about a topic to all members or to a few membergroups.'; + +$txt['permissiongroup_post'] = 'Posts'; +$txt['permissionname_delete'] = 'Delete posts'; +$txt['permissionhelp_delete'] = 'Remove posts. This does not allow a user to delete the first post of a topic.'; +$txt['permissionname_delete_own'] = 'Own post'; +$txt['permissionname_delete_any'] = 'Any post'; +$txt['permissionname_modify'] = 'Modify posts'; +$txt['permissionhelp_modify'] = 'Edit posts'; +$txt['permissionname_modify_own'] = 'Own post'; +$txt['permissionname_modify_any'] = 'Any post'; +$txt['permissionname_report_any'] = 'Report posts to the moderators'; +$txt['permissionhelp_report_any'] = 'This permission adds a link to each message, allowing a user to report a post to a moderator. On reporting, all moderators on that board will receive an email with a link to the reported post and a description of the problem (as given by the reporting user).'; + +$txt['permissiongroup_poll'] = 'Polls'; +$txt['permissionname_poll_view'] = 'View polls'; +$txt['permissionhelp_poll_view'] = 'This permission allows a user to view a poll. Without this permission, the user will only see the topic.'; +$txt['permissionname_poll_vote'] = 'Vote in polls'; +$txt['permissionhelp_poll_vote'] = 'This permission allows a (registered) user to cast one vote. It doesn\'t apply to guests.'; +$txt['permissionname_poll_post'] = 'Post polls'; +$txt['permissionhelp_poll_post'] = 'This permission allows a user to post a new poll. The user needs to have the \'Post new topics\' permission.'; +$txt['permissionname_poll_add'] = 'Add poll to topics'; +$txt['permissionhelp_poll_add'] = 'Add poll to topics allows a user to add a poll after the topic has been created. This permission requires sufficient rights to edit the first post of a topic.'; +$txt['permissionname_poll_add_own'] = 'Own topics'; +$txt['permissionname_poll_add_any'] = 'Any topics'; +$txt['permissionname_poll_edit'] = 'Edit polls'; +$txt['permissionhelp_poll_edit'] = 'This permission allows a user to edit the options of a poll and to reset the poll. In order to edit the maximum number of votes and the expiration time, a user needs to have the \'Moderate board\' permission.'; +$txt['permissionname_poll_edit_own'] = 'Own poll'; +$txt['permissionname_poll_edit_any'] = 'Any poll'; +$txt['permissionname_poll_lock'] = 'Lock polls'; +$txt['permissionhelp_poll_lock'] = 'Locking polls prevents the poll from accepting any more votes.'; +$txt['permissionname_poll_lock_own'] = 'Own poll'; +$txt['permissionname_poll_lock_any'] = 'Any poll'; +$txt['permissionname_poll_remove'] = 'Remove polls'; +$txt['permissionhelp_poll_remove'] = 'This permission allows removal of polls.'; +$txt['permissionname_poll_remove_own'] = 'Own poll'; +$txt['permissionname_poll_remove_any'] = 'Any poll'; + +$txt['permissiongroup_approval'] = 'Post Moderation'; +$txt['permissionname_approve_posts'] = 'Approve items awaiting moderation'; +$txt['permissionhelp_approve_posts'] = 'This permission allows a user to approve all unapproved items on a board.'; +$txt['permissionname_post_unapproved_replies'] = 'Post replies to topics, but hide until approved'; +$txt['permissionhelp_post_unapproved_replies'] = 'This permission allows a user to post replies to a topic. The replies will not be shown until approved by a moderator.'; +$txt['permissionname_post_unapproved_replies_own'] = 'Own topic'; +$txt['permissionname_post_unapproved_replies_any'] = 'Any topic'; +$txt['permissionname_post_unapproved_topics'] = 'Post new topics, but hide until approved'; +$txt['permissionhelp_post_unapproved_topics'] = 'This permission allows a user to post a new topic which will require approval before being shown.'; +$txt['permissionname_post_unapproved_attachments'] = 'Post attachments, but hide until approved'; +$txt['permissionhelp_post_unapproved_attachments'] = 'This permission allows a user to attach files to their posts. The attached files will then require approval before being shown to other users.'; + +$txt['permissiongroup_notification'] = 'Notifications'; +$txt['permissionname_mark_any_notify'] = 'Request notification on replies'; +$txt['permissionhelp_mark_any_notify'] = 'This feature allows users to receive a notification whenever someone replies to a topic they subscribed to.'; +$txt['permissionname_mark_notify'] = 'Request notification on new topics'; +$txt['permissionhelp_mark_notify'] = 'Notification on new topics is a feature that allows a user to receive an email every time a new topic is created on the board they subscribe to.'; + +$txt['permissiongroup_attachment'] = 'Attachments'; +$txt['permissionname_view_attachments'] = 'View attachments'; +$txt['permissionhelp_view_attachments'] = 'Attachments are files that are attached to posted messages. This feature can be enabled and configured in \'Attachments and avatars\'. Since attachments are not directly accessed, you can protect them from being downloaded by users that don\'t have this permission.'; +$txt['permissionname_post_attachment'] = 'Post attachments'; +$txt['permissionhelp_post_attachment'] = 'Attachments are files that are attached to posted messages. One message can contain multiple attachments.'; + +$txt['permissiongroup_simple_view_basic_info'] = 'Use basic forum functionality'; +$txt['permissiongroup_simple_use_pm_system'] = 'Contact members using the personal messaging system'; +$txt['permissiongroup_simple_post_calendar'] = 'Post events onto the calendar'; +$txt['permissiongroup_simple_edit_profile'] = 'Personalize their profile'; +$txt['permissiongroup_simple_delete_account'] = 'Delete their account'; +$txt['permissiongroup_simple_use_avatar'] = 'Select or upload an avatar'; +$txt['permissiongroup_simple_moderate_general'] = 'Moderate the entire forum'; +$txt['permissiongroup_simple_administrate'] = 'Carry out administrative duties'; + +$txt['permissionname_simple_calendar_edit_own'] = 'Edit their own calendar events'; +$txt['permissionname_simple_calendar_edit_any'] = 'Edit other people\'s calendar events'; +$txt['permissionname_simple_profile_view_own'] = 'View their own profile'; +$txt['permissionname_simple_profile_view_any'] = 'View other people\'s profiles'; +$txt['permissionname_simple_profile_identity_own'] = 'Edit their account settings'; +$txt['permissionname_simple_profile_identity_any'] = 'Edit other people\'s account settings'; +$txt['permissionname_simple_profile_extra_own'] = 'Edit their additional profile options'; +$txt['permissionname_simple_profile_extra_any'] = 'Edit other people\'s profile options'; +$txt['permissionname_simple_profile_title_own'] = 'Choose a custom title for themselves'; +$txt['permissionname_simple_profile_title_any'] = 'Edit other people\'s custom titles'; +$txt['permissionname_simple_profile_remove_own'] = 'Delete their own account'; +$txt['permissionname_simple_profile_remove_any'] = 'Delete other user\'s accounts'; + +$txt['permissiongroup_simple_make_unapproved_posts'] = 'Post topics and replies to the board only after they have been approved'; +$txt['permissiongroup_simple_make_posts'] = 'Post topics and replies to the board'; +$txt['permissiongroup_simple_post_polls'] = 'Make new polls'; +$txt['permissiongroup_simple_participate'] = 'View additional board content'; +$txt['permissiongroup_simple_modify'] = 'Modify their posts'; +$txt['permissiongroup_simple_notification'] = 'Request notifications'; +$txt['permissiongroup_simple_attach'] = 'Post attachments'; +$txt['permissiongroup_simple_moderate'] = 'Moderate the board'; + +$txt['permissionname_simple_post_unapproved_replies_own'] = 'Post replies to their own topic - but require approval'; +$txt['permissionname_simple_post_unapproved_replies_any'] = 'Post replies to any topic - but require approval'; +$txt['permissionname_simple_post_reply_own'] = 'Post replies to a topic they started'; +$txt['permissionname_simple_post_reply_any'] = 'Post replies to any topic'; +$txt['permissionname_simple_move_own'] = 'Move their own topics'; +$txt['permissionname_simple_move_any'] = 'Move anyone\'s topic'; +$txt['permissionname_simple_lock_own'] = 'Lock their own topic'; +$txt['permissionname_simple_lock_any'] = 'Lock anyone\'s topic'; +$txt['permissionname_simple_remove_own'] = 'Remove their own topic'; +$txt['permissionname_simple_remove_any'] = 'Remove anyone\'s topic'; +$txt['permissionname_simple_delete_own'] = 'Delete a post that they made'; +$txt['permissionname_simple_delete_any'] = 'Delete a post made by anyone'; +$txt['permissionname_simple_modify_own'] = 'Modify their own post'; +$txt['permissionname_simple_modify_any'] = 'Modify someone else\'s post'; +$txt['permissionname_simple_poll_add_own'] = 'Add a poll to a topic they created'; +$txt['permissionname_simple_poll_add_any'] = 'Add a poll to any topic'; +$txt['permissionname_simple_poll_edit_own'] = 'Edit a poll they created'; +$txt['permissionname_simple_poll_edit_any'] = 'Edit anyone\'s poll'; +$txt['permissionname_simple_poll_lock_own'] = 'Lock their own poll'; +$txt['permissionname_simple_poll_lock_any'] = 'Lock anyone\'s poll'; +$txt['permissionname_simple_poll_remove_own'] = 'Remove a poll they created'; +$txt['permissionname_simple_poll_remove_any'] = 'Remove anyone\'s poll'; + +$txt['permissionicon'] = ''; + +$txt['permission_settings_title'] = 'Permission Settings'; +$txt['groups_manage_permissions'] = 'Membergroups allowed to manage permissions'; +$txt['permission_settings_submit'] = 'Save'; +$txt['permission_settings_enable_deny'] = 'Enable the option to deny permissions'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['permission_disable_deny_warning'] = 'Turning off this option will update \\\'Deny\\\'-permissions to \\\'Disallow\\\'.'; +$txt['permission_by_board_desc'] = 'Here you can set which permissions profile a board uses. You can create new permission profiles from the "Edit Profiles" menu.'; +$txt['permission_settings_desc'] = 'Here you can set who has permission to change permissions, as well as how sophisticated the permission system should be.'; +$txt['permission_settings_enable_postgroups'] = 'Enable permissions for post count based groups'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['permission_disable_postgroups_warning'] = 'Disabling this setting will remove permissions currently set to post count based groups.'; + +$txt['permissions_post_moderation_desc'] = 'From this page you can easily change which groups have their posts moderated for a particular permissions profile.'; +$txt['permissions_post_moderation_deny_note'] = 'Note that while you have advanced permissions enabled you cannot apply the "deny" permission from this page. Please edit the permissions directly if you wish to apply a deny permission.'; +$txt['permissions_post_moderation_select'] = 'Select Profile'; +$txt['permissions_post_moderation_new_topics'] = 'New Topics'; +$txt['permissions_post_moderation_replies_own'] = 'Own Replies'; +$txt['permissions_post_moderation_replies_any'] = 'Any Replies'; +$txt['permissions_post_moderation_attachments'] = 'Attachments'; +$txt['permissions_post_moderation_legend'] = 'Legend'; +$txt['permissions_post_moderation_allow'] = 'Can create'; +$txt['permissions_post_moderation_moderate'] = 'Can create but requires approval'; +$txt['permissions_post_moderation_disallow'] = 'Cannot create'; +$txt['permissions_post_moderation_group'] = 'Group'; + +$txt['auto_approve_topics'] = 'Post new topics, without requiring approval'; +$txt['auto_approve_replies'] = 'Post replies to topics, without requiring approval'; +$txt['auto_approve_attachments'] = 'Post attachments, without requiring approval'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManageScheduledTasks.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManageScheduledTasks.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,57 @@ +Note: All times given below are server time and do not take any time offsets setup within SMF into account.'; +$txt['scheduled_tasks_were_run'] = 'All selected tasks were completed'; + +$txt['scheduled_tasks_na'] = 'N/A'; +$txt['scheduled_task_approval_notification'] = 'Approval Notifications'; +$txt['scheduled_task_desc_approval_notification'] = 'Send out emails to all moderators summarizing posts awaiting approval.'; +$txt['scheduled_task_auto_optimize'] = 'Optimize Database'; +$txt['scheduled_task_desc_auto_optimize'] = 'Optimize the database to resolve fragmentation issues.'; +$txt['scheduled_task_daily_maintenance'] = 'Daily Maintenance'; +$txt['scheduled_task_desc_daily_maintenance'] = 'Runs essential daily maintenance on the forum - should not be disabled.'; +$txt['scheduled_task_daily_digest'] = 'Daily Notification Summary'; +$txt['scheduled_task_desc_daily_digest'] = 'Emails out the daily digest for notification subscribers.'; +$txt['scheduled_task_weekly_digest'] = 'Weekly Notification Summary'; +$txt['scheduled_task_desc_weekly_digest'] = 'Emails out the weekly digest for notification subscribers.'; +$txt['scheduled_task_fetchSMfiles'] = 'Fetch Simple Machines Files'; +$txt['scheduled_task_desc_fetchSMfiles'] = 'Retrieves javascript files containing notifications of updates and other information.'; +$txt['scheduled_task_birthdayemails'] = 'Send Birthday Emails'; +$txt['scheduled_task_desc_birthdayemails'] = 'Sends out emails wishing members a happy birthday.'; +$txt['scheduled_task_weekly_maintenance'] = 'Weekly Maintenance'; +$txt['scheduled_task_desc_weekly_maintenance'] = 'Runs essential weekly maintenance on the forum - should not be disabled.'; +$txt['scheduled_task_paid_subscriptions'] = 'Paid Subscription Checks'; +$txt['scheduled_task_desc_paid_subscriptions'] = 'Sends out any necessary paid subscription reminders and removes expired member subscriptions.'; + +$txt['scheduled_task_reg_starting'] = 'Starting at %1$s'; +$txt['scheduled_task_reg_repeating'] = 'repeating every %1$d %2$s'; +$txt['scheduled_task_reg_unit_m'] = 'minute(s)'; +$txt['scheduled_task_reg_unit_h'] = 'hour(s)'; +$txt['scheduled_task_reg_unit_d'] = 'day(s)'; +$txt['scheduled_task_reg_unit_w'] = 'week(s)'; + +$txt['scheduled_task_edit'] = 'Edit Scheduled Task'; +$txt['scheduled_task_edit_repeat'] = 'Repeat task every'; +$txt['scheduled_task_edit_pick_unit'] = 'Pick Unit'; +$txt['scheduled_task_edit_interval'] = 'Interval'; +$txt['scheduled_task_edit_start_time'] = 'Start Time'; +$txt['scheduled_task_edit_start_time_desc'] = 'Time the first instance of the day should start (hours:minutes)'; +$txt['scheduled_task_time_offset'] = 'Note the start time should be the offset against the current server time. Current server time is: %1$s'; + +$txt['scheduled_view_log'] = 'View Log'; +$txt['scheduled_log_empty'] = 'There are currently no task log entries.'; +$txt['scheduled_log_time_run'] = 'Time Run'; +$txt['scheduled_log_time_taken'] = 'Time taken'; +$txt['scheduled_log_time_taken_seconds'] = '%1$d seconds'; +$txt['scheduled_log_empty_log'] = 'Empty Log'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManageSettings.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManageSettings.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,359 @@ +theme settings for more options. Click the help icons for more information about a setting.'; +$txt['security_settings_desc'] = 'This page allows you to set options specifically related to the security and moderation of your forum, including anti-spam options.'; +$txt['modification_settings_desc'] = 'This page contains settings added by any modifications to your forum'; + +$txt['modification_no_misc_settings'] = 'There are no modifications installed that have added any settings to this area yet.'; + +$txt['pollMode'] = 'Poll mode'; +$txt['disable_polls'] = 'Disable polls'; +$txt['enable_polls'] = 'Enable polls'; +$txt['polls_as_topics'] = 'Show existing polls as topics'; +$txt['allow_guestAccess'] = 'Allow guests to browse the forum'; +$txt['userLanguage'] = 'Enable user-selectable language support'; +$txt['allow_editDisplayName'] = 'Allow users to edit their displayed name'; +$txt['allow_hideOnline'] = 'Allow non-administrators to hide their online status'; +$txt['guest_hideContacts'] = 'Do not reveal contact details of members to guests'; +$txt['titlesEnable'] = 'Enable custom titles'; +$txt['enable_buddylist'] = 'Enable buddy/ignore lists'; +$txt['default_personal_text'] = 'Default personal text
    Personal text to assign to newly registered members.
    '; +$txt['number_format'] = 'Default number format'; +$txt['time_format'] = 'Default time format'; +$txt['setting_time_offset'] = 'Overall time offset
    (added to the member specific option.)
    '; +$txt['setting_default_timezone'] = 'Server timezone'; +$txt['failed_login_threshold'] = 'Failed login threshold'; +$txt['lastActive'] = 'User online time threshold'; +$txt['trackStats'] = 'Track daily statistics'; +$txt['hitStats'] = 'Track daily page views (must have stats enabled)'; +$txt['enableCompressedOutput'] = 'Enable compressed output'; +$txt['disableTemplateEval'] = 'Disable evaluation of templates'; +$txt['databaseSession_enable'] = 'Use database driven sessions'; +$txt['databaseSession_loose'] = 'Allow browsers to go back to cached pages'; +$txt['databaseSession_lifetime'] = 'Seconds before an unused session timeout'; +$txt['enableErrorLogging'] = 'Enable error logging'; +$txt['enableErrorQueryLogging'] = 'Include database query in the error log'; +$txt['pruningOptions'] = 'Enable pruning of log entries'; +$txt['pruneErrorLog'] = 'Remove error log entries older than
    (0 to disable)
    '; +$txt['pruneModLog'] = 'Remove moderation log entries older than
    (0 to disable)
    '; +$txt['pruneBanLog'] = 'Remove ban hit log entries older than
    (0 to disable)
    '; +$txt['pruneReportLog'] = 'Remove report to moderator log entries older than
    (0 to disable)
    '; +$txt['pruneScheduledTaskLog'] = 'Remove scheduled task log entries older than
    (0 to disable)
    '; +$txt['pruneSpiderHitLog'] = 'Remove search engine hit logs older than
    (0 to disable)
    '; +$txt['cookieTime'] = 'Default login cookies length (in minutes)'; +$txt['localCookies'] = 'Enable local storage of cookies
    (SSI won\'t work well with this on.)
    '; +$txt['globalCookies'] = 'Use subdomain independent cookies
    (turn off local cookies first!)
    '; +$txt['secureCookies'] = 'Force cookies to be secure
    (This only applies if you are using HTTPS - don\'t use otherwise!)
    '; +$txt['securityDisable'] = 'Disable administration security'; +$txt['send_validation_onChange'] = 'Require reactivation after email change'; +$txt['approveAccountDeletion'] = 'Require admin approval when member deletes account'; +$txt['autoOptMaxOnline'] = 'Maximum users online when optimizing
    (0 for no max.)
    '; +$txt['autoFixDatabase'] = 'Automatically fix broken tables'; +$txt['allow_disableAnnounce'] = 'Allow users to disable announcements'; +$txt['disallow_sendBody'] = 'Don\'t allow post text in notifications'; +$txt['queryless_urls'] = 'Search engine friendly URLs
    Apache/Lighttpd only!
    '; +$txt['max_image_width'] = 'Max width of posted pictures (0 = disable)'; +$txt['max_image_height'] = 'Max height of posted pictures (0 = disable)'; +$txt['enableReportPM'] = 'Enable reporting of personal messages'; +$txt['max_pm_recipients'] = 'Maximum number of recipients allowed in a personal message
    (0 for no limit, admins are exempt)
    '; +$txt['pm_posts_verification'] = 'Post count under which users must pass verification when sending personal messages
    (0 for no limit, admins are exempt)
    '; +$txt['pm_posts_per_hour'] = 'Number of personal messages a user may send in an hour
    (0 for no limit, moderators are exempt)
    '; +$txt['compactTopicPagesEnable'] = 'Limit number of displayed page links'; +$txt['contiguous_page_display'] = 'Contiguous pages to display'; +$txt['to_display'] = 'to display'; +$txt['todayMod'] = 'Enable shorthand date display'; +$txt['today_disabled'] = 'Disabled'; +$txt['today_only'] = 'Only Today'; +$txt['yesterday_today'] = 'Today & Yesterday'; +$txt['topbottomEnable'] = 'Enable Go Up/Go Down buttons'; +$txt['onlineEnable'] = 'Show online/offline in posts and PMs'; +$txt['enableVBStyleLogin'] = 'Show a quick login on every page'; +$txt['defaultMaxMembers'] = 'Members per page in member list'; +$txt['timeLoadPageEnable'] = 'Display time taken to create every page'; +$txt['disableHostnameLookup'] = 'Disable hostname lookups'; +$txt['who_enabled'] = 'Enable who\'s online list'; +$txt['make_email_viewable'] = 'Allow viewable email addresses'; +$txt['meta_keywords'] = 'Meta keywords associated with forum
    For search engines. Leave blank for default.
    '; + +$txt['karmaMode'] = 'Karma mode'; +$txt['karma_options'] = 'Disable karma|Enable karma total|Enable karma positive/negative'; +$txt['karmaMinPosts'] = 'Set the minimum posts needed to modify karma'; +$txt['karmaWaitTime'] = 'Set wait time in hours'; +$txt['karmaTimeRestrictAdmins'] = 'Restrict administrators to wait time'; +$txt['karmaLabel'] = 'Karma label'; +$txt['karmaApplaudLabel'] = 'Karma applaud label'; +$txt['karmaSmiteLabel'] = 'Karma smite label'; + +$txt['caching_information'] = '
    Important! Read this first before enabling these features.

    + SMF supports caching through the use of accelerators. The currently supported accelerators include:
    +
      +
    • APC
    • +
    • eAccelerator
    • +
    • Turck MMCache
    • +
    • Memcached
    • +
    • Zend Platform/Performance Suite (Not Zend Optimizer)
    • +
    • XCache
    • +
    + Caching will work best if you have PHP compiled with one of the above optimizers, or have memcache + available. If you do not have any optimizer installed SMF will do file based caching.

    + SMF performs caching at a variety of levels. The higher the level of caching enabled the more CPU time will be spent + retrieving cached information. If caching is available on your machine it is recommended that you try caching at level 1 first. +

    + Note that if you use memcached you need to provide the server details in the setting below. This should be entered as a comma separated list + as shown in the example below:
    + "server1,server2,server3:port,server4"

    + Note that if no port is specified SMF will use port 11211. SMF will attempt to perform rough/random load balancing across the servers. +

    + %1$s'; + +$txt['detected_no_caching'] = 'SMF has not been able to detect a compatible accelerator on your server.'; +$txt['detected_APC'] = 'SMF has detected that your server has APC installed.'; +$txt['detected_eAccelerator'] = 'SMF has detected that your server has eAccelerator installed.'; +$txt['detected_MMCache'] = 'SMF has detected that your server has MMCache installed.'; +$txt['detected_Zend'] = 'SMF has detected that your server has Zend installed.'; +$txt['detected_Memcached'] = 'SMF has detected that your server has Memcached installed.'; +$txt['detected_XCache'] = 'SMF has detected that your server has XCache installed.'; + +$txt['cache_enable'] = 'Caching Level'; +$txt['cache_off'] = 'No caching'; +$txt['cache_level1'] = 'Level 1 Caching (Recommended)'; +$txt['cache_level2'] = 'Level 2 Caching'; +$txt['cache_level3'] = 'Level 3 Caching (Not Recommended)'; +$txt['cache_memcached'] = 'Memcache settings'; + +$txt['loadavg_warning'] = 'Please note: the settings below are to be edited with care. Setting any of them too low may render your forum unusable! The current load average is %01.2f'; +$txt['loadavg_enable'] = 'Enable load balancing by load averages'; +$txt['loadavg_auto_opt'] = 'Threshold to disabling automatic database optimization'; +$txt['loadavg_search'] = 'Threshold to disabling search'; +$txt['loadavg_allunread'] = 'Threshold to disabling all unread topics'; +$txt['loadavg_unreadreplies'] = 'Threshold to disabling unread replies'; +$txt['loadavg_show_posts'] = 'Threshold to disabling showing user posts'; +$txt['loadavg_forum'] = 'Threshold to disabling the forum completely'; +$txt['loadavg_disabled_windows'] = 'Load balancing support is not available on Windows.'; +$txt['loadavg_disabled_conf'] = 'Load balancing support is disabled by your host configuration.'; + +$txt['setting_password_strength'] = 'Required strength for user passwords'; +$txt['setting_password_strength_low'] = 'Low - 4 character minimum'; +$txt['setting_password_strength_medium'] = 'Medium - cannot contain username'; +$txt['setting_password_strength_high'] = 'High - mixture of different characters'; + +$txt['antispam_Settings'] = 'Anti-Spam Verification'; +$txt['antispam_Settings_desc'] = 'This section allows you to setup verification checks to ensure the user is a human (and not a bot), and tweak how and where these apply.'; +$txt['setting_reg_verification'] = 'Require verification on registration page'; +$txt['posts_require_captcha'] = 'Post count under which users must pass verification to make a post'; +$txt['posts_require_captcha_desc'] = '(0 for no limit, moderators are exempt)'; +$txt['search_enable_captcha'] = 'Require verification on all guest searches'; +$txt['setting_guests_require_captcha'] = 'Guests must pass verification when making a post'; +$txt['setting_guests_require_captcha_desc'] = '(Automatically set if you specify a minimum post count below)'; +$txt['guests_report_require_captcha'] = 'Guests must pass verification when reporting a post'; + +$txt['configure_verification_means'] = 'Configure Verification Methods'; +$txt['setting_qa_verification_number'] = 'Number of verification questions user must answer'; +$txt['setting_qa_verification_number_desc'] = '(0 to disable; questions are set below)'; +$txt['configure_verification_means_desc'] = 'Below you can set which anti-spam features you wish to have enabled whenever a user needs to verify they are a human. Note that the user will have to pass all verification so if you enable both a verification image and a question/answer test they need to complete both to proceed.'; +$txt['setting_visual_verification_type'] = 'Visual verification image to display'; +$txt['setting_visual_verification_type_desc'] = 'The more complex the image the harder it is for bots to bypass'; +$txt['setting_image_verification_off'] = 'None'; +$txt['setting_image_verification_vsimple'] = 'Very Simple - Plain text on image'; +$txt['setting_image_verification_simple'] = 'Simple - Overlapping colored letters, no noise'; +$txt['setting_image_verification_medium'] = 'Medium - Overlapping colored letters, with noise/lines'; +$txt['setting_image_verification_high'] = 'High - Angled letters, considerable noise/lines'; +$txt['setting_image_verification_extreme'] = 'Extreme - Angled letters, noise, lines and blocks'; +$txt['setting_image_verification_sample'] = 'Sample'; +$txt['setting_image_verification_nogd'] = 'Note: as this server does not have the GD library installed the different complexity settings will have no effect.'; +$txt['setup_verification_questions'] = 'Verification Questions'; +$txt['setup_verification_questions_desc'] = 'If you want users to answer verification questions in order to stop spam bots you should setup a number of questions in the table below. You should pick relatively simple questions; answers are not case sensitive. You may use BBC in the questions for formatting, to remove a question simply delete the contents of that line.'; +$txt['setup_verification_question'] = 'Question'; +$txt['setup_verification_answer'] = 'Answer'; +$txt['setup_verification_add_more'] = 'Add another question'; + +$txt['moderation_settings'] = 'Moderation Settings'; +$txt['setting_warning_enable'] = 'Enable User Warning System'; +$txt['setting_warning_watch'] = 'Warning level for user watch
    The user warning level after which a user watch is put in place - 0 to disable.
    '; +$txt['setting_warning_moderate'] = 'Warning level for post moderation
    The user warning level after which a user has all posts moderated - 0 to disable.
    '; +$txt['setting_warning_mute'] = 'Warning level for user muting
    The user warning level after which a user cannot post any further - 0 to disable.
    '; +$txt['setting_user_limit'] = 'Maximum user warning points per day
    This value is the maximum amount of warning points a single moderator can assign to a user in a 24 hour period - 0 for no limit.
    '; +$txt['setting_warning_decrement'] = 'Warning points to decrement from users every 24 hours
    Only applies to users not warned within last 24 hours - set to 0 to disable.
    '; +$txt['setting_warning_show'] = 'Users who can see warning status
    Determines who can see the warning level of users on the forum.
    '; +$txt['setting_warning_show_mods'] = 'Moderators Only'; +$txt['setting_warning_show_user'] = 'Moderators and Warned Users'; +$txt['setting_warning_show_all'] = 'All Users'; + +$txt['signature_settings'] = 'Signature Settings'; +$txt['signature_settings_desc'] = 'Use the settings on this page to decide how member signatures should be treated in SMF.'; +$txt['signature_settings_warning'] = 'Note that settings are not applied to existing signatures by default. Click here to apply rules to all existing signatures.'; +$txt['signature_enable'] = 'Enable signatures'; +$txt['signature_max_length'] = 'Maximum allowed characters
    (0 for no max.)
    '; +$txt['signature_max_lines'] = 'Maximum amount of lines
    (0 for no max)
    '; +$txt['signature_max_images'] = 'Maximum image count
    (0 for no max - excludes smileys)
    '; +$txt['signature_allow_smileys'] = 'Allow smileys in signatures'; +$txt['signature_max_smileys'] = 'Maximum smiley count
    (0 for no max)
    '; +$txt['signature_max_image_width'] = 'Maximum width of signature images (pixels)
    (0 for no max)
    '; +$txt['signature_max_image_height'] = 'Maximum height of signature images (pixels)
    (0 for no max)
    '; +$txt['signature_max_font_size'] = 'Maximum font size allowed in signatures
    (0 for no max, in pixels)
    '; +$txt['signature_bbc'] = 'Enabled BBC tags'; + +$txt['custom_profile_title'] = 'Custom Profile Fields'; +$txt['custom_profile_desc'] = 'From this page you can create your own custom profile fields that fit in with your own forums requirements'; +$txt['custom_profile_active'] = 'Active'; +$txt['custom_profile_fieldname'] = 'Field Name'; +$txt['custom_profile_fieldtype'] = 'Field Type'; +$txt['custom_profile_make_new'] = 'New Field'; +$txt['custom_profile_none'] = 'You have not created any custom profile fields yet!'; +$txt['custom_profile_icon'] = 'Icon'; + +$txt['custom_profile_type_text'] = 'Text'; +$txt['custom_profile_type_textarea'] = 'Large Text'; +$txt['custom_profile_type_select'] = 'Select Box'; +$txt['custom_profile_type_radio'] = 'Radio Button'; +$txt['custom_profile_type_check'] = 'Checkbox'; + +$txt['custom_add_title'] = 'Add Profile Field'; +$txt['custom_edit_title'] = 'Edit Profile Field'; +$txt['custom_edit_general'] = 'Display Settings'; +$txt['custom_edit_input'] = 'Input Settings'; +$txt['custom_edit_advanced'] = 'Advanced Settings'; +$txt['custom_edit_name'] = 'Name'; +$txt['custom_edit_desc'] = 'Description'; +$txt['custom_edit_profile'] = 'Profile Section'; +$txt['custom_edit_profile_desc'] = 'Section of profile this is edited in.'; +$txt['custom_edit_profile_none'] = 'None'; +$txt['custom_edit_registration'] = 'Show on Registration'; +$txt['custom_edit_registration_disable'] = 'No'; +$txt['custom_edit_registration_allow'] = 'Yes'; +$txt['custom_edit_registration_require'] = 'Yes, and require entry'; +$txt['custom_edit_display'] = 'Show on Topic View'; +$txt['custom_edit_picktype'] = 'Field Type'; + +$txt['custom_edit_max_length'] = 'Maximum Length'; +$txt['custom_edit_max_length_desc'] = '(0 for no limit)'; +$txt['custom_edit_dimension'] = 'Dimensions'; +$txt['custom_edit_dimension_row'] = 'Rows'; +$txt['custom_edit_dimension_col'] = 'Columns'; +$txt['custom_edit_bbc'] = 'Allow BBC'; +$txt['custom_edit_options'] = 'Options'; +$txt['custom_edit_options_desc'] = 'Leave option box blank to remove. Radio button selects default option.'; +$txt['custom_edit_options_more'] = 'More'; +$txt['custom_edit_default'] = 'Default State'; +$txt['custom_edit_active'] = 'Active'; +$txt['custom_edit_active_desc'] = 'If not selected this field will not be shown to anyone.'; +$txt['custom_edit_privacy'] = 'Privacy'; +$txt['custom_edit_privacy_desc'] = 'Who can see and edit this field.'; +$txt['custom_edit_privacy_all'] = 'Users can see this field; owner can edit it'; +$txt['custom_edit_privacy_see'] = 'Users can see this field; only admins can edit it'; +$txt['custom_edit_privacy_owner'] = 'Users cannot see this field; owner and admins can edit it.'; +$txt['custom_edit_privacy_none'] = 'This field is only visible to admins'; +$txt['custom_edit_can_search'] = 'Searchable'; +$txt['custom_edit_can_search_desc'] = 'Can this field be searched from the members list.'; +$txt['custom_edit_mask'] = 'Input Mask'; +$txt['custom_edit_mask_desc'] = 'For text fields an input mask can be selected to validate the data.'; +$txt['custom_edit_mask_email'] = 'Valid Email'; +$txt['custom_edit_mask_number'] = 'Numeric'; +$txt['custom_edit_mask_nohtml'] = 'No HTML'; +$txt['custom_edit_mask_regex'] = 'Regex (Advanced)'; +$txt['custom_edit_enclose'] = 'Show Enclosed Within Text (Optional)'; +$txt['custom_edit_enclose_desc'] = 'We strongly recommend to use an input mask to validate the input supplied by the user.'; + +$txt['custom_edit_placement'] = 'Choose Placement'; +$txt['custom_edit_placement_standard'] = 'Standard (with title)'; +$txt['custom_edit_placement_withicons'] = 'With Icons'; +$txt['custom_edit_placement_abovesignature'] = 'Above Signature'; +$txt['custom_profile_placement'] = 'Placement'; +$txt['custom_profile_placement_standard'] = 'Standard'; +$txt['custom_profile_placement_withicons'] = 'With Icons'; +$txt['custom_profile_placement_abovesignature'] = 'Above Signature'; + +// Use numeric entities in the string below! +$txt['custom_edit_delete_sure'] = 'Are you sure you wish to delete this field - all related user data will be lost!'; + +$txt['standard_profile_title'] = 'Standard Profile Fields'; +$txt['standard_profile_field'] = 'Field'; + +$txt['core_settings_welcome_msg'] = 'Welcome to Your New Forum'; +$txt['core_settings_welcome_msg_desc'] = 'To get you started we suggest you select which of SMF\'s core features you want to enable. We\'d recommend only enabling with those features you need!'; +$txt['core_settings_item_cd'] = 'Calendar'; +$txt['core_settings_item_cd_desc'] = 'Enabling this feature will open up a selection of options to enable your users to view the calendar, add and review events, see users birthdates on a calendar and much, much more.'; +$txt['core_settings_item_cp'] = 'Advanced Profile Fields'; +$txt['core_settings_item_cp_desc'] = 'This enables you to hide standard profile fields, add profile fields to registration, and create new profile fields for your forum.'; +$txt['core_settings_item_k'] = 'Karma'; +$txt['core_settings_item_k_desc'] = 'Karma is a feature that shows the popularity of a member. Members, if allowed, can \'applaud\' or \'smite\' other members, which is how their popularity is calculated.'; +$txt['core_settings_item_ml'] = 'Moderation, Administration and User Logs'; +$txt['core_settings_item_ml_desc'] = 'Enable the moderation and administration logs to keep an audit trail of all the key actions taken on your forum. Also allows forum moderators to view a history of key changes a user makes to their profile.'; +$txt['core_settings_item_pm'] = 'Post Moderation'; +$txt['core_settings_item_pm_desc'] = 'Post moderation enables you to select groups and boards within which posts must be approved before they become public. Upon enabling this feature be sure to visit the permission section to set up the relevant permissions.'; +$txt['core_settings_item_ps'] = 'Paid Subscriptions'; +$txt['core_settings_item_ps_desc'] = 'Paid subscriptions allow users to pay for subscriptions to change membergroup within the forum and thus change their access rights.'; +$txt['core_settings_item_rg'] = 'Report Generation'; +$txt['core_settings_item_rg_desc'] = 'This administration feature allows the generation of reports (Which can be printed) to present your current forum setup in an easy to view manner - particularly useful for large forums.'; +$txt['core_settings_item_sp'] = 'Search Engine Tracking'; +$txt['core_settings_item_sp_desc'] = 'Enabling this feature will allow administrators to track search engines as they index your forum.'; +$txt['core_settings_item_w'] = 'Warning System'; +$txt['core_settings_item_w_desc'] = 'This functionality allows administrators and moderators to issue warnings to users; it also includes advanced functionality for automatically removing user rights as their warning level increases. Note to take full advantage of this function "Post Moderation" should be enabled.'; +$txt['core_settings_switch_on'] = 'Click to Enable'; +$txt['core_settings_switch_off'] = 'Click to Disable'; +$txt['core_settings_enabled'] = 'Enabled'; +$txt['core_settings_disabled'] = 'Disabled'; + +$txt['languages_lang_name'] = 'Language Name'; +$txt['languages_locale'] = 'Locale'; +$txt['languages_default'] = 'Default'; +$txt['languages_character_set'] = 'Character Set'; +$txt['languages_users'] = 'Users'; +$txt['language_settings_writable'] = 'Warning: Settings.php is not writable so the default language setting cannot be saved.'; +$txt['edit_languages'] = 'Edit Languages'; +$txt['lang_file_not_writable'] = 'Warning: The primary language file (%1$s) is not writable. You must make this writable before you can make any changes.'; +$txt['lang_entries_not_writable'] = 'Warning: The language file you wish to edit (%1$s) is not writable. You must make this writable before you can make any changes.'; +$txt['languages_ltr'] = 'Right to Left'; + +$txt['add_language'] = 'Add Language'; +$txt['add_language_smf'] = 'Download from Simple Machines'; +$txt['add_language_smf_browse'] = 'Type name of language to search for or leave blank to search for all.'; +$txt['add_language_smf_install'] = 'Install'; +$txt['add_language_smf_found'] = 'The following languages were found. Click the install link next to the language you wish to install, you will then be taken to the package manager to install.'; +$txt['add_language_error_no_response'] = 'The Simple Machines site is not responding. Please try again later.'; +$txt['add_language_error_no_files'] = 'No files could be found.'; +$txt['add_language_smf_desc'] = 'Description'; +$txt['add_language_smf_utf8'] = 'UTF-8'; +$txt['add_language_smf_version'] = 'Version'; + +$txt['edit_language_entries_primary'] = 'Below are the primary language settings for this language pack.'; +$txt['edit_language_entries'] = 'Edit Language Entries'; +$txt['edit_language_entries_file'] = 'Select entries to edit'; +$txt['languages_dictionary'] = 'Dictionary'; +$txt['languages_spelling'] = 'Spelling'; +$txt['languages_for_pspell'] = 'This is for pSpell - if installed'; +$txt['languages_rtl'] = 'Enable "Right to Left" Mode'; + +$txt['lang_file_desc_index'] = 'General Strings'; +$txt['lang_file_desc_EmailTemplates'] = 'Email Templates'; + +$txt['languages_download'] = 'Download Language Pack'; +$txt['languages_download_note'] = 'This page lists all the files that are contained within the language pack and some useful information about each one. All files that have their associated check box marked will be copied.'; +$txt['languages_download_info'] = 'Note: +
      +
    • Files which have the status "Not Writable" means SMF will not be able to copy this file to the directory at the present and you must make the destination writable either using an FTP client or by filling in your details at the bottom of the page.
    • +
    • The Version information for a file displays the last SMF version which it was updated for. If it is indicated in green then this is a newer version than you have at current. If amber this indicates it\'s the same version number as at current, red indicates you have a newer version installed than contained in the pack.
    • +
    • Where a file already exists on your forum the "Already Exists" column will have one of two values. "Identical" indicates that the file already exists in an identical form and need not be overwritten. "Different" means that the contents vary in some way and overwriting is probably the optimum solution.
    • +
    '; + +$txt['languages_download_main_files'] = 'Primary Files'; +$txt['languages_download_theme_files'] = 'Theme-related Files'; +$txt['languages_download_filename'] = 'File Name'; +$txt['languages_download_dest'] = 'Destination'; +$txt['languages_download_writable'] = 'Writable'; +$txt['languages_download_version'] = 'Version'; +$txt['languages_download_older'] = 'You have a newer version of this file installed, overwriting is not recommended.'; +$txt['languages_download_exists'] = 'Already Exists'; +$txt['languages_download_exists_same'] = 'Identical'; +$txt['languages_download_exists_different'] = 'Different'; +$txt['languages_download_copy'] = 'Copy'; +$txt['languages_download_not_chmod'] = 'You cannot proceed with the installation until all files selected to be copied are writable.'; +$txt['languages_download_illegal_paths'] = 'Package contains illegal paths - please contact Simple Machines'; +$txt['languages_download_complete'] = 'Installation Complete'; +$txt['languages_download_complete_desc'] = 'Language pack installed successfully. Please click here to return to the languages page'; +$txt['languages_delete_confirm'] = 'Are you sure you want to delete this language?'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ManageSmileys.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ManageSmileys.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,95 @@ +Here you can change the name and location of each smiley set - remember, however, that all sets share the same smileys.'; +$txt['smiley_editsmileys_explain'] = 'Change your smileys here by clicking on the smiley you want to modify. Remember that these smileys all have to exist in all the sets or some smileys won\'t show up! Don\'t forget to save after you are done editing!'; +$txt['smiley_setorder_explain'] = 'Change the order of the smileys here.'; +$txt['smiley_addsmiley_explain'] = 'Here you can add a new smiley - either from an existing file or by uploading new ones.'; + +$txt['smiley_set_select_default'] = 'Default Smiley Set'; +$txt['smiley_set_new'] = 'Create new smiley set'; +$txt['smiley_set_modify_existing'] = 'Modify existing smiley set'; +$txt['smiley_set_modify'] = 'Modify'; +$txt['smiley_set_import_directory'] = 'Import smileys already in this directory'; +$txt['smiley_set_import_single'] = 'There is one smiley in this smiley set not yet imported. Click'; +$txt['smiley_set_import_multiple'] = 'There are %1$d smileys in the directory that have not yet been imported. Click'; +$txt['smiley_set_to_import_single'] = 'to import it now.'; +$txt['smiley_set_to_import_multiple'] = 'to import them now.'; + +$txt['smileys_location'] = 'Location'; +$txt['smileys_location_form'] = 'Post form'; +$txt['smileys_location_hidden'] = 'Hidden'; +$txt['smileys_location_popup'] = 'Popup'; +$txt['smileys_modify'] = 'Modify'; +$txt['smileys_not_found_in_set'] = 'Smiley not found in set(s)'; +$txt['smileys_default_description'] = '(Insert a description)'; +$txt['smiley_new'] = 'Add new smiley'; +$txt['smiley_modify_existing'] = 'Modify smiley'; +$txt['smiley_preview'] = 'Preview'; +$txt['smiley_preview_using'] = 'using smiley set'; +$txt['smileys_confirm'] = 'Are you sure you want to remove these smileys?\\n\\nNote: This won\\\'t remove the images, just the choices.'; +$txt['smileys_location_form_description'] = 'These smileys will appear above the text area, when posting a new forum message or Personal Message.'; +$txt['smileys_location_popup_description'] = 'These smileys will be shown in a popup, that is shown after a user has clicked \'[more]\''; +$txt['smileys_move_select_destination'] = 'Select smiley destination'; +$txt['smileys_move_select_smiley'] = 'Select smiley to move'; +$txt['smileys_move_here'] = 'Move smiley to this location'; +$txt['smileys_no_entries'] = 'There are currently no smileys configured.'; + +$txt['icons_edit_icons_explain'] = 'From here you can change which message icons are available throughout your board. You can add, edit and remove icons, as well as limit their use to certain boards.'; +$txt['icons_edit_icons_all_boards'] = 'Available In All Boards'; +$txt['icons_board'] = 'Board'; +$txt['icons_confirm'] = 'Are you sure you wish to remove these icons?\\n\\nNote this will only stop new posters from using the icons, the images will remain.'; +$txt['icons_add_new'] = 'Add New Icon'; + +$txt['icons_edit_icon'] = 'Edit Message Icon'; +$txt['icons_new_icon'] = 'New Message Icon'; +$txt['icons_location_first_icon'] = 'As First Icon'; +$txt['icons_location_after'] = 'After'; +$txt['icons_filename_all_gif'] = 'All files must be "gif" files'; +$txt['icons_no_entries'] = 'There are currently no message icons configured.'; +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Manual.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Manual.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,36 @@ +Simple Machines Documentation Wiki and check out the credits to find out who has made SMF what it is today.'; + +$txt['manual_section_registering_title'] = 'Registering'; +$txt['manual_section_logging_in_title'] = 'Logging In'; +$txt['manual_section_profile_title'] = 'Profile'; +$txt['manual_section_search_title'] = 'Search'; +$txt['manual_section_posting_title'] = 'Posting'; +$txt['manual_section_bbc_title'] = 'Bulletin Board Code (BBC)'; +$txt['manual_section_personal_messages_title'] = 'Personal Messages'; +$txt['manual_section_memberlist_title'] = 'Memberlist'; +$txt['manual_section_calendar_title'] = 'Calendar'; +$txt['manual_section_features_title'] = 'Features'; + +$txt['manual_section_registering_desc'] = 'Many forums require users to register to gain full access.'; +$txt['manual_section_logging_in_desc'] = 'Once registered, users must login to access their account.'; +$txt['manual_section_profile_desc'] = 'Each member has their own personal profile.'; +$txt['manual_section_search_desc'] = 'Searching is an extremely helpful tool for finding information in posts and topics.'; +$txt['manual_section_posting_desc'] = 'The whole point of a forum, posting allows users to express themselves.'; +$txt['manual_section_bbc_desc'] = 'Posts can be spiced up with a little BBC.'; +$txt['manual_section_personal_messages_desc'] = 'Users can send personal messages to each other.'; +$txt['manual_section_memberlist_desc'] = 'The memberlist shows all the members of a forum.'; +$txt['manual_section_calendar_desc'] = 'Users can keep track of events, holidays, and birthdays with the calendar.'; +$txt['manual_section_features_desc'] = 'Here is a list of the most popular features in SMF.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ModSettings.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ModSettings.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,121 @@ +theme settings for more options. Click the help icons for more information about a setting.'; + +$txt['mods_cat_features'] = 'Basic Features'; +$txt['pollMode'] = 'Poll mode'; +$txt['smf34'] = 'Disable polls'; +$txt['smf32'] = 'Enable polls'; +$txt['smf33'] = 'Show existing polls as topics'; +$txt['allow_guestAccess'] = 'Allow guests to browse the forum'; +$txt['userLanguage'] = 'Enable user-selectable language support'; +$txt['allow_editDisplayName'] = 'Allow users to edit their displayed name?'; +$txt['allow_hideOnline'] = 'Allow non-administrators to hide their online status?'; +$txt['allow_hideEmail'] = 'Allow users to hide their email from everyone except admins?'; +$txt['guest_hideContacts'] = 'Do not reveal contact details of members to guests'; +$txt['titlesEnable'] = 'Enable custom titles'; +$txt['enable_buddylist'] = 'Enable buddy lists'; +$txt['default_personalText'] = 'Default personal text'; +$txt['max_signatureLength'] = 'Maximum allowed characters in signatures
    (0 for no max.)
    '; +$txt['number_format'] = 'Default number format'; +$txt['time_format'] = 'Default time format'; +$txt['time_offset'] = 'Overall time offset
    (added to the member specific option.)
    '; +$txt['failed_login_threshold'] = 'Failed login threshold'; +$txt['lastActive'] = 'User online time threshold'; +$txt['trackStats'] = 'Track daily statistics'; +$txt['hitStats'] = 'Track daily page views (must have stats enabled)'; +$txt['enableCompressedOutput'] = 'Enable compressed output'; +$txt['databaseSession_enable'] = 'Use database driven sessions'; +$txt['databaseSession_loose'] = 'Allow browsers to go back to cached pages'; +$txt['databaseSession_lifetime'] = 'Seconds before an unused session timeout'; +$txt['enableErrorLogging'] = 'Enable error logging'; +$txt['cookieTime'] = 'Default login cookies length (in minutes)'; +$txt['localCookies'] = 'Enable local storage of cookies
    (SSI won\'t work well with this on.)
    '; +$txt['globalCookies'] = 'Use subdomain independent cookies
    (turn off local cookies first!)
    '; +$txt['securityDisable'] = 'Disable administration security'; +$txt['send_validation_onChange'] = 'Require reactivation after email change'; +$txt['approveAccountDeletion'] = 'Require admin approval when member deletes account'; +$txt['autoOptDatabase'] = 'Optimize tables every how many days?
    (0 to disable.)
    '; +$txt['autoOptMaxOnline'] = 'Maximum users online when optimizing
    (0 for no max.)
    '; +$txt['autoFixDatabase'] = 'Automatically fix broken tables'; +$txt['allow_disableAnnounce'] = 'Allow users to disable announcements'; +$txt['disallow_sendBody'] = 'Don\'t allow post text in notifications?'; +$txt['modlog_enabled'] = 'Log moderation actions'; +$txt['queryless_urls'] = 'Search engine friendly URLs
    Apache only!
    '; +$txt['max_image_width'] = 'Max width of posted pictures (0 = disable)'; +$txt['max_image_height'] = 'Max height of posted pictures (0 = disable)'; +$txt['mail_type'] = 'Mail type'; +$txt['mail_type_default'] = '(PHP default)'; +$txt['smtp_host'] = 'SMTP server'; +$txt['smtp_port'] = 'SMTP port'; +$txt['smtp_username'] = 'SMTP username'; +$txt['smtp_password'] = 'SMTP password'; +$txt['enableReportPM'] = 'Enable reporting of personal messages'; +$txt['max_pm_recipients'] = 'Maximum number of recipients allowed in a personal message.
    (0 for no limit, admins are exempt)
    '; +$txt['pm_posts_verification'] = 'Post count under which users must enter code when sending personal messages.
    (0 for no limit, admins are exempt)
    '; +$txt['pm_posts_per_hour'] = 'Number of personal messages a user may send in an hour.
    (0 for no limit, moderators are exempt)
    '; + +$txt['mods_cat_layout'] = 'Layout and Options'; +$txt['compactTopicPagesEnable'] = 'Limit number of displayed page links'; +$txt['smf235'] = 'Contiguous pages to display:'; +$txt['smf236'] = 'to display'; +$txt['todayMod'] = 'Enable "Today" feature'; +$txt['smf290'] = 'Disabled'; +$txt['smf291'] = 'Only Today'; +$txt['smf292'] = 'Today & Yesterday'; +$txt['topbottomEnable'] = 'Enable Go Up/Go Down buttons'; +$txt['onlineEnable'] = 'Show online/offline in posts and PMs'; +$txt['enableVBStyleLogin'] = 'Show a quick login on every page'; +$txt['defaultMaxMembers'] = 'Members per page in member list'; +$txt['timeLoadPageEnable'] = 'Display time taken to create every page'; +$txt['disableHostnameLookup'] = 'Disable hostname lookups?'; +$txt['who_enabled'] = 'Enable who\'s online list'; + +$txt['smf293'] = 'Karma'; +$txt['karmaMode'] = 'Karma mode'; +$txt['smf64'] = 'Disable karma|Enable karma total|Enable karma positive/negative'; +$txt['karmaMinPosts'] = 'Set the minimum posts needed to modify karma'; +$txt['karmaWaitTime'] = 'Set wait time in hours'; +$txt['karmaTimeRestrictAdmins'] = 'Restrict administrators to wait time'; +$txt['karmaLabel'] = 'Karma label'; +$txt['karmaApplaudLabel'] = 'Karma applaud label'; +$txt['karmaSmiteLabel'] = 'Karma smite label'; + +$txt['caching_information'] = '
    Important! Read this first before enabling these features.

    + SMF supports caching through the use of accelerators. The currently supported accelerators include:
    +
      +
    • APC
    • +
    • eAccelerator
    • +
    • Turck MMCache
    • +
    • Memcached
    • +
    • Zend Platform/Performance Suite (Not Zend Optimizer)
    • +
    + Caching will only work on your server if you have PHP compiled with one of the above optimizers, or have memcache + available.

    + SMF performs caching at a variety of levels. The higher the level of caching enabled the more CPU time will be spent + retrieving cached information. If caching is available on your machine it is recommended that you try caching at level 1 first. +

    + Note that if you use memcached you need to provide the server details in the setting below. This should be entered as a comma separated list + as shown in the example below:
    + "server1,server2,server3:port,server4"

    + Note that if no port is specified SMF will use port 11211. SMF will attempt to perform rough/random load balancing across the servers. +

    + %s +
    '; + +$txt['detected_no_caching'] = 'SMF has not been able to detect a compatible accelerator on your server.'; +$txt['detected_APC'] = 'SMF has detected that your server has APC installed.'; +$txt['detected_eAccelerator'] = 'SMF has detected that your server has eAccelerator installed.'; +$txt['detected_MMCache'] = 'SMF has detected that your server has MMCache installed.'; +$txt['detected_Zend'] = 'SMF has detected that your server has Zend installed.'; +$txt['detected_Memcached'] = 'SMF has detected that your server has Memcached installed.'; + +$txt['cache_enable'] = 'Caching Level'; +$txt['cache_off'] = 'No caching'; +$txt['cache_level1'] = 'Level 1 Caching'; +$txt['cache_level2'] = 'Level 2 Caching (Not Recommended)'; +$txt['cache_level3'] = 'Level 3 Caching (Not Recommended)'; +$txt['cache_memcached'] = 'Memcache settings'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/ModerationCenter.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/ModerationCenter.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,145 @@ +here.'; +$txt['mc_group_requests'] = 'Membergroup Requests'; +$txt['mc_unapproved_posts'] = 'Unapproved Posts'; +$txt['mc_watched_users'] = 'Recent Watched Members'; +$txt['mc_watched_topics'] = 'Watched Topics'; +$txt['mc_scratch_board'] = 'Moderator Scratch Board'; +$txt['mc_latest_news'] = 'Simple Machines Latest News'; +$txt['mc_recent_reports'] = 'Recent Topic Reports'; +$txt['mc_warnings'] = 'Warnings'; +$txt['mc_notes'] = 'Moderator Notes'; + +$txt['mc_cannot_connect_sm'] = 'You are unable to connect to simplemachines.org\'s latest news file.'; + +$txt['mc_recent_reports_none'] = 'There are no outstanding reports'; +$txt['mc_watched_users_none'] = 'There are not currently any watches in place.'; +$txt['mc_group_requests_none'] = 'There are no open requests for group membership.'; + +$txt['mc_seen'] = '%1$s last seen %2$s'; +$txt['mc_seen_never'] = '%1$s never seen'; +$txt['mc_groupr_by'] = 'by'; + +$txt['mc_reported_posts_desc'] = 'Here you can review all the post reports raised by members of the community.'; +$txt['mc_reportedp_active'] = 'Active Reports'; +$txt['mc_reportedp_closed'] = 'Old Reports'; +$txt['mc_reportedp_by'] = 'by'; +$txt['mc_reportedp_reported_by'] = 'Reported By'; +$txt['mc_reportedp_last_reported'] = 'Last Reported'; +$txt['mc_reportedp_none_found'] = 'No Reports Found'; + +$txt['mc_reportedp_details'] = 'Details'; +$txt['mc_reportedp_close'] = 'Close'; +$txt['mc_reportedp_open'] = 'Open'; +$txt['mc_reportedp_ignore'] = 'Ignore'; +$txt['mc_reportedp_unignore'] = 'Un-Ignore'; +// Do not use numeric entries in the below string. +$txt['mc_reportedp_ignore_confirm'] = 'Are you sure you wish to ignore further reports about this message?\\n\\nThis will turn off further reports for all moderators of the forum.'; +$txt['mc_reportedp_close_selected'] = 'Close Selected'; + +$txt['mc_groupr_group'] = 'Membergroups'; +$txt['mc_groupr_member'] = 'Member'; +$txt['mc_groupr_reason'] = 'Reason'; +$txt['mc_groupr_none_found'] = 'There are currently no outstanding membergroup requests.'; +$txt['mc_groupr_submit'] = 'Submit'; +$txt['mc_groupr_reason_desc'] = 'Reason to reject %1$s\'s request to join "%2$s"'; +$txt['mc_groups_reason_title'] = 'Reasons for Rejection'; +$txt['with_selected'] = 'With Selected'; +$txt['mc_groupr_approve'] = 'Approve Request'; +$txt['mc_groupr_reject'] = 'Reject Request (No Reason)'; +$txt['mc_groupr_reject_w_reason'] = 'Reject Request with Reason'; +// Do not use numeric entries in the below string. +$txt['mc_groupr_warning'] = 'Are you sure you wish to do this?'; + +$txt['mc_unapproved_attachments_none_found'] = 'There are currently no attachments awaiting approval'; +$txt['mc_unapproved_replies_none_found'] = 'There are currently no posts awaiting approval'; +$txt['mc_unapproved_topics_none_found'] = 'There are currently no topics awaiting approval'; +$txt['mc_unapproved_posts_desc'] = 'From here you can approve or delete any posts awaiting moderation.'; +$txt['mc_unapproved_replies'] = 'Replies'; +$txt['mc_unapproved_topics'] = 'Topics'; +$txt['mc_unapproved_by'] = 'by'; +$txt['mc_unapproved_sure'] = 'Are you sure you want to do this?'; +$txt['mc_unapproved_attach_name'] = 'Attachment Name'; +$txt['mc_unapproved_attach_size'] = 'Filesize'; +$txt['mc_unapproved_attach_poster'] = 'Poster'; +$txt['mc_viewmodreport'] = 'Moderation Report for %1$s by %2$s'; +$txt['mc_modreport_summary'] = 'There have been %1$d report(s) concerning this post. The last report was %2$s.'; +$txt['mc_modreport_whoreported_title'] = 'Members who have reported this post'; +$txt['mc_modreport_whoreported_data'] = 'Reported by %1$s on %2$s. They left the following message:'; +$txt['mc_modreport_modactions'] = 'Actions taken by other moderators'; +$txt['mc_modreport_mod_comments'] = 'Moderator Comments'; +$txt['mc_modreport_no_mod_comment'] = 'There are not currently any moderator comments'; +$txt['mc_modreport_add_mod_comment'] = 'Add Comment'; + +$txt['show_notice'] = 'Notice Text'; +$txt['show_notice_subject'] = 'Subject'; +$txt['show_notice_text'] = 'Text'; + +$txt['mc_watched_users_title'] = 'Watched Members'; +$txt['mc_watched_users_desc'] = 'Here you can keep a track of all members who have been assigned a "watch" by the moderation team.'; +$txt['mc_watched_users_post'] = 'View by Post'; +$txt['mc_watched_users_warning'] = 'Warning Level'; +$txt['mc_watched_users_last_login'] = 'Last Login'; +$txt['mc_watched_users_last_post'] = 'Last Post'; +$txt['mc_watched_users_no_posts'] = 'There are no posts from watched members.'; +// Don't use entities in the two strings below. +$txt['mc_watched_users_delete_post'] = 'Are you sure you want to delete this post?'; +$txt['mc_watched_users_delete_posts'] = 'Are you sure you want to delete these posts?'; +$txt['mc_watched_users_posted'] = 'Posted'; +$txt['mc_watched_users_member'] = 'Member'; + +$txt['mc_warnings_description'] = 'From this section you can see which warnings have been issued to members of the forum. You can also add and modify the notification templates used when sending a warning to a member.'; +$txt['mc_warning_log'] = 'Log'; +$txt['mc_warning_templates'] = 'Custom Templates'; +$txt['mc_warning_log_title'] = 'Viewing Warning Log'; +$txt['mc_warning_templates_title'] = 'Custom Warning Templates'; + +$txt['mc_warnings_none'] = 'No warnings have been issued yet!'; +$txt['mc_warnings_recipient'] = 'Recipient'; + +$txt['mc_warning_templates_none'] = 'No warning templates have been created yet'; +$txt['mc_warning_templates_time'] = 'Time Created'; +$txt['mc_warning_templates_name'] = 'Template'; +$txt['mc_warning_templates_creator'] = 'Created By'; +$txt['mc_warning_template_add'] = 'Add Template'; +$txt['mc_warning_template_modify'] = 'Edit Template'; +$txt['mc_warning_template_delete'] = 'Delete Selected'; +$txt['mc_warning_template_delete_confirm'] = 'Are you sure you want to delete the selected templates?'; + +$txt['mc_warning_template_desc'] = 'Use this page to fill in the details of the template. Note that the subject for the email is not part of the template. Note that as the notification is sent by PM you can use BBC within the template. Note if you use the {MESSAGE} variable then this template will not be available when issuing a generic warning (i.e. A warning not linked to a post).'; +$txt['mc_warning_template_title'] = 'Template Title'; +$txt['mc_warning_template_body_desc'] = 'The content of the notification message. Note that you can use the following shortcuts in this template.
    • {MEMBER} - Member Name.
    • {MESSAGE} - Link to Offending Post. (If Applicable)
    • {FORUMNAME} - Forum Name.
    • {SCRIPTURL} - Web address of forum.
    • {REGARDS} - Standard email sign-off.
    '; +$txt['mc_warning_template_body_default'] = '{MEMBER},' . "\n\n" . 'You have received a warning for inappropriate activity. Please cease these activities and abide by the forum rules otherwise we will take further action.' . "\n\n" . '{REGARDS}'; +$txt['mc_warning_template_personal'] = 'Personal Template'; +$txt['mc_warning_template_personal_desc'] = 'If you select this option only you will be able to see, edit and use this template. If not selected all moderators will be able to use this template.'; +$txt['mc_warning_template_error_empty'] = 'You must set both a title and notification body.'; + +$txt['mc_prefs'] = 'Preferences'; +$txt['mc_settings'] = 'Change Settings'; +$txt['mc_prefs_title'] = 'Moderation Preferences'; +$txt['mc_prefs_desc'] = 'This section allows you to set some personal preferences for moderation related activities such as email notifications.'; +$txt['mc_prefs_homepage'] = 'Items to show on moderation homepage'; +$txt['mc_prefs_latest_news'] = 'SM News'; +$txt['mc_prefs_show_reports'] = 'Show open report count in forum header'; +$txt['mc_prefs_notify_report'] = 'Notify of topic reports'; +$txt['mc_prefs_notify_report_never'] = 'Never'; +$txt['mc_prefs_notify_report_moderator'] = 'Only if it\'s a board I moderate'; +$txt['mc_prefs_notify_report_always'] = 'Always'; +$txt['mc_prefs_notify_approval'] = 'Notify of items awaiting approval'; + +// Use entities in the below string. +$txt['mc_click_add_note'] = 'Add a new note'; +$txt['mc_add_note'] = 'Add'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Modifications.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Modifications.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,4 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Modlog.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Modlog.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,87 @@ +Please note: Entries cannot be removed from this log until they are at least twenty-four hours old.'; +$txt['modlog_no_entries_found'] = 'There are currently no moderation log entries.'; +$txt['modlog_remove'] = 'Remove'; +$txt['modlog_removeall'] = 'Remove All'; +$txt['modlog_go'] = 'Go'; +$txt['modlog_add'] = 'Add'; +$txt['modlog_search'] = 'Quick Search'; +$txt['modlog_by'] = 'By'; +$txt['modlog_id'] = 'Deleted - ID:%1$d'; + +$txt['modlog_ac_add_warn_template'] = 'Added warning template: "{template}"'; +$txt['modlog_ac_modify_warn_template'] = 'Edited the warning template: "{template}"'; +$txt['modlog_ac_delete_warn_template'] = 'Deleted the warning template: "{template}"'; + +$txt['modlog_ac_ban'] = 'Added ban triggers:'; +$txt['modlog_ac_ban_trigger_member'] = ' Member: {member}'; +$txt['modlog_ac_ban_trigger_email'] = ' Email: {email}'; +$txt['modlog_ac_ban_trigger_ip_range'] = ' IP: {ip_range}'; +$txt['modlog_ac_ban_trigger_hostname'] = ' Hostname: {hostname}'; + +$txt['modlog_admin_log'] = 'Administration Log'; +$txt['modlog_admin_log_desc'] = 'Below is a list of administration actions which have been logged on your forum.
    Please note: Entries cannot be removed from this log until they are at least twenty-four hours old.'; +$txt['modlog_admin_log_no_entries_found'] = 'There are currently no administration log entries.'; + +// Admin type strings. +$txt['modlog_ac_upgrade'] = 'Upgraded the forum to version {version}'; +$txt['modlog_ac_install'] = 'Installed version {version}'; +$txt['modlog_ac_add_board'] = 'Added a new board: "{board}"'; +$txt['modlog_ac_edit_board'] = 'Edited the "{board}" board'; +$txt['modlog_ac_delete_board'] = 'Deleted the "{boardname}" board'; +$txt['modlog_ac_add_cat'] = 'Added a new category, "{catname}"'; +$txt['modlog_ac_edit_cat'] = 'Edited the "{catname}" category'; +$txt['modlog_ac_delete_cat'] = 'Deleted the "{catname}" category'; + +$txt['modlog_ac_delete_group'] = 'Deleted the "{group}" group'; +$txt['modlog_ac_add_group'] = 'Added the "{group}" group'; +$txt['modlog_ac_edited_group'] = 'Edited the "{group}" group'; +$txt['modlog_ac_added_to_group'] = 'Added "{member}" to the "{group}" group'; +$txt['modlog_ac_removed_from_group'] = 'Removed "{member}" from the "{group}" group'; +$txt['modlog_ac_removed_all_groups'] = 'Removed "{member}" from all groups'; + +$txt['modlog_ac_remind_member'] = 'Sent out a reminder to "{member}" to activate their account'; +$txt['modlog_ac_approve_member'] = 'Approved/Activated the account of "{member}"'; +$txt['modlog_ac_newsletter'] = 'Sent Newsletter'; + +$txt['modlog_ac_install_package'] = 'Installed new package: "{package}", version {version}'; +$txt['modlog_ac_upgrade_package'] = 'Upgraded package: "{package}" to version {version}'; +$txt['modlog_ac_uninstall_package'] = 'Uninstalled package: "{package}", version {version}'; + +// Restore topic. +$txt['modlog_ac_restore_topic'] = 'Restored topic "{topic}" from "{board}" to "{board_to}"'; +$txt['modlog_ac_restore_posts'] = 'Restored posts from "{subject}" to the topic "{topic}" in the "{board}" board.'; + +$txt['modlog_parameter_guest'] = 'Guest'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Packages.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Packages.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,262 @@ +strongly recommended that you do not continue with installation unless you know what you are doing, and have made a backup very recently. + This error may be caused by a conflict between the package you\'re trying to install and another package you have already installed, an error in the package, a package which requires another package that you don\'t have installed yet, or a package designed for another version of SMF.'; +// Don't use entities in the below string. +$txt['package_will_fail_popup'] = 'Are you sure you wish to continue installing this modification, even though it will not install successfully?'; +$txt['package_will_fail_popup_uninstall'] = 'Are you sure you wish to continue uninstalling this modification, even though it will not uninstall successfully?'; +$txt['package_install_now'] = 'Install Now'; +$txt['package_uninstall_now'] = 'Uninstall Now'; +$txt['package_other_themes'] = 'Install in Other Themes'; +$txt['package_other_themes_uninstall'] = 'UnInstall in Other Themes'; +$txt['package_other_themes_desc'] = 'To use this modification in themes other than the default, the package manager needs to make additional changes to the other themes. If you\'d like to install this modification in the other themes, please select these themes below.'; +// Don't use entities in the below string. +$txt['package_theme_failure_warning'] = 'At least one error was encountered during a test install of this theme. Are you sure you wish to attempt installation?'; + +$txt['package_bytes'] = 'bytes'; + +$txt['package_action_missing'] = 'File not found'; +$txt['package_action_error'] = 'Modification parse error'; +$txt['package_action_failure'] = 'Test failed'; +$txt['package_action_success'] = 'Test successful'; +$txt['package_action_skipping'] = 'Skipping file'; + +$txt['package_uninstall_actions'] = 'Uninstall Actions'; +$txt['package_uninstall_done'] = 'The package has been uninstalled, it should no longer take effect.'; +$txt['package_uninstall_cannot'] = 'This package cannot be uninstalled, because there is no uninstaller!

    Please contact the mod author for more information.'; + +$txt['package_install_options'] = 'Installation Options'; +$txt['package_install_options_ftp_why'] = 'Using the package manager\'s FTP functionality is the easiest way to avoid having to manually chmod the files writable through FTP yourself for the package manager to work.
    Here you can set the default values for some fields.'; +$txt['package_install_options_ftp_server'] = 'FTP Server'; +$txt['package_install_options_ftp_port'] = 'Port'; +$txt['package_install_options_ftp_user'] = 'Username'; +$txt['package_install_options_make_backups'] = 'Create Backup versions of replaced files with a tilde (~) on the end of their names.'; + +$txt['package_ftp_necessary'] = 'FTP Information Required'; +$txt['package_ftp_why'] = 'Some of the files the package manager needs to modify are not writable. This needs to be changed by logging into FTP and using it to chmod or create the files and folders. Your FTP information may be temporarily cached for proper operation of the package manager. Note you can also do this manually using an FTP client - to view a list of the affected files please click here.'; +$txt['package_ftp_why_file_list'] = 'The following files need to made writable to continue installation:'; +$txt['package_ftp_why_download'] = 'To download packages, the Packages directory and files in it need to be writable - and they are not currently. The package manager can use your FTP information to fix this.'; +$txt['package_ftp_server'] = 'FTP Server'; +$txt['package_ftp_port'] = 'Port'; +$txt['package_ftp_username'] = 'Username'; +$txt['package_ftp_password'] = 'Password'; +$txt['package_ftp_path'] = 'Local path to SMF'; +$txt['package_ftp_test'] = 'Test'; +$txt['package_ftp_test_connection'] = 'Test Connection'; +$txt['package_ftp_test_success'] = 'FTP connection established.'; +$txt['package_ftp_test_failed'] = 'Could not contact server.'; + +// For a break, use \\n instead of
    ... and don't use entities. +$txt['package_delete_bad'] = 'The package you are about to delete is currently installed! If you delete it, you may not be able to uninstall it later.\\n\\nAre you sure?'; + +$txt['package_examine_file'] = 'View file in package'; +$txt['package_file_contents'] = 'Contents of file'; + +$txt['package_upload_title'] = 'Upload a Package'; +$txt['package_upload_select'] = 'Package to Upload'; +$txt['package_upload'] = 'Upload'; +$txt['package_uploaded_success'] = 'Package uploaded successfully'; +$txt['package_uploaded_successfully'] = 'The package has been uploaded successfully'; + +$txt['package_modification_malformed'] = 'Malformed or invalid modification file.'; +$txt['package_modification_missing'] = 'The file could not be found.'; +$txt['package_no_zlib'] = 'Sorry, your PHP configuration doesn\'t have support for zlib. Without this, the package manager cannot function. Please contact your host about this for more information.'; + +$txt['package_cleanperms_title'] = 'Cleanup Permissions'; +$txt['package_cleanperms_desc'] = 'This interface allows you to reset the permissions for files throughout your installation, so as to increase security or solve any permission problems you may encounter while installing packages.'; +$txt['package_cleanperms_type'] = 'Change all file permissions throughout the forum such that'; +$txt['package_cleanperms_standard'] = 'Only the standard files are writable.'; +$txt['package_cleanperms_free'] = 'All files are writable.'; +$txt['package_cleanperms_restrictive'] = 'The minimum files are writable.'; +$txt['package_cleanperms_go'] = 'Change file permissions'; + +$txt['package_download_by_url'] = 'Download a package by url'; +$txt['package_download_filename'] = 'Name of the file'; +$txt['package_download_filename_info'] = 'Optional value. Should be used when the url does not end in the filename. For example: index.php?mod=5'; + +$txt['package_db_uninstall'] = 'Remove all data associated with this modification.'; +$txt['package_db_uninstall_details'] = 'Details'; +$txt['package_db_uninstall_actions'] = 'Checking this option will result in the following database changes'; +$txt['package_db_remove_table'] = 'Drop table "%1$s"'; +$txt['package_db_remove_column'] = 'Remove column "%2$s" from "%1$s"'; +$txt['package_db_remove_index'] = 'Remove index "%1$s" from "%2$s"'; + +$txt['package_advanced_button'] = 'Advanced'; +$txt['package_advanced_options'] = 'Advanced Options'; +$txt['package_apply'] = 'Apply'; +$txt['package_emulate'] = 'Emulate Version'; +$txt['package_emulate_revert'] = 'Revert'; +$txt['package_emulate_desc'] = 'Sometimes packages are locked to early versions of SMF but remain compatible with a newer version. Here you can choose to "emulate" a different SMF version within the package manager.'; + +// Operations. +$txt['operation_find'] = 'Find'; +$txt['operation_replace'] = 'Replace'; +$txt['operation_after'] = 'Add After'; +$txt['operation_before'] = 'Add Before'; +$txt['operation_title'] = 'Operations'; +$txt['operation_ignore'] = 'Ignore Errors'; +$txt['operation_invalid'] = 'The operation that you selected is invalid.'; + +$txt['package_file_perms_desc'] = 'You can use this section to review the writable status of critical files and folders within your forum directory. Note this only considers key forum folders and files - use an FTP client for additional options.'; +$txt['package_file_perms_name'] = 'File/Folder Name'; +$txt['package_file_perms_status'] = 'Current Status'; +$txt['package_file_perms_new_status'] = 'New Status'; +$txt['package_file_perms_status_read'] = 'Read'; +$txt['package_file_perms_status_write'] = 'Write'; +$txt['package_file_perms_status_execute'] = 'Execute'; +$txt['package_file_perms_status_custom'] = 'Custom'; +$txt['package_file_perms_status_no_change'] = 'No Change'; +$txt['package_file_perms_writable'] = 'Writable'; +$txt['package_file_perms_not_writable'] = 'Not Writable'; +$txt['package_file_perms_chmod'] = 'chmod'; +$txt['package_file_perms_more_files'] = 'More Files'; + +$txt['package_file_perms_change'] = 'Change File Permissions'; +$txt['package_file_perms_predefined'] = 'Use predefined permission profile'; +$txt['package_file_perms_predefined_note'] = 'Note that this only applies the predefined profile to key SMF folders and files.'; +$txt['package_file_perms_apply'] = 'Apply individual file permissions settings selected above.'; +$txt['package_file_perms_custom'] = 'If "Custom" has been selected use chmod value of'; +$txt['package_file_perms_pre_restricted'] = 'Restricted - minimum files writable'; +$txt['package_file_perms_pre_standard'] = 'Standard - key files writable'; +$txt['package_file_perms_pre_free'] = 'Free - all files writable'; +$txt['package_file_perms_ftp_details'] = 'On most servers it is only possible to change file permissions using an FTP account. Please enter your FTP details below'; +$txt['package_file_perms_ftp_retain'] = 'Note, SMF will only retain the password information temporarily to aid operation of the package manager.'; +$txt['package_file_perms_go'] = 'Make Changes'; + +$txt['package_file_perms_applying'] = 'Applying Changes'; +$txt['package_file_perms_items_done'] = '%1$d of %2$d items completed'; +$txt['package_file_perms_skipping_ftp'] = 'Warning: Failed to connect to FTP server, attempting to change permissions without. This is likely to fail - please check the results upon completion and try again with correct FTP details if necessary.'; + +$txt['package_file_perms_dirs_done'] = '%1$d of %2$d directories completed'; +$txt['package_file_perms_files_done'] = '%1$d of %2$d files done in current directory'; + +$txt['chmod_value_invalid'] = 'You have tried to enter an invalid chmod value. Chmod must be between 0444 and 0777'; + +$txt['package_restore_permissions'] = 'Restore File Permissions'; +$txt['package_restore_permissions_desc'] = 'The following file permissions were changed by SMF to install the selected package(s). You can return these files back to their original status by clicking "Restore" below.'; +$txt['package_restore_permissions_restore'] = 'Restore'; +$txt['package_restore_permissions_filename'] = 'Filename'; +$txt['package_restore_permissions_orig_status'] = 'Original Status'; +$txt['package_restore_permissions_cur_status'] = 'Current Status'; +$txt['package_restore_permissions_result'] = 'Result'; +$txt['package_restore_permissions_pre_change'] = '%1$s (%3$s)'; +$txt['package_restore_permissions_post_change'] = '%2$s (%3$s - was %2$s)'; +$txt['package_restore_permissions_action_skipped'] = 'Skipped'; +$txt['package_restore_permissions_action_success'] = 'Success'; +$txt['package_restore_permissions_action_failure'] = 'Failed'; +$txt['package_restore_permissions_action_done'] = 'SMF has attempted to restore the selected files back to their original permissions, the results can be seen below. If a change failed, or for a more detailed view of file permissions, please see the File Permissions section.'; + +$txt['package_file_perms_warning'] = 'Please Note'; +$txt['package_file_perms_warning_desc'] = ' +
  13. Be careful when changing file permissions from this section - incorrect permissions can adversely affect the operation of your forum!
  14. +
  15. On some server configurations selecting the wrong permissions may stop SMF from operating.
  16. +
  17. Certain directories such as attachments need to be writable to use that functionality.
  18. +
  19. This functionality is mainly applicable on non-Windows based servers - it will not work as expected on Windows in regards to permission flags.
  20. +
  21. Before proceeding make sure you have an FTP client installed in case you do make an error and need to FTP into the server to remedy it.
  22. '; + +$txt['package_confirm_view_package_content'] = 'Are you sure you want to view the package contents from this location:

    %1$s'; +$txt['package_confirm_proceed'] = 'Proceed'; +$txt['package_confirm_go_back'] = 'Go back'; + +$txt['package_readme_default'] = 'Default'; +$txt['package_available_readme_language'] = 'Available Readme Languages:'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/PersonalMessage.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/PersonalMessage.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,196 @@ +Note: You appear to have javascript disabled. We highly recommend you enable javascript to use this feature.
    '; +$txt['pm_rule_criteria'] = 'Criteria'; +$txt['pm_rule_criteria_add'] = 'Add Criteria'; +$txt['pm_rule_criteria_pick'] = 'Choose Criteria'; +$txt['pm_rule_mid'] = 'Sender Name'; +$txt['pm_rule_gid'] = 'Sender\'s Group'; +$txt['pm_rule_sub'] = 'Message Subject Contains'; +$txt['pm_rule_msg'] = 'Message Body Contains'; +$txt['pm_rule_bud'] = 'Sender is Buddy'; +$txt['pm_rule_sel_group'] = 'Select Group'; +$txt['pm_rule_logic'] = 'When Checking Criteria'; +$txt['pm_rule_logic_and'] = 'All criteria must be met'; +$txt['pm_rule_logic_or'] = 'Any criteria can be met'; +$txt['pm_rule_actions'] = 'Actions'; +$txt['pm_rule_sel_action'] = 'Select an Action'; +$txt['pm_rule_add_action'] = 'Add Action'; +$txt['pm_rule_label'] = 'Label message with'; +$txt['pm_rule_sel_label'] = 'Select Label'; +$txt['pm_rule_delete'] = 'Delete Message'; +$txt['pm_rule_no_name'] = 'You forgot to enter a name for the rule.'; +$txt['pm_rule_no_criteria'] = 'A rule must have at least one criteria and one action set.'; +$txt['pm_rule_too_complex'] = 'The rule you are creating is too long for SMF to store. Try breaking it up into smaller rules.'; + +$txt['pm_readable_and'] = 'and'; +$txt['pm_readable_or'] = 'or'; +$txt['pm_readable_start'] = 'If '; +$txt['pm_readable_end'] = '.'; +$txt['pm_readable_member'] = 'message is from "{MEMBER}"'; +$txt['pm_readable_group'] = 'sender is from the "{GROUP}" group'; +$txt['pm_readable_subject'] = 'message subject contains "{SUBJECT}"'; +$txt['pm_readable_body'] = 'message body contains "{BODY}"'; +$txt['pm_readable_buddy'] = 'sender is a buddy'; +$txt['pm_readable_label'] = 'apply label "{LABEL}"'; +$txt['pm_readable_delete'] = 'delete the message'; +$txt['pm_readable_then'] = 'then'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Post.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Post.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,196 @@ + 'it\\\'s'. +$txt['bbc_quote'] = 'Insert Quote'; +$txt['list'] = 'Insert List'; +$txt['list_unordered'] = 'Insert Unordered List'; +$txt['list_ordered'] = 'Insert Ordered List'; + +$txt['change_color'] = 'Change Color'; +$txt['black'] = 'Black'; +$txt['red'] = 'Red'; +$txt['yellow'] = 'Yellow'; +$txt['pink'] = 'Pink'; +$txt['green'] = 'Green'; +$txt['orange'] = 'Orange'; +$txt['purple'] = 'Purple'; +$txt['blue'] = 'Blue'; +$txt['beige'] = 'Beige'; +$txt['brown'] = 'Brown'; +$txt['teal'] = 'Teal'; +$txt['navy'] = 'Navy'; +$txt['maroon'] = 'Maroon'; +$txt['lime_green'] = 'Lime Green'; +$txt['white'] = 'White'; +$txt['disable_smileys'] = 'Disable Smileys'; +$txt['dont_use_smileys'] = 'Don\'t use smileys.'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['posted_on'] = 'Posted on'; +$txt['standard'] = 'Standard'; +$txt['thumbs_up'] = 'Thumb Up'; +$txt['thumbs_down'] = 'Thumb Down'; +$txt['excamation_point'] = 'Exclamation point'; +$txt['question_mark'] = 'Question mark'; +$txt['lamp'] = 'Lamp'; +$txt['add_smileys'] = 'Add Smileys'; +$txt['flash'] = 'Insert Flash'; +$txt['ftp'] = 'Insert FTP Link'; +$txt['image'] = 'Insert Image'; +$txt['table'] = 'Insert Table'; +$txt['table_td'] = 'Insert Table Column'; +$txt['topic_notify_no'] = 'There are no topics with Notification.'; +$txt['marquee'] = 'Marquee'; +$txt['teletype'] = 'Teletype'; +$txt['strike'] = 'Strikethrough'; +$txt['glow'] = 'Glow'; +$txt['shadow'] = 'Shadow'; +$txt['preformatted'] = 'Preformatted Text'; +$txt['left_align'] = 'Left Align'; +$txt['right_align'] = 'Right Align'; +$txt['superscript'] = 'Superscript'; +$txt['subscript'] = 'Subscript'; +$txt['table_tr'] = 'Insert Table Row'; +$txt['post_too_long'] = 'Your message is too long. Please go back and shorten it, then try again.'; +$txt['horizontal_rule'] = 'Horizontal Rule'; +$txt['font_size'] = 'Font Size'; +$txt['font_face'] = 'Font Face'; +$txt['toggle_view'] = 'Toggle View'; +$txt['unformat_text'] = 'Remove Formatting'; + +$txt['rich_edit_wont_work'] = 'Your browser does not support Rich Text editing.'; +$txt['rich_edit_function_disabled'] = 'Your browser does not support this function.'; + +// Use numeric entities in the below five strings. +$txt['notifyUnsubscribe'] = 'Unsubscribe to this topic by clicking here'; + +$txt['lock_after_post'] = 'Lock after Post'; +$txt['notify_replies'] = 'Notify me of replies.'; +$txt['lock_topic'] = 'Lock this topic.'; +$txt['shortcuts'] = 'shortcuts: hit alt+s to submit/post or alt+p to preview'; +$txt['shortcuts_firefox'] = 'shortcuts: hit shift+alt+s to submit/post or shift+alt+p to preview'; +$txt['option'] = 'Option'; +$txt['reset_votes'] = 'Reset Vote Count'; +$txt['reset_votes_check'] = 'Check this if you want to reset all vote counts to 0.'; +$txt['votes'] = 'votes'; +$txt['attach'] = 'Attach'; +$txt['clean_attach'] = 'Clear Attachment'; +$txt['attached'] = 'Attached'; +$txt['allowed_types'] = 'Allowed file types'; +$txt['cant_upload_type'] = 'You cannot upload that type of file. The only allowed extensions are'; +$txt['uncheck_unwatchd_attach'] = 'Uncheck the attachments you no longer want attached'; +$txt['restricted_filename'] = 'That is a restricted filename. Please try a different filename.'; +$txt['topic_locked_no_reply'] = 'Warning: topic is currently/will be locked!
    Only admins and moderators can reply.'; +$txt['awaiting_approval'] = 'Awaiting approval'; +$txt['attachment_requires_approval'] = 'Note that any files attached will not be displayed until approved by a moderator.'; +$txt['error_temp_attachments'] = 'There are attachments found, which you have attached before but not posted. These attachments are now attached to this post. If you do not want to include them in this post, you can remove them here.'; +// Use numeric entities in the below string. +$txt['js_post_will_require_approval'] = 'Reminder: This post will not appear until approved by a moderator.'; + +$txt['enter_comment'] = 'Enter comment'; +// Use numeric entities in the below two strings. +$txt['reported_post'] = 'Reported post'; +$txt['reported_to_mod_by'] = 'by'; +$txt['rtm10'] = 'Submit'; +// Use numeric entities in the below four strings. +$txt['report_following_post'] = 'The following post, "%1$s" by'; +$txt['reported_by'] = 'has been reported by'; +$txt['board_moderate'] = 'on a board you moderate'; +$txt['report_comment'] = 'The reporter has made the following comment'; + +$txt['attach_restrict_attachmentPostLimit'] = 'maximum total size %1$dKB'; +$txt['attach_restrict_attachmentSizeLimit'] = 'maximum individual size %1$dKB'; +$txt['attach_restrict_attachmentNumPerPostLimit'] = '%1$d per post'; +$txt['attach_restrictions'] = 'Restrictions:'; + +$txt['post_additionalopt'] = 'Attachments and other options'; +$txt['sticky_after'] = 'Sticky this topic.'; +$txt['move_after2'] = 'Move this topic.'; +$txt['back_to_topic'] = 'Return to this topic.'; +$txt['approve_this_post'] = 'Approve this Post'; + +$txt['retrieving_quote'] = 'Retrieving Quote...'; + +$txt['post_visual_verification_label'] = 'Verification'; +$txt['post_visual_verification_desc'] = 'Please enter the code in the image above to make this post.'; + +$txt['poll_options'] = 'Poll Options'; +$txt['poll_run'] = 'Run the poll for'; +$txt['poll_run_limit'] = '(Leave blank for no limit.)'; +$txt['poll_results_visibility'] = 'Result visibility'; +$txt['poll_results_anyone'] = 'Show the poll\'s results to anyone.'; +$txt['poll_results_voted'] = 'Only show the results after someone has voted.'; +$txt['poll_results_after'] = 'Only show the results after the poll has expired.'; +$txt['poll_max_votes'] = 'Maximum votes per user'; +$txt['poll_do_change_vote'] = 'Allow users to change vote'; +$txt['poll_too_many_votes'] = 'You selected too many options. For this poll, you may only select %1$s options.'; +$txt['poll_add_option'] = 'Add Option'; +$txt['poll_guest_vote'] = 'Allow guests to vote'; + +$txt['spellcheck_done'] = 'Spell checking complete.'; +$txt['spellcheck_change_to'] = 'Change To:'; +$txt['spellcheck_suggest'] = 'Suggestions:'; +$txt['spellcheck_change'] = 'Change'; +$txt['spellcheck_change_all'] = 'Change All'; +$txt['spellcheck_ignore'] = 'Ignore'; +$txt['spellcheck_ignore_all'] = 'Ignore All'; + +$txt['more_attachments'] = 'more attachments'; +// Don't use entities in the below string. +$txt['more_attachments_error'] = 'Sorry, you aren\'t allowed to post any more attachments.'; + +$txt['more_smileys'] = 'more'; +$txt['more_smileys_title'] = 'Additional smileys'; +$txt['more_smileys_pick'] = 'Pick a smiley'; +$txt['more_smileys_close_window'] = 'Close Window'; + +$txt['error_new_reply'] = 'Warning - while you were typing a new reply has been posted. You may wish to review your post.'; +$txt['error_new_replies'] = 'Warning - while you were typing %1$d new replies have been posted. You may wish to review your post.'; +$txt['error_new_reply_reading'] = 'Warning - while you were reading a new reply has been posted. You may wish to review your post.'; +$txt['error_new_replies_reading'] = 'Warning - while you were reading %1$d new replies have been posted. You may wish to review your post.'; + +$txt['announce_this_topic'] = 'Send an announcement about this topic to the members:'; +$txt['announce_title'] = 'Send an announcement'; +$txt['announce_desc'] = 'This form allows you to send an announcement to the selected membergroups about this topic.'; +$txt['announce_sending'] = 'Sending announcement of topic'; +$txt['announce_done'] = 'done'; +$txt['announce_continue'] = 'Continue'; +$txt['announce_topic'] = 'Announce topic.'; +$txt['announce_regular_members'] = 'Regular Members'; + +$txt['digest_subject_daily'] = 'Daily Digest'; +$txt['digest_subject_weekly'] = 'Weekly Digest'; +$txt['digest_intro_daily'] = 'Below is a summary of all activity in your subscribed boards and topics at %1$s today. To unsubscribe please visit the link below.'; +$txt['digest_intro_weekly'] = 'Below is a summary of all activity in your subscribed boards and topics at %1$s this week. To unsubscribe please visit the link below.'; +$txt['digest_new_topics'] = 'The following topics have been started'; +$txt['digest_new_topics_line'] = '"%1$s" in "%2$s"'; +$txt['digest_new_replies'] = 'Replies have been made in the following topics'; +$txt['digest_new_replies_one'] = '1 reply in "%1$s"'; +$txt['digest_new_replies_many'] = '%1$d replies in "%2$s"'; +$txt['digest_mod_actions'] = 'The following moderation actions have taken place'; +$txt['digest_mod_act_sticky'] = '"%1$s" was stickied'; +$txt['digest_mod_act_lock'] = '"%1$s" was locked'; +$txt['digest_mod_act_unlock'] = '"%1$s" was unlocked'; +$txt['digest_mod_act_remove'] = '"%1$s" was removed'; +$txt['digest_mod_act_move'] = '"%1$s" was moved'; +$txt['digest_mod_act_merge'] = '"%1$s" was merged'; +$txt['digest_mod_act_split'] = '"%1$s" was split'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Profile.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Profile.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,471 @@ +http://www.mypage.com/mypic.gif)'; +$txt['my_own_pic'] = 'Specify avatar by URL'; +$txt['date_format'] = 'The format here will be used to show dates throughout this forum.'; +$txt['time_format'] = 'Time Format'; +$txt['display_name_desc'] = 'This is the displayed name that people will see.'; +$txt['personal_time_offset'] = 'Number of hours to +/- to make displayed time equal to your local time.'; +$txt['dob'] = 'Birthdate'; +$txt['dob_month'] = 'Month (MM)'; +$txt['dob_day'] = 'Day (DD)'; +$txt['dob_year'] = 'Year (YYYY)'; +$txt['password_strength'] = 'For best security, you should use eight or more characters with a combination of letters, numbers, and symbols.'; +$txt['include_website_url'] = 'This must be included if you specify a URL below.'; +$txt['complete_url'] = 'This must be a complete URL.'; +$txt['your_icq'] = 'This is your ICQ number.'; +$txt['your_aim'] = 'This is your AOL Instant Messenger nickname.'; +$txt['your_yim'] = 'This is your Yahoo! Instant Messenger nickname.'; +$txt['sig_info'] = 'Signatures are displayed at the bottom of each post or personal message. BBCode and smileys may be used in your signature.'; +$txt['max_sig_characters'] = 'Max characters: %1$d; characters remaining: '; +$txt['send_member_pm'] = 'Send this member a personal message'; +$txt['hidden'] = 'hidden'; +$txt['current_time'] = 'Current forum time'; +$txt['digits_only'] = 'The \'number of posts\' box can only contain digits.'; + +$txt['language'] = 'Language'; +$txt['avatar_too_big'] = 'Avatar image is too big, please resize it and try again (max'; +$txt['invalid_registration'] = 'Invalid Date Registered value, valid example:'; +$txt['msn_email_address'] = 'Your MSN messenger email address'; +$txt['current_password'] = 'Current Password'; +// Don't use entities in the below string, except the main ones. (lt, gt, quot.) +$txt['required_security_reasons'] = 'For security reasons, your current password is required to make changes to your account.'; + +$txt['timeoffset_autodetect'] = '(auto detect)'; + +$txt['secret_question'] = 'Secret Question'; +$txt['secret_desc'] = 'To help retrieve your password, enter a question here with an answer that only you know.'; +$txt['secret_desc2'] = 'Choose carefully, you wouldn\'t want someone guessing your answer!'; +$txt['secret_answer'] = 'Answer'; +$txt['secret_ask'] = 'Ask me my question'; +$txt['cant_retrieve'] = 'You can\'t retrieve your password, but you can set a new one by following a link sent to you by email. You also have the option of setting a new password by answering your secret question.'; +$txt['incorrect_answer'] = 'Sorry, but you did not specify a valid combination of Secret Question and Answer in your profile. Please click on the back button, and use the default method of obtaining your password.'; +$txt['enter_new_password'] = 'Please enter the answer to your question, and the password you would like to use. Your password will be changed to the one you select provided you answer the question correctly.'; +$txt['password_success'] = 'Your password was changed successfully.
    Click here to login.'; +$txt['secret_why_blank'] = 'why is this blank?'; + +$txt['authentication_reminder'] = 'Authentication Reminder'; +$txt['password_reminder_desc'] = 'If you\'ve forgotten your login details, don\'t worry, they can be retrieved. To start this process please enter your username or email address below.'; +$txt['authentication_options'] = 'Please select one of the two options below'; +$txt['authentication_openid_email'] = 'Email me a reminder of my OpenID identity'; +$txt['authentication_openid_secret'] = 'Answer my "secret question" to display my OpenID identity'; +$txt['authentication_password_email'] = 'Email me a new password'; +$txt['authentication_password_secret'] = 'Let me set a new password by answering my "secret question"'; +$txt['openid_secret_reminder'] = 'Please enter your answer to the question below. If you get it correct your OpenID identity will be shown.'; +$txt['reminder_openid_is'] = 'The OpenID identity associated with your account is:
        %1$s

    Please make a note of this for future reference.'; +$txt['reminder_continue'] = 'Continue'; + +$txt['current_theme'] = 'Current Theme'; +$txt['change'] = '(change)'; +$txt['theme_preferences'] = 'Theme preferences'; +$txt['theme_forum_default'] = 'Forum or Board Default'; +$txt['theme_forum_default_desc'] = 'This is the default theme, which means your theme will change along with the administrator\'s settings and the board you are viewing.'; + +$txt['profileConfirm'] = 'Do you really want to delete this member?'; + +$txt['custom_title'] = 'Custom Title'; + +$txt['lastLoggedIn'] = 'Last Active'; + +$txt['notify_settings'] = 'Notification Settings:'; +$txt['notify_save'] = 'Save settings'; +$txt['notify_important_email'] = 'Receive forum newsletters, announcements and important notifications by email.'; +$txt['notify_regularity'] = 'For topics and boards I\'ve requested notification on, notify me'; +$txt['notify_regularity_instant'] = 'Instantly'; +$txt['notify_regularity_first_only'] = 'Instantly - but only for the first unread reply'; +$txt['notify_regularity_daily'] = 'Daily'; +$txt['notify_regularity_weekly'] = 'Weekly'; +$txt['auto_notify'] = 'Turn notification on when you post or reply to a topic.'; +$txt['notify_send_types'] = 'For topics and boards I\'ve requested notification on, notify me of'; +$txt['notify_send_type_everything'] = 'Replies and moderation'; +$txt['notify_send_type_everything_own'] = 'Moderation only if I started the topic'; +$txt['notify_send_type_only_replies'] = 'Only replies'; +$txt['notify_send_type_nothing'] = 'Nothing at all'; +$txt['notify_send_body'] = 'When sending notification of a reply to a topic, send the post in the email (but please don\'t reply to these emails.)'; + +$txt['notifications_topics'] = 'Current Topic Notifications'; +$txt['notifications_topics_list'] = 'You are being notified of replies to the following topics'; +$txt['notifications_topics_none'] = 'You are not currently receiving any notifications from topics.'; +$txt['notifications_topics_howto'] = 'To receive notifications from a topic, click the "notify" button while viewing it.'; +$txt['notifications_boards'] = 'Current Board Notifications'; +$txt['notifications_boards_list'] = 'You are being notified of new topics posted in the following boards'; +$txt['notifications_boards_none'] = 'You aren\'t receiving notifications on any boards right now.'; +$txt['notifications_boards_howto'] = 'To request notifications from a specific board, click the "notify" button in the index of that board.'; +$txt['notifications_update'] = 'Unnotify'; + +$txt['statPanel_showStats'] = 'User statistics for: '; +$txt['statPanel_users_votes'] = 'Number of Votes Cast'; +$txt['statPanel_users_polls'] = 'Number of Polls Created'; +$txt['statPanel_total_time_online'] = 'Total Time Spent Online'; +$txt['statPanel_noPosts'] = 'No posts to speak of!'; +$txt['statPanel_generalStats'] = 'General Statistics'; +$txt['statPanel_posts'] = 'posts'; +$txt['statPanel_topics'] = 'topics'; +$txt['statPanel_total_posts'] = 'Total Posts'; +$txt['statPanel_total_topics'] = 'Total Topics Started'; +$txt['statPanel_votes'] = 'votes'; +$txt['statPanel_polls'] = 'polls'; +$txt['statPanel_topBoards'] = 'Most Popular Boards By Posts'; +$txt['statPanel_topBoards_posts'] = '%1$d posts of the board\'s %2$d posts (%3$01.2f%%)'; +$txt['statPanel_topBoards_memberposts'] = '%1$d posts of the member\'s %2$d posts (%3$01.2f%%)'; +$txt['statPanel_topBoardsActivity'] = 'Most Popular Boards By Activity'; +$txt['statPanel_activityTime'] = 'Posting Activity By Time'; +$txt['statPanel_activityTime_posts'] = '%1$d posts (%2$d%%)'; +$txt['statPanel_timeOfDay'] = 'Time of Day'; + +$txt['deleteAccount_warning'] = 'Warning - These actions are irreversible!'; +$txt['deleteAccount_desc'] = 'From this page you can delete this user\'s account and posts.'; +$txt['deleteAccount_member'] = 'Delete this member\'s account'; +$txt['deleteAccount_posts'] = 'Remove posts made by this member'; +$txt['deleteAccount_none'] = 'None'; +$txt['deleteAccount_all_posts'] = 'All Posts'; +$txt['deleteAccount_topics'] = 'Topics and Posts'; +$txt['deleteAccount_confirm'] = 'Are you completely sure you want to delete this account?'; +$txt['deleteAccount_approval'] = 'Please note that the forum moderators will have to approve this account\'s deletion before it will be removed.'; + +$txt['profile_of_username'] = 'Profile of %1$s'; +$txt['profileInfo'] = 'Profile Info'; +$txt['showPosts'] = 'Show Posts'; +$txt['showPosts_help'] = 'This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.'; +$txt['showMessages'] = 'Messages'; +$txt['showTopics'] = 'Topics'; +$txt['showAttachments'] = 'Attachments'; +$txt['statPanel'] = 'Show Stats'; +$txt['editBuddyIgnoreLists'] = 'Buddies/Ignore List'; +$txt['editBuddies'] = 'Edit Buddies'; +$txt['editIgnoreList'] = 'Edit Ignore List'; +$txt['trackUser'] = 'Track User'; +$txt['trackActivity'] = 'Activity'; +$txt['trackIP'] = 'IP Address'; + +$txt['authentication'] = 'Authentication'; +$txt['change_authentication'] = 'From this section you can change how you login to the forum. You may choose to either use an OpenID account for your authentication, or alternatively switch to use a username and password.'; + +$txt['profileEdit'] = 'Modify Profile'; +$txt['account_info'] = 'These are your account settings. This page holds all critical information that identifies you on this forum. For security reasons, you will need to enter your (current) password to make changes to this information.'; +$txt['forumProfile_info'] = 'You can change your personal information on this page. This information will be displayed throughout ' . $context['forum_name_html_safe'] . '. If you aren\'t comfortable with sharing some information, simply skip it - nothing here is required.'; +$txt['theme'] = 'Look and Layout'; +$txt['theme_info'] = 'This section allows you to customize the look and layout of the forum.'; +$txt['notification'] = 'Notifications'; +$txt['notification_info'] = 'SMF allows you to be notified of replies to posts, newly posted topics, and forum announcements. You can change those settings here, or oversee the topics and boards you are currently receiving notifications for.'; +$txt['groupmembership'] = 'Group Membership'; +$txt['groupMembership_info'] = 'In this section of your profile you can change which groups you belong to.'; +$txt['ignoreboards'] = 'Ignore Boards Options'; +$txt['ignoreboards_info'] = 'This page lets you ignore particular boards. When a board is ignored, the new post indicator will not show up on the board index. New posts will not show up using the "unread post" search link (when searching it will not look in those boards) however, ignored boards will still appear on the board index and upon entering will show which topics have new posts. When using the "unread replies" link, new posts in an ignored board will still be shown.'; +$txt['pmprefs'] = 'Personal Messaging'; + +$txt['profileAction'] = 'Actions'; +$txt['deleteAccount'] = 'Delete this account'; +$txt['profileSendIm'] = 'Send personal message'; +$txt['profile_sendpm_short'] = 'Send PM'; + +$txt['profileBanUser'] = 'Ban this user'; + +$txt['display_name'] = 'Display name'; +$txt['enter_ip'] = 'Enter IP (range)'; +$txt['errors_by'] = 'Error messages by'; +$txt['errors_desc'] = 'Below is a list of all the recent errors that this user has generated/experienced.'; +$txt['errors_from_ip'] = 'Error messages from IP (range)'; +$txt['errors_from_ip_desc'] = 'Below is a list of all recent error messages generated by this IP (range).'; +$txt['ip_address'] = 'IP address'; +$txt['ips_in_errors'] = 'IPs used in error messages'; +$txt['ips_in_messages'] = 'IPs used in recent posts'; +$txt['members_from_ip'] = 'Members from IP (range)'; +$txt['members_in_range'] = 'Members possibly in the same range'; +$txt['messages_from_ip'] = 'Messages posted from IP (range)'; +$txt['messages_from_ip_desc'] = 'Below is a list of all messages posted from this IP (range).'; +$txt['most_recent_ip'] = 'Most recent IP address'; +$txt['why_two_ip_address'] = 'Why are there two IP addresses listed?'; +$txt['no_errors_from_ip'] = 'No error messages from the specified IP (range) found'; +$txt['no_errors_from_user'] = 'No error messages from the specified user found'; +$txt['no_members_from_ip'] = 'No members from the specified IP (range) found'; +$txt['no_messages_from_ip'] = 'No messages from the specified IP (range) found'; +$txt['none'] = 'None'; +$txt['own_profile_confirm'] = 'Are you sure you want to delete your account?'; +$txt['view_ips_by'] = 'View IPs used by'; + +$txt['avatar_will_upload'] = 'Upload an avatar'; + +$txt['activate_changed_email_title'] = 'Email Address Changed'; +$txt['activate_changed_email_desc'] = 'You\'ve changed your email address. In order to validate this address you will receive an email. Click the link in that email to reactivate your account.'; + +// Use numeric entities in the below three strings. +$txt['no_reminder_email'] = 'Unable to send reminder email.'; +$txt['send_email'] = 'Send an email to'; +$txt['to_ask_password'] = 'to ask for your authentication details'; + +$txt['user_email'] = 'Username/Email'; + +// Use numeric entities in the below two strings. +$txt['reminder_subject'] = 'New password for ' . $context['forum_name']; +$txt['reminder_mail'] = 'This mail was sent because the \'forgot password\' function has been applied to your account. To set a new password, click the following link'; +$txt['reminder_sent'] = 'A mail has been sent to your email address. Click the link in that mail to set a new password.'; +$txt['reminder_openid_sent'] = 'Your current OpenID identity has been sent to your email address.'; +$txt['reminder_set_password'] = 'Set Password'; +$txt['reminder_password_set'] = 'Password successfully set'; +$txt['reminder_error'] = '%1$s failed to answer their secret question correctly when attempting to change a forgotten password.'; + +$txt['registration_not_approved'] = 'Sorry, this account has not yet been approved. If you need to change your email address please click'; +$txt['registration_not_activated'] = 'Sorry, this account has not yet been activated. If you need to resend the activation email please click'; + +$txt['primary_membergroup'] = 'Primary Membergroup'; +$txt['additional_membergroups'] = 'Additional Membergroups'; +$txt['additional_membergroups_show'] = '[ show additional groups ]'; +$txt['no_primary_membergroup'] = '(no primary membergroup)'; +$txt['deadmin_confirm'] = 'Are you sure you wish to irrevocably remove your admin status?'; + +$txt['account_activate_method_2'] = 'Account requires reactivation after email change'; +$txt['account_activate_method_3'] = 'Account is not approved'; +$txt['account_activate_method_4'] = 'Account is awaiting approval for deletion'; +$txt['account_activate_method_5'] = 'Account is an "under age" account awaiting approval'; +$txt['account_not_activated'] = 'Account is currently not activated'; +$txt['account_activate'] = 'activate'; +$txt['account_approve'] = 'approve'; +$txt['user_is_banned'] = 'User is currently banned'; +$txt['view_ban'] = 'View'; +$txt['user_banned_by_following'] = 'This user is currently affected by the following bans'; +$txt['user_cannot_due_to'] = 'User cannot %1$s as a result of ban: "%2$s"'; +$txt['ban_type_post'] = 'post'; +$txt['ban_type_register'] = 'register'; +$txt['ban_type_login'] = 'login'; +$txt['ban_type_access'] = 'access forum'; + +$txt['show_online'] = 'Show others my online status'; + +$txt['return_to_post'] = 'Return to topics after posting by default.'; +$txt['no_new_reply_warning'] = 'Don\'t warn on new replies made while posting.'; +$txt['posts_apply_ignore_list'] = 'Hide messages posted by members on my ignore list.'; +$txt['recent_posts_at_top'] = 'Show most recent posts at the top.'; +$txt['recent_pms_at_top'] = 'Show most recent personal messages at top.'; +$txt['wysiwyg_default'] = 'Show WYSIWYG editor on post page by default.'; + +$txt['timeformat_default'] = '(Forum Default)'; +$txt['timeformat_easy1'] = 'Month Day, Year, HH:MM:SS am/pm'; +$txt['timeformat_easy2'] = 'Month Day, Year, HH:MM:SS (24 hour)'; +$txt['timeformat_easy3'] = 'YYYY-MM-DD, HH:MM:SS'; +$txt['timeformat_easy4'] = 'DD Month YYYY, HH:MM:SS'; +$txt['timeformat_easy5'] = 'DD-MM-YYYY, HH:MM:SS'; + +$txt['poster'] = 'Poster'; + +$txt['board_desc_inside'] = 'Show board descriptions inside boards.'; +$txt['show_children'] = 'Show child boards on every page inside boards, not just the first.'; +$txt['use_sidebar_menu'] = 'Use sidebar menus instead of dropdown menus when possible.'; +$txt['show_no_avatars'] = 'Don\'t show users\' avatars.'; +$txt['show_no_signatures'] = 'Don\'t show users\' signatures.'; +$txt['show_no_censored'] = 'Leave words uncensored.'; +$txt['topics_per_page'] = 'Topics to display per page:'; +$txt['messages_per_page'] = 'Messages to display per page:'; +$txt['per_page_default'] = 'forum default'; +$txt['calendar_start_day'] = 'First day of the week on the calendar'; +$txt['display_quick_reply'] = 'Use quick reply on topic display: '; +$txt['display_quick_reply1'] = 'don\'t show at all'; +$txt['display_quick_reply2'] = 'show, off by default'; +$txt['display_quick_reply3'] = 'show, on by default'; +$txt['display_quick_mod'] = 'Show quick-moderation as '; +$txt['display_quick_mod_none'] = 'don\'t show.'; +$txt['display_quick_mod_check'] = 'checkboxes.'; +$txt['display_quick_mod_image'] = 'icons.'; + +$txt['whois_title'] = 'Look up IP on a regional whois-server'; +$txt['whois_afrinic'] = 'AfriNIC (Africa)'; +$txt['whois_apnic'] = 'APNIC (Asia Pacific region)'; +$txt['whois_arin'] = 'ARIN (North America, a portion of the Caribbean and sub-Saharan Africa)'; +$txt['whois_lacnic'] = 'LACNIC (Latin American and Caribbean region)'; +$txt['whois_ripe'] = 'RIPE (Europe, the Middle East and parts of Africa and Asia)'; + +$txt['moderator_why_missing'] = 'why isn\'t moderator here?'; +$txt['username_change'] = 'change'; +$txt['username_warning'] = 'To change this member\'s username, the forum must also reset their password, which will be emailed to the member with their new username.'; + +$txt['show_member_posts'] = 'View Member Posts'; +$txt['show_member_topics'] = 'View Member Topics'; +$txt['show_member_attachments'] = 'View Member Attachments'; +$txt['show_posts_none'] = 'No posts have been posted yet.'; +$txt['show_topics_none'] = 'No topics have been posted yet.'; +$txt['show_attachments_none'] = 'No attachments have been posted yet.'; +$txt['show_attach_filename'] = 'Filename'; +$txt['show_attach_downloads'] = 'Downloads'; +$txt['show_attach_posted'] = 'Posted'; + +$txt['showPermissions'] = 'Show Permissions'; +$txt['showPermissions_status'] = 'Permission status'; +$txt['showPermissions_help'] = 'This section allows you to view all permissions for this member (denied permissions are struck out).'; +$txt['showPermissions_given'] = 'Given by'; +$txt['showPermissions_denied'] = 'Denied by'; +$txt['showPermissions_permission'] = 'Permission (denied permissions are struck out)'; +$txt['showPermissions_none_general'] = 'This member has no general permissions set.'; +$txt['showPermissions_none_board'] = 'This member has no board specific permissions set.'; +$txt['showPermissions_all'] = 'As an administrator, this member has all possible permissions.'; +$txt['showPermissions_select'] = 'Board specific permissions for'; +$txt['showPermissions_general'] = 'General Permissions'; +$txt['showPermissions_global'] = 'All boards'; +$txt['showPermissions_restricted_boards'] = 'Restricted boards'; +$txt['showPermissions_restricted_boards_desc'] = 'The following boards are not accessible by this user'; + +$txt['local_time'] = 'Local Time'; +$txt['posts_per_day'] = 'per day'; + +$txt['buddy_ignore_desc'] = 'This area allows you to maintain your buddy and ignore lists for this forum. Adding members to these lists will, amongst other things, help control mail and PM traffic, depending on your preferences.'; + +$txt['buddy_add'] = 'Add To Buddy List'; +$txt['buddy_remove'] = 'Remove From Buddy List'; +$txt['buddy_add_button'] = 'Add'; +$txt['no_buddies'] = 'Your buddy list is currently empty'; + +$txt['ignore_add'] = 'Add To Ignore List'; +$txt['ignore_remove'] = 'Remove From Ignore List'; +$txt['ignore_add_button'] = 'Add'; +$txt['no_ignore'] = 'Your ignore list is currently empty'; + +$txt['regular_members'] = 'Registered Members'; +$txt['regular_members_desc'] = 'Every member of the forum is a member of this group.'; +$txt['group_membership_msg_free'] = 'Your group membership was successfully updated.'; +$txt['group_membership_msg_request'] = 'Your request has been submitted, please be patient while the request is considered.'; +$txt['group_membership_msg_primary'] = 'Your primary group has been updated'; +$txt['current_membergroups'] = 'Current Membergroups'; +$txt['available_groups'] = 'Available Groups'; +$txt['join_group'] = 'Join Group'; +$txt['leave_group'] = 'Leave Group'; +$txt['request_group'] = 'Request Membership'; +$txt['approval_pending'] = 'Approval Pending'; +$txt['make_primary'] = 'Make Primary Group'; + +$txt['request_group_membership'] = 'Request Group Membership'; +$txt['request_group_membership_desc'] = 'Before you can join this group your membership must be approved by the moderator. Please give a reason for joining this group'; +$txt['submit_request'] = 'Submit Request'; + +$txt['profile_updated_own'] = 'Your profile has been updated successfully.'; +$txt['profile_updated_else'] = 'The profile for %1$s has been updated successfully.'; + +$txt['profile_error_signature_max_length'] = 'Your signature cannot be greater than %1$d characters'; +$txt['profile_error_signature_max_lines'] = 'Your signature cannot span more than %1$d lines'; +$txt['profile_error_signature_max_image_size'] = 'Images in your signature must be no greater than %1$dx%2$d pixels'; +$txt['profile_error_signature_max_image_width'] = 'Images in your signature must be no wider than %1$d pixels'; +$txt['profile_error_signature_max_image_height'] = 'Images in your signature must be no higher than %1$d pixels'; +$txt['profile_error_signature_max_image_count'] = 'You cannot have more than %1$d images in your signature'; +$txt['profile_error_signature_max_font_size'] = 'Text in your signature must be smaller than %1$s in size'; +$txt['profile_error_signature_allow_smileys'] = 'You are not allowed to use any smileys within your signature'; +$txt['profile_error_signature_max_smileys'] = 'You are not allowed to use more than %1$d smileys within your signature'; +$txt['profile_error_signature_disabled_bbc'] = 'The following BBC is not allowed within your signature: %1$s'; + +$txt['profile_view_warnings'] = 'View Warnings'; +$txt['profile_issue_warning'] = 'Issue a Warning'; +$txt['profile_warning_level'] = 'Warning Level'; +$txt['profile_warning_desc'] = 'From this section you can adjust the user\'s warning level and issue them with a written warning if necessary. You can also track their warning history and view the effects of their current warning level as determined by the administrator.'; +$txt['profile_warning_name'] = 'Member Name'; +$txt['profile_warning_impact'] = 'Result'; +$txt['profile_warning_reason'] = 'Reason for Warning'; +$txt['profile_warning_reason_desc'] = 'This is required and will be logged.'; +$txt['profile_warning_effect_none'] = 'None.'; +$txt['profile_warning_effect_watch'] = 'User will be added to moderator watch list.'; +$txt['profile_warning_effect_own_watched'] = 'You are on the moderator watch list.'; +$txt['profile_warning_is_watch'] = 'being watched'; +$txt['profile_warning_effect_moderation'] = 'All users posts will be moderated.'; +$txt['profile_warning_effect_own_moderated'] = 'All your posts will be moderated.'; +$txt['profile_warning_is_moderation'] = 'posts are moderated'; +$txt['profile_warning_effect_mute'] = 'User will not be able to post.'; +$txt['profile_warning_effect_own_muted'] = 'You will not be able to post.'; +$txt['profile_warning_is_muted'] = 'cannot post'; +$txt['profile_warning_effect_text'] = 'Level >= %1$d: %2$s'; +$txt['profile_warning_notify'] = 'Send a Notification'; +$txt['profile_warning_notify_template'] = 'Select template:'; +$txt['profile_warning_notify_subject'] = 'Notification Subject'; +$txt['profile_warning_notify_body'] = 'Notification Message'; +$txt['profile_warning_notify_template_subject'] = 'You have received a warning'; +// Use numeric entities in below string. +$txt['profile_warning_notify_template_outline'] = '{MEMBER},' . "\n\n" . 'You have received a warning for %1$s. Please cease these activities and abide by the forum rules otherwise we will take further action.' . "\n\n" . '{REGARDS}'; +$txt['profile_warning_notify_template_outline_post'] = '{MEMBER},' . "\n\n" . 'You have received a warning for %1$s in regards to the message:' . "\n" . '{MESSAGE}.' . "\n\n" . 'Please cease these activities and abide by the forum rules otherwise we will take further action.' . "\n\n" . '{REGARDS}'; +$txt['profile_warning_notify_for_spamming'] = 'spamming'; +$txt['profile_warning_notify_title_spamming'] = 'Spamming'; +$txt['profile_warning_notify_for_offence'] = 'posting offensive material'; +$txt['profile_warning_notify_title_offence'] = 'Posting Offensive Material'; +$txt['profile_warning_notify_for_insulting'] = 'insulting other users and/or staff members'; +$txt['profile_warning_notify_title_insulting'] = 'Insulting Users/Staff'; +$txt['profile_warning_issue'] = 'Issue Warning'; +$txt['profile_warning_max'] = '(Max 100)'; +$txt['profile_warning_limit_attribute'] = 'Note you can not adjust this user\'s level by more than %1$d%% in a 24 hour period.'; +$txt['profile_warning_errors_occured'] = 'Warning has not been sent due to following errors'; +$txt['profile_warning_success'] = 'Warning Successfully Issued'; +$txt['profile_warning_new_template'] = 'New Template'; + +$txt['profile_warning_previous'] = 'Previous Warnings'; +$txt['profile_warning_previous_none'] = 'This user has not received any previous warnings.'; +$txt['profile_warning_previous_issued'] = 'Issued By'; +$txt['profile_warning_previous_time'] = 'Time'; +$txt['profile_warning_previous_level'] = 'Points'; +$txt['profile_warning_previous_reason'] = 'Reason'; +$txt['profile_warning_previous_notice'] = 'View Notice Sent to Member'; + +$txt['viewwarning'] = 'View Warnings'; +$txt['profile_viewwarning_for_user'] = 'Warnings for %1$s'; +$txt['profile_viewwarning_no_warnings'] = 'No warnings have yet been issued.'; +$txt['profile_viewwarning_desc'] = 'Below is a summary of all the warnings that have been issued by the forum moderation team.'; +$txt['profile_viewwarning_previous_warnings'] = 'Previous Warnings'; +$txt['profile_viewwarning_impact'] = 'Warning Impact'; + +$txt['subscriptions'] = 'Paid Subscriptions'; + +$txt['pm_settings_desc'] = 'From this page you can change a variety of personal messaging options, including how messages are displayed and who may send them to you.'; +$txt['email_notify'] = 'Notify by email every time you receive a personal message:'; +$txt['email_notify_never'] = 'Never'; +$txt['email_notify_buddies'] = 'From Buddies Only'; +$txt['email_notify_always'] = 'Always'; + +$txt['pm_receive_from'] = 'Receive personal messages from:'; +$txt['pm_receive_from_everyone'] = 'All members'; +$txt['pm_receive_from_ignore'] = 'All members, except those on my ignore list'; +$txt['pm_receive_from_admins'] = 'Administrators only'; +$txt['pm_receive_from_buddies'] = 'Buddies and Administrators only'; + +$txt['copy_to_outbox'] = 'Save a copy of each personal message in my sent items by default.'; +$txt['popup_messages'] = 'Show a popup when I receive new messages.'; +$txt['pm_remove_inbox_label'] = 'Remove the inbox label when applying another label'; +$txt['pm_display_mode'] = 'Display personal messages'; +$txt['pm_display_mode_all'] = 'All at once'; +$txt['pm_display_mode_one'] = 'One at a time'; +$txt['pm_display_mode_linked'] = 'As a conversation'; +// Use entities in the below string. +$txt['pm_recommend_enable_outbox'] = 'To make the most of this setting we suggest you enable "Save a copy of each Personal Message in my sent items by default"\\n\\nThis will help ensure that the conversations flow better as you can see both sides of the conversation.'; + +$txt['tracking'] = 'Tracking'; +$txt['tracking_description'] = 'This section allows you to review certain profile actions performed on this member\'s profile as well as track their IP address.'; + +$txt['trackEdits'] = 'Profile Edits'; +$txt['trackEdit_deleted_member'] = 'Deleted Member'; +$txt['trackEdit_no_edits'] = 'No edits have so far been recorded for this member.'; +$txt['trackEdit_action'] = 'Field'; +$txt['trackEdit_before'] = 'Value Before'; +$txt['trackEdit_after'] = 'Value After'; +$txt['trackEdit_applicator'] = 'Changed By'; + +$txt['trackEdit_action_real_name'] = 'Member Name'; +$txt['trackEdit_action_usertitle'] = 'Custom Title'; +$txt['trackEdit_action_member_name'] = 'Username'; +$txt['trackEdit_action_email_address'] = 'Email Address'; +$txt['trackEdit_action_id_group'] = 'Primary Membergroup'; +$txt['trackEdit_action_additional_groups'] = 'Additional Membergroups'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Reports.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Reports.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,140 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Search.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Search.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,159 @@ +e.g. Orwell "Animal Farm" -movie'; + +$txt['search_engines_description'] = 'From this area you can decide in what detail you wish to track search engines as they index your forum as well as review search engine logs.'; +$txt['spider_mode'] = 'Search Engine Tracking Level
    Note higher level tracking increases server resource requirement.
    '; +$txt['spider_mode_off'] = 'Disabled'; +$txt['spider_mode_standard'] = 'Standard - Logs minimal spider activity.'; +$txt['spider_mode_high'] = 'High - Provides more accurate statistics.'; +$txt['spider_mode_vhigh'] = 'Very High - As for "High" but logs data about each page visited.'; +$txt['spider_settings_desc'] = 'You can change settings for spider tracking from this page. Note, if you wish to enable automatic pruning of the hit logs you can set this up here'; + +$txt['spider_group'] = 'Apply restrictive permissions from group
    To enable you to stop spiders indexing some pages.
    '; +$txt['spider_group_none'] = 'Disabled'; + +$txt['show_spider_online'] = 'Show spiders in the online list'; +$txt['show_spider_online_no'] = 'Not at all'; +$txt['show_spider_online_summary'] = 'Show spider quantity'; +$txt['show_spider_online_detail'] = 'Show spider names'; +$txt['show_spider_online_detail_admin'] = 'Show spider names - admin only'; + +$txt['spider_name'] = 'Spider Name'; +$txt['spider_last_seen'] = 'Last Seen'; +$txt['spider_last_never'] = 'Never'; +$txt['spider_agent'] = 'User Agent'; +$txt['spider_ip_info'] = 'IP Addresses'; +$txt['spiders_add'] = 'Add New Spider'; +$txt['spiders_edit'] = 'Edit Spider'; +$txt['spiders_remove_selected'] = 'Remove Selected Spiders'; +$txt['spider_remove_selected_confirm'] = 'Are you sure you wish to remove these spiders?\\n\\nAll associated statistics will also be deleted!'; +$txt['spiders_no_entries'] = 'There are currently no spiders configured.'; + +$txt['add_spider_desc'] = 'From this page you can edit the parameters against which a spider is categorised. If a guest\'s user agent/IP address matches those entered below it will be detected as a search engine spider and tracked as per the forum preferences.'; +$txt['spider_name_desc'] = 'Name by which the spider will be referred.'; +$txt['spider_agent_desc'] = 'User agent associated with this spider.'; +$txt['spider_ip_info_desc'] = 'Comma separated list of IP addresses associated with this spider.'; + +$txt['spider'] = 'Spider'; +$txt['spider_time'] = 'Time'; +$txt['spider_viewing'] = 'Viewing'; +$txt['spider_logs_empty'] = 'There are currently no spider log entries.'; +$txt['spider_logs_info'] = 'Note that logging of every spider action only occurs if tracking is set to either "high" or "very high". Detail of every spiders action is only logged if tracking is set to "very high".'; +$txt['spider_disabled'] = 'Disabled'; + +$txt['spider_logs_delete'] = 'Delete Entries'; +$txt['spider_logs_delete_older'] = 'Delete all entries older than'; +$txt['spider_logs_delete_day'] = 'days.'; +$txt['spider_logs_delete_submit'] = 'Delete'; +// Don't use entities in the below string. +$txt['spider_logs_delete_confirm'] = 'Are you sure you wish to empty out all log entries?'; + +$txt['spider_stats_select_month'] = 'Jump To Month'; +$txt['spider_stats_page_hits'] = 'Page Hits'; +$txt['spider_stats_no_entries'] = 'There are currently no spider statistics available.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Settings.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Settings.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ +
    Author: The Simple Machines Team'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Stats.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Stats.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,44 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Themes.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Themes.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,148 @@ +
    Don\'t forget to look at the theme settings for your themes for layout options.'; +$txt['themeadmin_list_desc'] = 'From here, you can view the list of themes you currently have installed, change their paths and settings, and uninstall them.'; +$txt['themeadmin_reset_desc'] = 'Below you will see an interface to change the current theme-specific options for all your members. You will only see those themes that have their own set of settings.'; +$txt['themeadmin_edit_desc'] = 'Modify the stylesheet and source code of your installed themes. Please consult the documentation for more information.'; + +$txt['themeadmin_list_heading'] = 'Theme Settings Overview'; +$txt['themeadmin_list_tip'] = 'Remember, the layout settings may be different between one theme and another. Click on the theme\'s names below to set their options, change their directory or URL settings, or to find other options.'; +$txt['themeadmin_list_theme_dir'] = 'Theme directory (templates)'; +$txt['themeadmin_list_invalid'] = '(warning, this path is not correct!)'; +$txt['themeadmin_list_theme_url'] = 'URL to above directory'; +$txt['themeadmin_list_images_url'] = 'URL to images directory'; +$txt['themeadmin_list_reset'] = 'Reset Theme URLs and Directories'; +$txt['themeadmin_list_reset_dir'] = 'Base path to Themes directory'; +$txt['themeadmin_list_reset_url'] = 'Base URL to the same directory'; +$txt['themeadmin_list_reset_go'] = 'Attempt to reset all themes'; + +$txt['themeadmin_reset_tip'] = 'Each theme may have its own custom options for selection by your members. These include things like "quick reply", avatars and signatures, layout options, and other similar options. Here you can change the defaults or reset everyone\'s options.

    Please note that some themes may use the default options, in which case they will not have their own options.'; +$txt['themeadmin_reset_defaults'] = 'Configure guest and new user options for this theme'; +$txt['themeadmin_reset_defaults_current'] = 'options currently set.'; +$txt['themeadmin_reset_members'] = 'Change current options for all members using this theme'; +$txt['themeadmin_reset_remove'] = 'Remove all members\' options and use the defaults'; +$txt['themeadmin_reset_remove_current'] = 'members currently using their own options.'; +// Don't use entities in the below string. +$txt['themeadmin_reset_remove_confirm'] = 'Are you sure you want to remove all theme options?\\nThis may reset some custom profile fields as well.'; +$txt['themeadmin_reset_options_info'] = 'The options below will reset options for everyone. To change an option, select "change" in the box next to it, and then select a value for it. To use the default, select "remove". Otherwise, use "don\'t change" to keep it as-is.'; +$txt['themeadmin_reset_options_change'] = 'Change'; +$txt['themeadmin_reset_options_none'] = 'Don\'t change'; +$txt['themeadmin_reset_options_remove'] = 'Remove'; + +$txt['themeadmin_edit_browse'] = 'Browse the templates and files in this theme.'; +$txt['themeadmin_edit_style'] = 'Edit this theme\'s stylesheets.'; +$txt['themeadmin_edit_copy_template'] = 'Copy a template from the theme this is based on.'; +$txt['themeadmin_edit_exists'] = 'already exists'; +$txt['themeadmin_edit_do_copy'] = 'copy'; +$txt['themeadmin_edit_copy_warning'] = 'When SMF needs a template or language file which is not in the current theme, it looks in the theme it is based on, or the default theme.
    Unless you need to modify a template, it\'s better not to copy it.'; +$txt['themeadmin_edit_copy_confirm'] = 'Are you sure you want to copy this template?'; +$txt['themeadmin_edit_overwrite_confirm'] = 'Are you sure you want to copy this template over the one that already exists?\nThis will OVERWRITE any changes you\\\'ve made!'; +$txt['themeadmin_edit_no_copy'] = '(can\'t copy)'; +$txt['themeadmin_edit_filename'] = 'Filename'; +$txt['themeadmin_edit_modified'] = 'Last Modified'; +$txt['themeadmin_edit_size'] = 'Size'; +$txt['themeadmin_edit_bytes'] = 'B'; +$txt['themeadmin_edit_kilobytes'] = 'KB'; +$txt['themeadmin_edit_error'] = 'The file you tried to save generated the following error:'; +$txt['themeadmin_edit_on_line'] = 'Beginning on line'; +$txt['themeadmin_edit_preview'] = 'Preview'; +$txt['themeadmin_selectable'] = 'Themes that the user is able to select'; +$txt['themeadmin_themelist_link'] = 'Show the list of themes'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Who.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Who.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,156 @@ +Nothing, or nothing you can see...'; +$txt['who_unknown'] = 'Unknown Action'; +$txt['who_user'] = 'User'; +$txt['who_time'] = 'Time'; +$txt['who_action'] = 'Action'; +$txt['who_show1'] = 'Show '; +$txt['who_show_members_only'] = 'Members Only'; +$txt['who_show_guests_only'] = 'Guests Only'; +$txt['who_show_spiders_only'] = 'Spiders Only'; +$txt['who_show_all'] = 'Everyone'; +$txt['who_no_online_spiders'] = 'There are currently no spiders online.'; +$txt['who_no_online_guests'] = 'There are currently no guests online.'; +$txt['who_no_online_members'] = 'There are currently no members online.'; + +$txt['whospider_login'] = 'Viewing the login page.'; +$txt['whospider_register'] = 'Viewing the registration page.'; +$txt['whospider_reminder'] = 'Viewing the reminder page.'; + +$txt['whoall_activate'] = 'Activating their account.'; +$txt['whoall_buddy'] = 'Modifying their buddy list.'; +$txt['whoall_coppa'] = 'Filling out parent/guardian consent form.'; +$txt['whoall_credits'] = 'Viewing credits page.'; +$txt['whoall_emailuser'] = 'Sending email to another member.'; +$txt['whoall_groups'] = 'Viewing the member groups page.'; +$txt['whoall_help'] = 'Viewing the help page.'; +$txt['whoall_helpadmin'] = 'Viewing a help popup.'; +$txt['whoall_pm'] = 'Viewing their messages.'; +$txt['whoall_login'] = 'Logging into the forum.'; +$txt['whoall_login2'] = 'Logging into the forum.'; +$txt['whoall_logout'] = 'Logging out of the forum.'; +$txt['whoall_markasread'] = 'Marking topics read or unread.'; +$txt['whoall_modifykarma_applaud'] = 'Applauding a member.'; +$txt['whoall_modifykarma_smite'] = 'Smiting a member.'; +$txt['whoall_news'] = 'Viewing the news.'; +$txt['whoall_notify'] = 'Changing their notification settings.'; +$txt['whoall_notifyboard'] = 'Changing their notification settings.'; +$txt['whoall_openidreturn'] = 'Logging in using OpenID.'; +$txt['whoall_quickmod'] = 'Moderating a board.'; +$txt['whoall_recent'] = 'Viewing a list of recent topics.'; +$txt['whoall_register'] = 'Registering for an account on the forum.'; +$txt['whoall_register2'] = 'Registering for an account on the forum.'; +$txt['whoall_reminder'] = 'Requesting a password reminder.'; +$txt['whoall_reporttm'] = 'Reporting a topic to a moderator.'; +$txt['whoall_spellcheck'] = 'Using the spellchecker'; +$txt['whoall_unread'] = 'Viewing unread topics since their last visit.'; +$txt['whoall_unreadreplies'] = 'Viewing unread replies since their last visit.'; +$txt['whoall_who'] = 'Viewing Who\'s Online.'; + +$txt['whoall_collapse_collapse'] = 'Collapsing a category.'; +$txt['whoall_collapse_expand'] = 'Expanding a category.'; +$txt['whoall_pm_removeall'] = 'Removing all their messages.'; +$txt['whoall_pm_send'] = 'Sending a message.'; +$txt['whoall_pm_send2'] = 'Sending a message.'; + +$txt['whotopic_announce'] = 'Announcing the topic "%2$s".'; +$txt['whotopic_attachapprove'] = 'Approving an attachment.'; +$txt['whotopic_dlattach'] = 'Viewing an attachment.'; +$txt['whotopic_deletemsg'] = 'Deleting a message.'; +$txt['whotopic_editpoll'] = 'Editing the poll in "%2$s".'; +$txt['whotopic_editpoll2'] = 'Editing the poll in "%2$s".'; +$txt['whotopic_jsmodify'] = 'Modifying a post in "%2$s".'; +$txt['whotopic_lock'] = 'Locking the topic "%2$s".'; +$txt['whotopic_lockvoting'] = 'Locking the poll in "%2$s".'; +$txt['whotopic_mergetopics'] = 'Merging the topic "%2$s" with another topic.'; +$txt['whotopic_movetopic'] = 'Moving the topic "%2$s" to another board.'; +$txt['whotopic_movetopic2'] = 'Moving the topic "%2$s" to another board.'; +$txt['whotopic_post'] = 'Posting in %2$s.'; +$txt['whotopic_post2'] = 'Posting in %2$s.'; +$txt['whotopic_printpage'] = 'Printing the topic "%2$s".'; +$txt['whotopic_quickmod2'] = 'Moderating the topic %2$s.'; +$txt['whotopic_removepoll'] = 'Removing the poll in "%2$s".'; +$txt['whotopic_removetopic2'] = 'Removing the topic %2$s.'; +$txt['whotopic_sendtopic'] = 'Sending the topic "%2$s" to a friend.'; +$txt['whotopic_splittopics'] = 'Splitting the topic "%2$s" into two topics.'; +$txt['whotopic_sticky'] = 'Setting the topic "%2$s" as sticky.'; +$txt['whotopic_vote'] = 'Voting in %2$s.'; + +$txt['whopost_quotefast'] = 'Quoting a post from "%2$s".'; + +$txt['whoadmin_editagreement'] = 'Editing the registration agreement.'; +$txt['whoadmin_featuresettings'] = 'Editing forum features and options.'; +$txt['whoadmin_modlog'] = 'Viewing the moderator log.'; +$txt['whoadmin_serversettings'] = 'Editing the forum settings.'; +$txt['whoadmin_packageget'] = 'Getting packages.'; +$txt['whoadmin_packages'] = 'Viewing the package manager.'; +$txt['whoadmin_permissions'] = 'Editing the forum permissions.'; +$txt['whoadmin_pgdownload'] = 'Downloading a package.'; +$txt['whoadmin_theme'] = 'Editing the theme settings.'; +$txt['whoadmin_trackip'] = 'Tracking an IP address.'; + +$txt['whoallow_manageboards'] = 'Editing the board and category settings.'; +$txt['whoallow_admin'] = 'Viewing the administration center.'; +$txt['whoallow_ban'] = 'Editing the ban list.'; +$txt['whoallow_boardrecount'] = 'Recounting the forum totals.'; +$txt['whoallow_calendar'] = 'Viewing the calendar.'; +$txt['whoallow_editnews'] = 'Editing the news.'; +$txt['whoallow_mailing'] = 'Sending a forum email.'; +$txt['whoallow_maintain'] = 'Performing routine forum maintenance.'; +$txt['whoallow_manageattachments'] = 'Managing the attachments.'; +$txt['whoallow_moderate'] = 'Viewing the Moderation Center.'; +$txt['whoallow_mlist'] = 'Viewing the memberlist.'; +$txt['whoallow_optimizetables'] = 'Optimizing the database tables.'; +$txt['whoallow_repairboards'] = 'Repairing the database tables.'; +$txt['whoallow_search'] = 'Searching the forum.'; +$txt['whoallow_search2'] = 'Viewing the results of a search.'; +$txt['whoallow_setcensor'] = 'Editing the censor text.'; +$txt['whoallow_setreserve'] = 'Editing the reserved names.'; +$txt['whoallow_stats'] = 'Viewing the forum stats.'; +$txt['whoallow_viewErrorLog'] = 'Viewing the error log.'; +$txt['whoallow_viewmembers'] = 'Viewing a list of members.'; + +$txt['who_topic'] = 'Viewing the topic %2$s.'; +$txt['who_board'] = 'Viewing the board %2$s.'; +$txt['who_index'] = 'Viewing the board index of ' . $context['forum_name'] . '.'; +$txt['who_viewprofile'] = 'Viewing %2$s\'s profile.'; +$txt['who_profile'] = 'Editing the profile of %2$s.'; +$txt['who_post'] = 'Posting a new topic in %2$s.'; +$txt['who_poll'] = 'Posting a new poll in %2$s.'; + +// Credits text +$txt['credits'] = 'Credits'; +$txt['credits_intro'] = 'Simple Machines wants to thank everyone who helped make SMF 2.0 what it is today; shaping and directing our project, all through the thick and the thin. It wouldn\'t have been possible without you. This includes our users and especially Charter Members - thanks for installing and using our software as well as providing valuable feedback, bug reports, and opinions.'; +$txt['credits_team'] = 'The Team'; +$txt['credits_special'] = 'Special Thanks'; +$txt['credits_and'] = 'and'; +$txt['credits_anyone'] = 'And for anyone we may have missed, thank you!'; +$txt['credits_copyright'] = 'Copyrights'; +$txt['credits_forum'] = 'Forum'; +$txt['credits_modifications'] = 'Modifications'; +$txt['credits_groups_ps'] = 'Project Support'; +$txt['credits_groups_dev'] = 'Developers'; +$txt['credits_groups_support'] = 'Support Specialists'; +$txt['credits_groups_customize'] = 'Customizers'; +$txt['credits_groups_docs'] = 'Documentation Writers'; +$txt['credits_groups_marketing'] = 'Marketing'; +$txt['credits_groups_internationalizers'] = 'Localizers'; +$txt['credits_groups_servers'] = 'Servers Administrators'; +$txt['credits_groups_site'] = 'Site Administrators'; +// Replace "English" with the name of this language pack in the string below. +$txt['credits_groups_translation'] = 'English Translation'; +$txt['credits_groups_translators'] = 'Language Translators'; +$txt['credits_translators_message'] = 'Thank you for your efforts which make it possible for people all around the world to use SMF.'; +$txt['credits_groups_consultants'] = 'Consulting Developers'; +$txt['credits_groups_beta'] = 'Beta Testers'; +$txt['credits_beta_message'] = 'The invaluable few who tirelessly find bugs, provide feedback, and drive the developers crazier.'; +$txt['credits_groups_founder'] = 'Founding Father of SMF'; +$txt['credits_groups_orignal_pm'] = 'Original Project Managers'; + +// List of people who have made more than a token contribution to this translation. (blank for English) +$txt['translation_credits'] = array(); +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/Wireless.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/Wireless.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,49 @@ +%1$d new)'; +$txt['wireless_pm_by'] = 'by'; +$txt['wireless_pm_add_buddy'] = 'Add buddy'; +$txt['wireless_pm_select_buddy'] = 'Select a buddy'; +$txt['wireless_pm_search_member'] = 'Search member'; +$txt['wireless_pm_search_name'] = 'Name'; +$txt['wireless_pm_no_recipients'] = 'No recipients (yet)'; +$txt['wireless_pm_reply'] = 'Reply'; +$txt['wireless_pm_reply_all'] = 'Reply All'; +$txt['wireless_pm_reply_to'] = 'Reply to'; + +$txt['wireless_recent_unread_posts'] = 'Unread posts'; +$txt['wireless_recent_unread_replies'] = 'Unread replies'; + +$txt['wireless_display_edit'] = 'Edit'; +$txt['wireless_display_moderate'] = 'Moderate'; +$txt['wireless_display_sticky'] = 'Sticky'; +$txt['wireless_display_unsticky'] = 'Un-Sticky'; +$txt['wireless_display_lock'] = 'Lock'; +$txt['wireless_display_unlock'] = 'Unlock'; + +$txt['wireless_end_code'] = 'End code'; +$txt['wireless_end_quote'] = 'End quote'; + +$txt['wireless_profile_pm'] = 'Send Personal Message'; +$txt['wireless_ban_ip'] = 'Ban on IP'; +$txt['wireless_ban_hostname'] = 'Ban on Hostname'; +$txt['wireless_ban_email'] = 'Ban on Email'; +$txt['wireless_additional_info'] = 'Additional Information'; +$txt['wireless_go_to_full_version'] = 'Go to full version'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/index.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/index.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,753 @@ + 'January'. (or translated, of course.) +$txt['months'] = array(1 => 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'); +$txt['months_titles'] = array(1 => 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'); +$txt['months_short'] = array(1 => 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); + +$txt['time_am'] = 'am'; +$txt['time_pm'] = 'pm'; + +$txt['newmessages0'] = 'is new'; +$txt['newmessages1'] = 'are new'; +$txt['newmessages3'] = 'New'; +$txt['newmessages4'] = ','; + +$txt['admin'] = 'Admin'; +$txt['moderate'] = 'Moderate'; + +$txt['save'] = 'Save'; + +$txt['modify'] = 'Modify'; +$txt['forum_index'] = '%1$s - Index'; +$txt['members'] = 'Members'; +$txt['board_name'] = 'Board name'; +$txt['posts'] = 'Posts'; + +$txt['member_postcount'] = 'Posts'; +$txt['no_subject'] = '(No subject)'; +$txt['view_profile'] = 'View Profile'; +$txt['guest_title'] = 'Guest'; +$txt['author'] = 'Author'; +$txt['on'] = 'on'; +$txt['remove'] = 'Remove'; +$txt['start_new_topic'] = 'Start new topic'; + +$txt['login'] = 'Login'; +// Use numeric entities in the below string. +$txt['username'] = 'Username'; +$txt['password'] = 'Password'; + +$txt['username_no_exist'] = 'That username does not exist.'; +$txt['no_user_with_email'] = 'There are no usernames associated with that email.'; + +$txt['board_moderator'] = 'Board Moderator'; +$txt['remove_topic'] = 'Remove Topic'; +$txt['topics'] = 'Topics'; +$txt['modify_msg'] = 'Modify message'; +$txt['name'] = 'Name'; +$txt['email'] = 'Email'; +$txt['subject'] = 'Subject'; +$txt['message'] = 'Message'; +$txt['redirects'] = 'Redirects'; +$txt['quick_modify'] = 'Modify Inline'; + +$txt['choose_pass'] = 'Choose password'; +$txt['verify_pass'] = 'Verify password'; +$txt['position'] = 'Position'; + +$txt['profile_of'] = 'View the profile of'; +$txt['total'] = 'Total'; +$txt['posts_made'] = 'Posts'; +$txt['website'] = 'Website'; +$txt['register'] = 'Register'; +$txt['warning_status'] = 'Warning Status'; +$txt['user_warn_watch'] = 'User is on moderator watch list'; +$txt['user_warn_moderate'] = 'User posts join approval queue'; +$txt['user_warn_mute'] = 'User is banned from posting'; +$txt['warn_watch'] = 'Watched'; +$txt['warn_moderate'] = 'Moderated'; +$txt['warn_mute'] = 'Muted'; + +$txt['message_index'] = 'Message Index'; +$txt['news'] = 'News'; +$txt['home'] = 'Home'; + +$txt['lock_unlock'] = 'Lock/Unlock Topic'; +$txt['post'] = 'Post'; +$txt['error_occured'] = 'An Error Has Occurred!'; +$txt['at'] = 'at'; +$txt['logout'] = 'Logout'; +$txt['started_by'] = 'Started by'; +$txt['replies'] = 'Replies'; +$txt['last_post'] = 'Last post'; +$txt['admin_login'] = 'Administration Login'; +// Use numeric entities in the below string. +$txt['topic'] = 'Topic'; +$txt['help'] = 'Help'; +$txt['notify'] = 'Notify'; +$txt['unnotify'] = 'Unnotify'; +$txt['notify_request'] = 'Do you want a notification email if someone replies to this topic?'; +// Use numeric entities in the below string. +$txt['regards_team'] = 'Regards,' . "\n" . 'The ' . $context['forum_name'] . ' Team.'; +$txt['notify_replies'] = 'Notify of replies'; +$txt['move_topic'] = 'Move Topic'; +$txt['move_to'] = 'Move to'; +$txt['pages'] = 'Pages'; +$txt['users_active'] = 'Users active in past %1$d minutes'; +$txt['personal_messages'] = 'Personal Messages'; +$txt['reply_quote'] = 'Reply with quote'; +$txt['reply'] = 'Reply'; +$txt['reply_noun'] = 'Reply'; +$txt['approve'] = 'Approve'; +$txt['approve_all'] = 'approve all'; +$txt['awaiting_approval'] = 'Awaiting Approval'; +$txt['attach_awaiting_approve'] = 'Attachments awaiting approval'; +$txt['post_awaiting_approval'] = 'Note: This message is awaiting approval by a moderator.'; +$txt['there_are_unapproved_topics'] = 'There are %1$s topics and %2$s posts awaiting approval in this board. Click here to view them all.'; + +$txt['msg_alert_none'] = 'No messages...'; +$txt['msg_alert_you_have'] = 'you have'; +$txt['msg_alert_messages'] = 'messages'; +$txt['remove_message'] = 'Remove this message'; + +$txt['online_users'] = 'Users Online'; +$txt['personal_message'] = 'Personal Message'; +$txt['jump_to'] = 'Jump to'; +$txt['go'] = 'go'; +$txt['are_sure_remove_topic'] = 'Are you sure you want to remove this topic?'; +$txt['yes'] = 'Yes'; +$txt['no'] = 'No'; + +$txt['search_end_results'] = 'End of results'; +$txt['search_on'] = 'on'; + +$txt['search'] = 'Search'; +$txt['all'] = 'All'; + +$txt['back'] = 'Back'; +$txt['password_reminder'] = 'Password reminder'; +$txt['topic_started'] = 'Topic started by'; +$txt['title'] = 'Title'; +$txt['post_by'] = 'Post by'; +$txt['memberlist_searchable'] = 'Searchable list of all registered members.'; +$txt['welcome_member'] = 'Please welcome'; +$txt['admin_center'] = 'Administration Center'; +$txt['last_edit'] = 'Last Edit'; +$txt['notify_deactivate'] = 'Would you like to deactivate notification on this topic?'; + +$txt['recent_posts'] = 'Recent Posts'; + +$txt['location'] = 'Location'; +$txt['gender'] = 'Gender'; +$txt['date_registered'] = 'Date Registered'; + +$txt['recent_view'] = 'View the most recent posts on the forum.'; +$txt['recent_updated'] = 'is the most recently updated topic'; + +$txt['male'] = 'Male'; +$txt['female'] = 'Female'; + +$txt['error_invalid_characters_username'] = 'Invalid character used in Username.'; + +$txt['welcome_guest'] = 'Welcome, %1$s. Please login or register.'; +$txt['login_or_register'] = 'Please login or register.'; +$txt['welcome_guest_activate'] = '
    Did you miss your activation email?'; +$txt['hello_member'] = 'Hey,'; +// Use numeric entities in the below string. +$txt['hello_guest'] = 'Welcome,'; +$txt['welmsg_hey'] = 'Hey,'; +$txt['welmsg_welcome'] = 'Welcome,'; +$txt['welmsg_please'] = 'Please'; +$txt['select_destination'] = 'Please select a destination'; + +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['posted_by'] = 'Posted by'; + +$txt['icon_smiley'] = 'Smiley'; +$txt['icon_angry'] = 'Angry'; +$txt['icon_cheesy'] = 'Cheesy'; +$txt['icon_laugh'] = 'Laugh'; +$txt['icon_sad'] = 'Sad'; +$txt['icon_wink'] = 'Wink'; +$txt['icon_grin'] = 'Grin'; +$txt['icon_shocked'] = 'Shocked'; +$txt['icon_cool'] = 'Cool'; +$txt['icon_huh'] = 'Huh'; +$txt['icon_rolleyes'] = 'Roll Eyes'; +$txt['icon_tongue'] = 'Tongue'; +$txt['icon_embarrassed'] = 'Embarrassed'; +$txt['icon_lips'] = 'Lips sealed'; +$txt['icon_undecided'] = 'Undecided'; +$txt['icon_kiss'] = 'Kiss'; +$txt['icon_cry'] = 'Cry'; + +$txt['moderator'] = 'Moderator'; +$txt['moderators'] = 'Moderators'; + +$txt['mark_board_read'] = 'Mark Topics as Read for this Board'; +$txt['views'] = 'Views'; +$txt['new'] = 'New'; + +$txt['view_all_members'] = 'View All Members'; +$txt['view'] = 'View'; + +$txt['viewing_members'] = 'Viewing Members %1$s to %2$s'; +$txt['of_total_members'] = 'of %1$s total members'; + +$txt['forgot_your_password'] = 'Forgot your password?'; + +$txt['date'] = 'Date'; +// Use numeric entities in the below string. +$txt['from'] = 'From'; +$txt['check_new_messages'] = 'Check for new messages'; +$txt['to'] = 'To'; + +$txt['board_topics'] = 'Topics'; +$txt['members_title'] = 'Members'; +$txt['members_list'] = 'Members List'; +$txt['new_posts'] = 'New Posts'; +$txt['old_posts'] = 'No New Posts'; +$txt['redirect_board'] = 'Redirect Board'; + +$txt['sendtopic_send'] = 'Send'; +$txt['report_sent'] = 'Your report has been sent successfully.'; + +$txt['time_offset'] = 'Time Offset'; +$txt['or'] = 'or'; + +$txt['no_matches'] = 'Sorry, no matches were found'; + +$txt['notification'] = 'Notification'; + +$txt['your_ban'] = 'Sorry %1$s, you are banned from using this forum!'; +$txt['your_ban_expires'] = 'This ban is set to expire %1$s.'; +$txt['your_ban_expires_never'] = 'This ban is not set to expire.'; +$txt['ban_continue_browse'] = 'You may continue to browse the forum as a guest.'; + +$txt['mark_as_read'] = 'Mark ALL messages as read'; + +$txt['hot_topics'] = 'Hot Topic (More than %1$d replies)'; +$txt['very_hot_topics'] = 'Very Hot Topic (More than %1$d replies)'; +$txt['locked_topic'] = 'Locked Topic'; +$txt['normal_topic'] = 'Normal Topic'; +$txt['participation_caption'] = 'Topic you have posted in'; + +$txt['go_caps'] = 'GO'; + +$txt['print'] = 'Print'; +$txt['profile'] = 'Profile'; +$txt['topic_summary'] = 'Topic Summary'; +$txt['not_applicable'] = 'N/A'; +$txt['message_lowercase'] = 'message'; +$txt['name_in_use'] = 'This name is already in use by another member.'; + +$txt['total_members'] = 'Total Members'; +$txt['total_posts'] = 'Total Posts'; +$txt['total_topics'] = 'Total Topics'; + +$txt['mins_logged_in'] = 'Minutes to stay logged in'; + +$txt['preview'] = 'Preview'; +$txt['always_logged_in'] = 'Always stay logged in'; + +$txt['logged'] = 'Logged'; +// Use numeric entities in the below string. +$txt['ip'] = 'IP'; + +$txt['www'] = 'WWW'; + +$txt['by'] = 'by'; + +$txt['hours'] = 'hours'; +$txt['days_word'] = 'days'; + +$txt['newest_member'] = ', our newest member.'; + +$txt['search_for'] = 'Search for'; + +$txt['aim'] = 'AIM'; +// In this string, please use +'s for spaces. +$txt['aim_default_message'] = 'Hi.+Are+you+there?'; +$txt['aim_title'] = 'AOL Instant Messenger'; +$txt['icq'] = 'ICQ'; +$txt['icq_title'] = 'ICQ Messenger'; +$txt['msn'] = 'MSN'; +$txt['msn_title'] = 'MSN Messenger'; +$txt['yim'] = 'YIM'; +$txt['yim_title'] = 'Yahoo Instant Messenger'; + +$txt['maintain_mode_on'] = 'Remember, this forum is in \'Maintenance Mode\'.'; + +$txt['read'] = 'Read'; +$txt['times'] = 'times'; + +$txt['forum_stats'] = 'Forum Stats'; +$txt['latest_member'] = 'Latest Member'; +$txt['total_cats'] = 'Total Categories'; +$txt['latest_post'] = 'Latest Post'; + +$txt['you_have'] = 'You\'ve got'; +$txt['click'] = 'Click'; +$txt['here'] = 'here'; +$txt['to_view'] = 'to view them.'; + +$txt['total_boards'] = 'Total Boards'; + +$txt['print_page'] = 'Print Page'; + +$txt['valid_email'] = 'This must be a valid email address.'; + +$txt['geek'] = 'I am a geek!!'; +$txt['info_center_title'] = '%1$s - Info Center'; + +$txt['send_topic'] = 'Send this topic'; + +$txt['sendtopic_title'] = 'Send the topic "%1$s" to a friend.'; +$txt['sendtopic_sender_name'] = 'Your name'; +$txt['sendtopic_sender_email'] = 'Your email address'; +$txt['sendtopic_receiver_name'] = 'Recipient\'s name'; +$txt['sendtopic_receiver_email'] = 'Recipient\'s email address'; +$txt['sendtopic_comment'] = 'Add a comment'; + +$txt['allow_user_email'] = 'Allow users to email me'; + +$txt['check_all'] = 'Check all'; + +// Use numeric entities in the below string. +$txt['database_error'] = 'Database Error'; +$txt['try_again'] = 'Please try again. If you come back to this error screen, report the error to an administrator.'; +$txt['file'] = 'File'; +$txt['line'] = 'Line'; +// Use numeric entities in the below string. +$txt['tried_to_repair'] = 'SMF has detected and automatically tried to repair an error in your database. If you continue to have problems, or continue to receive these emails, please contact your host.'; +$txt['database_error_versions'] = 'Note: It appears that your database may require an upgrade. Your forum\'s files are currently at version %1$s, while your database is at version %2$s. The above error might possibly go away if you execute the latest version of upgrade.php.'; +$txt['template_parse_error'] = 'Template Parse Error!'; +$txt['template_parse_error_message'] = 'It seems something has gone sour on the forum with the template system. This problem should only be temporary, so please come back later and try again. If you continue to see this message, please contact the administrator.

    You can also try refreshing this page.'; +$txt['template_parse_error_details'] = 'There was a problem loading the %1$s template or language file. Please check the syntax and try again - remember, single quotes (\') often have to be escaped with a slash (\\). To see more specific error information from PHP, try accessing the file directly.

    You may want to try to refresh this page or use the default theme.'; + +$txt['today'] = 'Today at '; +$txt['yesterday'] = 'Yesterday at '; +$txt['new_poll'] = 'New poll'; +$txt['poll_question'] = 'Question'; +$txt['poll_vote'] = 'Submit Vote'; +$txt['poll_total_voters'] = 'Total Members Voted'; +$txt['shortcuts'] = 'shortcuts: hit alt+s to submit/post or alt+p to preview'; +$txt['shortcuts_firefox'] = 'shortcuts: hit shift+alt+s to submit/post or shift+alt+p to preview'; +$txt['poll_results'] = 'View results'; +$txt['poll_lock'] = 'Lock Voting'; +$txt['poll_unlock'] = 'Unlock Voting'; +$txt['poll_edit'] = 'Edit Poll'; +$txt['poll'] = 'Poll'; +$txt['one_day'] = '1 Day'; +$txt['one_week'] = '1 Week'; +$txt['one_month'] = '1 Month'; +$txt['forever'] = 'Forever'; +$txt['quick_login_dec'] = 'Login with username, password and session length'; +$txt['one_hour'] = '1 Hour'; +$txt['moved'] = 'MOVED'; +$txt['moved_why'] = 'Please enter a brief description as to
    why this topic is being moved.'; +$txt['board'] = 'Board'; +$txt['in'] = 'in'; +$txt['sticky_topic'] = 'Sticky Topic'; + +$txt['delete'] = 'Delete'; + +$txt['your_pms'] = 'Your Personal Messages'; + +$txt['kilobyte'] = 'kB'; + +$txt['more_stats'] = '[More Stats]'; + +// Use numeric entities in the below three strings. +$txt['code'] = 'Code'; +$txt['code_select'] = '[Select]'; +$txt['quote_from'] = 'Quote from'; +$txt['quote'] = 'Quote'; + +$txt['merge_to_topic_id'] = 'ID of target topic'; +$txt['split'] = 'Split Topic'; +$txt['merge'] = 'Merge Topics'; +$txt['subject_new_topic'] = 'Subject For New Topic'; +$txt['split_this_post'] = 'Only split this post.'; +$txt['split_after_and_this_post'] = 'Split topic after and including this post.'; +$txt['select_split_posts'] = 'Select posts to split.'; +$txt['new_topic'] = 'New Topic'; +$txt['split_successful'] = 'Topic successfully split into two topics.'; +$txt['origin_topic'] = 'Origin Topic'; +$txt['please_select_split'] = 'Please select which posts you wish to split.'; +$txt['merge_successful'] = 'Topics successfully merged.'; +$txt['new_merged_topic'] = 'Newly Merged Topic'; +$txt['topic_to_merge'] = 'Topic to be merged'; +$txt['target_board'] = 'Target board'; +$txt['target_topic'] = 'Target topic'; +$txt['merge_confirm'] = 'Are you sure you want to merge'; +$txt['with'] = 'with'; +$txt['merge_desc'] = 'This function will merge the messages of two topics into one topic. The messages will be sorted according to the time of posting. Therefore the earliest posted message will be the first message of the merged topic.'; + +$txt['set_sticky'] = 'Set topic sticky'; +$txt['set_nonsticky'] = 'Set topic non-sticky'; +$txt['set_lock'] = 'Lock topic'; +$txt['set_unlock'] = 'Unlock topic'; + +$txt['search_advanced'] = 'Advanced search'; + +$txt['security_risk'] = 'MAJOR SECURITY RISK:'; +$txt['not_removed'] = 'You have not removed '; +$txt['not_removed_extra'] ='%1$s is a backup of %2$s that was not generated by SMF. It can be accessed directly and used to gain unauthorised access to your forum. You should delete it immediately.'; + +$txt['cache_writable_head'] = 'Performance Warning'; +$txt['cache_writable'] = 'The cache directory is not writable - this will adversely affect the performance of your forum.'; + +$txt['page_created'] = 'Page created in '; +$txt['seconds_with'] = ' seconds with '; +$txt['queries'] = ' queries.'; + +$txt['report_to_mod_func'] = 'Use this function to inform the moderators and administrators of an abusive or wrongly posted message.
    Please note that your email address will be revealed to the moderators if you use this.'; + +$txt['online'] = 'Online'; +$txt['offline'] = 'Offline'; +$txt['pm_online'] = 'Personal Message (Online)'; +$txt['pm_offline'] = 'Personal Message (Offline)'; +$txt['status'] = 'Status'; + +$txt['go_up'] = 'Go Up'; +$txt['go_down'] = 'Go Down'; + +$forum_copyright = '%1$s | + SMF © 2013, Simple Machines'; + +$txt['birthdays'] = 'Birthdays:'; +$txt['events'] = 'Events:'; +$txt['birthdays_upcoming'] = 'Upcoming Birthdays:'; +$txt['events_upcoming'] = 'Upcoming Events:'; +// Prompt for holidays in the calendar, leave blank to just display the holiday's name. +$txt['calendar_prompt'] = ''; +$txt['calendar_month'] = 'Month:'; +$txt['calendar_year'] = 'Year:'; +$txt['calendar_day'] = 'Day:'; +$txt['calendar_event_title'] = 'Event Title'; +$txt['calendar_event_options'] = 'Event Options'; +$txt['calendar_post_in'] = 'Post In:'; +$txt['calendar_edit'] = 'Edit Event'; +$txt['event_delete_confirm'] = 'Delete this event?'; +$txt['event_delete'] = 'Delete Event'; +$txt['calendar_post_event'] = 'Post Event'; +$txt['calendar'] = 'Calendar'; +$txt['calendar_link'] = 'Link to Calendar'; +$txt['calendar_upcoming'] = 'Upcoming Calendar'; +$txt['calendar_today'] = 'Today\'s Calendar'; +$txt['calendar_week'] = 'Week'; +$txt['calendar_week_title'] = 'Week %1$d of %2$d'; +$txt['calendar_numb_days'] = 'Number of Days:'; +$txt['calendar_how_edit'] = 'how do you edit these events?'; +$txt['calendar_link_event'] = 'Link Event To Post:'; +$txt['calendar_confirm_delete'] = 'Are you sure you want to delete this event?'; +$txt['calendar_linked_events'] = 'Linked Events'; +$txt['calendar_click_all'] = 'click to see all %1$s'; + +$txt['moveTopic1'] = 'Post a redirection topic'; +$txt['moveTopic2'] = 'Change the topic\'s subject'; +$txt['moveTopic3'] = 'New subject'; +$txt['moveTopic4'] = 'Change every message\'s subject'; +$txt['move_topic_unapproved_js'] = 'Warning! This topic has not yet been approved.\\n\\nIt is not recommended that you create a redirection topic unless you intend to approve the post immediately following the move.'; + +$txt['theme_template_error'] = 'Unable to load the \'%1$s\' template.'; +$txt['theme_language_error'] = 'Unable to load the \'%1$s\' language file.'; + +$txt['parent_boards'] = 'Child Boards'; + +$txt['smtp_no_connect'] = 'Could not connect to SMTP host'; +$txt['smtp_port_ssl'] = 'SMTP port setting incorrect; it should be 465 for SSL servers.'; +$txt['smtp_bad_response'] = 'Couldn\'t get mail server response codes'; +$txt['smtp_error'] = 'Ran into problems sending Mail. Error: '; +$txt['mail_send_unable'] = 'Unable to send mail to the email address \'%1$s\''; + +$txt['mlist_search'] = 'Search For Members'; +$txt['mlist_search_again'] = 'Search again'; +$txt['mlist_search_email'] = 'Search by email address'; +$txt['mlist_search_messenger'] = 'Search by messenger nickname'; +$txt['mlist_search_group'] = 'Search by position'; +$txt['mlist_search_name'] = 'Search by name'; +$txt['mlist_search_website'] = 'Search by website'; +$txt['mlist_search_results'] = 'Search results for'; +$txt['mlist_search_by'] = 'Search by %1$s'; +$txt['mlist_menu_view'] = 'View the memberlist'; + +$txt['attach_downloaded'] = 'downloaded'; +$txt['attach_viewed'] = 'viewed'; +$txt['attach_times'] = 'times'; + +$txt['settings'] = 'Settings'; +$txt['never'] = 'Never'; +$txt['more'] = 'more'; + +$txt['hostname'] = 'Hostname'; +$txt['you_are_post_banned'] = 'Sorry %1$s, you are banned from posting and sending personal messages on this forum.'; +$txt['ban_reason'] = 'Reason'; + +$txt['tables_optimized'] = 'Database tables optimized'; + +$txt['add_poll'] = 'Add poll'; +$txt['poll_options6'] = 'You may only select up to %1$s options.'; +$txt['poll_remove'] = 'Remove Poll'; +$txt['poll_remove_warn'] = 'Are you sure you want to remove this poll from the topic?'; +$txt['poll_results_expire'] = 'Results will be shown when voting has closed'; +$txt['poll_expires_on'] = 'Voting closes'; +$txt['poll_expired_on'] = 'Voting closed'; +$txt['poll_change_vote'] = 'Remove Vote'; +$txt['poll_return_vote'] = 'Voting options'; +$txt['poll_cannot_see'] = 'You cannot see the results of this poll at the moment.'; + +$txt['quick_mod_approve'] = 'Approve selected'; +$txt['quick_mod_remove'] = 'Remove selected'; +$txt['quick_mod_lock'] = 'Lock/Unlock selected'; +$txt['quick_mod_sticky'] = 'Sticky/Unsticky selected'; +$txt['quick_mod_move'] = 'Move selected to'; +$txt['quick_mod_merge'] = 'Merge selected'; +$txt['quick_mod_markread'] = 'Mark selected read'; +$txt['quick_mod_go'] = 'Go!'; +$txt['quickmod_confirm'] = 'Are you sure you want to do this?'; + +$txt['spell_check'] = 'Spell Check'; + +$txt['quick_reply'] = 'Quick Reply'; +$txt['quick_reply_desc'] = 'With Quick-Reply you can write a post when viewing a topic without loading a new page. You can still use bulletin board code and smileys as you would in a normal post.'; +$txt['quick_reply_warning'] = 'Warning: this topic is currently locked! Only admins and moderators can reply.'; +$txt['quick_reply_verification'] = 'After submitting your post you will be directed to the regular post page to verify your post %1$s.'; +$txt['quick_reply_verification_guests'] = '(required for all guests)'; +$txt['quick_reply_verification_posts'] = '(required for all users with less than %1$d posts)'; +$txt['wait_for_approval'] = 'Note: this post will not display until it\'s been approved by a moderator.'; + +$txt['notification_enable_board'] = 'Are you sure you wish to enable notification of new topics for this board?'; +$txt['notification_disable_board'] = 'Are you sure you wish to disable notification of new topics for this board?'; +$txt['notification_enable_topic'] = 'Are you sure you wish to enable notification of new replies for this topic?'; +$txt['notification_disable_topic'] = 'Are you sure you wish to disable notification of new replies for this topic?'; + +$txt['report_to_mod'] = 'Report to moderator'; +$txt['issue_warning_post'] = 'Issue a warning because of this message'; + +$txt['unread_topics_visit'] = 'Recent Unread Topics'; +$txt['unread_topics_visit_none'] = 'No unread topics found since your last visit. Click here to try all unread topics.'; +$txt['unread_topics_all'] = 'All Unread Topics'; +$txt['unread_replies'] = 'Updated Topics'; + +$txt['who_title'] = 'Who\'s Online'; +$txt['who_and'] = ' and '; +$txt['who_viewing_topic'] = ' are viewing this topic.'; +$txt['who_viewing_board'] = ' are viewing this board.'; +$txt['who_member'] = 'Member'; + +// No longer used by default theme, but for backwards compat +$txt['powered_by_php'] = 'Powered by PHP'; +$txt['powered_by_mysql'] = 'Powered by MySQL'; +$txt['valid_css'] = 'Valid CSS!'; + +// Current footer strings +$txt['valid_html'] = 'Valid HTML 4.01!'; +$txt['valid_xhtml'] = 'Valid XHTML 1.0!'; +$txt['wap2'] = 'WAP2'; +$txt['rss'] = 'RSS'; +$txt['xhtml'] = 'XHTML'; +$txt['html'] = 'HTML'; + +$txt['guest'] = 'Guest'; +$txt['guests'] = 'Guests'; +$txt['user'] = 'User'; +$txt['users'] = 'Users'; +$txt['hidden'] = 'Hidden'; +$txt['buddy'] = 'Buddy'; +$txt['buddies'] = 'Buddies'; +$txt['most_online_ever'] = 'Most Online Ever'; +$txt['most_online_today'] = 'Most Online Today'; + +$txt['merge_select_target_board'] = 'Select the target board of the merged topic'; +$txt['merge_select_poll'] = 'Select which poll the merged topic should have'; +$txt['merge_topic_list'] = 'Select topics to be merged'; +$txt['merge_select_subject'] = 'Select subject of merged topic'; +$txt['merge_custom_subject'] = 'Custom subject'; +$txt['merge_enforce_subject'] = 'Change the subject of all the messages'; +$txt['merge_include_notifications'] = 'Include notifications?'; +$txt['merge_check'] = 'Merge?'; +$txt['merge_no_poll'] = 'No poll'; + +$txt['response_prefix'] = 'Re: '; +$txt['current_icon'] = 'Current Icon'; +$txt['message_icon'] = 'Message Icon'; + +$txt['smileys_current'] = 'Current Smiley Set'; +$txt['smileys_none'] = 'No Smileys'; +$txt['smileys_forum_board_default'] = 'Forum/Board Default'; + +$txt['search_results'] = 'Search Results'; +$txt['search_no_results'] = 'Sorry, no matches were found'; + +$txt['totalTimeLogged1'] = 'Total time logged in: '; +$txt['totalTimeLogged2'] = ' days, '; +$txt['totalTimeLogged3'] = ' hours and '; +$txt['totalTimeLogged4'] = ' minutes.'; +$txt['totalTimeLogged5'] = 'd '; +$txt['totalTimeLogged6'] = 'h '; +$txt['totalTimeLogged7'] = 'm'; + +$txt['approve_thereis'] = 'There is'; +$txt['approve_thereare'] = 'There are'; +$txt['approve_member'] = 'one member'; +$txt['approve_members'] = 'members'; +$txt['approve_members_waiting'] = 'awaiting approval.'; + +$txt['notifyboard_turnon'] = 'Do you want a notification email when someone posts a new topic in this board?'; +$txt['notifyboard_turnoff'] = 'Are you sure you do not want to receive new topic notifications for this board?'; + +$txt['activate_code'] = 'Your activation code is'; + +$txt['find_members'] = 'Find Members'; +$txt['find_username'] = 'Name, username, or email address'; +$txt['find_buddies'] = 'Show Buddies Only?'; +$txt['find_wildcards'] = 'Allowed Wildcards: *, ?'; +$txt['find_no_results'] = 'No results found'; +$txt['find_results'] = 'Results'; +$txt['find_close'] = 'Close'; + +$txt['unread_since_visit'] = 'Show unread posts since last visit.'; +$txt['show_unread_replies'] = 'Show new replies to your posts.'; + +$txt['change_color'] = 'Change Color'; + +$txt['quickmod_delete_selected'] = 'Remove Selected'; + +// In this string, don't use entities. (&, etc.) +$txt['show_personal_messages'] = 'You have received one or more new personal messages.\\nWould you like to open a new window to view them?'; + +$txt['previous_next_back'] = '« previous'; +$txt['previous_next_forward'] = 'next »'; + +$txt['movetopic_auto_board'] = '[BOARD]'; +$txt['movetopic_auto_topic'] = '[TOPIC LINK]'; +$txt['movetopic_default'] = 'This topic has been moved to ' . $txt['movetopic_auto_board'] . ".\n\n" . $txt['movetopic_auto_topic']; + +$txt['upshrink_description'] = 'Shrink or expand the header.'; + +$txt['mark_unread'] = 'Mark unread'; + +$txt['ssi_not_direct'] = 'Please don\'t access SSI.php by URL directly; you may want to use the path (%1$s) or add ?ssi_function=something.'; +$txt['ssi_session_broken'] = 'SSI.php was unable to load a session! This may cause problems with logout and other functions - please make sure SSI.php is included before *anything* else in all your scripts!'; + +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['preview_title'] = 'Preview post'; +$txt['preview_fetch'] = 'Fetching preview...'; +$txt['preview_new'] = 'New message'; +$txt['error_while_submitting'] = 'The following error or errors occurred while posting this message:'; +$txt['error_old_topic'] = 'Warning: this topic has not been posted in for at least %1$d days.
    Unless you\'re sure you want to reply, please consider starting a new topic.'; + +$txt['split_selected_posts'] = 'Selected posts'; +$txt['split_selected_posts_desc'] = 'The posts below will form a new topic after splitting.'; +$txt['split_reset_selection'] = 'reset selection'; + +$txt['modify_cancel'] = 'Cancel'; +$txt['mark_read_short'] = 'Mark Read'; + +$txt['pm_short'] = 'My Messages'; +$txt['pm_menu_read'] = 'Read your messages'; +$txt['pm_menu_send'] = 'Send a message'; + +$txt['hello_member_ndt'] = 'Hello'; + +$txt['unapproved_posts'] = 'Unapproved Posts (Topics: %1$d, Posts: %2$d)'; + +$txt['ajax_in_progress'] = 'Loading...'; + +$txt['mod_reports_waiting'] = 'There are currently %1$d moderator reports open.'; + +$txt['view_unread_category'] = 'Unread Posts'; +$txt['verification'] = 'Verification'; +$txt['visual_verification_description'] = 'Type the letters shown in the picture'; +$txt['visual_verification_sound'] = 'Listen to the letters'; +$txt['visual_verification_request_new'] = 'Request another image'; + +// Sub menu labels +$txt['summary'] = 'Summary'; +$txt['account'] = 'Account Settings'; +$txt['forumprofile'] = 'Forum Profile'; + +$txt['modSettings_title'] = 'Features and Options'; +$txt['package'] = 'Package Manager'; +$txt['errlog'] = 'Error Log'; +$txt['edit_permissions'] = 'Permissions'; +$txt['mc_unapproved_attachments'] = 'Unapproved Attachments'; +$txt['mc_unapproved_poststopics'] = 'Unapproved Posts and Topics'; +$txt['mc_reported_posts'] = 'Reported Posts'; +$txt['modlog_view'] = 'Moderation Log'; +$txt['calendar_menu'] = 'View Calendar'; + +//!!! Send email strings - should move? +$txt['send_email'] = 'Send Email'; +$txt['send_email_disclosed'] = 'Note this will be visible to the recipient.'; +$txt['send_email_subject'] = 'Email Subject'; + +$txt['ignoring_user'] = 'You are ignoring this user.'; +$txt['show_ignore_user_post'] = 'Show me the post.'; + +$txt['spider'] = 'Spider'; +$txt['spiders'] = 'Spiders'; +$txt['openid'] = 'OpenID'; + +$txt['downloads'] = 'Downloads'; +$txt['filesize'] = 'Filesize'; +$txt['subscribe_webslice'] = 'Subscribe to Webslice'; + +// Restore topic +$txt['restore_topic'] = 'Restore Topic'; +$txt['restore_message'] = 'Restore'; +$txt['quick_mod_restore'] = 'Restore Selected'; + +// Editor prompt. +$txt['prompt_text_email'] = 'Please enter the email address.'; +$txt['prompt_text_ftp'] = 'Please enter the ftp address.'; +$txt['prompt_text_url'] = 'Please enter the URL you wish to link to.'; +$txt['prompt_text_img'] = 'Enter image location'; + +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['autosuggest_delete_item'] = 'Delete Item'; + +// Debug related - when $db_show_debug is true. +$txt['debug_templates'] = 'Templates: '; +$txt['debug_subtemplates'] = 'Sub templates: '; +$txt['debug_language_files'] = 'Language files: '; +$txt['debug_stylesheets'] = 'Style sheets: '; +$txt['debug_files_included'] = 'Files included: '; +$txt['debug_kb'] = 'KB.'; +$txt['debug_show'] = 'show'; +$txt['debug_cache_hits'] = 'Cache hits: '; +$txt['debug_cache_seconds_bytes'] = '%1$ss - %2$s bytes'; +$txt['debug_cache_seconds_bytes_total'] = '%1$ss for %2$s bytes'; +$txt['debug_queries_used'] = 'Queries used: %1$d.'; +$txt['debug_queries_used_and_warnings'] = 'Queries used: %1$d, %2$d warnings.'; +$txt['debug_query_in_line'] = 'in %1$s line %2$s, '; +$txt['debug_query_which_took'] = 'which took %1$s seconds.'; +$txt['debug_query_which_took_at'] = 'which took %1$s seconds at %2$s into request.'; +$txt['debug_show_queries'] = '[Show Queries]'; +$txt['debug_hide_queries'] = '[Hide Queries]'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/languages/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/languages/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/license.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/license.txt Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,27 @@ +Copyright © 2011 Simple Machines. All rights reserved. + +Developed by: Simple Machines Forum Project + Simple Machines + http://www.simplemachines.org + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + 3. Neither the names of Simple Machines Forum, Simple Machines, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior written + permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +WITH THE SOFTWARE. + +This license may be viewed online at http://www.simplemachines.org/about/smf/license.php \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/print.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/print.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ +#headerarea +{ + display: none; +} + +.tborder +{ + border: none; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/script.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/script.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,486 @@ +var smf_formSubmitted = false; + +// Define document.getElementById for Internet Explorer 4. +if (typeof(document.getElementById) == "undefined") + document.getElementById = function (id) + { + // Just return the corresponding index of all. + return document.all[id]; + } +// Define XMLHttpRequest for IE 5 and above. (don't bother for IE 4 :/.... works in Opera 7.6 and Safari 1.2!) +else if (!window.XMLHttpRequest && window.ActiveXObject) + window.XMLHttpRequest = function () + { + return new ActiveXObject(navigator.userAgent.indexOf("MSIE 5") != -1 ? "Microsoft.XMLHTTP" : "MSXML2.XMLHTTP"); + }; + +// Some older versions of Mozilla don't have this, for some reason. +if (typeof(document.forms) == "undefined") + document.forms = document.getElementsByTagName("form"); + +// Load an XML document using XMLHttpRequest. +function getXMLDocument(url, callback) +{ + if (!window.XMLHttpRequest) + return false; + + var myDoc = new XMLHttpRequest(); + if (typeof(callback) != "undefined") + { + myDoc.onreadystatechange = function () + { + if (myDoc.readyState != 4) + return; + + if (myDoc.responseXML != null && myDoc.status == 200) + callback(myDoc.responseXML); + }; + } + myDoc.open('GET', url, true); + myDoc.send(null); + + return true; +} + +// Send a post form to the server using XMLHttpRequest. +function sendXMLDocument(url, content, callback) +{ + if (!window.XMLHttpRequest) + return false; + + var sendDoc = new window.XMLHttpRequest(); + if (typeof(callback) != "undefined") + { + sendDoc.onreadystatechange = function () + { + if (sendDoc.readyState != 4) + return; + + if (sendDoc.responseXML != null && sendDoc.status == 200) + callback(sendDoc.responseXML); + else + callback(false); + }; + } + sendDoc.open('POST', url, true); + if (typeof(sendDoc.setRequestHeader) != "undefined") + sendDoc.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + sendDoc.send(content); + + return true; +} + +function textToEntities(text) +{ + var entities = ""; + for (var i = 0; i < text.length; i++) + { + if (text.charCodeAt(i) > 127) + entities += "&#" + text.charCodeAt(i) + ";"; + else + entities += text.charAt(i); + } + + return entities; +} + +// Open a new window. +function reqWin(desktopURL, alternateWidth, alternateHeight, noScrollbars) +{ + if ((alternateWidth && self.screen.availWidth * 0.8 < alternateWidth) || (alternateHeight && self.screen.availHeight * 0.8 < alternateHeight)) + { + noScrollbars = false; + alternateWidth = Math.min(alternateWidth, self.screen.availWidth * 0.8); + alternateHeight = Math.min(alternateHeight, self.screen.availHeight * 0.8); + } + else + noScrollbars = typeof(noScrollbars) != "undefined" && noScrollbars == true; + + window.open(desktopURL, 'requested_popup', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=' + (noScrollbars ? 'no' : 'yes') + ',width=' + (alternateWidth ? alternateWidth : 480) + ',height=' + (alternateHeight ? alternateHeight : 220) + ',resizable=no'); + + // Return false so the click won't follow the link ;). + return false; +} + +// Remember the current position. +function storeCaret(text) +{ + // Only bother if it will be useful. + if (typeof(text.createTextRange) != "undefined") + text.caretPos = document.selection.createRange().duplicate(); +} + +// Replaces the currently selected text with the passed text. +function replaceText(text, textarea) +{ + // Attempt to create a text range (IE). + if (typeof(textarea.caretPos) != "undefined" && textarea.createTextRange) + { + var caretPos = textarea.caretPos; + + caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text; + caretPos.select(); + } + // Mozilla text range replace. + else if (typeof(textarea.selectionStart) != "undefined") + { + var begin = textarea.value.substr(0, textarea.selectionStart); + var end = textarea.value.substr(textarea.selectionEnd); + var scrollPos = textarea.scrollTop; + + textarea.value = begin + text + end; + + if (textarea.setSelectionRange) + { + textarea.focus(); + textarea.setSelectionRange(begin.length + text.length, begin.length + text.length); + } + textarea.scrollTop = scrollPos; + } + // Just put it on the end. + else + { + textarea.value += text; + textarea.focus(textarea.value.length - 1); + } +} + +// Surrounds the selected text with text1 and text2. +function surroundText(text1, text2, textarea) +{ + // Can a text range be created? + if (typeof(textarea.caretPos) != "undefined" && textarea.createTextRange) + { + var caretPos = textarea.caretPos, temp_length = caretPos.text.length; + + caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text1 + caretPos.text + text2 + ' ' : text1 + caretPos.text + text2; + + if (temp_length == 0) + { + caretPos.moveStart("character", -text2.length); + caretPos.moveEnd("character", -text2.length); + caretPos.select(); + } + else + textarea.focus(caretPos); + } + // Mozilla text range wrap. + else if (typeof(textarea.selectionStart) != "undefined") + { + var begin = textarea.value.substr(0, textarea.selectionStart); + var selection = textarea.value.substr(textarea.selectionStart, textarea.selectionEnd - textarea.selectionStart); + var end = textarea.value.substr(textarea.selectionEnd); + var newCursorPos = textarea.selectionStart; + var scrollPos = textarea.scrollTop; + + textarea.value = begin + text1 + selection + text2 + end; + + if (textarea.setSelectionRange) + { + if (selection.length == 0) + textarea.setSelectionRange(newCursorPos + text1.length, newCursorPos + text1.length); + else + textarea.setSelectionRange(newCursorPos, newCursorPos + text1.length + selection.length + text2.length); + textarea.focus(); + } + textarea.scrollTop = scrollPos; + } + // Just put them on the end, then. + else + { + textarea.value += text1 + text2; + textarea.focus(textarea.value.length - 1); + } +} + +// Checks if the passed input's value is nothing. +function isEmptyText(theField) +{ + // Copy the value so changes can be made.. + var theValue = theField.value; + + // Strip whitespace off the left side. + while (theValue.length > 0 && (theValue.charAt(0) == ' ' || theValue.charAt(0) == '\t')) + theValue = theValue.substring(1, theValue.length); + // Strip whitespace off the right side. + while (theValue.length > 0 && (theValue.charAt(theValue.length - 1) == ' ' || theValue.charAt(theValue.length - 1) == '\t')) + theValue = theValue.substring(0, theValue.length - 1); + + if (theValue == '') + return true; + else + return false; +} + +// Only allow form submission ONCE. +function submitonce(theform) +{ + smf_formSubmitted = true; +} +function submitThisOnce(form) +{ + // Hateful, hateful fix for Safari 1.3 beta. + if (navigator.userAgent.indexOf('AppleWebKit') != -1) + return !smf_formSubmitted; + + if (typeof(form.form) != "undefined") + form = form.form; + + for (var i = 0; i < form.length; i++) + if (typeof(form[i]) != "undefined" && form[i].tagName.toLowerCase() == "textarea") + form[i].readOnly = true; + + return !smf_formSubmitted; +} + +// Set the "inside" HTML of an element. +function setInnerHTML(element, toValue) +{ + // IE has this built in... + if (typeof(element.innerHTML) != 'undefined') + element.innerHTML = toValue; + // Otherwise, try createContextualFragment(). + else + { + var range = document.createRange(); + range.selectNodeContents(element); + range.deleteContents(); + element.appendChild(range.createContextualFragment(toValue)); + } +} + +// Set the "outer" HTML of an element. +function setOuterHTML(element, toValue) +{ + if (typeof(element.outerHTML) != 'undefined') + element.outerHTML = toValue; + else + { + var range = document.createRange(); + range.setStartBefore(element); + element.parentNode.replaceChild(range.createContextualFragment(toValue), element); + } +} + +// Get the inner HTML of an element. +function getInnerHTML(element) +{ + if (typeof(element.innerHTML) != 'undefined') + return element.innerHTML; + else + { + var returnStr = ''; + for (var i = 0; i < element.childNodes.length; i++) + returnStr += getOuterHTML(element.childNodes[i]); + + return returnStr; + } +} + +function getOuterHTML(node) +{ + if (typeof(node.outerHTML) != 'undefined') + return node.outerHTML; + + var str = ''; + + switch (node.nodeType) + { + // An element. + case 1: + str += '<' + node.nodeName; + + for (var i = 0; i < node.attributes.length; i++) + { + if (node.attributes[i].nodeValue != null) + str += ' ' + node.attributes[i].nodeName + '="' + node.attributes[i].nodeValue + '"'; + } + + if (node.childNodes.length == 0 && in_array(node.nodeName.toLowerCase(), ['hr', 'input', 'img', 'link', 'meta', 'br'])) + str += ' />'; + else + str += '>' + getInnerHTML(node) + ''; + break; + + // 2 is an attribute. + + // Just some text.. + case 3: + str += node.nodeValue; + break; + + // A CDATA section. + case 4: + str += ''; + break; + + // Entity reference.. + case 5: + str += '&' + node.nodeName + ';'; + break; + + // 6 is an actual entity, 7 is a PI. + + // Comment. + case 8: + str += ''; + break; + } + + return str; +} + +// Checks for variable in theArray. +function in_array(variable, theArray) +{ + for (var i = 0; i < theArray.length; i++) + { + if (theArray[i] == variable) + return true; + } + return false; +} + +// Find a specific radio button in its group and select it. +function selectRadioByName(radioGroup, name) +{ + if (typeof(radioGroup.length) == "undefined") + return radioGroup.checked = true; + + for (var i = 0; i < radioGroup.length; i++) + { + if (radioGroup[i].value == name) + return radioGroup[i].checked = true; + } + + return false; +} + +// Invert all checkboxes at once by clicking a single checkbox. +function invertAll(headerfield, checkform, mask) +{ + for (var i = 0; i < checkform.length; i++) + { + if (typeof(checkform[i].name) == "undefined" || (typeof(mask) != "undefined" && checkform[i].name.substr(0, mask.length) != mask)) + continue; + + if (!checkform[i].disabled) + checkform[i].checked = headerfield.checked; + } +} + +// Keep the session alive - always! +var lastKeepAliveCheck = new Date().getTime(); +function smf_sessionKeepAlive() +{ + var curTime = new Date().getTime(); + + // Prevent a Firefox bug from hammering the server. + if (smf_scripturl && curTime - lastKeepAliveCheck > 900000) + { + var tempImage = new Image(); + tempImage.src = smf_scripturl + (smf_scripturl.indexOf("?") == -1 ? "?" : "&") + "action=keepalive;" + curTime; + lastKeepAliveCheck = curTime; + } + + window.setTimeout("smf_sessionKeepAlive();", 1200000); +} +window.setTimeout("smf_sessionKeepAlive();", 1200000); + +// Set a theme option through javascript. +function smf_setThemeOption(option, value, theme, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + var tempImage = new Image(); + tempImage.src = smf_scripturl + (smf_scripturl.indexOf("?") == -1 ? "?" : "&") + "action=jsoption;var=" + option + ";val=" + value + ";sesc=" + cur_session_id + (theme == null ? "" : "&id=" + theme) + ";" + (new Date().getTime()); +} + +function smf_avatarResize() +{ + var possibleAvatars = document.getElementsByTagName ? document.getElementsByTagName("img") : document.all.tags("img"); + + for (var i = 0; i < possibleAvatars.length; i++) + { + if (possibleAvatars[i].className != "avatar") + continue; + + var tempAvatar = new Image(); + tempAvatar.src = possibleAvatars[i].src; + + if (smf_avatarMaxWidth != 0 && tempAvatar.width > smf_avatarMaxWidth) + { + possibleAvatars[i].height = (smf_avatarMaxWidth * tempAvatar.height) / tempAvatar.width; + possibleAvatars[i].width = smf_avatarMaxWidth; + } + else if (smf_avatarMaxHeight != 0 && tempAvatar.height > smf_avatarMaxHeight) + { + possibleAvatars[i].width = (smf_avatarMaxHeight * tempAvatar.width) / tempAvatar.height; + possibleAvatars[i].height = smf_avatarMaxHeight; + } + else + { + possibleAvatars[i].width = tempAvatar.width; + possibleAvatars[i].height = tempAvatar.height; + } + } + + if (typeof(window_oldAvatarOnload) != "undefined" && window_oldAvatarOnload) + { + window_oldAvatarOnload(); + window_oldAvatarOnload = null; + } +} + +function hashLoginPassword(doForm, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + if (typeof(hex_sha1) == "undefined") + return; + // Are they using an email address? + if (doForm.user.value.indexOf("@") != -1) + return; + + // Unless the browser is Opera, the password will not save properly. + if (typeof(window.opera) == "undefined") + doForm.passwrd.autocomplete = "off"; + + doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id); + + // It looks nicer to fill it with asterisks, but Firefox will try to save that. + if (navigator.userAgent.indexOf("Firefox/") != -1) + doForm.passwrd.value = ""; + else + doForm.passwrd.value = doForm.passwrd.value.replace(/./g, "*"); +} + +function hashAdminPassword(doForm, username, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + if (typeof(hex_sha1) == "undefined") + return; + + doForm.admin_hash_pass.value = hex_sha1(hex_sha1(username.toLowerCase() + doForm.admin_pass.value) + cur_session_id); + doForm.admin_pass.value = doForm.admin_pass.value.replace(/./g, "*"); +} + +function ajax_indicator(turn_on) +{ + var indicator = document.getElementById("ajax_in_progress"); + if (indicator != null) + { + if (navigator.appName == "Microsoft Internet Explorer" && navigator.userAgent.indexOf("MSIE 7") == -1) + { + indicator.style.top = document.documentElement.scrollTop; + } + indicator.style.display = turn_on ? "block" : "none"; + } +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/PersonalMessage.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/PersonalMessage.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,88 @@ + +// Handle the JavaScript surrounding personal messages send form. +function smf_PersonalMessageSend(oOptions) +{ + this.opt = oOptions; + this.oBccDiv = null; + this.oBccDiv2 = null; + this.oToAutoSuggest = null; + this.oBccAutoSuggest = null; + this.oToListContainer = null; + this.init(); +} + +smf_PersonalMessageSend.prototype.init = function() +{ + if (!this.opt.bBccShowByDefault) + { + // Hide the BCC control. + this.oBccDiv = document.getElementById(this.opt.sBccDivId); + this.oBccDiv.style.display = 'none'; + this.oBccDiv2 = document.getElementById(this.opt.sBccDivId2); + this.oBccDiv2.style.display = 'none'; + + // Show the link to bet the BCC control back. + var oBccLinkContainer = document.getElementById(this.opt.sBccLinkContainerId); + oBccLinkContainer.style.display = ''; + setInnerHTML(oBccLinkContainer, this.opt.sShowBccLinkTemplate); + + // Make the link show the BCC control. + var oBccLink = document.getElementById(this.opt.sBccLinkId); + oBccLink.instanceRef = this; + oBccLink.onclick = function () { + this.instanceRef.showBcc(); + return false; + }; + } + + var oToControl = document.getElementById(this.opt.sToControlId); + this.oToAutoSuggest = new smc_AutoSuggest({ + sSelf: this.opt.sSelf + '.oToAutoSuggest', + sSessionId: this.opt.sSessionId, + sSessionVar: this.opt.sSessionVar, + sSuggestId: 'to_suggest', + sControlId: this.opt.sToControlId, + sSearchType: 'member', + sPostName: 'recipient_to', + sURLMask: 'action=profile;u=%item_id%', + sTextDeleteItem: this.opt.sTextDeleteItem, + bItemList: true, + sItemListContainerId: 'to_item_list_container', + aListItems: this.opt.aToRecipients + }); + this.oToAutoSuggest.registerCallback('onBeforeAddItem', this.opt.sSelf + '.callbackAddItem'); + + this.oBccAutoSuggest = new smc_AutoSuggest({ + sSelf: this.opt.sSelf + '.oBccAutoSuggest', + sSessionId: this.opt.sSessionId, + sSessionVar: this.opt.sSessionVar, + sSuggestId: 'bcc_suggest', + sControlId: this.opt.sBccControlId, + sSearchType: 'member', + sPostName: 'recipient_bcc', + sURLMask: 'action=profile;u=%item_id%', + sTextDeleteItem: this.opt.sTextDeleteItem, + bItemList: true, + sItemListContainerId: 'bcc_item_list_container', + aListItems: this.opt.aBccRecipients + }); + this.oBccAutoSuggest.registerCallback('onBeforeAddItem', this.opt.sSelf + '.callbackAddItem'); + +} + +smf_PersonalMessageSend.prototype.showBcc = function() +{ + // No longer hide it, show it to the world! + this.oBccDiv.style.display = ''; + this.oBccDiv2.style.display = ''; +} + + +// Prevent items to be added twice or to both the 'To' and 'Bcc'. +smf_PersonalMessageSend.prototype.callbackAddItem = function(oAutoSuggestInstance, sSuggestId) +{ + this.oToAutoSuggest.deleteAddedItem(sSuggestId); + this.oBccAutoSuggest.deleteAddedItem(sSuggestId); + + return true; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/admin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/admin.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,330 @@ +/* + smf_AdminIndex(oOptions) + { + public init() + public loadAdminIndex() + public setAnnouncements() + public showCurrentVersion() + public checkUpdateAvailable() + } + + smf_ViewVersions(oOptions) + { + public init() + public loadViewVersions + public swapOption(oSendingElement, sName) + public compareVersions(sCurrent, sTarget) + public determineVersions() + } +*/ + + + +// Handle the JavaScript surrounding the admin and moderation center. +function smf_AdminIndex(oOptions) +{ + this.opt = oOptions; + this.init(); +} + +smf_AdminIndex.prototype.init = function () +{ + window.adminIndexInstanceRef = this; + var fHandlePageLoaded = function () { + window.adminIndexInstanceRef.loadAdminIndex(); + } + addLoadEvent(fHandlePageLoaded); +} + +smf_AdminIndex.prototype.loadAdminIndex = function () +{ + // Load the text box containing the latest news items. + if (this.opt.bLoadAnnouncements) + this.setAnnouncements(); + + // Load the current SMF and your SMF version numbers. + if (this.opt.bLoadVersions) + this.showCurrentVersion(); + + // Load the text box that sais there's a new version available. + if (this.opt.bLoadUpdateNotification) + this.checkUpdateAvailable(); +} + + +smf_AdminIndex.prototype.setAnnouncements = function () +{ + if (!('smfAnnouncements' in window) || !('length' in window.smfAnnouncements)) + return; + + var sMessages = ''; + for (var i = 0; i < window.smfAnnouncements.length; i++) + sMessages += this.opt.sAnnouncementMessageTemplate.replace('%href%', window.smfAnnouncements[i].href).replace('%subject%', window.smfAnnouncements[i].subject).replace('%time%', window.smfAnnouncements[i].time).replace('%message%', window.smfAnnouncements[i].message); + + setInnerHTML(document.getElementById(this.opt.sAnnouncementContainerId), this.opt.sAnnouncementTemplate.replace('%content%', sMessages)); +} + +smf_AdminIndex.prototype.showCurrentVersion = function () +{ + if (!('smfVersion' in window)) + return; + + var oSmfVersionContainer = document.getElementById(this.opt.sSmfVersionContainerId); + var oYourVersionContainer = document.getElementById(this.opt.sYourVersionContainerId); + + setInnerHTML(oSmfVersionContainer, window.smfVersion); + + var sCurrentVersion = getInnerHTML(oYourVersionContainer); + if (sCurrentVersion != window.smfVersion) + setInnerHTML(oYourVersionContainer, this.opt.sVersionOutdatedTemplate.replace('%currentVersion%', sCurrentVersion)); +} + +smf_AdminIndex.prototype.checkUpdateAvailable = function () +{ + if (!('smfUpdatePackage' in window)) + return; + + var oContainer = document.getElementById(this.opt.sUpdateNotificationContainerId); + + // Are we setting a custom title and message? + var sTitle = 'smfUpdateTitle' in window ? window.smfUpdateTitle : this.opt.sUpdateNotificationDefaultTitle; + var sMessage = 'smfUpdateNotice' in window ? window.smfUpdateNotice : this.opt.sUpdateNotificationDefaultMessage; + + setInnerHTML(oContainer, this.opt.sUpdateNotificationTemplate.replace('%title%', sTitle).replace('%message%', sMessage)); + + // Parse in the package download URL if it exists in the string. + document.getElementById('update-link').href = this.opt.sUpdateNotificationLink.replace('%package%', window.smfUpdatePackage); + + // If we decide to override life into "red" mode, do it. + if ('smfUpdateCritical' in window) + { + document.getElementById('update_table').style.backgroundColor = '#aa2222'; + document.getElementById('update_title').style.backgroundColor = '#dd2222'; + document.getElementById('update_title').style.color = 'white'; + document.getElementById('update_message').style.backgroundColor = '#eebbbb'; + document.getElementById('update_message').style.color = 'black'; + } +} + + + +function smf_ViewVersions (oOptions) +{ + this.opt = oOptions; + this.oSwaps = {}; + this.init(); +} + +smf_ViewVersions.prototype.init = function () +{ + // Load this on loading of the page. + window.viewVersionsInstanceRef = this; + var fHandlePageLoaded = function () { + window.viewVersionsInstanceRef.loadViewVersions(); + } + addLoadEvent(fHandlePageLoaded); +} + +smf_ViewVersions.prototype.loadViewVersions = function () +{ + this.determineVersions(); +} + +smf_ViewVersions.prototype.swapOption = function (oSendingElement, sName) +{ + // If it is undefined, or currently off, turn it on - otherwise off. + this.oSwaps[sName] = !(sName in this.oSwaps) || !this.oSwaps[sName]; + document.getElementById(sName).style.display = this.oSwaps[sName] ? '' : 'none'; + + // Unselect the link and return false. + oSendingElement.blur(); + return false; +} + +smf_ViewVersions.prototype.compareVersions = function (sCurrent, sTarget) +{ + var aVersions = aParts = new Array(); + var aCompare = new Array(sCurrent, sTarget); + + for (var i = 0; i < 2; i++) + { + // Clean the version and extract the version parts. + var sClean = aCompare[i].toLowerCase().replace(/ /g, '').replace(/2.0rc1-1/, '2.0rc1.1'); + aParts = sClean.match(/(\d+)(?:\.(\d+|))?(?:\.)?(\d+|)(?:(alpha|beta|rc)(\d+|)(?:\.)?(\d+|))?(?:(dev))?(\d+|)/); + + // No matches? + if (aParts == null) + return false; + + // Build an array of parts. + aVersions[i] = [ + aParts[1] > 0 ? parseInt(aParts[1]) : 0, + aParts[2] > 0 ? parseInt(aParts[2]) : 0, + aParts[3] > 0 ? parseInt(aParts[3]) : 0, + typeof(aParts[4]) == 'undefined' ? 'stable' : aParts[4], + aParts[5] > 0 ? parseInt(aParts[5]) : 0, + aParts[6] > 0 ? parseInt(aParts[6]) : 0, + typeof(aParts[7]) != 'undefined', + ]; + } + + // Loop through each category. + for (i = 0; i < 7; i++) + { + // Is there something for us to calculate? + if (aVersions[0][i] != aVersions[1][i]) + { + // Dev builds are a problematic exception. + // (stable) dev < (stable) but (unstable) dev = (unstable) + if (i == 3) + return aVersions[0][i] < aVersions[1][i] ? !aVersions[1][6] : aVersions[0][6]; + else if (i == 6) + return aVersions[0][6] ? aVersions[1][3] == 'stable' : false; + // Otherwise a simple comparison. + else + return aVersions[0][i] < aVersions[1][i]; + } + } + + // They are the same! + return false; +} + +smf_ViewVersions.prototype.determineVersions = function () +{ + var oHighYour = { + Sources: '??', + Default: '??', + Languages: '??', + Templates: '??' + }; + var oHighCurrent = { + Sources: '??', + Default: '??', + Languages: '??', + Templates: '??' + }; + var oLowVersion = { + Sources: false, + Default: false, + Languages: false, + Templates: false + }; + + var sSections = [ + 'Sources', + 'Default', + 'Languages', + 'Templates' + ]; + + for (var i = 0, n = sSections.length; i < n; i++) + { + // Collapse all sections. + var oSection = document.getElementById(sSections[i]); + if (typeof(oSection) == 'object' && oSection != null) + oSection.style.display = 'none'; + + // Make all section links clickable. + var oSectionLink = document.getElementById(sSections[i] + '-link'); + if (typeof(oSectionLink) == 'object' && oSectionLink != null) + { + oSectionLink.instanceRef = this; + oSectionLink.sSection = sSections[i]; + oSectionLink.onclick = function () { + this.instanceRef.swapOption(this, this.sSection); + return false; + }; + } + } + + if (!('smfVersions' in window)) + window.smfVersions = {}; + + for (var sFilename in window.smfVersions) + { + if (!document.getElementById('current' + sFilename)) + continue; + + var sYourVersion = getInnerHTML(document.getElementById('your' + sFilename)); + + var sCurVersionType; + for (var sVersionType in oLowVersion) + if (sFilename.substr(0, sVersionType.length) == sVersionType) + { + sCurVersionType = sVersionType; + break; + } + + if (typeof(sCurVersionType) != 'undefined') + { + if ((this.compareVersions(oHighYour[sCurVersionType], sYourVersion) || oHighYour[sCurVersionType] == '??') && !oLowVersion[sCurVersionType]) + oHighYour[sCurVersionType] = sYourVersion; + if (this.compareVersions(oHighCurrent[sCurVersionType], smfVersions[sFilename]) || oHighCurrent[sCurVersionType] == '??') + oHighCurrent[sCurVersionType] = smfVersions[sFilename]; + + if (this.compareVersions(sYourVersion, smfVersions[sFilename])) + { + oLowVersion[sCurVersionType] = sYourVersion; + document.getElementById('your' + sFilename).style.color = 'red'; + } + } + else if (this.compareVersions(sYourVersion, smfVersions[sFilename])) + oLowVersion[sCurVersionType] = sYourVersion; + + setInnerHTML(document.getElementById('current' + sFilename), smfVersions[sFilename]); + setInnerHTML(document.getElementById('your' + sFilename), sYourVersion); + } + + if (!('smfLanguageVersions' in window)) + window.smfLanguageVersions = {}; + + for (sFilename in window.smfLanguageVersions) + { + for (var i = 0; i < this.opt.aKnownLanguages.length; i++) + { + if (!document.getElementById('current' + sFilename + this.opt.aKnownLanguages[i])) + continue; + + setInnerHTML(document.getElementById('current' + sFilename + this.opt.aKnownLanguages[i]), smfLanguageVersions[sFilename]); + + sYourVersion = getInnerHTML(document.getElementById('your' + sFilename + this.opt.aKnownLanguages[i])); + setInnerHTML(document.getElementById('your' + sFilename + this.opt.aKnownLanguages[i]), sYourVersion); + + if ((this.compareVersions(oHighYour.Languages, sYourVersion) || oHighYour.Languages == '??') && !oLowVersion.Languages) + oHighYour.Languages = sYourVersion; + if (this.compareVersions(oHighCurrent.Languages, smfLanguageVersions[sFilename]) || oHighCurrent.Languages == '??') + oHighCurrent.Languages = smfLanguageVersions[sFilename]; + + if (this.compareVersions(sYourVersion, smfLanguageVersions[sFilename])) + { + oLowVersion.Languages = sYourVersion; + document.getElementById('your' + sFilename + this.opt.aKnownLanguages[i]).style.color = 'red'; + } + } + } + + setInnerHTML(document.getElementById('yourSources'), oLowVersion.Sources ? oLowVersion.Sources : oHighYour.Sources); + setInnerHTML(document.getElementById('currentSources'), oHighCurrent.Sources); + if (oLowVersion.Sources) + document.getElementById('yourSources').style.color = 'red'; + + setInnerHTML(document.getElementById('yourDefault'), oLowVersion.Default ? oLowVersion.Default : oHighYour.Default); + setInnerHTML(document.getElementById('currentDefault'), oHighCurrent.Default); + if (oLowVersion.Default) + document.getElementById('yourDefault').style.color = 'red'; + + if (document.getElementById('Templates')) + { + setInnerHTML(document.getElementById('yourTemplates'), oLowVersion.Templates ? oLowVersion.Templates : oHighYour.Templates); + setInnerHTML(document.getElementById('currentTemplates'), oHighCurrent.Templates); + + if (oLowVersion.Templates) + document.getElementById('yourTemplates').style.color = 'red'; + } + + setInnerHTML(document.getElementById('yourLanguages'), oLowVersion.Languages ? oLowVersion.Languages : oHighYour.Languages); + setInnerHTML(document.getElementById('currentLanguages'), oHighCurrent.Languages); + if (oLowVersion.Languages) + document.getElementById('yourLanguages').style.color = 'red'; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/captcha.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/captcha.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,79 @@ +// This file contains javascript associated with the captcha visual verification stuffs. + +function smfCaptcha(imageURL, uniqueID, useLibrary, letterCount) +{ + // By default the letter count is five. + if (!letterCount) + letterCount = 5; + + uniqueID = uniqueID ? '_' + uniqueID : ''; + autoCreate(); + + // Automatically get the captcha event handlers in place and the like. + function autoCreate() + { + // Is there anything to cycle images with - if so attach the refresh image function? + var cycleHandle = document.getElementById('visual_verification' + uniqueID + '_refresh'); + if (cycleHandle) + { + createEventListener(cycleHandle); + cycleHandle.addEventListener('click', refreshImages, false); + } + + // Maybe a voice is here to spread light? + var soundHandle = document.getElementById('visual_verification' + uniqueID + '_sound'); + if (soundHandle) + { + createEventListener(soundHandle); + soundHandle.addEventListener('click', playSound, false); + } + } + + // Change the images. + function refreshImages() + { + // Make sure we are using a new rand code. + var new_url = new String(imageURL); + new_url = new_url.substr(0, new_url.indexOf("rand=") + 5); + + // Quick and dirty way of converting decimal to hex + var hexstr = "0123456789abcdef"; + for(var i=0; i < 32; i++) + new_url = new_url + hexstr.substr(Math.floor(Math.random() * 16), 1); + + if (useLibrary && document.getElementById("verification_image" + uniqueID)) + { + document.getElementById("verification_image" + uniqueID).src = new_url; + } + else if (document.getElementById("verification_image" + uniqueID)) + { + for (i = 1; i <= letterCount; i++) + if (document.getElementById("verification_image" + uniqueID + "_" + i)) + document.getElementById("verification_image" + uniqueID + "_" + i).src = new_url + ";letter=" + i; + } + + return false; + } + + // Request a sound... play it Mr Soundman... + function playSound(ev) + { + if (!ev) + ev = window.event; + + popupFailed = reqWin(imageURL + ";sound", 400, 120); + // Don't follow the link if the popup worked, which it would have done! + if (!popupFailed) + { + if (is_ie && ev.cancelBubble) + ev.cancelBubble = true; + else if (ev.stopPropagation) + { + ev.stopPropagation(); + ev.preventDefault(); + } + } + + return popupFailed; + } +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/editor.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/editor.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1744 @@ +// *** smc_Editor class. +function smc_Editor(oOptions) +{ + this.opt = oOptions; + + // Create some links to the editor object. + this.oTextHandle = null; + this.sCurrentText = 'sText' in this.opt ? this.opt.sText : ''; + + // How big? + this.sEditWidth = 'sEditWidth' in this.opt ? this.opt.sEditWidth : '70%'; + this.sEditHeight = 'sEditHeight' in this.opt ? this.opt.sEditHeight : '150px'; + + this.showDebug = false; + this.bRichTextEnabled = 'bWysiwyg' in this.opt && this.opt.bWysiwyg; + // This doesn't work on Opera as they cannot restore focus after clicking a BBC button. + this.bRichTextPossible = !this.opt.bRichEditOff && ((is_ie5up && !is_ie50) || is_ff || is_opera95up || is_safari || is_chrome) && !(is_iphone || is_android); + + this.oFrameHandle = null; + this.oFrameDocument = null; + this.oFrameWindow = null; + + // These hold the breadcrumb. + this.oBreadHandle = null; + this.oResizerElement = null; + + // Kinda holds all the useful stuff. + this.aKeyboardShortcuts = new Array(); + + // This tracks the cursor position on IE to avoid refocus problems. + this.cursorX = 0; + this.cursorY = 0; + + // This is all the elements that can have a simple execCommand. + this.oSimpleExec = { + b: 'bold', + u: 'underline', + i: 'italic', + s: 'strikethrough', + left: 'justifyleft', + center: 'justifycenter', + right: 'justifyright', + hr: 'inserthorizontalrule', + list: 'insertunorderedlist', + orderlist: 'insertorderedlist', + sub: 'subscript', + sup: 'superscript', + indent: 'indent', + outdent: 'outdent' + } + + // Codes to call a private function + this.oSmfExec = { + unformat: 'removeFormatting', + toggle: 'toggleView' + } + + // Any special breadcrumb mappings to ensure we show a consistant tag name. + this.breadCrumbNameTags = { + strike: 's', + strong: 'b', + em: 'i' + } + + this.aBreadCrumbNameStyles = [ + { + sStyleType: 'text-decoration', + sStyleValue: 'underline', + sBbcTag: 'u' + }, + { + sStyleType: 'text-decoration', + sStyleValue: 'line-through', + sBbcTag: 's' + }, + { + sStyleType: 'text-align', + sStyleValue: 'left', + sBbcTag: 'left' + }, + { + sStyleType: 'text-align', + sStyleValue: 'center', + sBbcTag: 'center' + }, + { + sStyleType: 'text-align', + sStyleValue: 'right', + sBbcTag: 'right' + }, + { + sStyleType: 'font-weight', + sStyleValue: 'bold', + sBbcTag: 'b' + }, + { + sStyleType: 'font-style', + sStyleValue: 'italic', + sBbcTag: 'i' + } + ]; + + // All the fonts in the world. + this.aFontFaces = [ + 'Arial', + 'Arial Black', + 'Impact', + 'Verdana', + 'Times New Roman', + 'Georgia', + 'Andale Mono', + 'Trebuchet MS', + 'Comic Sans MS' + ]; + // Font maps (HTML => CSS size) + this.aFontSizes = [ + 0, + 8, + 10, + 12, + 14, + 18, + 24, + 36 + ]; + // Color maps! (hex => name) + this.oFontColors = { + black: '#000000', + red: '#ff0000', + yellow: '#ffff00', + pink: '#ffc0cb', + green: '#008000', + orange: '#ffa500', + purple: '#800080', + blue: '#0000ff', + beige: '#f5f5dc', + brown: '#a52a2a', + teal: '#008080', + navy: '#000080', + maroon: '#800000', + limegreen: '#32cd32' + } + + this.sFormId = 'sFormId' in this.opt ? this.opt.sFormId : 'postmodify'; + this.iArrayPosition = smf_editorArray.length; + + // Current resize state. + this.osmc_EditorCurrentResize = {}; + + this.init(); +} + +smc_Editor.prototype.init = function() +{ + // Define the event wrapper functions. + var oCaller = this; + this.aEventWrappers = { + editorKeyUp: function(oEvent) {return oCaller.editorKeyUp(oEvent);}, + shortcutCheck: function(oEvent) {return oCaller.shortcutCheck(oEvent);}, + editorBlur: function(oEvent) {return oCaller.editorBlur(oEvent);}, + editorFocus: function(oEvent) {return oCaller.editorFocus(oEvent);}, + startResize: function(oEvent) {return oCaller.startResize(oEvent);}, + resizeOverDocument: function(oEvent) {return oCaller.resizeOverDocument(oEvent);}, + endResize: function(oEvent) {return oCaller.endResize(oEvent);}, + resizeOverIframe: function(oEvent) {return oCaller.resizeOverIframe(oEvent);} + }; + + // Set the textHandle. + this.oTextHandle = document.getElementById(this.opt.sUniqueId); + + // Ensure the currentText is set correctly depending on the mode. + if (this.sCurrentText == '' && !this.bRichTextEnabled) + this.sCurrentText = getInnerHTML(this.oTextHandle).php_unhtmlspecialchars(); + + // Only try to do this if rich text is supported. + if (this.bRichTextPossible) + { + // Make the iframe itself, stick it next to the current text area, and give it an ID. + this.oFrameHandle = document.createElement('iframe'); + this.oFrameHandle.src = 'about:blank'; + this.oFrameHandle.id = 'html_' + this.opt.sUniqueId; + this.oFrameHandle.className = 'rich_editor_frame'; + this.oFrameHandle.style.display = 'none'; + this.oFrameHandle.style.margin = '0px'; + this.oFrameHandle.tabIndex = this.oTextHandle.tabIndex; + this.oTextHandle.parentNode.appendChild(this.oFrameHandle); + + // Create some handy shortcuts. + this.oFrameDocument = this.oFrameHandle.contentDocument ? this.oFrameHandle.contentDocument : ('contentWindow' in this.oFrameHandle ? this.oFrameHandle.contentWindow.document : this.oFrameHandle.document); + this.oFrameWindow = 'contentWindow' in this.oFrameHandle ? this.oFrameHandle.contentWindow : this.oFrameHandle.document.parentWindow; + + // Create the debug window... and stick this under the main frame - make it invisible by default. + this.oBreadHandle = document.createElement('div'); + this.oBreadHandle.id = 'bread_' . uid; + this.oBreadHandle.style.visibility = 'visible'; + this.oBreadHandle.style.display = 'none'; + this.oFrameHandle.parentNode.appendChild(this.oBreadHandle); + + // Size the iframe dimensions to something sensible. + this.oFrameHandle.style.width = this.sEditWidth; + this.oFrameHandle.style.height = this.sEditHeight; + this.oFrameHandle.style.visibility = 'visible'; + + // Only bother formatting the debug window if debug is enabled. + if (this.showDebug) + { + this.oBreadHandle.style.width = this.sEditWidth; + this.oBreadHandle.style.height = '20px'; + this.oBreadHandle.className = 'windowbg2'; + this.oBreadHandle.style.border = '1px black solid'; + this.oBreadHandle.style.display = ''; + } + + // Populate the editor with nothing by default. + if (!is_opera95up) + { + this.oFrameDocument.open(); + this.oFrameDocument.write(''); + this.oFrameDocument.close(); + } + + // Right to left mode? + if (this.opt.bRTL) + { + this.oFrameDocument.dir = "rtl"; + this.oFrameDocument.body.dir = "rtl"; + } + + // Mark it as editable... + if (this.oFrameDocument.body.contentEditable) + this.oFrameDocument.body.contentEditable = true; + else + { + this.oFrameHandle.style.display = ''; + this.oFrameDocument.designMode = 'on'; + this.oFrameHandle.style.display = 'none'; + } + + // Now we need to try and style the editor - internet explorer allows us to do the whole lot. + if (document.styleSheets['editor_css'] || document.styleSheets['editor_ie_css']) + { + var oMyStyle = this.oFrameDocument.createElement('style'); + this.oFrameDocument.documentElement.firstChild.appendChild(oMyStyle); + oMyStyle.styleSheet.cssText = document.styleSheets['editor_ie_css'] ? document.styleSheets['editor_ie_css'].cssText : document.styleSheets['editor_css'].cssText; + } + // Otherwise we seem to have to try to rip out each of the styles one by one! + else if (document.styleSheets.length) + { + var bFoundSomething = false; + // First we need to find the right style sheet. + for (var i = 0, iNumStyleSheets = document.styleSheets.length; i < iNumStyleSheets; i++) + { + // Start off looking for the right style sheet. + if (!document.styleSheets[i].href || document.styleSheets[i].href.indexOf('editor') < 1) + continue; + + // Firefox won't allow us to get a CSS file which ain't in the right URL. + try + { + if (document.styleSheets[i].cssRules.length < 1) + continue; + } + catch (e) + { + continue; + } + + // Manually try to find the rich_editor class. + for (var r = 0, iNumRules = document.styleSheets[i].cssRules.length; r < iNumRules; r++) + { + // Got the main editor? + if (document.styleSheets[i].cssRules[r].selectorText == '.rich_editor') + { + // Set some possible styles. + if (document.styleSheets[i].cssRules[r].style.color) + this.oFrameDocument.body.style.color = document.styleSheets[i].cssRules[r].style.color; + if (document.styleSheets[i].cssRules[r].style.backgroundColor) + this.oFrameDocument.body.style.backgroundColor = document.styleSheets[i].cssRules[r].style.backgroundColor; + if (document.styleSheets[i].cssRules[r].style.fontSize) + this.oFrameDocument.body.style.fontSize = document.styleSheets[i].cssRules[r].style.fontSize; + if (document.styleSheets[i].cssRules[r].style.fontFamily) + this.oFrameDocument.body.style.fontFamily = document.styleSheets[i].cssRules[r].style.fontFamily; + if (document.styleSheets[i].cssRules[r].style.border) + this.oFrameDocument.body.style.border = document.styleSheets[i].cssRules[r].style.border; + bFoundSomething = true; + } + // The frame? + else if (document.styleSheets[i].cssRules[r].selectorText == '.rich_editor_frame') + { + if (document.styleSheets[i].cssRules[r].style.border) + this.oFrameHandle.style.border = document.styleSheets[i].cssRules[r].style.border; + } + } + } + + // Didn't find it? + if (!bFoundSomething) + { + // Do something that is better than nothing. + this.oFrameDocument.body.style.color = 'black'; + this.oFrameDocument.body.style.backgroundColor = 'white'; + this.oFrameDocument.body.style.fontSize = '78%'; + this.oFrameDocument.body.style.fontFamily = '"Verdana", "Arial", "Helvetica", "sans-serif"'; + this.oFrameDocument.body.style.border = 'none'; + this.oFrameHandle.style.border = '1px solid #808080'; + if (is_opera) + this.oFrameDocument.body.style.height = '99%'; + } + } + + // Apply the class... + this.oFrameDocument.body.className = 'rich_editor'; + + // Set the frame padding/margin inside the editor. + this.oFrameDocument.body.style.padding = '1px'; + this.oFrameDocument.body.style.margin = '0'; + + // Listen for input. + this.oFrameDocument.instanceRef = this; + this.oFrameHandle.instanceRef = this; + this.oTextHandle.instanceRef = this; + + // Attach addEventListener for those browsers that don't support it. + createEventListener(this.oFrameHandle); + createEventListener(this.oFrameDocument); + createEventListener(this.oTextHandle); + createEventListener(window); + createEventListener(document); + + // Attach functions to the key and mouse events. + this.oFrameDocument.addEventListener('keyup', this.aEventWrappers.editorKeyUp, true); + this.oFrameDocument.addEventListener('mouseup', this.aEventWrappers.editorKeyUp, true); + this.oFrameDocument.addEventListener('keydown', this.aEventWrappers.shortcutCheck, true); + this.oTextHandle.addEventListener('keydown', this.aEventWrappers.shortcutCheck, true); + + if (is_ie) + { + this.oFrameDocument.addEventListener('blur', this.aEventWrappers.editorBlur, true); + this.oFrameDocument.addEventListener('focus', this.aEventWrappers.editorFocus, true); + } + + // Show the iframe only if wysiwyrg is on - and hide the text area. + this.oTextHandle.style.display = this.bRichTextEnabled ? 'none' : ''; + this.oFrameHandle.style.display = this.bRichTextEnabled ? '' : 'none'; + this.oBreadHandle.style.display = this.bRichTextEnabled ? '' : 'none'; + } + // If we can't do advanced stuff then just do the basics. + else + { + // Cannot have WYSIWYG anyway! + this.bRichTextEnabled = false; + + // We need some of the event handlers. + createEventListener(this.oTextHandle); + createEventListener(window); + createEventListener(document); + } + + // Make sure we set the message mode correctly. + document.getElementById(this.opt.sUniqueId + '_mode').value = this.bRichTextEnabled ? 1 : 0; + + // Show the resizer. + if (document.getElementById(this.opt.sUniqueId + '_resizer') && (!is_opera || is_opera95up) && !(is_chrome && !this.bRichTextEnabled)) + { + // Currently nothing is being resized...I assume! + window.smf_oCurrentResizeEditor = null; + + this.oResizerElement = document.getElementById(this.opt.sUniqueId + '_resizer'); + this.oResizerElement.style.display = ''; + + createEventListener(this.oResizerElement); + this.oResizerElement.addEventListener('mousedown', this.aEventWrappers.startResize, false); + } + + // Set the text - if WYSIWYG is enabled that is. + if (this.bRichTextEnabled) + { + this.insertText(this.sCurrentText, true); + + // Better make us the focus! + this.setFocus(); + } + + // Finally, register shortcuts. + this.registerDefaultShortcuts(); + this.updateEditorControls(); +} + +// Return the current text. +smc_Editor.prototype.getText = function(bPrepareEntities, bModeOverride) +{ + var bCurMode = typeof(bModeOverride) != 'undefined' ? bModeOverride : this.bRichTextEnabled; + + if (!bCurMode || this.oFrameDocument == null) + { + var sText = this.oTextHandle.value; + if (bPrepareEntities) + sText = sText.replace(//g, '#smgt#').replace(/&/g, '#smamp#'); + } + else + { + var sText = this.oFrameDocument.body.innerHTML; + if (bPrepareEntities) + sText = sText.replace(/</g, '#smlt#').replace(/>/g, '#smgt#').replace(/&/g, '#smamp#'); + } + + // Clean it up - including removing semi-colons. + if (bPrepareEntities) + sText = sText.replace(/ /g, ' ').replace(/;/g, '#smcol#'); + + // Return it. + return sText; +} + +// Return the current text. +smc_Editor.prototype.unprotectText = function(sText) +{ + var bCurMode = typeof(bModeOverride) != 'undefined' ? bModeOverride : this.bRichTextEnabled; + + // This restores smlt, smgt and smamp into boring entities, to unprotect against XML'd information like quotes. + sText = sText.replace(/#smlt#/g, '<').replace(/#smgt#/g, '>').replace(/#smamp#/g, '&'); + + // Return it. + return sText; +} + +smc_Editor.prototype.editorKeyUp = function() +{ + // Rebuild the breadcrumb. + this.updateEditorControls(); +} + +smc_Editor.prototype.editorBlur = function() +{ + if (!is_ie) + return; + + // Need to do something here. +} + +smc_Editor.prototype.editorFocus = function() +{ + if (!is_ie) + return; + + // Need to do something here. +} + +// Rebuild the breadcrumb etc - and set things to the correct context. +smc_Editor.prototype.updateEditorControls = function() +{ + // Everything else is specific to HTML mode. + if (!this.bRichTextEnabled) + { + // Set none of the buttons active. + if (this.opt.oBBCBox) + this.opt.oBBCBox.setActive([]); + return; + } + + var aCrumb = new Array(); + var aAllCrumbs = new Array(); + var iMaxLength = 6; + + // What is the current element? + var oCurTag = this.getCurElement(); + + var i = 0; + while (typeof(oCurTag) == 'object' && oCurTag != null && oCurTag.nodeName.toLowerCase() != 'body' && i < iMaxLength) + { + aCrumb[i++] = oCurTag; + oCurTag = oCurTag.parentNode; + } + + // Now print out the tree. + var sTree = ''; + var sCurFontName = ''; + var sCurFontSize = ''; + var sCurFontColor = ''; + for (var i = 0, iNumCrumbs = aCrumb.length; i < iNumCrumbs; i++) + { + var sCrumbName = aCrumb[i].nodeName.toLowerCase(); + + // Does it have an alternative name? + if (sCrumbName in this.breadCrumbNameTags) + sCrumbName = this.breadCrumbNameTags[sCrumbName]; + // Don't bother with this... + else if (sCrumbName == 'p') + continue; + // A link? + else if (sCrumbName == 'a') + { + var sUrlInfo = aCrumb[i].getAttribute('href'); + sCrumbName = 'url'; + if (typeof(sUrlInfo) == 'string') + { + if (sUrlInfo.substr(0, 3) == 'ftp') + sCrumbName = 'ftp'; + else if (sUrlInfo.substr(0, 6) == 'mailto') + sCrumbName = 'email'; + } + } + else if (sCrumbName == 'span' || sCrumbName == 'div') + { + if (aCrumb[i].style) + { + for (var j = 0, iNumStyles = this.aBreadCrumbNameStyles.length; j < iNumStyles; j++) + { + // Do we have a font? + if (aCrumb[i].style.fontFamily && aCrumb[i].style.fontFamily != '' && sCurFontName == '') + { + sCurFontName = aCrumb[i].style.fontFamily; + sCrumbName = 'face'; + } + // ... or a font size? + if (aCrumb[i].style.fontSize && aCrumb[i].style.fontSize != '' && sCurFontSize == '') + { + sCurFontSize = aCrumb[i].style.fontSize; + sCrumbName = 'size'; + } + // ... even color? + if (aCrumb[i].style.color && aCrumb[i].style.color != '' && sCurFontColor == '') + { + sCurFontColor = aCrumb[i].style.color; + if (in_array(sCurFontColor, this.oFontColors)) + sCurFontColor = array_search(sCurFontColor, this.oFontColors); + sCrumbName = 'color'; + } + + if (this.aBreadCrumbNameStyles[j].sStyleType == 'text-align' && aCrumb[i].style.textAlign && aCrumb[i].style.textAlign == this.aBreadCrumbNameStyles[j].sStyleValue) + sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag; + else if (this.aBreadCrumbNameStyles[j].sStyleType == 'text-decoration' && aCrumb[i].style.textDecoration && aCrumb[i].style.textDecoration == this.aBreadCrumbNameStyles[j].sStyleValue) + sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag; + else if (this.aBreadCrumbNameStyles[j].sStyleType == 'font-weight' && aCrumb[i].style.fontWeight && aCrumb[i].style.fontWeight == this.aBreadCrumbNameStyles[j].sStyleValue) + sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag; + else if (this.aBreadCrumbNameStyles[j].sStyleType == 'font-style' && aCrumb[i].style.fontStyle && aCrumb[i].style.fontStyle == this.aBreadCrumbNameStyles[j].sStyleValue) + sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag; + } + } + } + // Do we have a font? + else if (sCrumbName == 'font') + { + if (aCrumb[i].getAttribute('face') && sCurFontName == '') + { + sCurFontName = aCrumb[i].getAttribute('face').toLowerCase(); + sCrumbName = 'face'; + } + if (aCrumb[i].getAttribute('size') && sCurFontSize == '') + { + sCurFontSize = aCrumb[i].getAttribute('size'); + sCrumbName = 'size'; + } + if (aCrumb[i].getAttribute('color') && sCurFontColor == '') + { + sCurFontColor = aCrumb[i].getAttribute('color'); + if (in_array(sCurFontColor, this.oFontColors)) + sCurFontColor = array_search(sCurFontColor, this.oFontColors); + sCrumbName = 'color'; + } + // Something else - ignore. + if (sCrumbName == 'font') + continue; + } + + sTree += (i != 0 ? ' >' : '') + ' ' + sCrumbName; + aAllCrumbs[aAllCrumbs.length] = sCrumbName; + } + + // Since we're in WYSIWYG state, show the toggle button as active. + aAllCrumbs[aAllCrumbs.length] = 'toggle'; + + this.opt.oBBCBox.setActive(aAllCrumbs); + + // Try set the font boxes correct. + this.opt.oBBCBox.setSelect('sel_face', sCurFontName); + this.opt.oBBCBox.setSelect('sel_size', sCurFontSize); + this.opt.oBBCBox.setSelect('sel_color', sCurFontColor); + + if (this.showDebug) + setInnerHTML(this.oBreadHandle, sTree); +} + +// Set the HTML content to be that of the text box - if we are in wysiwyg mode. +smc_Editor.prototype.doSubmit = function() +{ + if (this.bRichTextEnabled) + this.oTextHandle.value = this.oFrameDocument.body.innerHTML; +} + +// Populate the box with text. +smc_Editor.prototype.insertText = function(sText, bClear, bForceEntityReverse, iMoveCursorBack) +{ + if (bForceEntityReverse) + sText = this.unprotectText(sText); + + // Erase it all? + if (bClear) + { + if (this.bRichTextEnabled) + { + // This includes a work around for FF to get the cursor to show! + this.oFrameDocument.body.innerHTML = sText; + + // If FF trick the cursor into coming back! + if (is_ff || is_opera) + { + // For some entirely unknown reason FF3 Beta 2 and some Opera versions + // require this. + this.oFrameDocument.body.contentEditable = false; + + this.oFrameDocument.designMode = 'off'; + this.oFrameDocument.designMode = 'on'; + } + } + else + this.oTextHandle.value = sText; + } + else + { + this.setFocus(); + if (this.bRichTextEnabled) + { + // IE croaks if you have an image selected and try to insert! + if ('selection' in this.oFrameDocument && this.oFrameDocument.selection.type != 'Text' && this.oFrameDocument.selection.type != 'None' && this.oFrameDocument.selection.clear) + this.oFrameDocument.selection.clear(); + + var oRange = this.getRange(); + + if (oRange.pasteHTML) + { + oRange.pasteHTML(sText); + + // Do we want to move the cursor back at all? + if (iMoveCursorBack) + oRange.moveEnd('character', -iMoveCursorBack); + + oRange.select(); + } + else + { + // If the cursor needs to be positioned, insert the last fragment first. + if (typeof(iMoveCursorBack) != 'undefined' && iMoveCursorBack > 0 && sText.length > iMoveCursorBack) + { + var oSelection = this.getSelect(false, false); + var oRange = oSelection.getRangeAt(0); + oRange.insertNode(this.oFrameDocument.createTextNode(sText.substr(sText.length - iMoveCursorBack))); + } + + this.smf_execCommand('inserthtml', false, typeof(iMoveCursorBack) == 'undefined' ? sText : sText.substr(0, sText.length - iMoveCursorBack)); + } + } + else + { + replaceText(sText, this.oTextHandle); + } + } +} + + +// Special handler for WYSIWYG. +smc_Editor.prototype.smf_execCommand = function(sCommand, bUi, sValue) +{ + return this.oFrameDocument.execCommand(sCommand, bUi, sValue); +} + +smc_Editor.prototype.insertSmiley = function(oSmileyProperties) +{ + // In text mode we just add it in as we always did. + if (!this.bRichTextEnabled) + this.insertText(' ' + oSmileyProperties.sCode); + + // Otherwise we need to do a whole image... + else + { + var iUniqueSmileyId = 1000 + Math.floor(Math.random() * 100000); + this.insertText(''); + } +} + +smc_Editor.prototype.handleButtonClick = function (oButtonProperties) +{ + this.setFocus(); + + // A special SMF function? + if (oButtonProperties.sCode in this.oSmfExec) + this[this.oSmfExec[oButtonProperties.sCode]](); + + else + { + // In text this is easy... + if (!this.bRichTextEnabled) + { + // Replace? + if (!('sAfter' in oButtonProperties) || oButtonProperties.sAfter == null) + replaceText(oButtonProperties.sBefore.replace(/\\n/g, '\n'), this.oTextHandle) + + // Surround! + else + surroundText(oButtonProperties.sBefore.replace(/\\n/g, '\n'), oButtonProperties.sAfter.replace(/\\n/g, '\n'), this.oTextHandle) + } + else + { + // Is it easy? + if (oButtonProperties.sCode in this.oSimpleExec) + this.smf_execCommand(this.oSimpleExec[oButtonProperties.sCode], false, null); + + // A link? + else if (oButtonProperties.sCode == 'url' || oButtonProperties.sCode == 'email' || oButtonProperties.sCode == 'ftp') + this.insertLink(oButtonProperties.sCode); + + // Maybe an image? + else if (oButtonProperties.sCode == 'img') + this.insertImage(); + + // Everything else means doing something ourselves. + else if ('sBefore' in oButtonProperties) + this.insertCustomHTML(oButtonProperties.sBefore.replace(/\\n/g, '\n'), oButtonProperties.sAfter.replace(/\\n/g, '\n')); + + } + } + + this.updateEditorControls(); + + // Finally set the focus. + this.setFocus(); +} + +// Changing a select box? +smc_Editor.prototype.handleSelectChange = function (oSelectProperties) +{ + this.setFocus(); + + var sValue = oSelectProperties.oSelect.value; + if (sValue == '') + return true; + + // Changing font face? + if (oSelectProperties.sName == 'sel_face') + { + // Not in HTML mode? + if (!this.bRichTextEnabled) + { + sValue = sValue.replace(/"/, ''); + surroundText('[font=' + sValue + ']', '[/font]', this.oTextHandle); + oSelectProperties.oSelect.selectedIndex = 0; + } + else + { + if (is_webkit) + this.smf_execCommand('styleWithCSS', false, true); + this.smf_execCommand('fontname', false, sValue); + } + } + + // Font size? + else if (oSelectProperties.sName == 'sel_size') + { + // Are we in boring mode? + if (!this.bRichTextEnabled) + { + surroundText('[size=' + this.aFontSizes[sValue] + 'pt]', '[/size]', this.oTextHandle); + oSelectProperties.oSelect.selectedIndex = 0; + } + + else + this.smf_execCommand('fontsize', false, sValue); + } + // Or color even? + else if (oSelectProperties.sName == 'sel_color') + { + // Are we in boring mode? + if (!this.bRichTextEnabled) + { + surroundText('[color=' + sValue + ']', '[/color]', this.oTextHandle); + oSelectProperties.oSelect.selectedIndex = 0; + } + + else + this.smf_execCommand('forecolor', false, sValue); + } + + this.updateEditorControls(); + + return true; +} + +// Put in some custom HTML. +smc_Editor.prototype.insertCustomHTML = function(sLeftTag, sRightTag) +{ + var sSelection = this.getSelect(true, true); + if (sSelection.length == 0) + sSelection = ''; + + // Are we overwriting? + if (sRightTag == '') + this.insertText(sLeftTag); + // If something was selected, replace and position cursor at the end of it. + else if (sSelection.length > 0) + this.insertText(sLeftTag + sSelection + sRightTag, false, false, 0); + // Wrap the tags around the cursor position. + else + this.insertText(sLeftTag + sRightTag, false, false, sRightTag.length); + +} + +// Insert a URL link. +smc_Editor.prototype.insertLink = function(sType) +{ + if (sType == 'email') + var sPromptText = oEditorStrings['prompt_text_email']; + else if (sType == 'ftp') + var sPromptText = oEditorStrings['prompt_text_ftp']; + else + var sPromptText = oEditorStrings['prompt_text_url']; + + // IE has a nice prompt for this - others don't. + if (sType != 'email' && sType != 'ftp' && is_ie) + this.smf_execCommand('createlink', true, 'http://'); + + else + { + // Ask them where to link to. + var sText = prompt(sPromptText, sType == 'email' ? '' : (sType == 'ftp' ? 'ftp://' : 'http://')); + if (!sText) + return; + + if (sType == 'email' && sText.indexOf('mailto:') != 0) + sText = 'mailto:' + sText; + + // Check if we have text selected and if not force us to have some. + var oCurText = this.getSelect(true, true); + + if (oCurText.toString().length != 0) + { + this.smf_execCommand('unlink'); + this.smf_execCommand('createlink', false, sText); + } + else + this.insertText('' + sText + ''); + } +} + +smc_Editor.prototype.insertImage = function(sSrc) +{ + if (!sSrc) + { + sSrc = prompt(oEditorStrings['prompt_text_img'], 'http://'); + if (!sSrc || sSrc.length < 10) + return; + } + this.smf_execCommand('insertimage', false, sSrc); +} + +smc_Editor.prototype.getSelect = function(bWantText, bWantHTMLText) +{ + if (is_ie && 'selection' in this.oFrameDocument) + { + // Just want plain text? + if (bWantText && !bWantHTMLText) + return this.oFrameDocument.selection.createRange().text; + // We want the HTML flavoured variety? + else if (bWantHTMLText) + return this.oFrameDocument.selection.createRange().htmlText; + + return this.oFrameDocument.selection; + } + + // This is mainly Firefox. + if ('getSelection' in this.oFrameWindow) + { + // Plain text? + if (bWantText && !bWantHTMLText) + return this.oFrameWindow.getSelection().toString(); + + // HTML is harder - currently using: http://www.faqts.com/knowledge_base/view.phtml/aid/32427 + else if (bWantHTMLText) + { + var oSelection = this.oFrameWindow.getSelection(); + if (oSelection.rangeCount > 0) + { + var oRange = oSelection.getRangeAt(0); + var oClonedSelection = oRange.cloneContents(); + var oDiv = this.oFrameDocument.createElement('div'); + oDiv.appendChild(oClonedSelection); + return oDiv.innerHTML; + } + else + return ''; + } + + // Want the whole object then. + return this.oFrameWindow.getSelection(); + } + + // If we're here it's not good. + return this.oFrameDocument.getSelection(); +} + +smc_Editor.prototype.getRange = function() +{ + // Get the current selection. + var oSelection = this.getSelect(); + + if (!oSelection) + return null; + + if (is_ie && oSelection.createRange) + return oSelection.createRange(); + + return oSelection.rangeCount == 0 ? null : oSelection.getRangeAt(0); +} + +// Get the current element. +smc_Editor.prototype.getCurElement = function() +{ + var oRange = this.getRange(); + + if (!oRange) + return null; + + if (is_ie) + { + if (oRange.item) + return oRange.item(0); + else + return oRange.parentElement(); + } + else + { + var oElement = oRange.commonAncestorContainer; + return this.getParentElement(oElement); + } +} + +smc_Editor.prototype.getParentElement = function(oNode) +{ + if (oNode.nodeType == 1) + return oNode; + + for (var i = 0; i < 50; i++) + { + if (!oNode.parentNode) + break; + + oNode = oNode.parentNode; + if (oNode.nodeType == 1) + return oNode; + } + return null; +} + +// Remove formatting for the selected text. +smc_Editor.prototype.removeFormatting = function() +{ + // Do both at once. + if (this.bRichTextEnabled) + { + this.smf_execCommand('removeformat'); + this.smf_execCommand('unlink'); + } + // Otherwise do a crude move indeed. + else + { + // Get the current selection first. + if (this.oTextHandle.caretPos) + var sCurrentText = this.oTextHandle.caretPos.text; + + else if ('selectionStart' in this.oTextHandle) + var sCurrentText = this.oTextHandle.value.substr(this.oTextHandle.selectionStart, (this.oTextHandle.selectionEnd - this.oTextHandle.selectionStart)); + + else + return; + + // Do bits that are likely to have attributes. + sCurrentText = sCurrentText.replace(RegExp("\\[/?(url|img|iurl|ftp|email|img|color|font|size|list|bdo).*?\\]", "g"), ''); + // Then just anything that looks like BBC. + sCurrentText = sCurrentText.replace(RegExp("\\[/?[A-Za-z]+\\]", "g"), ''); + + replaceText(sCurrentText, this.oTextHandle); + } +} + +// Toggle wysiwyg/normal mode. +smc_Editor.prototype.toggleView = function(bView) +{ + if (!this.bRichTextPossible) + { + alert(oEditorStrings['wont_work']); + return false; + } + + // Overriding or alternating? + if (typeof(bView) == 'undefined') + bView = !this.bRichTextEnabled; + + this.requestParsedMessage(bView); + + return true; +} + +// Request the message in a different form. +smc_Editor.prototype.requestParsedMessage = function(bView) +{ + // Replace with a force reload. + if (!window.XMLHttpRequest) + { + alert(oEditorStrings['func_disabled']); + return; + } + + // Get the text. + var sText = this.getText(true, !bView).replace(/&#/g, "&#").php_to8bit().php_urlencode(); + + this.tmpMethod = sendXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=' + (bView ? 1 : 0) + ';' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, this.onToggleDataReceived); + delete tmpMethod; +} + +smc_Editor.prototype.onToggleDataReceived = function(oXMLDoc) +{ + var sText = ''; + for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++) + sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue; + + // What is this new view we have? + this.bRichTextEnabled = oXMLDoc.getElementsByTagName('message')[0].getAttribute('view') != '0'; + + if (this.bRichTextEnabled) + { + this.oFrameHandle.style.display = ''; + if (this.showDebug) + this.oBreadHandle.style.display = ''; + this.oTextHandle.style.display = 'none'; + } + else + { + sText = sText.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); + this.oFrameHandle.style.display = 'none'; + this.oBreadHandle.style.display = 'none'; + this.oTextHandle.style.display = ''; + } + + // First we focus. + this.setFocus(); + + this.insertText(sText, true); + + // Record the new status. + document.getElementById(this.opt.sUniqueId + '_mode').value = this.bRichTextEnabled ? '1' : '0'; + + // Rebuild the bread crumb! + this.updateEditorControls(); +} + +// Set the focus for the editing window. +smc_Editor.prototype.setFocus = function(force_both) +{ + if (!this.bRichTextEnabled) + this.oTextHandle.focus(); + else if (is_ff || is_opera) + this.oFrameHandle.focus(); + else + this.oFrameWindow.focus(); +} + +// Start up the spellchecker! +smc_Editor.prototype.spellCheckStart = function() +{ + if (!spellCheck) + return false; + + // If we're in HTML mode we need to get the non-HTML text. + if (this.bRichTextEnabled) + { + var sText = escape(this.getText(true, 1).php_to8bit()); + + this.tmpMethod = sendXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=0;' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, this.onSpellCheckDataReceived); + delete tmpMethod; + } + // Otherwise start spellchecking right away. + else + spellCheck(this.sFormId, this.opt.sUniqueId); + + return true; +} + +// This contains the spellcheckable text. +smc_Editor.prototype.onSpellCheckDataReceived = function(oXMLDoc) +{ + var sText = ''; + for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++) + sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue; + + sText = sText.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); + + this.oTextHandle.value = sText; + spellCheck(this.sFormId, this.opt.sUniqueId); +} + +// Function called when the Spellchecker is finished and ready to pass back. +smc_Editor.prototype.spellCheckEnd = function() +{ + // If HTML edit put the text back! + if (this.bRichTextEnabled) + { + var sText = escape(this.getText(true, 0).php_to8bit()); + + this.tmpMethod = sendXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=1;' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, smf_editorArray[this.iArrayPosition].onSpellCheckCompleteDataReceived); + delete tmpMethod; + } + else + this.setFocus(); +} + +// The corrected text. +smc_Editor.prototype.onSpellCheckCompleteDataReceived = function(oXMLDoc) +{ + var sText = ''; + for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++) + sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue; + + this.insertText(sText, true); + this.setFocus(); +} + +smc_Editor.prototype.resizeTextArea = function(newHeight, newWidth, is_change) +{ + // Work out what the new height is. + if (is_change) + { + // We'll assume pixels but may not be. + newHeight = this._calculateNewDimension(this.oTextHandle.style.height, newHeight); + if (newWidth) + newWidth = this._calculateNewDimension(this.oTextHandle.style.width, newWidth); + } + + // Do the HTML editor - but only if it's enabled! + if (this.bRichTextPossible) + { + this.oFrameHandle.style.height = newHeight; + if (newWidth) + this.oFrameHandle.style.width = newWidth; + } + // Do the text box regardless! + this.oTextHandle.style.height = newHeight; + if (newWidth) + this.oTextHandle.style.width = newWidth; +} + +// A utility instruction to save repetition when trying to work out what to change on a height/width. +smc_Editor.prototype._calculateNewDimension = function(old_size, change_size) +{ + // We'll assume pixels but may not be. + changeReg = change_size.toString().match(/(-)?(\d+)(\D*)/); + curReg = old_size.toString().match(/(\d+)(\D*)/); + + if (!changeReg[3]) + changeReg[3] = 'px'; + + if (changeReg[1] == '-') + changeReg[2] = 0 - changeReg[2]; + + // Both the same type? + if (changeReg[3] == curReg[2]) + { + new_size = parseInt(changeReg[2]) + parseInt(curReg[1]); + if (new_size < 50) + new_size = 50; + new_size = new_size.toString() + changeReg[3]; + } + // Is the change a percentage? + else if (changeReg[3] == '%') + new_size = (parseInt(curReg[1]) + parseInt((parseInt(changeReg[2]) * parseInt(curReg[1])) / 100)).toString() + 'px'; + // Otherwise just guess! + else + new_size = (parseInt(curReg[1]) + (parseInt(changeReg[2]) / 10)).toString() + '%'; + + return new_size; +} + +// Register default keyboard shortcuts. +smc_Editor.prototype.registerDefaultShortcuts = function() +{ + if (is_ff) + { + this.registerShortcut('b', 'ctrl', 'b'); + this.registerShortcut('u', 'ctrl', 'u'); + this.registerShortcut('i', 'ctrl', 'i'); + this.registerShortcut('p', 'alt', 'preview'); + this.registerShortcut('s', 'alt', 'submit'); + } +} + +// Register a keyboard shortcut. +smc_Editor.prototype.registerShortcut = function(sLetter, sModifiers, sCodeName) +{ + if (!sCodeName) + return; + + var oNewShortcut = { + code : sCodeName, + key: sLetter.toUpperCase().charCodeAt(0), + alt : false, + ctrl : false + }; + + var aSplitModifiers = sModifiers.split(','); + for(var i = 0, n = aSplitModifiers.length; i < n; i++) + if (aSplitModifiers[i] in oNewShortcut) + oNewShortcut[aSplitModifiers[i]] = true; + + this.aKeyboardShortcuts[this.aKeyboardShortcuts.length] = oNewShortcut; +} + +// Check whether the key has triggered a shortcut? +smc_Editor.prototype.checkShortcut = function(oEvent) +{ + // To be a shortcut it needs to be one of these, duh! + if (!oEvent.altKey && !oEvent.ctrlKey) + return false; + + var sReturnCode = false; + + // Let's take a look at each of our shortcuts shall we? + for (var i = 0, n = this.aKeyboardShortcuts.length; i < n; i++) + { + // Found something? + if (oEvent.altKey == this.aKeyboardShortcuts[i].alt && oEvent.ctrlKey == this.aKeyboardShortcuts[i].ctrl && oEvent.keyCode == this.aKeyboardShortcuts[i].key) + sReturnCode = this.aKeyboardShortcuts[i].code; + } + + return sReturnCode; +} + +// The actual event check for the above! +smc_Editor.prototype.shortcutCheck = function(oEvent) +{ + var sFoundCode = this.checkShortcut(oEvent); + + // Run it and exit. + if (typeof(sFoundCode) == 'string' && sFoundCode != '') + { + var bCancelEvent = false; + if (sFoundCode == 'submit') + { + // So much to do! + var oForm = document.getElementById(this.sFormId); + submitThisOnce(oForm); + submitonce(oForm); + smc_saveEntities(oForm.name, ['subject', this.opt.sUniqueId, 'guestname', 'evtitle', 'question']); + oForm.submit(); + + bCancelEvent = true; + } + else if (sFoundCode == 'preview') + { + previewPost(); + bCancelEvent = true; + } + else + bCancelEvent = this.opt.oBBCBox.emulateClick(sFoundCode); + + if (bCancelEvent) + { + if (is_ie && oEvent.cancelBubble) + oEvent.cancelBubble = true; + + else if (oEvent.stopPropagation) + { + oEvent.stopPropagation(); + oEvent.preventDefault(); + } + + return false; + } + } + + return true; +} + +// This is the method called after clicking the resize bar. +smc_Editor.prototype.startResize = function(oEvent) +{ + if ('event' in window) + oEvent = window.event; + + if (!oEvent || window.smf_oCurrentResizeEditor != null) + return true; + + window.smf_oCurrentResizeEditor = this.iArrayPosition; + + var aCurCoordinates = smf_mousePose(oEvent); + this.osmc_EditorCurrentResize.old_y = aCurCoordinates[1]; + this.osmc_EditorCurrentResize.old_rel_y = null; + this.osmc_EditorCurrentResize.cur_height = parseInt(this.oTextHandle.style.height); + + // Set the necessary events for resizing. + var oResizeEntity = is_ie ? document : window; + oResizeEntity.addEventListener('mousemove', this.aEventWrappers.resizeOverDocument, false); + + if (this.bRichTextPossible) + this.oFrameDocument.addEventListener('mousemove', this.aEventWrappers.resizeOverIframe, false); + + document.addEventListener('mouseup', this.aEventWrappers.endResize, true); + + if (this.bRichTextPossible) + this.oFrameDocument.addEventListener('mouseup', this.aEventWrappers.endResize, true); + + return false; +} + +// This is kind of a cheat, as it only works over the IFRAME. +smc_Editor.prototype.resizeOverIframe = function(oEvent) +{ + if ('event' in window) + oEvent = window.event; + + if (!oEvent || window.smf_oCurrentResizeEditor == null) + return true; + + var newCords = smf_mousePose(oEvent); + + if (this.osmc_EditorCurrentResize.old_rel_y == null) + this.osmc_EditorCurrentResize.old_rel_y = newCords[1]; + else + { + var iNewHeight = newCords[1] - this.osmc_EditorCurrentResize.old_rel_y + this.osmc_EditorCurrentResize.cur_height; + if (iNewHeight < 0) + this.endResize(); + else + this.resizeTextArea(iNewHeight + 'px', 0, false); + } + + return false; +} + +// This resizes an editor. +smc_Editor.prototype.resizeOverDocument = function (oEvent) +{ + if ('event' in window) + oEvent = window.event; + + if (!oEvent || window.smf_oCurrentResizeEditor == null) + return true; + + var newCords = smf_mousePose(oEvent); + + var iNewHeight = newCords[1] - this.osmc_EditorCurrentResize.old_y + this.osmc_EditorCurrentResize.cur_height; + if (iNewHeight < 0) + this.endResize(); + else + this.resizeTextArea(iNewHeight + 'px', 0, false); + + return false; +} + +smc_Editor.prototype.endResize = function (oEvent) +{ + if ('event' in window) + oEvent = window.event; + + if (window.smf_oCurrentResizeEditor == null) + return true; + + window.smf_oCurrentResizeEditor = null; + + // Remove the event... + var oResizeEntity = is_ie ? document : window; + oResizeEntity.removeEventListener('mousemove', this.aEventWrappers.resizeOverDocument, false); + + if (this.bRichTextPossible) + this.oFrameDocument.removeEventListener('mousemove', this.aEventWrappers.resizeOverIframe, false); + + document.removeEventListener('mouseup', this.aEventWrappers.endResize, true); + + if (this.bRichTextPossible) + this.oFrameDocument.removeEventListener('mouseup', this.aEventWrappers.endResize, true); + + return false; +} + +// *** smc_SmileyBox class. +function smc_SmileyBox(oOptions) +{ + this.opt = oOptions; + this.oSmileyRowsContent = {}; + this.oSmileyPopupWindow = null; + this.init(); +} + +smc_SmileyBox.prototype.init = function () +{ + // Get the HTML content of the smileys visible on the post screen. + this.getSmileyRowsContent('postform'); + + // Inject the HTML. + setInnerHTML(document.getElementById(this.opt.sContainerDiv), this.opt.sSmileyBoxTemplate.easyReplace({ + smileyRows: this.oSmileyRowsContent.postform, + moreSmileys: this.opt.oSmileyLocations.popup.length == 0 ? '' : this.opt.sMoreSmileysTemplate.easyReplace({ + moreSmileysId: this.opt.sUniqueId + '_addMoreSmileys' + }) + })); + + // Initialize the smileys. + this.initSmileys('postform', document); + + // Initialize the [more] button. + if (this.opt.oSmileyLocations.popup.length > 0) + { + var oMoreLink = document.getElementById(this.opt.sUniqueId + '_addMoreSmileys'); + oMoreLink.instanceRef = this; + oMoreLink.onclick = function () { + this.instanceRef.handleShowMoreSmileys(); + return false; + } + } +} + +// Loop through the smileys to setup the HTML. +smc_SmileyBox.prototype.getSmileyRowsContent = function (sLocation) +{ + // If it's already defined, don't bother. + if (sLocation in this.oSmileyRowsContent) + return; + + this.oSmileyRowsContent[sLocation] = ''; + + for (var iSmileyRowIndex = 0, iSmileyRowCount = this.opt.oSmileyLocations[sLocation].length; iSmileyRowIndex < iSmileyRowCount; iSmileyRowIndex++) + { + var sSmileyRowContent = ''; + for (var iSmileyIndex = 0, iSmileyCount = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex].length; iSmileyIndex < iSmileyCount; iSmileyIndex++) + sSmileyRowContent += this.opt.sSmileyTemplate.easyReplace({ + smileySource: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sSrc.php_htmlspecialchars(), + smileyDescription: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sDescription.php_htmlspecialchars(), + smileyCode: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sCode.php_htmlspecialchars(), + smileyId: this.opt.sUniqueId + '_' + sLocation + '_' + iSmileyRowIndex.toString() + '_' + iSmileyIndex.toString() + }); + + this.oSmileyRowsContent[sLocation] += this.opt.sSmileyRowTemplate.easyReplace({ + smileyRow: sSmileyRowContent + }); + } +} + +smc_SmileyBox.prototype.initSmileys = function (sLocation, oDocument) +{ + for (var iSmileyRowIndex = 0, iSmileyRowCount = this.opt.oSmileyLocations[sLocation].length; iSmileyRowIndex < iSmileyRowCount; iSmileyRowIndex++) + { + for (var iSmileyIndex = 0, iSmileyCount = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex].length; iSmileyIndex < iSmileyCount; iSmileyIndex++) + { + var oSmiley = oDocument.getElementById(this.opt.sUniqueId + '_' + sLocation + '_' + iSmileyRowIndex.toString() + '_' + iSmileyIndex.toString()); + oSmiley.instanceRef = this; + oSmiley.style.cursor = 'pointer'; + oSmiley.onclick = function () { + this.instanceRef.clickHandler(this); + return false; + } + } + } +} + +smc_SmileyBox.prototype.clickHandler = function (oSmileyImg) +{ + // Dissect the id... + var aMatches = oSmileyImg.id.match(/([^_]+)_(\d+)_(\d+)$/); + if (aMatches.length != 4) + return false; + + // ...to determine its exact smiley properties. + var sLocation = aMatches[1]; + var iSmileyRowIndex = aMatches[2]; + var iSmileyIndex = aMatches[3]; + var oProperties = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex]; + + if ('sClickHandler' in this.opt) + eval(this.opt.sClickHandler + '(oProperties)'); + + return false; +} + +smc_SmileyBox.prototype.handleShowMoreSmileys = function () +{ + // Focus the window if it's already opened. + if (this.oSmileyPopupWindow != null && 'closed' in this.oSmileyPopupWindow && !this.oSmileyPopupWindow.closed) + { + this.oSmileyPopupWindow.focus(); + return; + } + + // Get the smiley HTML. + this.getSmileyRowsContent('popup'); + + // Open the popup. + this.oSmileyPopupWindow = window.open('about:blank', this.opt.sUniqueId + '_addMoreSmileysPopup', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,width=480,height=220,resizable=yes'); + + // Paste the template in the popup. + this.oSmileyPopupWindow.document.open('text/html', 'replace'); + this.oSmileyPopupWindow.document.write(this.opt.sMoreSmileysPopupTemplate.easyReplace({ + smileyRows: this.oSmileyRowsContent.popup, + moreSmileysCloseLinkId: this.opt.sUniqueId + '_closeMoreSmileys' + })); + this.oSmileyPopupWindow.document.close(); + + // Initialize the smileys that are in the popup window. + this.initSmileys('popup', this.oSmileyPopupWindow.document); + + // Add a function to the close window button. + var aCloseLink = this.oSmileyPopupWindow.document.getElementById(this.opt.sUniqueId + '_closeMoreSmileys'); + aCloseLink.instanceRef = this; + aCloseLink.onclick = function () { + this.instanceRef.oSmileyPopupWindow.close(); + return false; + } +} + + +// *** smc_BBCButtonBox class. +function smc_BBCButtonBox(oOptions) +{ + this.opt = oOptions; + this.init(); +} + +smc_BBCButtonBox.prototype.init = function () +{ + var sBbcContent = ''; + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + var sRowContent = ''; + var bPreviousWasDivider = false; + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurButton = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + switch (oCurButton.sType) + { + case 'button': + if (oCurButton.bEnabled) + { + sRowContent += this.opt.sButtonTemplate.easyReplace({ + buttonId: this.opt.sUniqueId.php_htmlspecialchars() + '_button_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString(), + buttonSrc: oCurButton.sImage.php_htmlspecialchars(), + buttonDescription: oCurButton.sDescription.php_htmlspecialchars() + }); + + bPreviousWasDivider = false; + } + break; + + case 'divider': + if (!bPreviousWasDivider) + sRowContent += this.opt.sDividerTemplate; + + bPreviousWasDivider = true; + break; + + case 'select': + var sOptions = ''; + + // Fighting javascript's idea of order in a for loop... :P + if ('' in oCurButton.oOptions) + sOptions = ''; + for (var sSelectValue in oCurButton.oOptions) + // we've been through this before + if (sSelectValue != '') + sOptions += ''; + + sRowContent += this.opt.sSelectTemplate.easyReplace({ + selectName: oCurButton.sName, + selectId: this.opt.sUniqueId.php_htmlspecialchars() + '_select_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString(), + selectOptions: sOptions + }); + + bPreviousWasDivider = false; + break; + } + } + sBbcContent += this.opt.sButtonRowTemplate.easyReplace({ + buttonRow: sRowContent + }); + } + + var oBbcContainer = document.getElementById(this.opt.sContainerDiv); + setInnerHTML(oBbcContainer, sBbcContent); + + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + switch (oCurControl.sType) + { + case 'button': + if (!oCurControl.bEnabled) + break; + + oCurControl.oImg = document.getElementById(this.opt.sUniqueId.php_htmlspecialchars() + '_button_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString()); + oCurControl.oImg.style.cursor = 'pointer'; + if ('sButtonBackgroundImage' in this.opt) + oCurControl.oImg.style.backgroundImage = 'url(' + this.opt.sButtonBackgroundImage + ')'; + + oCurControl.oImg.instanceRef = this; + oCurControl.oImg.onmouseover = function () { + this.instanceRef.handleButtonMouseOver(this); + }; + oCurControl.oImg.onmouseout = function () { + this.instanceRef.handleButtonMouseOut(this); + }; + oCurControl.oImg.onclick = function () { + this.instanceRef.handleButtonClick(this); + }; + + oCurControl.oImg.bIsActive = false; + oCurControl.oImg.bHover = false; + break; + + case 'select': + oCurControl.oSelect = document.getElementById(this.opt.sUniqueId.php_htmlspecialchars() + '_select_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString()); + + oCurControl.oSelect.instanceRef = this; + oCurControl.oSelect.onchange = oCurControl.onchange = function () { + this.instanceRef.handleSelectChange(this); + } + break; + } + } + } +} + +smc_BBCButtonBox.prototype.handleButtonMouseOver = function (oButtonImg) +{ + oButtonImg.bHover = true; + this.updateButtonStatus(oButtonImg); +} + +smc_BBCButtonBox.prototype.handleButtonMouseOut = function (oButtonImg) +{ + oButtonImg.bHover = false; + this.updateButtonStatus(oButtonImg); +} + +smc_BBCButtonBox.prototype.updateButtonStatus = function (oButtonImg) +{ + var sNewURL = ''; + if (oButtonImg.bHover && oButtonImg.bIsActive && 'sActiveButtonBackgroundImageHover' in this.opt) + sNewURL = 'url(' + this.opt.sActiveButtonBackgroundImageHover + ')'; + else if (!oButtonImg.bHover && oButtonImg.bIsActive && 'sActiveButtonBackgroundImage' in this.opt) + sNewURL = 'url(' + this.opt.sActiveButtonBackgroundImage + ')'; + else if (oButtonImg.bHover && 'sButtonBackgroundImageHover' in this.opt) + sNewURL = 'url(' + this.opt.sButtonBackgroundImageHover + ')'; + else if ('sButtonBackgroundImage' in this.opt) + sNewURL = 'url(' + this.opt.sButtonBackgroundImage + ')'; + + if (oButtonImg.style.backgroundImage != sNewURL) + oButtonImg.style.backgroundImage = sNewURL; +} + +smc_BBCButtonBox.prototype.handleButtonClick = function (oButtonImg) +{ + // Dissect the id attribute... + var aMatches = oButtonImg.id.match(/(\d+)_(\d+)$/); + if (aMatches.length != 3) + return false; + + // ...so that we can point to the exact button. + var iButtonRowIndex = aMatches[1]; + var iButtonIndex = aMatches[2]; + var oProperties = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + oProperties.bIsActive = oButtonImg.bIsActive; + + if ('sButtonClickHandler' in this.opt) + eval(this.opt.sButtonClickHandler + '(oProperties)'); + + return false; +} + +smc_BBCButtonBox.prototype.handleSelectChange = function (oSelectControl) +{ + // Dissect the id attribute... + var aMatches = oSelectControl.id.match(/(\d+)_(\d+)$/); + if (aMatches.length != 3) + return false; + + // ...so that we can point to the exact button. + var iButtonRowIndex = aMatches[1]; + var iButtonIndex = aMatches[2]; + var oProperties = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + + if ('sSelectChangeHandler' in this.opt) + eval(this.opt.sSelectChangeHandler + '(oProperties)'); + + return true; +} + +smc_BBCButtonBox.prototype.setActive = function (aButtons) +{ + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + if (oCurControl.sType == 'button' && oCurControl.bEnabled) + { + oCurControl.oImg.bIsActive = in_array(oCurControl.sCode, aButtons); + this.updateButtonStatus(oCurControl.oImg); + } + } + } +} + +smc_BBCButtonBox.prototype.emulateClick = function (sCode) +{ + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + if (oCurControl.sType == 'button' && oCurControl.sCode == sCode) + { + eval(this.opt.sButtonClickHandler + '(oCurControl)'); + return true; + } + } + } + return false; +} + +smc_BBCButtonBox.prototype.setSelect = function (sSelectName, sValue) +{ + if (!('sButtonClickHandler' in this.opt)) + return; + + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + if (oCurControl.sType == 'select' && oCurControl.sName == sSelectName) + oCurControl.oSelect.value = sValue; + } + } +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/fader.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/fader.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,217 @@ +function smf_NewsFader(oOptions) +{ + this.opt = oOptions; + + this.oFaderHandle = document.getElementById(this.opt.sFaderControlId); + + // Fade from... what text color? Default to black. + this.oFadeFrom = 'oFadeFrom' in this.opt ? this.opt.oFadeFrom : { + r: 0, + g: 0, + b: 0 + }; + + // To which background color? Default to white. + this.oFadeTo = 'oFadeTo' in this.opt ? this.opt.oFadeTo : { + r: 255, + g: 255, + b: 255 + }; + + // Surround each item with... anything special? + this.sItemTemplate = 'sItemTemplate' in this.opt ? this.opt.sItemTemplate : '%1$s'; + + // Fade delay (in milliseconds). + this.iFadeDelay = 'iFadeDelay' in this.opt ? this.opt.iFadeDelay : 5000; + + // The array that contains all the lines of the news for display. + this.aFaderItems = 'aFaderItems' in this.opt ? this.opt.aFaderItems : []; + + // Should we look for fader data, still? + this.bReceivedItemsOnConstruction = 'aFaderItems' in this.opt; + + // The current item in smfFadeContent. + this.iFadeIndex = -1; + + // Percent of fade (-64 to 510). + this.iFadePercent = 510 + + // Direction (in or out). + this.bFadeSwitch = false; + + // Just make sure the page is loaded before calling the init. + setTimeout(this.opt.sSelf + '.init();', 1); +} + +smf_NewsFader.prototype.init = function init() +{ + var oForeEl, oForeColor, oBackEl, oBackColor; + + // Try to find the fore- and background colors. + if ('currentStyle' in this.oFaderHandle) + { + oForeColor = this.oFaderHandle.currentStyle.color.match(/#([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/); + this.oFadeFrom = { + r: parseInt(oForeColor[1]), + g: parseInt(oForeColor[2]), + b: parseInt(oForeColor[3]) + }; + + oBackEl = this.oFaderHandle; + while (oBackEl.currentStyle.backgroundColor == 'transparent' && 'parentNode' in oBackEl) + oBackEl = oBackEl.parentNode; + + oBackColor = oBackEl.currentStyle.backgroundColor.match(/#([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/); + this.oFadeTo = { + r: eval('0x' + oBackColor[1]), + g: eval('0x' + oBackColor[2]), + b: eval('0x' + oBackColor[3]) + }; + } + else if (!('opera' in window) && 'defaultView' in document) + { + oForeEl = this.oFaderHandle; + while (document.defaultView.getComputedStyle(oForeEl, null).getPropertyCSSValue('color') == null && 'parentNode' in oForeEl && 'tagName' in oForeEl.parentNode) + oForeEl = oForeEl.parentNode; + + oForeColor = document.defaultView.getComputedStyle(oForeEl, null).getPropertyValue('color').match(/rgb\((\d+), (\d+), (\d+)\)/); + this.oFadeFrom = { + r: parseInt(oForeColor[1]), + g: parseInt(oForeColor[2]), + b: parseInt(oForeColor[3]) + }; + + oBackEl = this.oFaderHandle; + while (document.defaultView.getComputedStyle(oBackEl, null).getPropertyCSSValue('background-color') == null && 'parentNode' in oBackEl && 'tagName' in oBackEl.parentNode) + oBackEl = oBackEl.parentNode; + + oBackColor = document.defaultView.getComputedStyle(oBackEl, null).getPropertyValue('background-color'); + this.oFadeTo = { + r: parseInt(oBackColor[1]), + g: parseInt(oBackColor[2]), + b: parseInt(oBackColor[3]) + }; + } + + // Did we get our fader items on construction, or should we be gathering them instead? + if (!this.bReceivedItemsOnConstruction) + { + // Get the news from the list in boardindex + var oNewsItems = this.oFaderHandle.getElementsByTagName('li'); + + // Fill the array that has previously been created + for (var i = 0, n = oNewsItems.length; i < n; i ++) + this.aFaderItems[i] = oNewsItems[i].innerHTML; + } + + // The ranges to fade from for R, G, and B. (how far apart they are.) + this.oFadeRange = { + 'r': this.oFadeFrom.r - this.oFadeTo.r, + 'g': this.oFadeFrom.g - this.oFadeTo.g, + 'b': this.oFadeFrom.b - this.oFadeTo.b + }; + + // Divide by 20 because we are doing it 20 times per one ms. + this.iFadeDelay /= 20; + + // Start the fader! + window.setTimeout(this.opt.sSelf + '.fade();', 20); +} + +// Main fading function... called 50 times every second. +smf_NewsFader.prototype.fade = function fade() +{ + if (this.aFaderItems.length <= 1) + return; + + // A fix for Internet Explorer 4: wait until the document is loaded so we can use setInnerHTML(). + if ('readyState' in document && document.readyState != 'complete') + { + window.setTimeout(this.opt.sSelf + '.fade();', 20); + return; + } + + // Starting out? Set up the first item. + if (this.iFadeIndex == -1) + { + setInnerHTML(this.oFaderHandle, this.sItemTemplate.replace('%1$s', this.aFaderItems[0])); + this.iFadeIndex = 1; + + // In Mozilla, text jumps around from this when 1 or 0.5, etc... + if ('MozOpacity' in this.oFaderHandle.style) + this.oFaderHandle.style.MozOpacity = '0.90'; + else if ('opacity' in this.oFaderHandle.style) + this.oFaderHandle.style.opacity = '0.90'; + // In Internet Explorer, we have to define this to use it. + else if ('filter' in this.oFaderHandle.style) + this.oFaderHandle.style.filter = 'alpha(opacity=100)'; + } + + // Are we already done fading in? If so, fade out. + if (this.iFadePercent >= 510) + this.bFadeSwitch = !this.bFadeSwitch; + + // All the way faded out? + else if (this.iFadePercent <= -64) + { + this.bFadeSwitch = !this.bFadeSwitch; + + // Go to the next item, or first if we're out of items. + setInnerHTML(this.oFaderHandle, this.sItemTemplate.replace('%1$s', this.aFaderItems[this.iFadeIndex ++])); + if (this.iFadeIndex >= this.aFaderItems.length) + this.iFadeIndex = 0; + } + + // Increment or decrement the fade percentage. + if (this.bFadeSwitch) + this.iFadePercent -= 255 / this.iFadeDelay * 2; + else + this.iFadePercent += 255 / this.iFadeDelay * 2; + + // If it's not outside 0 and 256... (otherwise it's just delay time.) + if (this.iFadePercent < 256 && this.iFadePercent > 0) + { + // Easier... also faster... + var tempPercent = this.iFadePercent / 255, rounded; + + if ('MozOpacity' in this.oFaderHandle.style) + { + rounded = Math.round(tempPercent * 100) / 100; + this.oFaderHandle.style.MozOpacity = rounded == 1 ? '0.99' : rounded; + } + else if ('opacity' in this.oFaderHandle.style) + { + rounded = Math.round(tempPercent * 100) / 100; + this.oFaderHandle.style.opacity = rounded == 1 ? '0.99' : rounded; + } + else + { + var done = false; + if ('alpha' in this.oFaderHandle.filters) + { + try + { + this.oFaderHandle.filters.alpha.opacity = Math.round(tempPercent * 100); + done = true; + } + catch (err) + { + } + } + + if (!done) + { + // Get the new R, G, and B. (it should be bottom + (range of color * percent)...) + var r = Math.ceil(this.oFadeTo.r + this.oFadeRange.r * tempPercent); + var g = Math.ceil(this.oFadeTo.g + this.oFadeRange.g * tempPercent); + var b = Math.ceil(this.oFadeTo.b + this.oFadeRange.b * tempPercent); + + // Set the color in the style, thereby fading it. + this.oFaderHandle.style.color = 'rgb(' + r + ', ' + g + ', ' + b + ')'; + } + } + } + + // Keep going. + window.setTimeout(this.opt.sSelf + '.fade();', 20); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/profile.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/profile.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,40 @@ +var localTime = new Date(); +function autoDetectTimeOffset(currentTime) +{ + if (typeof(currentTime) != 'string') + var serverTime = currentTime; + else + var serverTime = new Date(currentTime); + + // Something wrong? + if (!localTime.getTime() || !serverTime.getTime()) + return 0; + + // Get the difference between the two, set it up so that the sign will tell us who is ahead of who. + var diff = Math.round((localTime.getTime() - serverTime.getTime())/3600000); + + // Make sure we are limiting this to one day's difference. + diff %= 24; + + return diff; +} + +// Prevent Chrome from auto completing fields when viewing/editing other members profiles +function disableAutoComplete() +{ + if (is_chrome && document.addEventListener) + document.addEventListener("DOMContentLoaded", disableAutoCompleteNow, false); +} + +// Once DOMContentLoaded is triggered, call the function +function disableAutoCompleteNow() +{ + for (var i = 0, n = document.forms.length; i < n; i++) + { + var die = document.forms[i].elements; + for (var j = 0, m = die.length; j < m; j++) + // Only bother with text/password fields? + if (die[j].type == "text" || die[j].type == "password") + die[j].setAttribute("autocomplete", "off"); + } +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/register.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/register.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,266 @@ +function smfRegister(formID, passwordDifficultyLevel, regTextStrings) +{ + this.addVerify = addVerificationField; + this.autoSetup = autoSetup; + this.refreshMainPassword = refreshMainPassword; + this.refreshVerifyPassword = refreshVerifyPassword; + + var verificationFields = new Array(); + var verificationFieldLength = 0; + var textStrings = regTextStrings ? regTextStrings : new Array(); + var passwordLevel = passwordDifficultyLevel ? passwordDifficultyLevel : 0; + + // Setup all the fields! + autoSetup(formID); + + // This is a field which requires some form of verification check. + function addVerificationField(fieldType, fieldID) + { + // Check the field exists. + if (!document.getElementById(fieldID)) + return; + + // Get the handles. + var inputHandle = document.getElementById(fieldID); + var imageHandle = document.getElementById(fieldID + '_img') ? document.getElementById(fieldID + '_img') : false; + var divHandle = document.getElementById(fieldID + '_div') ? document.getElementById(fieldID + '_div') : false; + + // What is the event handler? + var eventHandler = false; + if (fieldType == 'pwmain') + eventHandler = refreshMainPassword; + else if (fieldType == 'pwverify') + eventHandler = refreshVerifyPassword; + else if (fieldType == 'username') + eventHandler = refreshUsername; + else if (fieldType == 'reserved') + eventHandler = refreshMainPassword; + + // Store this field. + var vFieldIndex = fieldType == 'reserved' ? fieldType + verificationFieldLength : fieldType; + verificationFields[vFieldIndex] = Array(6); + verificationFields[vFieldIndex][0] = fieldID; + verificationFields[vFieldIndex][1] = inputHandle; + verificationFields[vFieldIndex][2] = imageHandle; + verificationFields[vFieldIndex][3] = divHandle; + verificationFields[vFieldIndex][4] = fieldType; + verificationFields[vFieldIndex][5] = inputHandle.className; + + // Keep a count to it! + verificationFieldLength++; + + // Step to it! + if (eventHandler) + { + createEventListener(inputHandle); + inputHandle.addEventListener('keyup', eventHandler, false); + eventHandler(); + + // Username will auto check on blur! + inputHandle.addEventListener('blur', autoCheckUsername, false); + } + + // Make the div visible! + if (divHandle) + divHandle.style.display = ''; + } + + // A button to trigger a username search? + function addUsernameSearchTrigger(elementID) + { + var buttonHandle = document.getElementById(elementID); + + // Attach the event to this element. + createEventListener(buttonHandle); + buttonHandle.addEventListener('click', checkUsername, false); + } + + // This function will automatically pick up all the necessary verification fields and initialise their visual status. + function autoSetup(formID) + { + if (!document.getElementById(formID)) + return false; + + var curElement, curType; + for (var i = 0, n = document.getElementById(formID).elements.length; i < n; i++) + { + curElement = document.getElementById(formID).elements[i]; + + // Does the ID contain the keyword 'autov'? + if (curElement.id.indexOf('autov') != -1 && (curElement.type == 'text' || curElement.type == 'password')) + { + // This is probably it - but does it contain a field type? + curType = 0; + // Username can only be done with XML. + if (curElement.id.indexOf('username') != -1 && window.XMLHttpRequest) + curType = 'username'; + else if (curElement.id.indexOf('pwmain') != -1) + curType = 'pwmain'; + else if (curElement.id.indexOf('pwverify') != -1) + curType = 'pwverify'; + // This means this field is reserved and cannot be contained in the password! + else if (curElement.id.indexOf('reserve') != -1) + curType = 'reserved'; + + // If we're happy let's add this element! + if (curType) + addVerificationField(curType, curElement.id); + + // If this is the username do we also have a button to find the user? + if (curType == 'username' && document.getElementById(curElement.id + '_link')) + { + addUsernameSearchTrigger(curElement.id + '_link'); + } + } + } + + return true; + } + + // What is the password state? + function refreshMainPassword(called_from_verify) + { + if (!verificationFields['pwmain']) + return false; + + var curPass = verificationFields['pwmain'][1].value; + var stringIndex = ''; + + // Is it a valid length? + if ((curPass.length < 8 && passwordLevel >= 1) || curPass.length < 4) + stringIndex = 'password_short'; + + // More than basic? + if (passwordLevel >= 1) + { + // If there is a username check it's not in the password! + if (verificationFields['username'] && verificationFields['username'][1].value && curPass.indexOf(verificationFields['username'][1].value) != -1) + stringIndex = 'password_reserved'; + + // Any reserved fields? + for (var i in verificationFields) + { + if (verificationFields[i][4] == 'reserved' && verificationFields[i][1].value && curPass.indexOf(verificationFields[i][1].value) != -1) + stringIndex = 'password_reserved'; + } + + // Finally - is it hard and as such requiring mixed cases and numbers? + if (passwordLevel > 1) + { + if (curPass == curPass.toLowerCase()) + stringIndex = 'password_numbercase'; + if (!curPass.match(/(\D\d|\d\D)/)) + stringIndex = 'password_numbercase'; + } + } + + var isValid = stringIndex == '' ? true : false; + if (stringIndex == '') + stringIndex = 'password_valid'; + + // Set the image. + setVerificationImage(verificationFields['pwmain'][2], isValid, textStrings[stringIndex] ? textStrings[stringIndex] : ''); + verificationFields['pwmain'][1].className = verificationFields['pwmain'][5] + ' ' + (isValid ? 'valid_input' : 'invalid_input'); + + // As this has changed the verification one may have too! + if (verificationFields['pwverify'] && !called_from_verify) + refreshVerifyPassword(); + + return isValid; + } + + // Check that the verification password matches the main one! + function refreshVerifyPassword() + { + // Can't do anything without something to check again! + if (!verificationFields['pwmain']) + return false; + + // Check and set valid status! + var isValid = verificationFields['pwmain'][1].value == verificationFields['pwverify'][1].value && refreshMainPassword(true); + var alt = textStrings[isValid == 1 ? 'password_valid' : 'password_no_match'] ? textStrings[isValid == 1 ? 'password_valid' : 'password_no_match'] : ''; + setVerificationImage(verificationFields['pwverify'][2], isValid, alt); + verificationFields['pwverify'][1].className = verificationFields['pwverify'][5] + ' ' + (isValid ? 'valid_input' : 'invalid_input'); + + return true; + } + + // If the username is changed just revert the status of whether it's valid! + function refreshUsername() + { + if (!verificationFields['username']) + return false; + + // Restore the class name. + if (verificationFields['username'][1].className) + verificationFields['username'][1].className = verificationFields['username'][5]; + // Check the image is correct. + var alt = textStrings['username_check'] ? textStrings['username_check'] : ''; + setVerificationImage(verificationFields['username'][2], 'check', alt); + + // Check the password is still OK. + refreshMainPassword(); + + return true; + } + + // This is a pass through function that ensures we don't do any of the AJAX notification stuff. + function autoCheckUsername() + { + checkUsername(true); + } + + // Check whether the username exists? + function checkUsername(is_auto) + { + if (!verificationFields['username']) + return false; + + // Get the username and do nothing without one! + var curUsername = verificationFields['username'][1].value; + if (!curUsername) + return false; + + if (!is_auto) + ajax_indicator(true); + + // Request a search on that username. + checkName = curUsername.php_to8bit().php_urlencode(); + getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=register;sa=usernamecheck;xml;username=' + checkName, checkUsernameCallback); + + return true; + } + + // Callback for getting the username data. + function checkUsernameCallback(XMLDoc) + { + if (XMLDoc.getElementsByTagName("username")) + isValid = XMLDoc.getElementsByTagName("username")[0].getAttribute("valid"); + else + isValid = true; + + // What to alt? + var alt = textStrings[isValid == 1 ? 'username_valid' : 'username_invalid'] ? textStrings[isValid == 1 ? 'username_valid' : 'username_invalid'] : ''; + + verificationFields['username'][1].className = verificationFields['username'][5] + ' ' + (isValid == 1 ? 'valid_input' : 'invalid_input'); + setVerificationImage(verificationFields['username'][2], isValid == 1, alt); + + ajax_indicator(false); + } + + // Set the image to be the correct type. + function setVerificationImage(imageHandle, imageIcon, alt) + { + if (!imageHandle) + return false; + if (!alt) + alt = '*'; + + var curImage = imageIcon ? (imageIcon == 'check' ? 'field_check.gif' : 'field_valid.gif') : 'field_invalid.gif'; + imageHandle.src = smf_images_url + '/icons/' + curImage; + imageHandle.alt = alt; + imageHandle.title = alt; + + return true; + } +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/script.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/script.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1412 @@ +var smf_formSubmitted = false; +var lastKeepAliveCheck = new Date().getTime(); +var smf_editorArray = new Array(); + +// Some very basic browser detection - from Mozilla's sniffer page. +var ua = navigator.userAgent.toLowerCase(); + +var is_opera = ua.indexOf('opera') != -1; +var is_opera5 = ua.indexOf('opera/5') != -1 || ua.indexOf('opera 5') != -1; +var is_opera6 = ua.indexOf('opera/6') != -1 || ua.indexOf('opera 6') != -1; +var is_opera7 = ua.indexOf('opera/7') != -1 || ua.indexOf('opera 7') != -1; +var is_opera8 = ua.indexOf('opera/8') != -1 || ua.indexOf('opera 8') != -1; +var is_opera9 = ua.indexOf('opera/9') != -1 || ua.indexOf('opera 9') != -1; +var is_opera95 = ua.indexOf('opera/9.5') != -1 || ua.indexOf('opera 9.5') != -1; +var is_opera96 = ua.indexOf('opera/9.6') != -1 || ua.indexOf('opera 9.6') != -1; +var is_opera10 = (ua.indexOf('opera/9.8') != -1 || ua.indexOf('opera 9.8') != -1 || ua.indexOf('opera/10.') != -1 || ua.indexOf('opera 10.') != -1) || ua.indexOf('version/10.') != -1; +var is_opera95up = is_opera95 || is_opera96 || is_opera10; + +var is_ff = (ua.indexOf('firefox') != -1 || ua.indexOf('iceweasel') != -1 || ua.indexOf('icecat') != -1 || ua.indexOf('shiretoko') != -1 || ua.indexOf('minefield') != -1) && !is_opera; +var is_gecko = ua.indexOf('gecko') != -1 && !is_opera; + +var is_chrome = ua.indexOf('chrome') != -1; +var is_safari = ua.indexOf('applewebkit') != -1 && !is_chrome; +var is_webkit = ua.indexOf('applewebkit') != -1; + +var is_ie = ua.indexOf('msie') != -1 && !is_opera; +var is_ie4 = is_ie && ua.indexOf('msie 4') != -1; +var is_ie5 = is_ie && ua.indexOf('msie 5') != -1; +var is_ie50 = is_ie && ua.indexOf('msie 5.0') != -1; +var is_ie55 = is_ie && ua.indexOf('msie 5.5') != -1; +var is_ie5up = is_ie && !is_ie4; +var is_ie6 = is_ie && ua.indexOf('msie 6') != -1; +var is_ie6up = is_ie5up && !is_ie55 && !is_ie5; +var is_ie6down = is_ie6 || is_ie5 || is_ie4; +var is_ie7 = is_ie && ua.indexOf('msie 7') != -1; +var is_ie7up = is_ie6up && !is_ie6; +var is_ie7down = is_ie7 || is_ie6 || is_ie5 || is_ie4; + +var is_ie8 = is_ie && ua.indexOf('msie 8') != -1; +var is_ie8up = is_ie8 && !is_ie7down; + +var is_iphone = ua.indexOf('iphone') != -1 || ua.indexOf('ipod') != -1; +var is_android = ua.indexOf('android') != -1; + +var ajax_indicator_ele = null; + +// Define document.getElementById for Internet Explorer 4. +if (!('getElementById' in document) && 'all' in document) + document.getElementById = function (sId) { + return document.all[sId]; + } + +// Define XMLHttpRequest for IE 5 and above. (don't bother for IE 4 :/.... works in Opera 7.6 and Safari 1.2!) +else if (!('XMLHttpRequest' in window) && 'ActiveXObject' in window) + window.XMLHttpRequest = function () { + return new ActiveXObject(is_ie5 ? 'Microsoft.XMLHTTP' : 'MSXML2.XMLHTTP'); + }; + +// Ensure the getElementsByTagName exists. +if (!'getElementsByTagName' in document && 'all' in document) + document.getElementsByTagName = function (sName) { + return document.all.tags[sName]; + } + +// Some older versions of Mozilla don't have this, for some reason. +if (!('forms' in document)) + document.forms = document.getElementsByTagName('form'); + +// Load an XML document using XMLHttpRequest. +function getXMLDocument(sUrl, funcCallback) +{ + if (!window.XMLHttpRequest) + return null; + + var oMyDoc = new XMLHttpRequest(); + var bAsync = typeof(funcCallback) != 'undefined'; + var oCaller = this; + if (bAsync) + { + oMyDoc.onreadystatechange = function () { + if (oMyDoc.readyState != 4) + return; + + if (oMyDoc.responseXML != null && oMyDoc.status == 200) + { + if (funcCallback.call) + { + funcCallback.call(oCaller, oMyDoc.responseXML); + } + // A primitive substitute for the call method to support IE 5.0. + else + { + oCaller.tmpMethod = funcCallback; + oCaller.tmpMethod(oMyDoc.responseXML); + delete oCaller.tmpMethod; + } + } + }; + } + oMyDoc.open('GET', sUrl, bAsync); + oMyDoc.send(null); + + return oMyDoc; +} + +// Send a post form to the server using XMLHttpRequest. +function sendXMLDocument(sUrl, sContent, funcCallback) +{ + if (!window.XMLHttpRequest) + return false; + + var oSendDoc = new window.XMLHttpRequest(); + var oCaller = this; + if (typeof(funcCallback) != 'undefined') + { + oSendDoc.onreadystatechange = function () { + if (oSendDoc.readyState != 4) + return; + + if (oSendDoc.responseXML != null && oSendDoc.status == 200) + funcCallback.call(oCaller, oSendDoc.responseXML); + else + funcCallback.call(oCaller, false); + }; + } + oSendDoc.open('POST', sUrl, true); + if ('setRequestHeader' in oSendDoc) + oSendDoc.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + oSendDoc.send(sContent); + + return true; +} + +// A property we'll be needing for php_to8bit. +String.prototype.oCharsetConversion = { + from: '', + to: '' +}; + +// Convert a string to an 8 bit representation (like in PHP). +String.prototype.php_to8bit = function () +{ + if (smf_charset == 'UTF-8') + { + var n, sReturn = ''; + + for (var i = 0, iTextLen = this.length; i < iTextLen; i++) + { + n = this.charCodeAt(i); + if (n < 128) + sReturn += String.fromCharCode(n) + else if (n < 2048) + sReturn += String.fromCharCode(192 | n >> 6) + String.fromCharCode(128 | n & 63); + else if (n < 65536) + sReturn += String.fromCharCode(224 | n >> 12) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); + else + sReturn += String.fromCharCode(240 | n >> 18) + String.fromCharCode(128 | n >> 12 & 63) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); + } + + return sReturn; + } + + else if (this.oCharsetConversion.from.length == 0) + { + switch (smf_charset) + { + case 'ISO-8859-1': + this.oCharsetConversion = { + from: '\xa0-\xff', + to: '\xa0-\xff' + }; + break; + + case 'ISO-8859-2': + this.oCharsetConversion = { + from: '\xa0\u0104\u02d8\u0141\xa4\u013d\u015a\xa7\xa8\u0160\u015e\u0164\u0179\xad\u017d\u017b\xb0\u0105\u02db\u0142\xb4\u013e\u015b\u02c7\xb8\u0161\u015f\u0165\u017a\u02dd\u017e\u017c\u0154\xc1\xc2\u0102\xc4\u0139\u0106\xc7\u010c\xc9\u0118\xcb\u011a\xcd\xce\u010e\u0110\u0143\u0147\xd3\xd4\u0150\xd6\xd7\u0158\u016e\xda\u0170\xdc\xdd\u0162\xdf\u0155\xe1\xe2\u0103\xe4\u013a\u0107\xe7\u010d\xe9\u0119\xeb\u011b\xed\xee\u010f\u0111\u0144\u0148\xf3\xf4\u0151\xf6\xf7\u0159\u016f\xfa\u0171\xfc\xfd\u0163\u02d9', + to: '\xa0-\xff' + }; + break; + + case 'ISO-8859-5': + this.oCharsetConversion = { + from: '\xa0\u0401-\u040c\xad\u040e-\u044f\u2116\u0451-\u045c\xa7\u045e\u045f', + to: '\xa0-\xff' + }; + break; + + case 'ISO-8859-9': + this.oCharsetConversion = { + from: '\xa0-\xcf\u011e\xd1-\xdc\u0130\u015e\xdf-\xef\u011f\xf1-\xfc\u0131\u015f\xff', + to: '\xa0-\xff' + }; + break; + + case 'ISO-8859-15': + this.oCharsetConversion = { + from: '\xa0-\xa3\u20ac\xa5\u0160\xa7\u0161\xa9-\xb3\u017d\xb5-\xb7\u017e\xb9-\xbb\u0152\u0153\u0178\xbf-\xff', + to: '\xa0-\xff' + }; + break; + + case 'tis-620': + this.oCharsetConversion = { + from: '\u20ac\u2026\u2018\u2019\u201c\u201d\u2022\u2013\u2014\xa0\u0e01-\u0e3a\u0e3f-\u0e5b', + to: '\x80\x85\x91-\x97\xa0-\xda\xdf-\xfb' + }; + break; + + case 'windows-1251': + this.oCharsetConversion = { + from: '\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\u040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u0459\u203a\u045a\u045c\u045b\u045f\xa0\u040e\u045e\u0408\xa4\u0490\xa6\xa7\u0401\xa9\u0404\xab-\xae\u0407\xb0\xb1\u0406\u0456\u0491\xb5-\xb7\u0451\u2116\u0454\xbb\u0458\u0405\u0455\u0457\u0410-\u044f', + to: '\x80-\x97\x99-\xff' + }; + break; + + case 'windows-1253': + this.oCharsetConversion = { + from: '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u203a\xa0\u0385\u0386\xa3-\xa9\xab-\xae\u2015\xb0-\xb3\u0384\xb5-\xb7\u0388-\u038a\xbb\u038c\xbd\u038e-\u03a1\u03a3-\u03ce', + to: '\x80\x82-\x87\x89\x8b\x91-\x97\x99\x9b\xa0-\xa9\xab-\xd1\xd3-\xfe' + }; + break; + + case 'windows-1255': + this.oCharsetConversion = { + from: '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u203a\xa0-\xa3\u20aa\xa5-\xa9\xd7\xab-\xb9\xf7\xbb-\xbf\u05b0-\u05b9\u05bb-\u05c3\u05f0-\u05f4\u05d0-\u05ea\u200e\u200f', + to: '\x80\x82-\x89\x8b\x91-\x99\x9b\xa0-\xc9\xcb-\xd8\xe0-\xfa\xfd\xfe' + }; + break; + + case 'windows-1256': + this.oCharsetConversion = { + from: '\u20ac\u067e\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0679\u2039\u0152\u0686\u0698\u0688\u06af\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u06a9\u2122\u0691\u203a\u0153\u200c\u200d\u06ba\xa0\u060c\xa2-\xa9\u06be\xab-\xb9\u061b\xbb-\xbe\u061f\u06c1\u0621-\u0636\xd7\u0637-\u063a\u0640-\u0643\xe0\u0644\xe2\u0645-\u0648\xe7-\xeb\u0649\u064a\xee\xef\u064b-\u064e\xf4\u064f\u0650\xf7\u0651\xf9\u0652\xfb\xfc\u200e\u200f\u06d2', + to: '\x80-\xff' + }; + break; + + default: + this.oCharsetConversion = { + from: '', + to: '' + }; + break; + } + var funcExpandString = function (sSearch) { + var sInsert = ''; + for (var i = sSearch.charCodeAt(0), n = sSearch.charCodeAt(2); i <= n; i++) + sInsert += String.fromCharCode(i); + return sInsert; + }; + this.oCharsetConversion.from = this.oCharsetConversion.from.replace(/.\-./g, funcExpandString); + this.oCharsetConversion.to = this.oCharsetConversion.to.replace(/.\-./g, funcExpandString); + } + + var sReturn = '', iOffsetFrom = 0; + for (var i = 0, n = this.length; i < n; i++) + { + iOffsetFrom = this.oCharsetConversion.from.indexOf(this.charAt(i)); + sReturn += iOffsetFrom > -1 ? this.oCharsetConversion.to.charAt(iOffsetFrom) : (this.charCodeAt(i) > 127 ? '&#' + this.charCodeAt(i) + ';' : this.charAt(i)); + } + + return sReturn +} + +// Character-level replacement function. +String.prototype.php_strtr = function (sFrom, sTo) +{ + return this.replace(new RegExp('[' + sFrom + ']', 'g'), function (sMatch) { + return sTo.charAt(sFrom.indexOf(sMatch)); + }); +} + +// Simulate PHP's strtolower (in SOME cases PHP uses ISO-8859-1 case folding). +String.prototype.php_strtolower = function () +{ + return typeof(smf_iso_case_folding) == 'boolean' && smf_iso_case_folding == true ? this.php_strtr( + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ\x8a\x8c\x8e\x9f\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde', + 'abcdefghijklmnopqrstuvwxyz\x9a\x9c\x9e\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe' + ) : this.php_strtr('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); +} + +String.prototype.php_urlencode = function() +{ + return escape(this).replace(/\+/g, '%2b').replace('*', '%2a').replace('/', '%2f').replace('@', '%40'); +} + +String.prototype.php_htmlspecialchars = function() +{ + return this.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +} + +String.prototype.php_unhtmlspecialchars = function() +{ + return this.replace(/"/g, '"').replace(/>/g, '>').replace(/</g, '<').replace(/&/g, '&'); +} + +String.prototype.php_addslashes = function() +{ + return this.replace(/\\/g, '\\\\').replace(/'/g, '\\\''); +} + +String.prototype._replaceEntities = function(sInput, sDummy, sNum) +{ + return String.fromCharCode(parseInt(sNum)); +} + +String.prototype.removeEntities = function() +{ + return this.replace(/&(amp;)?#(\d+);/g, this._replaceEntities); +} + +String.prototype.easyReplace = function (oReplacements) +{ + var sResult = this; + for (var sSearch in oReplacements) + sResult = sResult.replace(new RegExp('%' + sSearch + '%', 'g'), oReplacements[sSearch]); + + return sResult; +} + + +// Open a new window. +function reqWin(desktopURL, alternateWidth, alternateHeight, noScrollbars) +{ + if ((alternateWidth && self.screen.availWidth * 0.8 < alternateWidth) || (alternateHeight && self.screen.availHeight * 0.8 < alternateHeight)) + { + noScrollbars = false; + alternateWidth = Math.min(alternateWidth, self.screen.availWidth * 0.8); + alternateHeight = Math.min(alternateHeight, self.screen.availHeight * 0.8); + } + else + noScrollbars = typeof(noScrollbars) == 'boolean' && noScrollbars == true; + + window.open(desktopURL, 'requested_popup', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=' + (noScrollbars ? 'no' : 'yes') + ',width=' + (alternateWidth ? alternateWidth : 480) + ',height=' + (alternateHeight ? alternateHeight : 220) + ',resizable=no'); + + // Return false so the click won't follow the link ;). + return false; +} + +// Remember the current position. +function storeCaret(oTextHandle) +{ + // Only bother if it will be useful. + if ('createTextRange' in oTextHandle) + oTextHandle.caretPos = document.selection.createRange().duplicate(); +} + +// Replaces the currently selected text with the passed text. +function replaceText(text, oTextHandle) +{ + // Attempt to create a text range (IE). + if ('caretPos' in oTextHandle && 'createTextRange' in oTextHandle) + { + var caretPos = oTextHandle.caretPos; + + caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text; + caretPos.select(); + } + // Mozilla text range replace. + else if ('selectionStart' in oTextHandle) + { + var begin = oTextHandle.value.substr(0, oTextHandle.selectionStart); + var end = oTextHandle.value.substr(oTextHandle.selectionEnd); + var scrollPos = oTextHandle.scrollTop; + + oTextHandle.value = begin + text + end; + + if (oTextHandle.setSelectionRange) + { + oTextHandle.focus(); + var goForward = is_opera ? text.match(/\n/g).length : 0; + oTextHandle.setSelectionRange(begin.length + text.length + goForward, begin.length + text.length + goForward); + } + oTextHandle.scrollTop = scrollPos; + } + // Just put it on the end. + else + { + oTextHandle.value += text; + oTextHandle.focus(oTextHandle.value.length - 1); + } +} + +// Surrounds the selected text with text1 and text2. +function surroundText(text1, text2, oTextHandle) +{ + // Can a text range be created? + if ('caretPos' in oTextHandle && 'createTextRange' in oTextHandle) + { + var caretPos = oTextHandle.caretPos, temp_length = caretPos.text.length; + + caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text1 + caretPos.text + text2 + ' ' : text1 + caretPos.text + text2; + + if (temp_length == 0) + { + caretPos.moveStart('character', -text2.length); + caretPos.moveEnd('character', -text2.length); + caretPos.select(); + } + else + oTextHandle.focus(caretPos); + } + // Mozilla text range wrap. + else if ('selectionStart' in oTextHandle) + { + var begin = oTextHandle.value.substr(0, oTextHandle.selectionStart); + var selection = oTextHandle.value.substr(oTextHandle.selectionStart, oTextHandle.selectionEnd - oTextHandle.selectionStart); + var end = oTextHandle.value.substr(oTextHandle.selectionEnd); + var newCursorPos = oTextHandle.selectionStart; + var scrollPos = oTextHandle.scrollTop; + + oTextHandle.value = begin + text1 + selection + text2 + end; + + if (oTextHandle.setSelectionRange) + { + var goForward = is_opera ? text1.match(/\n/g).length : 0, goForwardAll = is_opera ? (text1 + text2).match(/\n/g).length : 0; + if (selection.length == 0) + oTextHandle.setSelectionRange(newCursorPos + text1.length + goForward, newCursorPos + text1.length + goForward); + else + oTextHandle.setSelectionRange(newCursorPos, newCursorPos + text1.length + selection.length + text2.length + goForwardAll); + oTextHandle.focus(); + } + oTextHandle.scrollTop = scrollPos; + } + // Just put them on the end, then. + else + { + oTextHandle.value += text1 + text2; + oTextHandle.focus(oTextHandle.value.length - 1); + } +} + +// Checks if the passed input's value is nothing. +function isEmptyText(theField) +{ + // Copy the value so changes can be made.. + var theValue = theField.value; + + // Strip whitespace off the left side. + while (theValue.length > 0 && (theValue.charAt(0) == ' ' || theValue.charAt(0) == '\t')) + theValue = theValue.substring(1, theValue.length); + // Strip whitespace off the right side. + while (theValue.length > 0 && (theValue.charAt(theValue.length - 1) == ' ' || theValue.charAt(theValue.length - 1) == '\t')) + theValue = theValue.substring(0, theValue.length - 1); + + if (theValue == '') + return true; + else + return false; +} + +// Only allow form submission ONCE. +function submitonce(theform) +{ + smf_formSubmitted = true; + + // If there are any editors warn them submit is coming! + for (var i = 0; i < smf_editorArray.length; i++) + smf_editorArray[i].doSubmit(); +} +function submitThisOnce(oControl) +{ + // Hateful, hateful fix for Safari 1.3 beta. + if (is_safari) + return !smf_formSubmitted; + + // oControl might also be a form. + var oForm = 'form' in oControl ? oControl.form : oControl; + + var aTextareas = oForm.getElementsByTagName('textarea'); + for (var i = 0, n = aTextareas.length; i < n; i++) + aTextareas[i].readOnly = true; + + return !smf_formSubmitted; +} + +// Deprecated, as innerHTML is supported everywhere. +function setInnerHTML(oElement, sToValue) +{ + oElement.innerHTML = sToValue; +} + +function getInnerHTML(oElement) +{ + return oElement.innerHTML; +} + +// Set the "outer" HTML of an element. +function setOuterHTML(oElement, sToValue) +{ + if ('outerHTML' in oElement) + oElement.outerHTML = sToValue; + else + { + var range = document.createRange(); + range.setStartBefore(oElement); + oElement.parentNode.replaceChild(range.createContextualFragment(sToValue), oElement); + } +} + +// Checks for variable in theArray. +function in_array(variable, theArray) +{ + for (var i in theArray) + if (theArray[i] == variable) + return true; + + return false; +} + +// Checks for variable in theArray. +function array_search(variable, theArray) +{ + for (var i in theArray) + if (theArray[i] == variable) + return i; + + return null; +} + +// Find a specific radio button in its group and select it. +function selectRadioByName(oRadioGroup, sName) +{ + if (!('length' in oRadioGroup)) + return oRadioGroup.checked = true; + + for (var i = 0, n = oRadioGroup.length; i < n; i++) + if (oRadioGroup[i].value == sName) + return oRadioGroup[i].checked = true; + + return false; +} + +// Invert all checkboxes at once by clicking a single checkbox. +function invertAll(oInvertCheckbox, oForm, sMask, bIgnoreDisabled) +{ + for (var i = 0; i < oForm.length; i++) + { + if (!('name' in oForm[i]) || (typeof(sMask) == 'string' && oForm[i].name.substr(0, sMask.length) != sMask && oForm[i].id.substr(0, sMask.length) != sMask)) + continue; + + if (!oForm[i].disabled || (typeof(bIgnoreDisabled) == 'boolean' && bIgnoreDisabled)) + oForm[i].checked = oInvertCheckbox.checked; + } +} + +// Keep the session alive - always! +var lastKeepAliveCheck = new Date().getTime(); +function smf_sessionKeepAlive() +{ + var curTime = new Date().getTime(); + + // Prevent a Firefox bug from hammering the server. + if (smf_scripturl && curTime - lastKeepAliveCheck > 900000) + { + var tempImage = new Image(); + tempImage.src = smf_prepareScriptUrl(smf_scripturl) + 'action=keepalive;time=' + curTime; + lastKeepAliveCheck = curTime; + } + + window.setTimeout('smf_sessionKeepAlive();', 1200000); +} +window.setTimeout('smf_sessionKeepAlive();', 1200000); + +// Set a theme option through javascript. +function smf_setThemeOption(option, value, theme, cur_session_id, cur_session_var, additional_vars) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + if (typeof(cur_session_var) == 'undefined') + cur_session_var = 'sesc'; + + if (additional_vars == null) + additional_vars = ''; + + var tempImage = new Image(); + tempImage.src = smf_prepareScriptUrl(smf_scripturl) + 'action=jsoption;var=' + option + ';val=' + value + ';' + cur_session_var + '=' + cur_session_id + additional_vars + (theme == null ? '' : '&th=' + theme) + ';time=' + (new Date().getTime()); +} + +function smf_avatarResize() +{ + var possibleAvatars = document.getElementsByTagName('img'); + + for (var i = 0; i < possibleAvatars.length; i++) + { + var tempAvatars = []; j = 0; + if (possibleAvatars[i].className != 'avatar') + continue; + + // Image.prototype.avatar = possibleAvatars[i]; + tempAvatars[j] = new Image(); + tempAvatars[j].avatar = possibleAvatars[i]; + + tempAvatars[j].onload = function() + { + this.avatar.width = this.width; + this.avatar.height = this.height; + if (smf_avatarMaxWidth != 0 && this.width > smf_avatarMaxWidth) + { + this.avatar.height = (smf_avatarMaxWidth * this.height) / this.width; + this.avatar.width = smf_avatarMaxWidth; + } + if (smf_avatarMaxHeight != 0 && this.avatar.height > smf_avatarMaxHeight) + { + this.avatar.width = (smf_avatarMaxHeight * this.avatar.width) / this.avatar.height; + this.avatar.height = smf_avatarMaxHeight; + } + } + tempAvatars[j].src = possibleAvatars[i].src; + j++; + } + + if (typeof(window_oldAvatarOnload) != 'undefined' && window_oldAvatarOnload) + { + window_oldAvatarOnload(); + window_oldAvatarOnload = null; + } +} + + +function hashLoginPassword(doForm, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + if (typeof(hex_sha1) == 'undefined') + return; + // Are they using an email address? + if (doForm.user.value.indexOf('@') != -1) + return; + + // Unless the browser is Opera, the password will not save properly. + if (!('opera' in window)) + doForm.passwrd.autocomplete = 'off'; + + doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id); + + // It looks nicer to fill it with asterisks, but Firefox will try to save that. + if (is_ff != -1) + doForm.passwrd.value = ''; + else + doForm.passwrd.value = doForm.passwrd.value.replace(/./g, '*'); +} + +function hashAdminPassword(doForm, username, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + if (typeof(hex_sha1) == 'undefined') + return; + + doForm.admin_hash_pass.value = hex_sha1(hex_sha1(username.php_to8bit().php_strtolower() + doForm.admin_pass.value.php_to8bit()) + cur_session_id); + doForm.admin_pass.value = doForm.admin_pass.value.replace(/./g, '*'); +} + +// Shows the page numbers by clicking the dots (in compact view). +function expandPages(spanNode, baseURL, firstPage, lastPage, perPage) +{ + var replacement = '', i, oldLastPage = 0; + var perPageLimit = 50; + + // The dots were bold, the page numbers are not (in most cases). + spanNode.style.fontWeight = 'normal'; + spanNode.onclick = ''; + + // Prevent too many pages to be loaded at once. + if ((lastPage - firstPage) / perPage > perPageLimit) + { + oldLastPage = lastPage; + lastPage = firstPage + perPageLimit * perPage; + } + + // Calculate the new pages. + for (i = firstPage; i < lastPage; i += perPage) + replacement += '' + (1 + i / perPage) + ' '; + + if (oldLastPage > 0) + replacement += ' ... '; + + // Replace the dots by the new page links. + setInnerHTML(spanNode, replacement); +} + +function smc_preCacheImage(sSrc) +{ + if (!('smc_aCachedImages' in window)) + window.smc_aCachedImages = []; + + if (!in_array(sSrc, window.smc_aCachedImages)) + { + var oImage = new Image(); + oImage.src = sSrc; + } +} + + +// *** smc_Cookie class. +function smc_Cookie(oOptions) +{ + this.opt = oOptions; + this.oCookies = {}; + this.init(); +} + +smc_Cookie.prototype.init = function() +{ + if ('cookie' in document && document.cookie != '') + { + var aCookieList = document.cookie.split(';'); + for (var i = 0, n = aCookieList.length; i < n; i++) + { + var aNameValuePair = aCookieList[i].split('='); + this.oCookies[aNameValuePair[0].replace(/^\s+|\s+$/g, '')] = decodeURIComponent(aNameValuePair[1]); + } + } +} + +smc_Cookie.prototype.get = function(sKey) +{ + return sKey in this.oCookies ? this.oCookies[sKey] : null; +} + +smc_Cookie.prototype.set = function(sKey, sValue) +{ + document.cookie = sKey + '=' + encodeURIComponent(sValue); +} + + +// *** smc_Toggle class. +function smc_Toggle(oOptions) +{ + this.opt = oOptions; + this.bCollapsed = false; + this.oCookie = null; + this.init(); +} + +smc_Toggle.prototype.init = function () +{ + // The master switch can disable this toggle fully. + if ('bToggleEnabled' in this.opt && !this.opt.bToggleEnabled) + return; + + // If cookies are enabled and they were set, override the initial state. + if ('oCookieOptions' in this.opt && this.opt.oCookieOptions.bUseCookie) + { + // Initialize the cookie handler. + this.oCookie = new smc_Cookie({}); + + // Check if the cookie is set. + var cookieValue = this.oCookie.get(this.opt.oCookieOptions.sCookieName) + if (cookieValue != null) + this.opt.bCurrentlyCollapsed = cookieValue == '1'; + } + + // If the init state is set to be collapsed, collapse it. + if (this.opt.bCurrentlyCollapsed) + this.changeState(true, true); + + // Initialize the images to be clickable. + if ('aSwapImages' in this.opt) + { + for (var i = 0, n = this.opt.aSwapImages.length; i < n; i++) + { + var oImage = document.getElementById(this.opt.aSwapImages[i].sId); + if (typeof(oImage) == 'object' && oImage != null) + { + // Display the image in case it was hidden. + if (oImage.style.display == 'none') + oImage.style.display = ''; + + oImage.instanceRef = this; + oImage.onclick = function () { + this.instanceRef.toggle(); + this.blur(); + } + oImage.style.cursor = 'pointer'; + + // Preload the collapsed image. + smc_preCacheImage(this.opt.aSwapImages[i].srcCollapsed); + } + } + } + + // Initialize links. + if ('aSwapLinks' in this.opt) + { + for (var i = 0, n = this.opt.aSwapLinks.length; i < n; i++) + { + var oLink = document.getElementById(this.opt.aSwapLinks[i].sId); + if (typeof(oLink) == 'object' && oLink != null) + { + // Display the link in case it was hidden. + if (oLink.style.display == 'none') + oLink.style.display = ''; + + oLink.instanceRef = this; + oLink.onclick = function () { + this.instanceRef.toggle(); + this.blur(); + return false; + } + } + } + } +} + +// Collapse or expand the section. +smc_Toggle.prototype.changeState = function(bCollapse, bInit) +{ + // Default bInit to false. + bInit = typeof(bInit) == 'undefined' ? false : true; + + // Handle custom function hook before collapse. + if (!bInit && bCollapse && 'funcOnBeforeCollapse' in this.opt) + { + this.tmpMethod = this.opt.funcOnBeforeCollapse; + this.tmpMethod(); + delete this.tmpMethod; + } + + // Handle custom function hook before expand. + else if (!bInit && !bCollapse && 'funcOnBeforeExpand' in this.opt) + { + this.tmpMethod = this.opt.funcOnBeforeExpand; + this.tmpMethod(); + delete this.tmpMethod; + } + + // Loop through all the images that need to be toggled. + if ('aSwapImages' in this.opt) + { + for (var i = 0, n = this.opt.aSwapImages.length; i < n; i++) + { + var oImage = document.getElementById(this.opt.aSwapImages[i].sId); + if (typeof(oImage) == 'object' && oImage != null) + { + // Only (re)load the image if it's changed. + var sTargetSource = bCollapse ? this.opt.aSwapImages[i].srcCollapsed : this.opt.aSwapImages[i].srcExpanded; + if (oImage.src != sTargetSource) + oImage.src = sTargetSource; + + oImage.alt = oImage.title = bCollapse ? this.opt.aSwapImages[i].altCollapsed : this.opt.aSwapImages[i].altExpanded; + } + } + } + + // Loop through all the links that need to be toggled. + if ('aSwapLinks' in this.opt) + { + for (var i = 0, n = this.opt.aSwapLinks.length; i < n; i++) + { + var oLink = document.getElementById(this.opt.aSwapLinks[i].sId); + if (typeof(oLink) == 'object' && oLink != null) + setInnerHTML(oLink, bCollapse ? this.opt.aSwapLinks[i].msgCollapsed : this.opt.aSwapLinks[i].msgExpanded); + } + } + + // Now go through all the sections to be collapsed. + for (var i = 0, n = this.opt.aSwappableContainers.length; i < n; i++) + { + if (this.opt.aSwappableContainers[i] == null) + continue; + + var oContainer = document.getElementById(this.opt.aSwappableContainers[i]); + if (typeof(oContainer) == 'object' && oContainer != null) + oContainer.style.display = bCollapse ? 'none' : ''; + } + + // Update the new state. + this.bCollapsed = bCollapse; + + // Update the cookie, if desired. + if ('oCookieOptions' in this.opt && this.opt.oCookieOptions.bUseCookie) + this.oCookie.set(this.opt.oCookieOptions.sCookieName, this.bCollapsed ? '1' : '0'); + + if ('oThemeOptions' in this.opt && this.opt.oThemeOptions.bUseThemeSettings) + smf_setThemeOption(this.opt.oThemeOptions.sOptionName, this.bCollapsed ? '1' : '0', 'sThemeId' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sThemeId : null, this.opt.oThemeOptions.sSessionId, this.opt.oThemeOptions.sSessionVar, 'sAdditionalVars' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sAdditionalVars : null); +} + +smc_Toggle.prototype.toggle = function() +{ + // Change the state by reversing the current state. + this.changeState(!this.bCollapsed); +} + + +function ajax_indicator(turn_on) +{ + if (ajax_indicator_ele == null) + { + ajax_indicator_ele = document.getElementById('ajax_in_progress'); + + if (ajax_indicator_ele == null && typeof(ajax_notification_text) != null) + { + create_ajax_indicator_ele(); + } + } + + if (ajax_indicator_ele != null) + { + if (navigator.appName == 'Microsoft Internet Explorer' && !is_ie7up) + { + ajax_indicator_ele.style.position = 'absolute'; + ajax_indicator_ele.style.top = document.documentElement.scrollTop; + } + + ajax_indicator_ele.style.display = turn_on ? 'block' : 'none'; + } +} + +function create_ajax_indicator_ele() +{ + // Create the div for the indicator. + ajax_indicator_ele = document.createElement('div'); + + // Set the id so it'll load the style properly. + ajax_indicator_ele.id = 'ajax_in_progress'; + + // Add the image in and link to turn it off. + var cancel_link = document.createElement('a'); + cancel_link.href = 'javascript:ajax_indicator(false)'; + var cancel_img = document.createElement('img'); + cancel_img.src = smf_images_url + '/icons/quick_remove.gif'; + + if (typeof(ajax_notification_cancel_text) != 'undefined') + { + cancel_img.alt = ajax_notification_cancel_text; + cancel_img.title = ajax_notification_cancel_text; + } + + // Add the cancel link and image to the indicator. + cancel_link.appendChild(cancel_img); + ajax_indicator_ele.appendChild(cancel_link); + + // Set the text. (Note: You MUST append here and not overwrite.) + ajax_indicator_ele.innerHTML += ajax_notification_text; + + // Finally attach the element to the body. + document.body.appendChild(ajax_indicator_ele); +} + +function createEventListener(oTarget) +{ + if (!('addEventListener' in oTarget)) + { + if (oTarget.attachEvent) + { + oTarget.addEventListener = function (sEvent, funcHandler, bCapture) { + oTarget.attachEvent('on' + sEvent, funcHandler); + } + oTarget.removeEventListener = function (sEvent, funcHandler, bCapture) { + oTarget.detachEvent('on' + sEvent, funcHandler); + } + } + else + { + oTarget.addEventListener = function (sEvent, funcHandler, bCapture) { + oTarget['on' + sEvent] = funcHandler; + } + oTarget.removeEventListener = function (sEvent, funcHandler, bCapture) { + oTarget['on' + sEvent] = null; + } + } + } +} + +// This function will retrieve the contents needed for the jump to boxes. +function grabJumpToContent() +{ + var oXMLDoc = getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=xmlhttp;sa=jumpto;xml'); + var aBoardsAndCategories = new Array(); + + ajax_indicator(true); + + if (oXMLDoc.responseXML) + { + var items = oXMLDoc.responseXML.getElementsByTagName('smf')[0].getElementsByTagName('item'); + for (var i = 0, n = items.length; i < n; i++) + { + aBoardsAndCategories[aBoardsAndCategories.length] = { + id: parseInt(items[i].getAttribute('id')), + isCategory: items[i].getAttribute('type') == 'category', + name: items[i].firstChild.nodeValue.removeEntities(), + is_current: false, + childLevel: parseInt(items[i].getAttribute('childlevel')) + } + } + } + + ajax_indicator(false); + + for (var i = 0, n = aJumpTo.length; i < n; i++) + aJumpTo[i].fillSelect(aBoardsAndCategories); +} + +// This'll contain all JumpTo objects on the page. +var aJumpTo = new Array(); + +// *** JumpTo class. +function JumpTo(oJumpToOptions) +{ + this.opt = oJumpToOptions; + this.dropdownList = null; + this.showSelect(); +} + +// Show the initial select box (onload). Method of the JumpTo class. +JumpTo.prototype.showSelect = function () +{ + var sChildLevelPrefix = ''; + for (var i = this.opt.iCurBoardChildLevel; i > 0; i--) + sChildLevelPrefix += this.opt.sBoardChildLevelIndicator; + setInnerHTML(document.getElementById(this.opt.sContainerId), this.opt.sJumpToTemplate.replace(/%select_id%/, this.opt.sContainerId + '_select').replace(/%dropdown_list%/, ' ')); + this.dropdownList = document.getElementById(this.opt.sContainerId + '_select'); +} + +// Fill the jump to box with entries. Method of the JumpTo class. +JumpTo.prototype.fillSelect = function (aBoardsAndCategories) +{ + var bIE5x = !('implementation' in document); + var iIndexPointer = 0; + + // Create an option that'll be above and below the category. + var oDashOption = document.createElement('option'); + oDashOption.appendChild(document.createTextNode(this.opt.sCatSeparator)); + oDashOption.disabled = 'disabled'; + oDashOption.value = ''; + + // Reset the events and clear the list (IE5.x only). + if (bIE5x) + { + this.dropdownList.onmouseover = null; + this.dropdownList.remove(0); + } + if ('onbeforeactivate' in document) + this.dropdownList.onbeforeactivate = null; + else + this.dropdownList.onfocus = null; + + // Create a document fragment that'll allowing inserting big parts at once. + var oListFragment = bIE5x ? this.dropdownList : document.createDocumentFragment(); + + // Loop through all items to be added. + for (var i = 0, n = aBoardsAndCategories.length; i < n; i++) + { + var j, sChildLevelPrefix, oOption; + + // If we've reached the currently selected board add all items so far. + if (!aBoardsAndCategories[i].isCategory && aBoardsAndCategories[i].id == this.opt.iCurBoardId) + { + if (bIE5x) + iIndexPointer = this.dropdownList.options.length; + else + { + this.dropdownList.insertBefore(oListFragment, this.dropdownList.options[0]); + oListFragment = document.createDocumentFragment(); + continue; + } + } + + if (aBoardsAndCategories[i].isCategory) + oListFragment.appendChild(oDashOption.cloneNode(true)); + else + for (j = aBoardsAndCategories[i].childLevel, sChildLevelPrefix = ''; j > 0; j--) + sChildLevelPrefix += this.opt.sBoardChildLevelIndicator; + + oOption = document.createElement('option'); + oOption.appendChild(document.createTextNode((aBoardsAndCategories[i].isCategory ? this.opt.sCatPrefix : sChildLevelPrefix + this.opt.sBoardPrefix) + aBoardsAndCategories[i].name)); + oOption.value = aBoardsAndCategories[i].isCategory ? '#c' + aBoardsAndCategories[i].id : '?board=' + aBoardsAndCategories[i].id + '.0'; + oListFragment.appendChild(oOption); + + if (aBoardsAndCategories[i].isCategory) + oListFragment.appendChild(oDashOption.cloneNode(true)); + } + + // Add the remaining items after the currently selected item. + this.dropdownList.appendChild(oListFragment); + + if (bIE5x) + this.dropdownList.options[iIndexPointer].selected = true; + + // Internet Explorer needs this to keep the box dropped down. + this.dropdownList.style.width = 'auto'; + this.dropdownList.focus(); + + // Add an onchange action + this.dropdownList.onchange = function() { + if (this.selectedIndex > 0 && this.options[this.selectedIndex].value) + window.location.href = smf_scripturl + this.options[this.selectedIndex].value.substr(smf_scripturl.indexOf('?') == -1 || this.options[this.selectedIndex].value.substr(0, 1) != '?' ? 0 : 1); + } +} + +// A global array containing all IconList objects. +var aIconLists = new Array(); + +// *** IconList object. +function IconList(oOptions) +{ + if (!window.XMLHttpRequest) + return; + + this.opt = oOptions; + this.bListLoaded = false; + this.oContainerDiv = null; + this.funcMousedownHandler = null; + this.funcParent = this; + this.iCurMessageId = 0; + this.iCurTimeout = 0; + + // Add backwards compatibility with old themes. + if (!('sSessionVar' in this.opt)) + this.opt.sSessionVar = 'sesc'; + + this.initIcons(); +} + +// Replace all message icons by icons with hoverable and clickable div's. +IconList.prototype.initIcons = function () +{ + for (var i = document.images.length - 1, iPrefixLength = this.opt.sIconIdPrefix.length; i >= 0; i--) + if (document.images[i].id.substr(0, iPrefixLength) == this.opt.sIconIdPrefix) + setOuterHTML(document.images[i], '
    ' + document.images[i].alt + '
    '); +} + +// Event for the mouse hovering over the original icon. +IconList.prototype.onBoxHover = function (oDiv, bMouseOver) +{ + oDiv.style.border = bMouseOver ? this.opt.iBoxBorderWidthHover + 'px solid ' + this.opt.sBoxBorderColorHover : ''; + oDiv.style.background = bMouseOver ? this.opt.sBoxBackgroundHover : this.opt.sBoxBackground; + oDiv.style.padding = bMouseOver ? (3 - this.opt.iBoxBorderWidthHover) + 'px' : '3px' +} + +// Show the list of icons after the user clicked the original icon. +IconList.prototype.openPopup = function (oDiv, iMessageId) +{ + this.iCurMessageId = iMessageId; + + if (!this.bListLoaded && this.oContainerDiv == null) + { + // Create a container div. + this.oContainerDiv = document.createElement('div'); + this.oContainerDiv.id = 'iconList'; + this.oContainerDiv.style.display = 'none'; + this.oContainerDiv.style.cursor = is_ie && !is_ie6up ? 'hand' : 'pointer'; + this.oContainerDiv.style.position = 'absolute'; + this.oContainerDiv.style.width = oDiv.offsetWidth + 'px'; + this.oContainerDiv.style.background = this.opt.sContainerBackground; + this.oContainerDiv.style.border = this.opt.sContainerBorder; + this.oContainerDiv.style.padding = '1px'; + this.oContainerDiv.style.textAlign = 'center'; + document.body.appendChild(this.oContainerDiv); + + // Start to fetch its contents. + ajax_indicator(true); + this.tmpMethod = getXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=xmlhttp;sa=messageicons;board=' + this.opt.iBoardId + ';xml', this.onIconsReceived); + delete this.tmpMethod; + + createEventListener(document.body); + } + + // Set the position of the container. + var aPos = smf_itemPos(oDiv); + if (is_ie50) + aPos[1] += 4; + + this.oContainerDiv.style.top = (aPos[1] + oDiv.offsetHeight) + 'px'; + this.oContainerDiv.style.left = (aPos[0] - 1) + 'px'; + this.oClickedIcon = oDiv; + + if (this.bListLoaded) + this.oContainerDiv.style.display = 'block'; + + document.body.addEventListener('mousedown', this.onWindowMouseDown, false); +} + +// Setup the list of icons once it is received through xmlHTTP. +IconList.prototype.onIconsReceived = function (oXMLDoc) +{ + var icons = oXMLDoc.getElementsByTagName('smf')[0].getElementsByTagName('icon'); + var sItems = ''; + + for (var i = 0, n = icons.length; i < n; i++) + sItems += '
    ' + icons[i].getAttribute('name') + '
    '; + + setInnerHTML(this.oContainerDiv, sItems); + this.oContainerDiv.style.display = 'block'; + this.bListLoaded = true; + + if (is_ie) + this.oContainerDiv.style.width = this.oContainerDiv.clientWidth + 'px'; + + ajax_indicator(false); +} + +// Event handler for hovering over the icons. +IconList.prototype.onItemHover = function (oDiv, bMouseOver) +{ + oDiv.style.background = bMouseOver ? this.opt.sItemBackgroundHover : this.opt.sItemBackground; + oDiv.style.border = bMouseOver ? this.opt.sItemBorderHover : this.opt.sItemBorder; + if (this.iCurTimeout != 0) + window.clearTimeout(this.iCurTimeout); + if (bMouseOver) + this.onBoxHover(this.oClickedIcon, true); + else + this.iCurTimeout = window.setTimeout(this.opt.sBackReference + '.collapseList();', 500); +} + +// Event handler for clicking on one of the icons. +IconList.prototype.onItemMouseDown = function (oDiv, sNewIcon) +{ + if (this.iCurMessageId != 0) + { + ajax_indicator(true); + this.tmpMethod = getXMLDocument; + var oXMLDoc = this.tmpMethod(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=jsmodify;topic=' + this.opt.iTopicId + ';msg=' + this.iCurMessageId + ';' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';icon=' + sNewIcon + ';xml'); + delete this.tmpMethod; + ajax_indicator(false); + + var oMessage = oXMLDoc.responseXML.getElementsByTagName('smf')[0].getElementsByTagName('message')[0]; + if (oMessage.getElementsByTagName('error').length == 0) + { + if (this.opt.bShowModify && oMessage.getElementsByTagName('modified').length != 0) + setInnerHTML(document.getElementById('modified_' + this.iCurMessageId), oMessage.getElementsByTagName('modified')[0].childNodes[0].nodeValue); + this.oClickedIcon.getElementsByTagName('img')[0].src = oDiv.getElementsByTagName('img')[0].src; + } + } +} + +// Event handler for clicking outside the list (will make the list disappear). +IconList.prototype.onWindowMouseDown = function () +{ + for (var i = aIconLists.length - 1; i >= 0; i--) + { + aIconLists[i].funcParent.tmpMethod = aIconLists[i].collapseList; + aIconLists[i].funcParent.tmpMethod(); + delete aIconLists[i].funcParent.tmpMethod; + } +} + +// Collapse the list of icons. +IconList.prototype.collapseList = function() +{ + this.onBoxHover(this.oClickedIcon, false); + this.oContainerDiv.style.display = 'none'; + this.iCurMessageId = 0; + document.body.removeEventListener('mousedown', this.onWindowMouseDown, false); +} + +// Handy shortcuts for getting the mouse position on the screen - only used for IE at the moment. +function smf_mousePose(oEvent) +{ + var x = 0; + var y = 0; + + if (oEvent.pageX) + { + y = oEvent.pageY; + x = oEvent.pageX; + } + else if (oEvent.clientX) + { + x = oEvent.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft); + y = oEvent.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop); + } + + return [x, y]; +} + +// Short function for finding the actual position of an item. +function smf_itemPos(itemHandle) +{ + var itemX = 0; + var itemY = 0; + + if ('offsetParent' in itemHandle) + { + itemX = itemHandle.offsetLeft; + itemY = itemHandle.offsetTop; + while (itemHandle.offsetParent && typeof(itemHandle.offsetParent) == 'object') + { + itemHandle = itemHandle.offsetParent; + itemX += itemHandle.offsetLeft; + itemY += itemHandle.offsetTop; + } + } + else if ('x' in itemHandle) + { + itemX = itemHandle.x; + itemY = itemHandle.y; + } + + return [itemX, itemY]; +} + +// This function takes the script URL and prepares it to allow the query string to be appended to it. +function smf_prepareScriptUrl(sUrl) +{ + return sUrl.indexOf('?') == -1 ? sUrl + '?' : sUrl + (sUrl.charAt(sUrl.length - 1) == '?' || sUrl.charAt(sUrl.length - 1) == '&' || sUrl.charAt(sUrl.length - 1) == ';' ? '' : ';'); +} + +var aOnloadEvents = new Array(); +function addLoadEvent(fNewOnload) +{ + // If there's no event set, just set this one + if (typeof(fNewOnload) == 'function' && (!('onload' in window) || typeof(window.onload) != 'function')) + window.onload = fNewOnload; + + // If there's just one event, setup the array. + else if (aOnloadEvents.length == 0) + { + aOnloadEvents[0] = window.onload; + aOnloadEvents[1] = fNewOnload; + window.onload = function() { + for (var i = 0, n = aOnloadEvents.length; i < n; i++) + { + if (typeof(aOnloadEvents[i]) == 'function') + aOnloadEvents[i](); + else if (typeof(aOnloadEvents[i]) == 'string') + eval(aOnloadEvents[i]); + } + } + } + + // This isn't the first event function, add it to the list. + else + aOnloadEvents[aOnloadEvents.length] = fNewOnload; +} + +function smfFooterHighlight(element, value) +{ + element.src = smf_images_url + '/' + (value ? 'h_' : '') + element.id + '.gif'; +} + +// Get the text in a code tag. +function smfSelectText(oCurElement, bActOnElement) +{ + // The place we're looking for is one div up, and next door - if it's auto detect. + if (typeof(bActOnElement) == 'boolean' && bActOnElement) + var oCodeArea = document.getElementById(oCurElement); + else + var oCodeArea = oCurElement.parentNode.nextSibling; + + if (typeof(oCodeArea) != 'object' || oCodeArea == null) + return false; + + // Start off with my favourite, internet explorer. + if ('createTextRange' in document.body) + { + var oCurRange = document.body.createTextRange(); + oCurRange.moveToElementText(oCodeArea); + oCurRange.select(); + } + // Firefox at el. + else if (window.getSelection) + { + var oCurSelection = window.getSelection(); + // Safari is special! + if (oCurSelection.setBaseAndExtent) + { + var oLastChild = oCodeArea.lastChild; + oCurSelection.setBaseAndExtent(oCodeArea, 0, oLastChild, 'innerText' in oLastChild ? oLastChild.innerText.length : oLastChild.textContent.length); + } + else + { + var curRange = document.createRange(); + curRange.selectNodeContents(oCodeArea); + + oCurSelection.removeAllRanges(); + oCurSelection.addRange(curRange); + } + } + + return false; +} + +// A function needed to discern HTML entities from non-western characters. +function smc_saveEntities(sFormName, aElementNames, sMask) +{ + if (typeof(sMask) == 'string') + { + for (var i = 0, n = document.forms[sFormName].elements.length; i < n; i++) + if (document.forms[sFormName].elements[i].id.substr(0, sMask.length) == sMask) + aElementNames[aElementNames.length] = document.forms[sFormName].elements[i].name; + } + + for (var i = 0, n = aElementNames.length; i < n; i++) + { + if (aElementNames[i] in document.forms[sFormName]) + document.forms[sFormName][aElementNames[i]].value = document.forms[sFormName][aElementNames[i]].value.replace(/&#/g, '&#'); + } +} + +// A function used to clean the attachments on post page +function cleanFileInput(idElement) +{ + // Simpler solutions work in Opera, IE, Safari and Chrome. + if (is_opera || is_ie || is_safari || is_chrome) + { + document.getElementById(idElement).outerHTML = document.getElementById(idElement).outerHTML; + } + // What else can we do? By the way, this doesn't work in Chrome and Mac's Safari. + else + { + document.getElementById(idElement).type = 'input'; + document.getElementById(idElement).type = 'file'; + } +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/sha1.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/sha1.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,205 @@ +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1 Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} + +/* + * Perform a simple self-test to see if the VM is working + */ +function sha1_vm_test() +{ + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; +} + +/* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ +function core_sha1(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for (var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for (var j = 0; j < 80; j++) + { + if (j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); +} + +/* + * Perform the appropriate triplet combination function for the current + * iteration + */ +function sha1_ft(t, b, c, d) +{ + if (t < 20) return (b & c) | ((~b) & d); + if (t < 40) return b ^ c ^ d; + if (t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +/* + * Determine the appropriate additive constant for the current iteration + */ +function sha1_kt(t) +{ + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; +} + +/* + * Calculate the HMAC-SHA1 of a key and some data + */ +function core_hmac_sha1(key, data) +{ + var bkey = str2binb(key); + if (bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for (var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ +function str2binb(str) +{ + var bin = Array(); + + for (var i = 0, n = 1 + ((str.length * chrsz) >> 5); i < n; i++) + bin[i] = 0; + + var mask = (1 << chrsz) - 1; + for (var i = 0; i < str.length * chrsz; i += chrsz) + bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i % 32); + return bin; +} + +/* + * Convert an array of big-endian words to a string + */ +function binb2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for (var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask); + return str; +} + +/* + * Convert an array of big-endian words to a hex string. + */ +function binb2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of big-endian words to a base-64 string + */ +function binb2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for (var j = 0; j < 4; j++) + { + if (i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/spellcheck.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/spellcheck.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,297 @@ +// These are variables the popup is going to want to access... +var spell_formname, spell_fieldname; + +// Spell check the specified field in the specified form. +function spellCheck(formName, fieldName) +{ + // Grab the (hidden) spell checking form. + var spellform = document.forms.spell_form; + + // Register the name of the editing form for future reference. + spell_formname = formName; + spell_fieldname = fieldName; + + // This should match a word (most of the time). + var regexpWordMatch = /(?:<[^>]+>)|(?:\[[^ ][^\]]*\])|(?:&[^; ]+;)|(?:[^0-9\s\]\[{};:"\\|,<.>\/?`~!@#$%^&*()_+=]+)/g; + + // These characters can appear within words. + var aWordCharacters = ['-', '\'']; + + var aWords = new Array(), aResult = new Array(); + var sText = document.forms[formName][fieldName].value; + var bInCode = false; + var iOffset1, iOffset2; + + // Loop through all words. + while ((aResult = regexpWordMatch.exec(sText)) && typeof(aResult) != 'undefined') + { + iOffset1 = 0; + iOffset2 = aResult[0].length - 1; + + // Strip the dashes and hyphens from the begin of the word. + while (in_array(aResult[0].charAt(iOffset1), aWordCharacters) && iOffset1 < iOffset2) + iOffset1++; + + // Strip the dashes and hyphens from the end of the word. + while (in_array(aResult[0].charAt(iOffset2), aWordCharacters) && iOffset1 < iOffset2) + iOffset2--; + + // I guess there's only dashes and hyphens in this word... + if (iOffset1 == iOffset2) + continue; + + // Ignore code blocks. + if (aResult[0].substr(0, 5).toLowerCase() == '[code') + bInCode = true; + + // Glad we're out of that code block! + else if (bInCode && aResult[0].substr(0, 7).toLowerCase() == '[/code]') + bInCode = false; + + // Now let's get to business. + else if (!bInCode && !in_array(aResult[0].charAt(0), ['[', '<']) && aResult[0].toUpperCase() != aResult[0]) + aWords[aWords.length] = aResult[0].substr(iOffset1, iOffset2 - iOffset1 + 1) + '|' + (iOffset1 + sText.substr(0, aResult.index).length) + '|' + (iOffset2 + sText.substr(0, aResult.index).length); + } + + // Open the window... + openSpellWin(640, 480); + + // Pass the data to a form... + spellform.spellstring.value = aWords.join('\n'); + + // and go! + spellform.submit(); + + return true; +} + +// Private functions ------------------------------- + +// Globals... +var wordindex = -1, offsetindex = 0; +var ignoredWords = []; + +// A "misspelled word" object. +function misp(word, start, end, suggestions) +{ + // The word, start index, end index, and array of suggestions. + this.word = word; + this.start = start; + this.end = end; + this.suggestions = suggestions; +} + +// Replace the word in the misps array at the "wordindex" index. The +// misps array is generated by a PHP script after the string to be spell +// checked is evaluated with pspell. +function replaceWord() +{ + var strstart = ""; + var strend; + + // If this isn't the beginning of the string then get all of the string + // that is before the word we are replacing. + if (misps[wordindex].start != 0) + strstart = mispstr.slice(0, misps[wordindex].start + offsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[wordindex].end + 1 + offsetindex); + + // Rebuild the string with the new word. + mispstr = strstart + document.forms.spellingForm.changeto.value + strend; + + // Update offsetindex to compensate for replacing a word with a word + // of a different length. + offsetindex += document.forms.spellingForm.changeto.value.length - misps[wordindex].word.length; + + // Update the word so future replaceAll calls don't change it. + misps[wordindex].word = document.forms.spellingForm.changeto.value; + + nextWord(false); +} + +// Replaces all instances of currently selected word with contents chosen by user. +// Note: currently only replaces words after highlighted word. I think we can re-index +// all words at replacement or ignore time to have it wrap to the beginning if we want +// to. +function replaceAll() +{ + var strend; + var idx; + var origword; + var localoffsetindex = offsetindex; + + origword = misps[wordindex].word; + + // Re-index everything past the current word. + for (idx = wordindex; idx < misps.length; idx++) + { + misps[idx].start += localoffsetindex; + misps[idx].end += localoffsetindex; + } + + localoffsetindex = 0; + + for (idx = 0; idx < misps.length; idx++) + { + if (misps[idx].word == origword) + { + var strstart = ""; + if (misps[idx].start != 0) + strstart = mispstr.slice(0, misps[idx].start + localoffsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[idx].end + 1 + localoffsetindex); + + // Rebuild the string with the new word. + mispstr = strstart + document.forms.spellingForm.changeto.value + strend; + + // Update offsetindex to compensate for replacing a word with a word + // of a different length. + localoffsetindex += document.forms.spellingForm.changeto.value.length - misps[idx].word.length; + } + + // We have to re-index everything after replacements. + misps[idx].start += localoffsetindex; + misps[idx].end += localoffsetindex; + } + + // Add the word to the ignore array. + ignoredWords[origword] = true; + + // Reset offsetindex since we re-indexed. + offsetindex = 0; + + nextWord(false); +} + +// Highlight the word that was selected using the nextWord function. +function highlightWord() +{ + var strstart = ""; + var strend; + + // If this isn't the beginning of the string then get all of the string + // that is before the word we are replacing. + if (misps[wordindex].start != 0) + strstart = mispstr.slice(0, misps[wordindex].start + offsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[wordindex].end + 1 + offsetindex); + + // Rebuild the string with a span wrapped around the misspelled word + // so we can highlight it in the div the user is viewing the string in. + var divptr, newValue; + divptr = document.getElementById("spellview"); + + newValue = htmlspecialchars(strstart) + '' + misps[wordindex].word + '' + htmlspecialchars(strend); + setInnerHTML(divptr, newValue.replace(/_\|_/g, '
    ')); + + // We could use scrollIntoView, but it's just not that great anyway. + var spellview_height = typeof(document.getElementById("spellview").currentStyle) != "undefined" ? parseInt(document.getElementById("spellview").currentStyle.height) : document.getElementById("spellview").offsetHeight; + var word_position = document.getElementById("h1").offsetTop; + var current_position = document.getElementById("spellview").scrollTop; + + // The spellview is not tall enough! Scroll down! + if (spellview_height <= (word_position + current_position)) + document.getElementById("spellview").scrollTop = word_position + current_position - spellview_height + 32; +} + +// Display the next misspelled word to the user and populate the suggested spellings box. +function nextWord(ignoreall) +{ + // Push ignored word onto ignoredWords array. + if (ignoreall) + ignoredWords[misps[wordindex].word] = true; + + // Update the index of all words we have processed... + // This must be done to accomodate the replaceAll function. + if (wordindex >= 0) + { + misps[wordindex].start += offsetindex; + misps[wordindex].end += offsetindex; + } + + // Increment the counter for the array of misspelled words. + wordindex++; + + // Draw it and quit if there are no more misspelled words to evaluate. + if (misps.length <= wordindex) + { + var divptr; + divptr = document.getElementById("spellview"); + setInnerHTML(divptr, htmlspecialchars(mispstr).replace(/_\|_/g, "
    ")); + + while (document.forms.spellingForm.suggestions.options.length > 0) + document.forms.spellingForm.suggestions.options[0] = null; + + alert(txt['done']); + document.forms.spellingForm.change.disabled = true; + document.forms.spellingForm.changeall.disabled = true; + document.forms.spellingForm.ignore.disabled = true; + document.forms.spellingForm.ignoreall.disabled = true; + + // Put line feeds back... + mispstr = mispstr.replace(/_\|_/g, "\n"); + + // Get a handle to the field we need to re-populate. + window.opener.document.forms[spell_formname][spell_fieldname].value = mispstr; + if (!window.opener.spellCheckDone) + window.opener.document.forms[spell_formname][spell_fieldname].focus(); + else + window.opener.spellCheckDone(); + + window.close(); + return true; + } + + // Check to see if word is supposed to be ignored. + if (typeof(ignoredWords[misps[wordindex].word]) != "undefined") + { + nextWord(false); + return false; + } + + // Clear out the suggestions box! + while (document.forms.spellingForm.suggestions.options.length > 0) + document.forms.spellingForm.suggestions.options[0] = null; + + // Re-populate the suggestions box if there are any suggested spellings for the word. + if (misps[wordindex].suggestions.length) + { + for (var sugidx = 0; sugidx < misps[wordindex].suggestions.length; sugidx++) + { + var newopt = new Option(misps[wordindex].suggestions[sugidx], misps[wordindex].suggestions[sugidx]); + document.forms.spellingForm.suggestions.options[sugidx] = newopt; + + if (sugidx == 0) + { + newopt.selected = true; + document.forms.spellingForm.changeto.value = newopt.value; + document.forms.spellingForm.changeto.select(); + } + } + } + + if (document.forms.spellingForm.suggestions.options.length == 0) + document.forms.spellingForm.changeto.value = ""; + + highlightWord(); + + return false; +} + +function htmlspecialchars(thetext) +{ + thetext = thetext.replace(/\/g, ">"); + thetext = thetext.replace(/\n/g, "
    "); + thetext = thetext.replace(/\ \ /g, "  "); + + return thetext; +} + +function openSpellWin(width, height) +{ + window.open("", "spellWindow", "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=no,width=" + width + ",height=" + height); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/stats.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/stats.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,239 @@ +function smf_StatsCenter(oOptions) +{ + this.opt = oOptions; + + this.oTable = null; + this.oYears = {}; + + this.bIsLoading = false; + + this.init(); +} + +smf_StatsCenter.prototype.init = function () +{ + this.oTable = document.getElementById(this.opt.sTableId); + + // Is the table actually present? + if (typeof(this.oTable) != 'object') + return; + + // Find all months and years defined in the table. + var aRows = this.oTable.getElementsByTagName('tr'); + var aResults = []; + + var sYearId = null; + var oCurYear = null; + + var sMonthId = null; + var oCurMonth = null; + for (var i = 0, n = aRows.length; i < n; i++) + { + // Check if the current row represents a year. + if ((aResults = this.opt.reYearPattern.exec(aRows[i].id)) != null) + { + // The id is part of the pattern match. + sYearId = aResults[1]; + + // Setup the object that'll have the state information of the year. + this.oYears[sYearId] = { + oCollapseImage: document.getElementById(this.opt.sYearImageIdPrefix + sYearId), + oMonths: {} + }; + + // Create a shortcut, makes things more readable. + oCurYear = this.oYears[sYearId]; + + // Use the collapse image to determine the current state. + oCurYear.bIsCollapsed = oCurYear.oCollapseImage.src.indexOf(this.opt.sYearImageCollapsed) >= 0; + + // Setup the toggle element for the year. + oCurYear.oToggle = new smc_Toggle({ + bToggleEnabled: true, + bCurrentlyCollapsed: oCurYear.bIsCollapsed, + instanceRef: this, + sYearId: sYearId, + funcOnBeforeCollapse: function () { + this.opt.instanceRef.onBeforeCollapseYear(this); + }, + aSwappableContainers: [ + ], + aSwapImages: [ + { + sId: this.opt.sYearImageIdPrefix + sYearId, + srcExpanded: smf_images_url + '/' + this.opt.sYearImageExpanded, + altExpanded: '-', + srcCollapsed: smf_images_url + '/' + this.opt.sYearImageCollapsed, + altCollapsed: '+' + } + ], + aSwapLinks: [ + { + sId: this.opt.sYearLinkIdPrefix + sYearId, + msgExpanded: sYearId, + msgCollapsed: sYearId + } + ] + }); + } + + // Or maybe the current row represents a month. + else if ((aResults = this.opt.reMonthPattern.exec(aRows[i].id)) != null) + { + // Set the id to the matched pattern. + sMonthId = aResults[1]; + + // Initialize the month as a child object of the year. + oCurYear.oMonths[sMonthId] = { + oCollapseImage: document.getElementById(this.opt.sMonthImageIdPrefix + sMonthId) + }; + + // Create a shortcut to the current month. + oCurMonth = oCurYear.oMonths[sMonthId]; + + // Determine whether the month is currently collapsed or expanded.. + oCurMonth.bIsCollapsed = oCurMonth.oCollapseImage.src.indexOf(this.opt.sMonthImageCollapsed) >= 0; + + var sLinkText = getInnerHTML(document.getElementById(this.opt.sMonthLinkIdPrefix + sMonthId)); + + // Setup the toggle element for the month. + oCurMonth.oToggle = new smc_Toggle({ + bToggleEnabled: true, + bCurrentlyCollapsed: oCurMonth.bIsCollapsed, + instanceRef: this, + sMonthId: sMonthId, + funcOnBeforeCollapse: function () { + this.opt.instanceRef.onBeforeCollapseMonth(this); + }, + funcOnBeforeExpand: function () { + this.opt.instanceRef.onBeforeExpandMonth(this); + }, + aSwappableContainers: [ + ], + aSwapImages: [ + { + sId: this.opt.sMonthImageIdPrefix + sMonthId, + srcExpanded: smf_images_url + '/' + this.opt.sMonthImageExpanded, + altExpanded: '-', + srcCollapsed: smf_images_url + '/' + this.opt.sMonthImageCollapsed, + altCollapsed: '+' + } + ], + aSwapLinks: [ + { + sId: this.opt.sMonthLinkIdPrefix + sMonthId, + msgExpanded: sLinkText, + msgCollapsed: sLinkText + } + ] + }); + + oCurYear.oToggle.opt.aSwappableContainers[oCurYear.oToggle.opt.aSwappableContainers.length] = aRows[i].id; + } + + else if((aResults = this.opt.reDayPattern.exec(aRows[i].id)) != null) + { + oCurMonth.oToggle.opt.aSwappableContainers[oCurMonth.oToggle.opt.aSwappableContainers.length] = aRows[i].id; + oCurYear.oToggle.opt.aSwappableContainers[oCurYear.oToggle.opt.aSwappableContainers.length] = aRows[i].id; + } + } + + // Collapse all collapsed years! + for (i = 0; i < this.opt.aCollapsedYears.length; i++) + this.oYears[this.opt.aCollapsedYears[i]].oToggle.toggle(); +} + +smf_StatsCenter.prototype.onBeforeCollapseYear = function (oToggle) +{ + // Tell SMF that all underlying months have disappeared. + for (var sMonth in this.oYears[oToggle.opt.sYearId].oMonths) + if (this.oYears[oToggle.opt.sYearId].oMonths[sMonth].oToggle.opt.aSwappableContainers.length > 0) + this.oYears[oToggle.opt.sYearId].oMonths[sMonth].oToggle.changeState(true); +} + + +smf_StatsCenter.prototype.onBeforeCollapseMonth = function (oToggle) +{ + if (!oToggle.bCollapsed) + { + // Tell SMF that it the state has changed. + getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=stats;collapse=' + oToggle.opt.sMonthId + ';xml'); + + // Remove the month rows from the year toggle. + var aNewContainers = []; + var oYearToggle = this.oYears[oToggle.opt.sMonthId.substr(0, 4)].oToggle; + + for (var i = 0, n = oYearToggle.opt.aSwappableContainers.length; i < n; i++) + if (!in_array(oYearToggle.opt.aSwappableContainers[i], oToggle.opt.aSwappableContainers)) + aNewContainers[aNewContainers.length] = oYearToggle.opt.aSwappableContainers[i]; + + oYearToggle.opt.aSwappableContainers = aNewContainers; + } +} + + +smf_StatsCenter.prototype.onBeforeExpandMonth = function (oToggle) +{ + // Ignore if we're still loading the previous batch. + if (this.bIsLoading) + return; + + if (oToggle.opt.aSwappableContainers.length == 0) + { + // A complicated way to call getXMLDocument, but stay in scope. + this.tmpMethod = getXMLDocument; + this.oXmlRequestHandle = this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=stats;expand=' + oToggle.opt.sMonthId + ';xml', this.onDocReceived); + delete this.tmpMethod; + + if ('ajax_indicator' in window) + ajax_indicator(true); + + this.bIsLoading = true; + } + + // Silently let SMF know this one is expanded. + else + getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=stats;expand=' + oToggle.opt.sMonthId + ';xml'); +} + +smf_StatsCenter.prototype.onDocReceived = function (oXMLDoc) +{ + // Loop through all the months we got from the XML. + var aMonthNodes = oXMLDoc.getElementsByTagName('month'); + for (var iMonthIndex = 0, iNumMonths = aMonthNodes.length; iMonthIndex < iNumMonths; iMonthIndex++) + { + var sMonthId = aMonthNodes[iMonthIndex].getAttribute('id'); + var iStart = document.getElementById('tr_month_' + sMonthId).rowIndex + 1; + var sYearId = sMonthId.substr(0, 4); + + // Within the current months, check out all the days. + var aDayNodes = aMonthNodes[iMonthIndex].getElementsByTagName('day'); + for (var iDayIndex = 0, iNumDays = aDayNodes.length; iDayIndex < iNumDays; iDayIndex++) + { + var oCurRow = this.oTable.insertRow(iStart + iDayIndex); + oCurRow.className = this.opt.sDayRowClassname; + oCurRow.id = this.opt.sDayRowIdPrefix + aDayNodes[iDayIndex].getAttribute('date'); + + for (var iCellIndex = 0, iNumCells = this.opt.aDataCells.length; iCellIndex < iNumCells; iCellIndex++) + { + var oCurCell = oCurRow.insertCell(-1); + + if (this.opt.aDataCells[iCellIndex] == 'date') + oCurCell.style.paddingLeft = '6ex'; + else + oCurCell.style.textAlign = 'center'; + + var sCurData = aDayNodes[iDayIndex].getAttribute(this.opt.aDataCells[iCellIndex]); + oCurCell.appendChild(document.createTextNode(sCurData)); + } + + // Add these day rows to the toggle objects in case of collapse. + this.oYears[sYearId].oMonths[sMonthId].oToggle.opt.aSwappableContainers[this.oYears[sYearId].oMonths[sMonthId].oToggle.opt.aSwappableContainers.length] = oCurRow.id; + this.oYears[sYearId].oToggle.opt.aSwappableContainers[this.oYears[sYearId].oToggle.opt.aSwappableContainers.length] = oCurRow.id; + } + } + + this.bIsLoading = false; + if (typeof(window.ajax_indicator) == 'function') + ajax_indicator(false); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/suggest.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/suggest.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,630 @@ +// This file contains javascript associated with a autosuggest control +function smc_AutoSuggest(oOptions) +{ + this.opt = oOptions; + + // Store the handle to the text box. + this.oTextHandle = document.getElementById(this.opt.sControlId); + this.oRealTextHandle = null; + + this.oSuggestDivHandle = null; + this.sLastSearch = ''; + this.sLastDirtySearch = ''; + this.oSelectedDiv = null; + this.aCache = []; + this.aDisplayData = []; + + this.sRetrieveURL = 'sRetrieveURL' in this.opt ? this.opt.sRetrieveURL : '%scripturl%action=suggest;suggest_type=%suggest_type%;search=%search%;%sessionVar%=%sessionID%;xml;time=%time%'; + + // How many objects can we show at once? + this.iMaxDisplayQuantity = 'iMaxDisplayQuantity' in this.opt ? this.opt.iMaxDisplayQuantity : 15; + + // How many characters shall we start searching on? + this.iMinimumSearchChars = 'iMinimumSearchChars' in this.opt ? this.opt.iMinimumSearchChars : 3; + + // Should selected items be added to a list? + this.bItemList = 'bItemList' in this.opt ? this.opt.bItemList : false; + + // Are there any items that should be added in advance? + this.aListItems = 'aListItems' in this.opt ? this.opt.aListItems : []; + + this.sItemTemplate = 'sItemTemplate' in this.opt ? this.opt.sItemTemplate : '%item_name% %delete_text%'; + + this.sTextDeleteItem = 'sTextDeleteItem' in this.opt ? this.opt.sTextDeleteItem : ''; + + this.oCallback = {}; + this.bDoAutoAdd = false; + this.iItemCount = 0; + + this.oHideTimer = null; + this.bPositionComplete = false; + + this.oXmlRequestHandle = null; + + // Just make sure the page is loaded before calling the init. + addLoadEvent(this.opt.sSelf + '.init();'); +} + +smc_AutoSuggest.prototype.init = function() +{ + if (!window.XMLHttpRequest) + return false; + + // Create a div that'll contain the results later on. + this.oSuggestDivHandle = document.createElement('div'); + this.oSuggestDivHandle.className = 'auto_suggest_div'; + document.body.appendChild(this.oSuggestDivHandle); + + // Create a backup text input. + this.oRealTextHandle = document.createElement('input'); + this.oRealTextHandle.type = 'hidden'; + this.oRealTextHandle.name = this.oTextHandle.name; + this.oRealTextHandle.value = this.oTextHandle.value; + this.oTextHandle.form.appendChild(this.oRealTextHandle); + + // Disable autocomplete in any browser by obfuscating the name. + this.oTextHandle.name = 'dummy_' + Math.floor(Math.random() * 1000000); + this.oTextHandle.autocomplete = 'off'; + + this.oTextHandle.instanceRef = this; + + var fOnKeyDown = function (oEvent) { + return this.instanceRef.handleKey(oEvent); + }; + is_opera ? this.oTextHandle.onkeypress = fOnKeyDown : this.oTextHandle.onkeydown = fOnKeyDown; + + this.oTextHandle.onkeyup = function (oEvent) { + return this.instanceRef.autoSuggestUpdate(oEvent); + }; + + this.oTextHandle.onchange = function (oEvent) { + return this.instanceRef.autoSuggestUpdate(oEvent); + }; + + this.oTextHandle.onblur = function (oEvent) { + return this.instanceRef.autoSuggestHide(oEvent); + }; + + this.oTextHandle.onfocus = function (oEvent) { + return this.instanceRef.autoSuggestUpdate(oEvent); + }; + + if (this.bItemList) + { + if ('sItemListContainerId' in this.opt) + this.oItemList = document.getElementById(this.opt.sItemListContainerId); + else + { + this.oItemList = document.createElement('div'); + this.oTextHandle.parentNode.insertBefore(this.oItemList, this.oTextHandle.nextSibling); + } + } + + if (this.aListItems.length > 0) + for (var i = 0, n = this.aListItems.length; i < n; i++) + this.addItemLink(this.aListItems[i].sItemId, this.aListItems[i].sItemName); + + return true; +} + +// Was it an enter key - if so assume they are trying to select something. +smc_AutoSuggest.prototype.handleKey = function(oEvent) +{ + // Grab the event object, one way or the other + if (!oEvent) + oEvent = window.event; + + // Get the keycode of the key that was pressed. + var iKeyPress = 0; + if ('keyCode' in oEvent) + iKeyPress = oEvent.keyCode; + else if ('which' in oEvent) + iKeyPress = oEvent.which; + + switch (iKeyPress) + { + // Tab. + case 9: + if (this.aDisplayData.length > 0) + { + if (this.oSelectedDiv != null) + this.itemClicked(this.oSelectedDiv); + else + this.handleSubmit(); + } + + // Continue to the next control. + return true; + break; + + // Enter. + case 13: + if (this.aDisplayData.length > 0 && this.oSelectedDiv != null) + { + this.itemClicked(this.oSelectedDiv); + + // Do our best to stop it submitting the form! + return false; + } + else + return true; + + break; + + // Up/Down arrow? + case 38: + case 40: + if (this.aDisplayData.length && this.oSuggestDivHandle.style.visibility != 'hidden') + { + // Loop through the display data trying to find our entry. + var bPrevHandle = false; + var oToHighlight = null; + for (var i = 0; i < this.aDisplayData.length; i++) + { + // If we're going up and yet the top one was already selected don't go around. + if (this.oSelectedDiv != null && this.oSelectedDiv == this.aDisplayData[i] && i == 0 && iKeyPress == 38) + { + oToHighlight = this.oSelectedDiv; + break; + } + // If nothing is selected and we are going down then we select the first one. + if (this.oSelectedDiv == null && iKeyPress == 40) + { + oToHighlight = this.aDisplayData[i]; + break; + } + + // If the previous handle was the actual previously selected one and we're hitting down then this is the one we want. + if (bPrevHandle != false && bPrevHandle == this.oSelectedDiv && iKeyPress == 40) + { + oToHighlight = this.aDisplayData[i]; + break; + } + // If we're going up and this is the previously selected one then we want the one before, if there was one. + if (bPrevHandle != false && this.aDisplayData[i] == this.oSelectedDiv && iKeyPress == 38) + { + oToHighlight = bPrevHandle; + break; + } + // Make the previous handle this! + bPrevHandle = this.aDisplayData[i]; + } + + // If we don't have one to highlight by now then it must be the last one that we're after. + if (oToHighlight == null) + oToHighlight = bPrevHandle; + + // Remove any old highlighting. + if (this.oSelectedDiv != null) + this.itemMouseOut(this.oSelectedDiv); + // Mark what the selected div now is. + this.oSelectedDiv = oToHighlight; + this.itemMouseOver(this.oSelectedDiv); + } + break; + } + return true; +} + +// Functions for integration. +smc_AutoSuggest.prototype.registerCallback = function(sCallbackType, sCallback) +{ + switch (sCallbackType) + { + case 'onBeforeAddItem': + this.oCallback.onBeforeAddItem = sCallback; + break; + + case 'onAfterAddItem': + this.oCallback.onAfterAddItem = sCallback; + break; + + case 'onAfterDeleteItem': + this.oCallback.onAfterDeleteItem = sCallback; + break; + + case 'onBeforeUpdate': + this.oCallback.onBeforeUpdate = sCallback; + break; + } +} + +// User hit submit? +smc_AutoSuggest.prototype.handleSubmit = function() +{ + var bReturnValue = true; + var oFoundEntry = null; + + // Do we have something that matches the current text? + for (var i = 0; i < this.aCache.length; i++) + { + if (this.sLastSearch.toLowerCase() == this.aCache[i].sItemName.toLowerCase().substr(0, this.sLastSearch.length)) + { + // Exact match? + if (this.sLastSearch.length == this.aCache[i].sItemName.length) + { + // This is the one! + oFoundEntry = { + sItemId: this.aCache[i].sItemId, + sItemName: this.aCache[i].sItemName + }; + break; + } + + // Not an exact match, but it'll do for now. + else + { + // If we have two matches don't find anything. + if (oFoundEntry != null) + bReturnValue = false; + else + oFoundEntry = { + sItemId: this.aCache[i].sItemId, + sItemName: this.aCache[i].sItemName + }; + } + } + } + + if (oFoundEntry == null || bReturnValue == false) + return bReturnValue; + else + { + this.addItemLink(oFoundEntry.sItemId, oFoundEntry.sItemName, true); + return false; + } +} + +// Positions the box correctly on the window. +smc_AutoSuggest.prototype.positionDiv = function() +{ + // Only do it once. + if (this.bPositionComplete) + return true; + + this.bPositionComplete = true; + + // Put the div under the text box. + var aParentPos = smf_itemPos(this.oTextHandle); + + this.oSuggestDivHandle.style.left = aParentPos[0] + 'px'; + this.oSuggestDivHandle.style.top = (aParentPos[1] + this.oTextHandle.offsetHeight) + 'px'; + this.oSuggestDivHandle.style.width = this.oTextHandle.style.width; + + return true; +} + +// Do something after clicking an item. +smc_AutoSuggest.prototype.itemClicked = function(oCurElement) +{ + // Is there a div that we are populating? + if (this.bItemList) + this.addItemLink(oCurElement.sItemId, oCurElement.innerHTML); + + // Otherwise clear things down. + else + this.oTextHandle.value = oCurElement.innerHTML.php_unhtmlspecialchars(); + + this.oRealTextHandle.value = this.oTextHandle.value; + this.autoSuggestActualHide(); + this.oSelectedDiv = null; +} + +// Remove the last searched for name from the search box. +smc_AutoSuggest.prototype.removeLastSearchString = function () +{ + // Remove the text we searched for from the div. + var sTempText = this.oTextHandle.value.toLowerCase(); + var iStartString = sTempText.indexOf(this.sLastSearch.toLowerCase()); + // Just attempt to remove the bits we just searched for. + if (iStartString != -1) + { + while (iStartString > 0) + { + if (sTempText.charAt(iStartString - 1) == '"' || sTempText.charAt(iStartString - 1) == ',' || sTempText.charAt(iStartString - 1) == ' ') + { + iStartString--; + if (sTempText.charAt(iStartString - 1) == ',') + break; + } + else + break; + } + + // Now remove anything from iStartString upwards. + this.oTextHandle.value = this.oTextHandle.value.substr(0, iStartString); + } + // Just take it all. + else + this.oTextHandle.value = ''; +} + +// Add a result if not already done. +smc_AutoSuggest.prototype.addItemLink = function (sItemId, sItemName, bFromSubmit) +{ + // Increase the internal item count. + this.iItemCount ++; + + // If there's a callback then call it. + if ('oCallback' in this && 'onBeforeAddItem' in this.oCallback && typeof(this.oCallback.onBeforeAddItem) == 'string') + { + // If it returns false the item must not be added. + if (!eval(this.oCallback.onBeforeAddItem + '(' + this.opt.sSelf + ', \'' + sItemId + '\');')) + return; + } + + var oNewDiv = document.createElement('div'); + oNewDiv.id = 'suggest_' + this.opt.sSuggestId + '_' + sItemId; + setInnerHTML(oNewDiv, this.sItemTemplate.replace(/%post_name%/g, this.opt.sPostName).replace(/%item_id%/g, sItemId).replace(/%item_href%/g, smf_prepareScriptUrl(smf_scripturl) + this.opt.sURLMask.replace(/%item_id%/g, sItemId)).replace(/%item_name%/g, sItemName).replace(/%images_url%/g, smf_images_url).replace(/%self%/g, this.opt.sSelf).replace(/%delete_text%/g, this.sTextDeleteItem)); + this.oItemList.appendChild(oNewDiv); + + // If there's a registered callback, call it. + if ('oCallback' in this && 'onAfterAddItem' in this.oCallback && typeof(this.oCallback.onAfterAddItem) == 'string') + eval(this.oCallback.onAfterAddItem + '(' + this.opt.sSelf + ', \'' + oNewDiv.id + '\', ' + this.iItemCount + ');'); + + // Clear the div a bit. + this.removeLastSearchString(); + + // If we came from a submit, and there's still more to go, turn on auto add for all the other things. + this.bDoAutoAdd = this.oTextHandle.value != '' && bFromSubmit; + + // Update the fellow.. + this.autoSuggestUpdate(); +} + +// Delete an item that has been added, if at all? +smc_AutoSuggest.prototype.deleteAddedItem = function (sItemId) +{ + var oDiv = document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId); + + // Remove the div if it exists. + if (typeof(oDiv) == 'object' && oDiv != null) + { + oDiv.parentNode.removeChild(document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId)); + + // Decrease the internal item count. + this.iItemCount --; + + // If there's a registered callback, call it. + if ('oCallback' in this && 'onAfterDeleteItem' in this.oCallback && typeof(this.oCallback.onAfterDeleteItem) == 'string') + eval(this.oCallback.onAfterDeleteItem + '(' + this.opt.sSelf + ', ' + this.iItemCount + ');'); + } + + return false; +} + +// Hide the box. +smc_AutoSuggest.prototype.autoSuggestHide = function () +{ + // Delay to allow events to propogate through.... + this.oHideTimer = setTimeout(this.opt.sSelf + '.autoSuggestActualHide();', 250); +} + +// Do the actual hiding after a timeout. +smc_AutoSuggest.prototype.autoSuggestActualHide = function() +{ + this.oSuggestDivHandle.style.display = 'none'; + this.oSuggestDivHandle.style.visibility = 'hidden'; + this.oSelectedDiv = null; +} + +// Show the box. +smc_AutoSuggest.prototype.autoSuggestShow = function() +{ + if (this.oHideTimer) + { + clearTimeout(this.oHideTimer); + this.oHideTimer = false; + } + + this.positionDiv(); + + this.oSuggestDivHandle.style.visibility = 'visible'; + this.oSuggestDivHandle.style.display = ''; +} + +// Populate the actual div. +smc_AutoSuggest.prototype.populateDiv = function(aResults) +{ + // Cannot have any children yet. + while (this.oSuggestDivHandle.childNodes.length > 0) + { + // Tidy up the events etc too. + this.oSuggestDivHandle.childNodes[0].onmouseover = null; + this.oSuggestDivHandle.childNodes[0].onmouseout = null; + this.oSuggestDivHandle.childNodes[0].onclick = null; + + this.oSuggestDivHandle.removeChild(this.oSuggestDivHandle.childNodes[0]); + } + + // Something to display? + if (typeof(aResults) == 'undefined') + { + this.aDisplayData = []; + return false; + } + + var aNewDisplayData = []; + for (var i = 0; i < (aResults.length > this.iMaxDisplayQuantity ? this.iMaxDisplayQuantity : aResults.length); i++) + { + // Create the sub element + var oNewDivHandle = document.createElement('div'); + oNewDivHandle.sItemId = aResults[i].sItemId; + oNewDivHandle.className = 'auto_suggest_item'; + oNewDivHandle.innerHTML = aResults[i].sItemName; + //oNewDivHandle.style.width = this.oTextHandle.style.width; + + this.oSuggestDivHandle.appendChild(oNewDivHandle); + + // Attach some events to it so we can do stuff. + oNewDivHandle.instanceRef = this; + oNewDivHandle.onmouseover = function (oEvent) + { + this.instanceRef.itemMouseOver(this); + } + oNewDivHandle.onmouseout = function (oEvent) + { + this.instanceRef.itemMouseOut(this); + } + oNewDivHandle.onclick = function (oEvent) + { + this.instanceRef.itemClicked(this); + } + + + aNewDisplayData[i] = oNewDivHandle; + } + + this.aDisplayData = aNewDisplayData; + + return true; +} + +// Refocus the element. +smc_AutoSuggest.prototype.itemMouseOver = function (oCurElement) +{ + this.oSelectedDiv = oCurElement; + oCurElement.className = 'auto_suggest_item_hover'; +} + +// Onfocus the element +smc_AutoSuggest.prototype.itemMouseOut = function (oCurElement) +{ + oCurElement.className = 'auto_suggest_item'; +} + +smc_AutoSuggest.prototype.onSuggestionReceived = function (oXMLDoc) +{ + var sQuoteText = ''; + var aItems = oXMLDoc.getElementsByTagName('item'); + this.aCache = []; + for (var i = 0; i < aItems.length; i++) + { + this.aCache[i] = { + sItemId: aItems[i].getAttribute('id'), + sItemName: aItems[i].childNodes[0].nodeValue + }; + + // If we're doing auto add and we find the exact person, then add them! + if (this.bDoAutoAdd && this.sLastSearch == this.aCache[i].sItemName) + { + var oReturnValue = { + sItemId: this.aCache[i].sItemId, + sItemName: this.aCache[i].sItemName + }; + this.aCache = []; + return this.addItemLink(oReturnValue.sItemId, oReturnValue.sItemName, true); + } + } + + // Check we don't try to keep auto updating! + this.bDoAutoAdd = false; + + // Populate the div. + this.populateDiv(this.aCache); + + // Make sure we can see it - if we can. + if (aItems.length == 0) + this.autoSuggestHide(); + else + this.autoSuggestShow(); + + return true; +} + +// Get a new suggestion. +smc_AutoSuggest.prototype.autoSuggestUpdate = function () +{ + // If there's a callback then call it. + if ('onBeforeUpdate' in this.oCallback && typeof(this.oCallback.onBeforeUpdate) == 'string') + { + // If it returns false the item must not be added. + if (!eval(this.oCallback.onBeforeUpdate + '(' + this.opt.sSelf + ');')) + return false; + } + + this.oRealTextHandle.value = this.oTextHandle.value; + + if (isEmptyText(this.oTextHandle)) + { + this.aCache = []; + + this.populateDiv(); + + this.autoSuggestHide(); + + return true; + } + + // Nothing changed? + if (this.oTextHandle.value == this.sLastDirtySearch) + return true; + this.sLastDirtySearch = this.oTextHandle.value; + + // We're only actually interested in the last string. + var sSearchString = this.oTextHandle.value.replace(/^("[^"]+",[ ]*)+/, '').replace(/^([^,]+,[ ]*)+/, ''); + if (sSearchString.substr(0, 1) == '"') + sSearchString = sSearchString.substr(1); + + // Stop replication ASAP. + var sRealLastSearch = this.sLastSearch; + this.sLastSearch = sSearchString; + + // Either nothing or we've completed a sentance. + if (sSearchString == '' || sSearchString.substr(sSearchString.length - 1) == '"') + { + this.populateDiv(); + return true; + } + + // Nothing? + if (sRealLastSearch == sSearchString) + return true; + + // Too small? + else if (sSearchString.length < this.iMinimumSearchChars) + { + this.aCache = []; + this.autoSuggestHide(); + return true; + } + else if (sSearchString.substr(0, sRealLastSearch.length) == sRealLastSearch) + { + // Instead of hitting the server again, just narrow down the results... + var aNewCache = []; + var j = 0; + var sLowercaseSearch = sSearchString.toLowerCase(); + for (var k = 0; k < this.aCache.length; k++) + { + if (this.aCache[k].sItemName.substr(0, sSearchString.length).toLowerCase() == sLowercaseSearch) + aNewCache[j++] = this.aCache[k]; + } + + this.aCache = []; + if (aNewCache.length != 0) + { + this.aCache = aNewCache; + // Repopulate. + this.populateDiv(this.aCache); + + // Check it can be seen. + this.autoSuggestShow(); + + return true; + } + } + + // In progress means destroy! + if (typeof(this.oXmlRequestHandle) == 'object' && this.oXmlRequestHandle != null) + this.oXmlRequestHandle.abort(); + + // Clean the text handle. + sSearchString = sSearchString.php_to8bit().php_urlencode(); + + // Get the document. + this.tmpMethod = getXMLDocument; + this.oXmlRequestHandle = this.tmpMethod(this.sRetrieveURL.replace(/%scripturl%/g, smf_prepareScriptUrl(smf_scripturl)).replace(/%suggest_type%/g, this.opt.sSearchType).replace(/%search%/g, sSearchString).replace(/%sessionVar%/g, this.opt.sSessionVar).replace(/%sessionID%/g, this.opt.sSessionId).replace(/%time%/g, new Date().getTime()), this.onSuggestionReceived); + delete this.tmpMethod; + + return true; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/theme.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/theme.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,96 @@ +// The purpose of this code is to fix the height of overflow: auto blocks, because some browsers can't figure it out for themselves. +function smf_codeBoxFix() +{ + var codeFix = document.getElementsByTagName('code'); + for (var i = codeFix.length - 1; i >= 0; i--) + { + if (is_webkit && codeFix[i].offsetHeight < 20) + codeFix[i].style.height = (codeFix[i].offsetHeight + 20) + 'px'; + + else if (is_ff && (codeFix[i].scrollWidth > codeFix[i].clientWidth || codeFix[i].clientWidth == 0)) + codeFix[i].style.overflow = 'scroll'; + + else if ('currentStyle' in codeFix[i] && codeFix[i].currentStyle.overflow == 'auto' && (codeFix[i].currentStyle.height == '' || codeFix[i].currentStyle.height == 'auto') && (codeFix[i].scrollWidth > codeFix[i].clientWidth || codeFix[i].clientWidth == 0) && (codeFix[i].offsetHeight != 0)) + codeFix[i].style.height = (codeFix[i].offsetHeight + 24) + 'px'; + } +} + +// Add a fix for code stuff? +if ((is_ie && !is_ie4) || is_webkit || is_ff) + addLoadEvent(smf_codeBoxFix); + +// Toggles the element height and width styles of an image. +function smc_toggleImageDimensions() +{ + var oImages = document.getElementsByTagName('IMG'); + for (oImage in oImages) + { + // Not a resized image? Skip it. + if (oImages[oImage].className == undefined || oImages[oImage].className.indexOf('bbc_img resized') == -1) + continue; + + oImages[oImage].style.cursor = 'pointer'; + oImages[oImage].onclick = function() { + this.style.width = this.style.height = this.style.width == 'auto' ? null : 'auto'; + }; + } +} + +// Add a load event for the function above. +addLoadEvent(smc_toggleImageDimensions); + +// Adds a button to a certain button strip. +function smf_addButton(sButtonStripId, bUseImage, oOptions) +{ + var oButtonStrip = document.getElementById(sButtonStripId); + var aItems = oButtonStrip.getElementsByTagName('span'); + + // Remove the 'last' class from the last item. + if (aItems.length > 0) + { + var oLastSpan = aItems[aItems.length - 1]; + oLastSpan.className = oLastSpan.className.replace(/\s*last/, 'position_holder'); + } + + // Add the button. + var oButtonStripList = oButtonStrip.getElementsByTagName('ul')[0]; + var oNewButton = document.createElement('li'); + setInnerHTML(oNewButton, '' + oOptions.sText + ''); + + oButtonStripList.appendChild(oNewButton); +} + +// Adds hover events to list items. Used for a versions of IE that don't support this by default. +var smf_addListItemHoverEvents = function() +{ + var cssRule, newSelector; + + // Add a rule for the list item hover event to every stylesheet. + for (var iStyleSheet = 0; iStyleSheet < document.styleSheets.length; iStyleSheet ++) + for (var iRule = 0; iRule < document.styleSheets[iStyleSheet].rules.length; iRule ++) + { + oCssRule = document.styleSheets[iStyleSheet].rules[iRule]; + if (oCssRule.selectorText.indexOf('LI:hover') != -1) + { + sNewSelector = oCssRule.selectorText.replace(/LI:hover/gi, 'LI.iehover'); + document.styleSheets[iStyleSheet].addRule(sNewSelector, oCssRule.style.cssText); + } + } + + // Now add handling for these hover events. + var oListItems = document.getElementsByTagName('LI'); + for (oListItem in oListItems) + { + oListItems[oListItem].onmouseover = function() { + this.className += ' iehover'; + }; + + oListItems[oListItem].onmouseout = function() { + this.className = this.className.replace(new RegExp(' iehover\\b'), ''); + }; + } +} + +// Add hover events to list items if the browser requires it. +if (is_ie7down && 'attachEvent' in window) + window.attachEvent('onload', smf_addListItemHoverEvents); diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/scripts/topic.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/scripts/topic.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,541 @@ +var cur_topic_id, cur_msg_id, buff_subject, cur_subject_div, in_edit_mode = 0; +var hide_prefixes = Array(); + +function modify_topic(topic_id, first_msg_id) +{ + if (!('XMLHttpRequest' in window)) + return; + + if ('opera' in window) + { + var oTest = new XMLHttpRequest(); + if (!('setRequestHeader' in oTest)) + return; + } + + // Add backwards compatibility with old themes. + if (typeof(cur_session_var) == 'undefined') + cur_session_var = 'sesc'; + + if (in_edit_mode == 1) + { + if (cur_topic_id == topic_id) + return; + else + modify_topic_cancel(); + } + + in_edit_mode = 1; + mouse_on_div = 1; + cur_topic_id = topic_id; + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + "action=quotefast;quote=" + first_msg_id + ";modify;xml", onDocReceived_modify_topic); +} + +function onDocReceived_modify_topic(XMLDoc) +{ + cur_msg_id = XMLDoc.getElementsByTagName("message")[0].getAttribute("id"); + + cur_subject_div = document.getElementById('msg_' + cur_msg_id.substr(4)); + buff_subject = getInnerHTML(cur_subject_div); + + // Here we hide any other things they want hiding on edit. + set_hidden_topic_areas('none'); + + modify_topic_show_edit(XMLDoc.getElementsByTagName("subject")[0].childNodes[0].nodeValue); + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + +function modify_topic_cancel() +{ + setInnerHTML(cur_subject_div, buff_subject); + set_hidden_topic_areas(''); + + in_edit_mode = 0; + return false; +} + +function modify_topic_save(cur_session_id, cur_session_var) +{ + if (!in_edit_mode) + return true; + + // Add backwards compatibility with old themes. + if (typeof(cur_session_var) == 'undefined') + cur_session_var = 'sesc'; + + var i, x = new Array(); + x[x.length] = 'subject=' + document.forms.quickModForm['subject'].value.replace(/&#/g, "&#").php_to8bit().php_urlencode(); + x[x.length] = 'topic=' + parseInt(document.forms.quickModForm.elements['topic'].value); + x[x.length] = 'msg=' + parseInt(document.forms.quickModForm.elements['msg'].value); + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + sendXMLDocument(smf_prepareScriptUrl(smf_scripturl) + "action=jsmodify;topic=" + parseInt(document.forms.quickModForm.elements['topic'].value) + ";" + cur_session_var + "=" + cur_session_id + ";xml", x.join("&"), modify_topic_done); + + return false; +} + +function modify_topic_done(XMLDoc) +{ + if (!XMLDoc) + { + modify_topic_cancel(); + return true; + } + + var message = XMLDoc.getElementsByTagName("smf")[0].getElementsByTagName("message")[0]; + var subject = message.getElementsByTagName("subject")[0]; + var error = message.getElementsByTagName("error")[0]; + + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); + + if (!subject || error) + return false; + + subjectText = subject.childNodes[0].nodeValue; + + modify_topic_hide_edit(subjectText); + + set_hidden_topic_areas(''); + + in_edit_mode = 0; + + return false; +} + +// Simply restore any hidden bits during topic editing. +function set_hidden_topic_areas(set_style) +{ + for (var i = 0; i < hide_prefixes.length; i++) + { + if (document.getElementById(hide_prefixes[i] + cur_msg_id.substr(4)) != null) + document.getElementById(hide_prefixes[i] + cur_msg_id.substr(4)).style.display = set_style; + } +} + +// *** QuickReply object. +function QuickReply(oOptions) +{ + this.opt = oOptions; + this.bCollapsed = this.opt.bDefaultCollapsed; +} + +// When a user presses quote, put it in the quick reply box (if expanded). +QuickReply.prototype.quote = function (iMessageId, xDeprecated) +{ + // Compatibility with older templates. + if (typeof(xDeprecated) != 'undefined') + return true; + + if (this.bCollapsed) + { + window.location.href = smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=post;quote=' + iMessageId + ';topic=' + this.opt.iTopicId + '.' + this.opt.iStart; + return false; + } + else + { + // Doing it the XMLhttp way? + if (window.XMLHttpRequest) + { + ajax_indicator(true); + getXMLDocument(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=quotefast;quote=' + iMessageId + ';xml', this.onQuoteReceived); + } + // Or with a smart popup! + else + reqWin(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=quotefast;quote=' + iMessageId, 240, 90); + + // Move the view to the quick reply box. + if (navigator.appName == 'Microsoft Internet Explorer') + window.location.hash = this.opt.sJumpAnchor; + else + window.location.hash = '#' + this.opt.sJumpAnchor; + + return false; + } +} + +// This is the callback function used after the XMLhttp request. +QuickReply.prototype.onQuoteReceived = function (oXMLDoc) +{ + var sQuoteText = ''; + + for (var i = 0; i < oXMLDoc.getElementsByTagName('quote')[0].childNodes.length; i++) + sQuoteText += oXMLDoc.getElementsByTagName('quote')[0].childNodes[i].nodeValue; + + replaceText(sQuoteText, document.forms.postmodify.message); + + ajax_indicator(false); +} + +// The function handling the swapping of the quick reply. +QuickReply.prototype.swap = function () +{ + document.getElementById(this.opt.sImageId).src = this.opt.sImagesUrl + "/" + (this.bCollapsed ? this.opt.sImageCollapsed : this.opt.sImageExpanded); + document.getElementById(this.opt.sContainerId).style.display = this.bCollapsed ? '' : 'none'; + + this.bCollapsed = !this.bCollapsed; +} + +// *** QuickModify object. +function QuickModify(oOptions) +{ + this.opt = oOptions; + this.bInEditMode = false; + this.sCurMessageId = ''; + this.oCurMessageDiv = null; + this.oCurSubjectDiv = null; + this.sMessageBuffer = ''; + this.sSubjectBuffer = ''; + this.bXmlHttpCapable = this.isXmlHttpCapable(); + + // Show the edit buttons + if (this.bXmlHttpCapable) + { + for (var i = document.images.length - 1; i >= 0; i--) + if (document.images[i].id.substr(0, 14) == 'modify_button_') + document.images[i].style.display = ''; + } +} + +// Determine whether the quick modify can actually be used. +QuickModify.prototype.isXmlHttpCapable = function () +{ + if (typeof(window.XMLHttpRequest) == 'undefined') + return false; + + // Opera didn't always support POST requests. So test it first. + if ('opera' in window) + { + var oTest = new XMLHttpRequest(); + if (!('setRequestHeader' in oTest)) + return false; + } + + return true; +} + +// Function called when a user presses the edit button. +QuickModify.prototype.modifyMsg = function (iMessageId) +{ + if (!this.bXmlHttpCapable) + return; + + // Add backwards compatibility with old themes. + if (typeof(sSessionVar) == 'undefined') + sSessionVar = 'sesc'; + + // First cancel if there's another message still being edited. + if (this.bInEditMode) + this.modifyCancel(); + + // At least NOW we're in edit mode + this.bInEditMode = true; + + // Send out the XMLhttp request to get more info + ajax_indicator(true); + + // For IE 5.0 support, 'call' is not yet used. + this.tmpMethod = getXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=quotefast;quote=' + iMessageId + ';modify;xml', this.onMessageReceived); + delete this.tmpMethod; +} + +// The callback function used for the XMLhttp request retrieving the message. +QuickModify.prototype.onMessageReceived = function (XMLDoc) +{ + var sBodyText = '', sSubjectText = ''; + + // No longer show the 'loading...' sign. + ajax_indicator(false); + + // Grab the message ID. + this.sCurMessageId = XMLDoc.getElementsByTagName('message')[0].getAttribute('id'); + + // If this is not valid then simply give up. + if (!document.getElementById(this.sCurMessageId)) + return this.modifyCancel(); + + // Replace the body part. + for (var i = 0; i < XMLDoc.getElementsByTagName("message")[0].childNodes.length; i++) + sBodyText += XMLDoc.getElementsByTagName("message")[0].childNodes[i].nodeValue; + this.oCurMessageDiv = document.getElementById(this.sCurMessageId); + this.sMessageBuffer = getInnerHTML(this.oCurMessageDiv); + + // We have to force the body to lose its dollar signs thanks to IE. + sBodyText = sBodyText.replace(/\$/g, '{&dollarfix;$}'); + + // Actually create the content, with a bodge for disappearing dollar signs. + setInnerHTML(this.oCurMessageDiv, this.opt.sTemplateBodyEdit.replace(/%msg_id%/g, this.sCurMessageId.substr(4)).replace(/%body%/, sBodyText).replace(/\{&dollarfix;\$\}/g, '$')); + + // Replace the subject part. + this.oCurSubjectDiv = document.getElementById('subject_' + this.sCurMessageId.substr(4)); + this.sSubjectBuffer = getInnerHTML(this.oCurSubjectDiv); + + sSubjectText = XMLDoc.getElementsByTagName('subject')[0].childNodes[0].nodeValue.replace(/\$/g, '{&dollarfix;$}'); + setInnerHTML(this.oCurSubjectDiv, this.opt.sTemplateSubjectEdit.replace(/%subject%/, sSubjectText).replace(/\{&dollarfix;\$\}/g, '$')); + + return true; +} + +// Function in case the user presses cancel (or other circumstances cause it). +QuickModify.prototype.modifyCancel = function () +{ + // Roll back the HTML to its original state. + if (this.oCurMessageDiv) + { + setInnerHTML(this.oCurMessageDiv, this.sMessageBuffer); + setInnerHTML(this.oCurSubjectDiv, this.sSubjectBuffer); + } + + // No longer in edit mode, that's right. + this.bInEditMode = false; + + return false; +} + +// The function called after a user wants to save his precious message. +QuickModify.prototype.modifySave = function (sSessionId, sSessionVar) +{ + // We cannot save if we weren't in edit mode. + if (!this.bInEditMode) + return true; + + // Add backwards compatibility with old themes. + if (typeof(sSessionVar) == 'undefined') + sSessionVar = 'sesc'; + + var i, x = new Array(); + x[x.length] = 'subject=' + escape(document.forms.quickModForm['subject'].value.replace(/&#/g, "&#").php_to8bit()).replace(/\+/g, "%2B"); + x[x.length] = 'message=' + escape(document.forms.quickModForm['message'].value.replace(/&#/g, "&#").php_to8bit()).replace(/\+/g, "%2B"); + x[x.length] = 'topic=' + parseInt(document.forms.quickModForm.elements['topic'].value); + x[x.length] = 'msg=' + parseInt(document.forms.quickModForm.elements['msg'].value); + + // Send in the XMLhttp request and let's hope for the best. + ajax_indicator(true); + sendXMLDocument.call(this, smf_prepareScriptUrl(this.opt.sScriptUrl) + "action=jsmodify;topic=" + this.opt.iTopicId + ";" + sSessionVar + "=" + sSessionId + ";xml", x.join("&"), this.onModifyDone); + + return false; +} + +// Callback function of the XMLhttp request sending the modified message. +QuickModify.prototype.onModifyDone = function (XMLDoc) +{ + // We've finished the loading stuff. + ajax_indicator(false); + + // If we didn't get a valid document, just cancel. + if (!XMLDoc || !XMLDoc.getElementsByTagName('smf')[0]) + { + // Mozilla will nicely tell us what's wrong. + if (XMLDoc.childNodes.length > 0 && XMLDoc.firstChild.nodeName == 'parsererror') + setInnerHTML(document.getElementById('error_box'), XMLDoc.firstChild.textContent); + else + this.modifyCancel(); + return; + } + + var message = XMLDoc.getElementsByTagName('smf')[0].getElementsByTagName('message')[0]; + var body = message.getElementsByTagName('body')[0]; + var error = message.getElementsByTagName('error')[0]; + + if (body) + { + // Show new body. + var bodyText = ''; + for (var i = 0; i < body.childNodes.length; i++) + bodyText += body.childNodes[i].nodeValue; + + this.sMessageBuffer = this.opt.sTemplateBodyNormal.replace(/%body%/, bodyText.replace(/\$/g, '{&dollarfix;$}')).replace(/\{&dollarfix;\$\}/g,'$'); + setInnerHTML(this.oCurMessageDiv, this.sMessageBuffer); + + // Show new subject. + var oSubject = message.getElementsByTagName('subject')[0]; + var sSubjectText = oSubject.childNodes[0].nodeValue.replace(/\$/g, '{&dollarfix;$}'); + this.sSubjectBuffer = this.opt.sTemplateSubjectNormal.replace(/%msg_id%/g, this.sCurMessageId.substr(4)).replace(/%subject%/, sSubjectText).replace(/\{&dollarfix;\$\}/g,'$'); + setInnerHTML(this.oCurSubjectDiv, this.sSubjectBuffer); + + // If this is the first message, also update the topic subject. + if (oSubject.getAttribute('is_first') == '1') + setInnerHTML(document.getElementById('top_subject'), this.opt.sTemplateTopSubject.replace(/%subject%/, sSubjectText).replace(/\{&dollarfix;\$\}/g, '$')); + + // Show this message as 'modified on x by y'. + if (this.opt.bShowModify) + setInnerHTML(document.getElementById('modified_' + this.sCurMessageId.substr(4)), message.getElementsByTagName('modified')[0].childNodes[0].nodeValue); + } + else if (error) + { + setInnerHTML(document.getElementById('error_box'), error.childNodes[0].nodeValue); + document.forms.quickModForm.message.style.border = error.getAttribute('in_body') == '1' ? this.opt.sErrorBorderStyle : ''; + document.forms.quickModForm.subject.style.border = error.getAttribute('in_subject') == '1' ? this.opt.sErrorBorderStyle : ''; + } +} + +function InTopicModeration(oOptions) +{ + this.opt = oOptions; + this.bButtonsShown = false; + this.iNumSelected = 0; + + // Add backwards compatibility with old themes. + if (typeof(this.opt.sSessionVar) == 'undefined') + this.opt.sSessionVar = 'sesc'; + + this.init(); +} + +InTopicModeration.prototype.init = function() +{ + // Add checkboxes to all the messages. + for (var i = 0, n = this.opt.aMessageIds.length; i < n; i++) + { + // Create the checkbox. + var oCheckbox = document.createElement('input'); + oCheckbox.type = 'checkbox'; + oCheckbox.className = 'input_check'; + oCheckbox.name = 'msgs[]'; + oCheckbox.value = this.opt.aMessageIds[i]; + oCheckbox.instanceRef = this; + oCheckbox.onclick = function () { + this.instanceRef.handleClick(this); + } + + // Append it to the container + var oCheckboxContainer = document.getElementById(this.opt.sCheckboxContainerMask + this.opt.aMessageIds[i]); + oCheckboxContainer.appendChild(oCheckbox); + oCheckboxContainer.style.display = ''; + } +} + +InTopicModeration.prototype.handleClick = function(oCheckbox) +{ + if (!this.bButtonsShown && this.opt.sButtonStripDisplay) + { + var oButtonStrip = document.getElementById(this.opt.sButtonStrip); + var oButtonStripDisplay = document.getElementById(this.opt.sButtonStripDisplay); + + // Make sure it can go somewhere. + if (typeof(oButtonStripDisplay) == 'object' && oButtonStripDisplay != null) + oButtonStripDisplay.style.display = ""; + else + { + var oNewDiv = document.createElement('div'); + var oNewList = document.createElement('ul'); + + oNewDiv.id = this.opt.sButtonStripDisplay; + oNewDiv.className = this.opt.sButtonStripClass ? this.opt.sButtonStripClass : 'buttonlist floatbottom'; + + oNewDiv.appendChild(oNewList); + oButtonStrip.appendChild(oNewDiv); + } + + // Add the 'remove selected items' button. + if (this.opt.bCanRemove) + smf_addButton(this.opt.sButtonStrip, this.opt.bUseImageButton, { + sId: this.opt.sSelf + '_remove_button', + sText: this.opt.sRemoveButtonLabel, + sImage: this.opt.sRemoveButtonImage, + sUrl: '#', + sCustom: ' onclick="return ' + this.opt.sSelf + '.handleSubmit(\'remove\')"' + }); + + // Add the 'restore selected items' button. + if (this.opt.bCanRestore) + smf_addButton(this.opt.sButtonStrip, this.opt.bUseImageButton, { + sId: this.opt.sSelf + '_restore_button', + sText: this.opt.sRestoreButtonLabel, + sImage: this.opt.sRestoreButtonImage, + sUrl: '#', + sCustom: ' onclick="return ' + this.opt.sSelf + '.handleSubmit(\'restore\')"' + }); + + // Adding these buttons once should be enough. + this.bButtonsShown = true; + } + + // Keep stats on how many items were selected. + this.iNumSelected += oCheckbox.checked ? 1 : -1; + + // Show the number of messages selected in the button. + if (this.opt.bCanRemove && !this.opt.bUseImageButton) + { + setInnerHTML(document.getElementById(this.opt.sSelf + '_remove_button'), this.opt.sRemoveButtonLabel + ' [' + this.iNumSelected + ']'); + document.getElementById(this.opt.sSelf + '_remove_button').style.display = this.iNumSelected < 1 ? "none" : ""; + } + + if (this.opt.bCanRestore && !this.opt.bUseImageButton) + { + setInnerHTML(document.getElementById(this.opt.sSelf + '_restore_button'), this.opt.sRestoreButtonLabel + ' [' + this.iNumSelected + ']'); + document.getElementById(this.opt.sSelf + '_restore_button').style.display = this.iNumSelected < 1 ? "none" : ""; + } + + // Try to restore the correct position. + var aItems = document.getElementById(this.opt.sButtonStrip).getElementsByTagName('span'); + if (aItems.length > 3) + { + if (this.iNumSelected < 1) + { + aItems[aItems.length - 3].className = aItems[aItems.length - 3].className.replace(/\s*position_holder/, 'last'); + aItems[aItems.length - 2].className = aItems[aItems.length - 2].className.replace(/\s*position_holder/, 'last'); + } + else + { + aItems[aItems.length - 2].className = aItems[aItems.length - 2].className.replace(/\s*last/, 'position_holder'); + aItems[aItems.length - 3].className = aItems[aItems.length - 3].className.replace(/\s*last/, 'position_holder'); + } + } +} + +InTopicModeration.prototype.handleSubmit = function (sSubmitType) +{ + var oForm = document.getElementById(this.opt.sFormId); + + // Make sure this form isn't submitted in another way than this function. + var oInput = document.createElement('input'); + oInput.type = 'hidden'; + oInput.name = this.opt.sSessionVar; + oInput.value = this.opt.sSessionId; + oForm.appendChild(oInput); + + switch (sSubmitType) + { + case 'remove': + if (!confirm(this.opt.sRemoveButtonConfirm)) + return false; + + oForm.action = oForm.action.replace(/;restore_selected=1/, ''); + break; + + case 'restore': + if (!confirm(this.opt.sRestoreButtonConfirm)) + return false; + + oForm.action = oForm.action + ';restore_selected=1'; + break; + + default: + return false; + break; + } + + oForm.submit(); + return true; +} + + +// *** Other functions... +function expandThumb(thumbID) +{ + var img = document.getElementById('thumb_' + thumbID); + var link = document.getElementById('link_' + thumbID); + var tmp = img.src; + img.src = link.href; + link.href = tmp; + img.style.width = ''; + img.style.height = ''; + return false; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/sha1.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/sha1.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,289 @@ +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1 Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} + +/* + * Perform a simple self-test to see if the VM is working + */ +function sha1_vm_test() +{ + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; +} + +/* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ +function core_sha1(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for (var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for (var j = 0; j < 80; j++) + { + if (j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); +} + +/* + * Perform the appropriate triplet combination function for the current + * iteration + */ +function sha1_ft(t, b, c, d) +{ + if (t < 20) return (b & c) | ((~b) & d); + if (t < 40) return b ^ c ^ d; + if (t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +/* + * Determine the appropriate additive constant for the current iteration + */ +function sha1_kt(t) +{ + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; +} + +/* + * Calculate the HMAC-SHA1 of a key and some data + */ +function core_hmac_sha1(key, data) +{ + var bkey = str2binb(key); + if (bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for (var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ +function str2binb(str) +{ + var bin = Array(); + var mask = (1 << chrsz) - 1; + for (var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32); + return bin; +} + +/* + * Convert an array of big-endian words to a string + */ +function binb2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for (var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask); + return str; +} + +/* + * Convert an array of big-endian words to a hex string. + */ +function binb2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of big-endian words to a base-64 string + */ +function binb2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for (var j = 0; j < 4; j++) + { + if (i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + +// Character-level replacement function. +String.prototype.php_strtr = function (sFrom, sTo) { + return this.replace(new RegExp('[' + sFrom + ']', 'g'), function (sMatch) { + return sTo.charAt(sFrom.indexOf(sMatch)); + }); +} + +// Simulate PHP's strtolower (in SOME cases PHP uses ISO-8859-1 case folding). +String.prototype.php_strtolower = function () { + return typeof(smf_iso_case_folding) != "undefined" && smf_iso_case_folding == true ? this.php_strtr( + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ\x8a\x8c\x8e\x9f\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde', + 'abcdefghijklmnopqrstuvwxyz\x9a\x9c\x9e\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe' + ) : this.php_strtr('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); +} + +// Convert a string to an 8 bit representation (like in PHP). +String.prototype.php_to8bit = function () { + if (smf_charset == 'UTF-8') + { + var n, sReturn = ''; + + for (var i = 0, iTextLen = this.length; i < iTextLen; i++) + { + n = this.charCodeAt(i); + if (n < 128) + sReturn += String.fromCharCode(n) + else if (n < 2048) + sReturn += String.fromCharCode(192 | n >> 6) + String.fromCharCode(128 | n & 63); + else if (n < 65536) + sReturn += String.fromCharCode(224 | n >> 12) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); + else + sReturn += String.fromCharCode(240 | n >> 18) + String.fromCharCode(128 | n >> 12 & 63) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); + } + + return sReturn; + } + else if (smf_charset == 'ISO-8859-2') + { + return this.php_strtr( + '\u0104\u02d8\u0141\u013d\u026a\u0160\u015e\u0164\u0179\u017d\u017b\u0105\u02db\u0142\u013e\u015b\u02c7\u0161\u015f\u0165\u017a\u02dd\u017e\u017c\u0154\u0102\u0139\u0106\u010c\u0118\u011a\u010e\u0110\u0143\u0147\u0150\u0158\u016e\u0170\u0162\u0155\u0103\u013a\u0107\u010d\u0119\u011b\u010f\u0111\u0144\u0148\u0151\u0159\u016f\u0171\u0163\u02d9', + '\xa1\xa2\xa3\xa5\xa6\xa9\xaa\xab\xac\xae\xaf\xb1\xb2\xb3\xb5\xb6\xb7\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc3\xc5\xc6\xc8\xca\xcc\xcf\xd0\xd1\xd2\xd5\xd8\xd9\xdc\xde\xe0\xe3\xe5\xe6\xe8\xea\xec\xef\xf0\xf1\xf2\xf5\xf8\xf9\xfb\xfe\xff' + ); + } + else if (smf_charset == 'ISO-8859-9') + { + return this.php_strtr( + '\u011e\u0130\u015e\u011f\u0131\u015f', + '\xd0\xdd\xde\xf0\xfd\xfe' + ); + } + else if (smf_charset == 'tis-620') + { + return this.php_strtr( + '\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e31\u0e32\u0e33\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\u0e3f\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0e4f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0e5a\u0e5b', + '\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb' + ); + } + else if (smf_charset == 'windows-1251') + { + return this.php_strtr( + '\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\u040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u0459\u203a\u045a\u045c\u045b\u045f\u040e\u045e\u0408\u0490\u0401\u0404\u0407\u0406\u0456\u0491\u0451\u2116\u0454\u0458\u0405\u0455\u0457\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f', + '\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa1\xa2\xa3\xa5\xa8\xaa\xaf\xb2\xb3\xb4\xb8\xb9\xba\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' + ); + } + else if (smf_charset == 'windows-1253') + { + return this.php_strtr( + '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u203a\u0385\u0386\u2015\u0384\u0388\u0389\u038a\u038c\u038e\u038f\u0390\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce', + '\x80\x82\x83\x84\x85\x86\x87\x89\x8b\x91\x92\x93\x94\x95\x96\x97\x99\x9b\xa1\xa2\xaf\xb4\xb8\xb9\xba\xbc\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe' + ); + } + else if (smf_charset == 'windows-1255') + { + return this.php_strtr( + '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u203a\u20aa\u00d7\u00f7\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2\u05c3\u05f0\u05f1\u05f2\u05f3\u05f4\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u200e\u200f', + '\x80\x82\x83\x84\x85\x86\x87\x88\x89\x8b\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9b\xa4\xaa\xba\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfd\xfe' + ); + } + else if (smf_charset == 'windows-1256') + { + return this.php_strtr( + '\u20ac\u067e\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0679\u2039\u0152\u0686\u0698\u0688\u06af\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u06a9\u2122\u0691\u203a\u0153\u200c\u200d\u06ba\u060c\u06be\u061b\u061f\u06c1\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0640\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u200e\u200f\u06d2', + '\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa1\xaa\xba\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe1\xe3\xe4\xe5\xe6\xec\xed\xf0\xf1\xf2\xf3\xf5\xf6\xf8\xfa\xfd\xfe\xff' + ); + } + else + return this; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/spellcheck.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/spellcheck.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,293 @@ +// These are variables the popup is going to want to access... +var spell_formname, spell_fieldname; + +// Spell check the specified field in the specified form. +function spellCheck(formName, fieldName) +{ + // Grab the (hidden) spell checking form. + var spellform = document.forms.spell_form; + + // Register the name of the editing form for future reference. + spell_formname = formName; + spell_fieldname = fieldName; + + // This should match a word (most of the time). + var regexpWordMatch = /(?:<[^>]+>)|(?:\[[^ ][^\]]*\])|(?:&[^; ]+;)|(?:[^0-9\s\]\[{};:"\\|,<.>\/?`~!@#$%^&*()_+=]+)/g; + + // These characters can appear within words. + var aWordCharacters = ['-', '\'']; + + var aWords = new Array(), aResult = new Array(); + var sText = document.forms[formName][fieldName].value + var bInCode = false; + var iOffset1, iOffset2; + + // Loop through all words. + while ((aResult = regexpWordMatch.exec(sText)) && typeof(aResult) != 'undefined') + { + iOffset1 = 0; + iOffset2 = aResult[0].length - 1; + + // Strip the dashes and hyphens from the begin of the word. + while (in_array(aResult[0].charAt(iOffset1), aWordCharacters) && iOffset1 < iOffset2) + iOffset1++; + + // Strip the dashes and hyphens from the end of the word. + while (in_array(aResult[0].charAt(iOffset2), aWordCharacters) && iOffset1 < iOffset2) + iOffset2--; + + // I guess there's only dashes and hyphens in this word... + if (iOffset1 == iOffset2) + continue; + + // Ignore code blocks. + if (aResult[0].substr(0, 5).toLowerCase() == '[code') + bInCode = true; + + // Glad we're out of that code block! + else if (bInCode && aResult[0].substr(0, 7).toLowerCase() == '[/code]') + bInCode = false; + + // Now let's get to business. + else if (!bInCode && !in_array(aResult[0].charAt(0), ['[', '<']) && aResult[0].toUpperCase() != aResult[0]) + aWords[aWords.length] = aResult[0].substr(iOffset1, iOffset2 - iOffset1 + 1) + '|' + (iOffset1 + sText.substr(0, aResult.index).length) + '|' + (iOffset2 + sText.substr(0, aResult.index).length); + } + + // Open the window... + openSpellWin(640, 480); + + // Pass the data to a form... + spellform.spellstring.value = aWords.join('\n'); + + // and go! + spellform.submit(); + + return true; +} + +// Private functions ------------------------------- + +// Globals... +var wordindex = -1, offsetindex = 0; +var ignoredWords = []; + +// A "misspelled word" object. +function misp(word, start, end, suggestions) +{ + // The word, start index, end index, and array of suggestions. + this.word = word; + this.start = start; + this.end = end; + this.suggestions = suggestions; +} + +// Replace the word in the misps array at the "wordindex" index. The +// misps array is generated by a PHP script after the string to be spell +// checked is evaluated with pspell. +function replaceWord() +{ + var strstart = ""; + var strend; + + // If this isn't the beginning of the string then get all of the string + // that is before the word we are replacing. + if (misps[wordindex].start != 0) + strstart = mispstr.slice(0, misps[wordindex].start + offsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[wordindex].end + 1 + offsetindex); + + // Rebuild the string with the new word. + mispstr = strstart + document.forms.spellingForm.changeto.value + strend; + + // Update offsetindex to compensate for replacing a word with a word + // of a different length. + offsetindex += document.forms.spellingForm.changeto.value.length - misps[wordindex].word.length; + + // Update the word so future replaceAll calls don't change it. + misps[wordindex].word = document.forms.spellingForm.changeto.value; + + nextWord(false); +} + +// Replaces all instances of currently selected word with contents chosen by user. +// Note: currently only replaces words after highlighted word. I think we can re-index +// all words at replacement or ignore time to have it wrap to the beginning if we want +// to. +function replaceAll() +{ + var strend; + var idx; + var origword; + var localoffsetindex = offsetindex; + + origword = misps[wordindex].word; + + // Re-index everything past the current word. + for (idx = wordindex; idx < misps.length; idx++) + { + misps[idx].start += localoffsetindex; + misps[idx].end += localoffsetindex; + } + + localoffsetindex = 0; + + for (idx = 0; idx < misps.length; idx++) + { + if (misps[idx].word == origword) + { + var strstart = ""; + if (misps[idx].start != 0) + strstart = mispstr.slice(0, misps[idx].start + localoffsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[idx].end + 1 + localoffsetindex); + + // Rebuild the string with the new word. + mispstr = strstart + document.forms.spellingForm.changeto.value + strend; + + // Update offsetindex to compensate for replacing a word with a word + // of a different length. + localoffsetindex += document.forms.spellingForm.changeto.value.length - misps[idx].word.length; + } + + // We have to re-index everything after replacements. + misps[idx].start += localoffsetindex; + misps[idx].end += localoffsetindex; + } + + // Add the word to the ignore array. + ignoredWords[origword] = true; + + // Reset offsetindex since we re-indexed. + offsetindex = 0; + + nextWord(false); +} + +// Highlight the word that was selected using the nextWord function. +function highlightWord() +{ + var strstart = ""; + var strend; + + // If this isn't the beginning of the string then get all of the string + // that is before the word we are replacing. + if (misps[wordindex].start != 0) + strstart = mispstr.slice(0, misps[wordindex].start + offsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[wordindex].end + 1 + offsetindex); + + // Rebuild the string with a span wrapped around the misspelled word + // so we can highlight it in the div the user is viewing the string in. + var divptr, newValue; + divptr = document.getElementById("spellview"); + + newValue = htmlspecialchars(strstart) + '' + misps[wordindex].word + '' + htmlspecialchars(strend); + setInnerHTML(divptr, newValue.replace(/_\|_/g, '
    ')); + + // We could use scrollIntoView, but it's just not that great anyway. + var spellview_height = typeof(document.getElementById("spellview").currentStyle) != "undefined" ? parseInt(document.getElementById("spellview").currentStyle.height) : document.getElementById("spellview").offsetHeight; + var word_position = document.getElementById("h1").offsetTop; + var current_position = document.getElementById("spellview").scrollTop; + + // The spellview is not tall enough! Scroll down! + if (spellview_height <= (word_position + current_position)) + document.getElementById("spellview").scrollTop = word_position + current_position - spellview_height + 32; +} + +// Display the next misspelled word to the user and populate the suggested spellings box. +function nextWord(ignoreall) +{ + // Push ignored word onto ignoredWords array. + if (ignoreall) + ignoredWords[misps[wordindex].word] = true; + + // Update the index of all words we have processed... + // This must be done to accomodate the replaceAll function. + if (wordindex >= 0) + { + misps[wordindex].start += offsetindex; + misps[wordindex].end += offsetindex; + } + + // Increment the counter for the array of misspelled words. + wordindex++; + + // Draw it and quit if there are no more misspelled words to evaluate. + if (misps.length <= wordindex) + { + var divptr; + divptr = document.getElementById("spellview"); + setInnerHTML(divptr, htmlspecialchars(mispstr).replace(/_\|_/g, "
    ")); + + while (document.forms.spellingForm.suggestions.options.length > 0) + document.forms.spellingForm.suggestions.options[0] = null; + + alert(txt['done']); + document.forms.spellingForm.change.disabled = true; + document.forms.spellingForm.changeall.disabled = true; + document.forms.spellingForm.ignore.disabled = true; + document.forms.spellingForm.ignoreall.disabled = true; + + // Put line feeds back... + mispstr = mispstr.replace(/_\|_/g, "\n"); + + // Get a handle to the field we need to re-populate. + window.opener.document.forms[spell_formname][spell_fieldname].value = mispstr; + window.opener.document.forms[spell_formname][spell_fieldname].focus(); + window.close(); + return true; + } + + // Check to see if word is supposed to be ignored. + if (typeof(ignoredWords[misps[wordindex].word]) != "undefined") + { + nextWord(false); + return false; + } + + // Clear out the suggestions box! + while (document.forms.spellingForm.suggestions.options.length > 0) + document.forms.spellingForm.suggestions.options[0] = null; + + // Re-populate the suggestions box if there are any suggested spellings for the word. + if (misps[wordindex].suggestions.length) + { + for (var sugidx = 0; sugidx < misps[wordindex].suggestions.length; sugidx++) + { + var newopt = new Option(misps[wordindex].suggestions[sugidx], misps[wordindex].suggestions[sugidx]); + document.forms.spellingForm.suggestions.options[sugidx] = newopt; + + if (sugidx == 0) + { + newopt.selected = true; + document.forms.spellingForm.changeto.value = newopt.value; + document.forms.spellingForm.changeto.select(); + } + } + } + + if (document.forms.spellingForm.suggestions.options.length == 0) + document.forms.spellingForm.changeto.value = ""; + + highlightWord(); + + return false; +} + +function htmlspecialchars(thetext) +{ + thetext = thetext.replace(/\/g, ">"); + thetext = thetext.replace(/\n/g, "
    "); + thetext = thetext.replace(/\ \ /g, "  "); + + return thetext; +} + +function openSpellWin(width, height) +{ + window.open("", "spellWindow", "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=no,width=" + width + ",height=" + height); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/style.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/style.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,451 @@ +/* Normal, standard links. */ +a:link +{ + color: #476C8E; + text-decoration: none; +} +a:visited +{ + color: #476C8E; + text-decoration: none; +} +a:hover +{ + text-decoration: underline; +} + +/* Navigation links - for the link tree. */ +.nav, .nav:link, .nav:visited +{ + color: #000000; + text-decoration: none; +} +a.nav:hover +{ + color: #cc3333; + text-decoration: underline; +} + +/* Tables should show empty cells. */ +table +{ + empty-cells: show; +} + +/* By default (td, body..) use verdana in black. */ +body, td, th , tr +{ + color: #000000; + font-size: small; + font-family: verdana, sans-serif; +} + +/* The main body of the entire forum. */ +body +{ + background-color: #E5E5E8; + margin: 0px; + padding: 12px 30px 4px 30px; +} + +/* Input boxes - just a bit smaller than normal so they align well. */ +input, textarea, button +{ + color: #000000; + font-family: verdana, sans-serif; +} +input, button +{ + font-size: 90%; +} + +textarea +{ + font-size: 100%; + color: #000000; + font-family: verdana, sans-serif; +} + +/* All input elements that are checkboxes or radio buttons. */ +input.check +{ +} + +/* Selects are a bit smaller, because it makes them look even better 8). */ +select +{ + font-size: 90%; + font-weight: normal; + color: #000000; + font-family: verdana, sans-serif; +} + +/* Standard horizontal rule.. ([hr], etc.) */ +hr, .hrcolor +{ + height: 1px; + border: 0; + color: #666666; + background-color: #666666; +} + +/* No image should have a border when linked */ +a img +{ + border: 0; +} +/* A quote, perhaps from another post. */ +.quote +{ + color: #000000; + background-color: #D7DAEC; + border: 1px solid #000000; + margin: 1px; + padding: 1px; + font-size: x-small; + line-height: 1.4em; +} + +/* A code block - maybe even PHP ;). */ +.code +{ + color: #000000; + background-color: #dddddd; + font-family: "courier new", "times new roman", monospace; + font-size: x-small; + line-height: 1.3em; + /* Put a nice border around it. */ + border: 1px solid #000000; + margin: 1px auto 1px auto; + padding: 1px; + width: 99%; + /* Don't wrap its contents, and show scrollbars. */ + white-space: nowrap; + overflow: auto; + /* Stop after about 24 lines, and just show a scrollbar. */ + max-height: 24em; +} + +/* The "Quote:" and "Code:" header parts... */ +.quoteheader, .codeheader +{ + color: #000000; + text-decoration: none; + font-style: normal; + font-weight: bold; + font-size: x-small; + line-height: 1.2em; +} + +/* Generally, those [?] icons. This makes your cursor a help icon. */ +.help +{ + cursor: help; +} + +/* /me uses this a lot. (emote, try typing /me in a post.) */ +.meaction +{ + color: red; +} + +/* The main post box - this makes it as wide as possible. */ +.editor +{ + width: 96%; +} + +/* Highlighted text - such as search results. */ +.highlight +{ + background-color: yellow; + font-weight: bold; + color: black; +} + +/* Alternating backgrounds for posts, and several other sections of the forum. */ +.windowbg, #preview_body +{ + color: #000000; + background-color: #ECEDF3; +} +.windowbg2 +{ + color: #000000; + background-color: #F6F6F6; +} +.windowbg3 +{ + color: #000000; + background-color: #E0E1E8; +} +/* the today container in calendar */ +.calendar_today +{ + background-color: #FFFFFF; +} + +/* These are used primarily for titles, but also for headers (the row that says what everything in the table is.) */ +.titlebg, tr.titlebg th, tr.titlebg td, .titlebg2, tr.titlebg2 th, tr.titlebg2 td +{ + color: black; + font-style: normal; + background: url(images/titlebg.jpg) #E9F0F6 repeat-x; + border-bottom: solid 1px #9BAEBF; + border-top: solid 1px #FFFFFF; + padding-left: 10px; + padding-right: 10px; +} +.titlebg, .titlebg a:link, .titlebg a:visited +{ + font-weight: bold; + color: black; + font-style: normal; +} + +.titlebg a:hover +{ + color: #404040; +} +/* same as titlebg, but used where bold text is not needed */ +.titlebg2 a:link, .titlebg2 a:visited +{ + color: black; + font-style: normal; + text-decoration: underline; +} + +.titlebg2 a:hover +{ + text-decoration: underline; +} + +/* This is used for categories, page indexes, and several other areas in the forum. +.catbg and .catbg2 is for boardindex, while .catbg3 is for messageindex and display headers*/ +.catbg , tr.catbg td , .catbg3 , tr.catbg3 td +{ + background: url(images/catbg.jpg) #88A6C0 repeat-x; + color: #ffffff; + padding-left: 10px; + padding-right: 10px; +} +.catbg2 , tr.catbg2 td +{ + background: url(images/catbg2.jpg) #A1BFD9 repeat-x; + color: #ffffff; + padding-left: 10px; + padding-right: 10px; +} +.catbg, .catbg2, .catbg3 +{ + border-bottom: solid 1px #375576; +} +.catbg, .catbg2 +{ + font-weight: bold; +} +.catbg3, tr.catbg3 td, .catbg3 a:link, .catbg3 a:visited +{ + font-size: 95%; + color: white; + text-decoration: none; +} +.catbg a:link, .catbg a:visited , .catbg2 a:link, .catbg2 a:visited +{ + color: white; + text-decoration: none; +} +.catbg a:hover, .catbg2 a:hover, .catbg3 a:hover +{ + color: #e0e0ff; +} +/* This is used for tables that have a grid/border background color (such as the topic listing.) */ +.bordercolor +{ + background-color: #ADADAD; + padding: 0px; +} + +/* This is used on tables that should just have a border around them. */ +.tborder +{ + padding: 1px; + border: 1px solid #696969; + background-color: #FFFFFF; +} + +/* Default font sizes: small (8pt), normal (10pt), and large (14pt). */ +.smalltext +{ + font-size: x-small; + font-family: verdana, sans-serif; +} +.middletext +{ + font-size: 90%; +} +.normaltext +{ + font-size: small; +} +.largetext +{ + font-size: large; +} + + +/* Posts and personal messages displayed throughout the forum. */ +.post, .personalmessage +{ + width: 100%; + overflow: auto; + line-height: 1.3em; +} + +/* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ +.signature +{ + width: 100%; + overflow: auto; + padding-bottom: 3px; + line-height: 1.3em; +} + +/* Sometimes there will be an error when you post */ +.error +{ + color: red; +} + + +/* definitions for the main tab, active means the tab reflects which page is displayed */ +.maintab_first, .maintab_back, .maintab_last, .maintab_active_first, .maintab_active_back, .maintab_active_last +{ + color: white; + text-transform: uppercase; + vertical-align: top; +} +.maintab_back, .maintab_active_back +{ + color: white; + text-decoration: none; + font-size: 9px; + vertical-align: top; + padding: 2px 6px 6px 6px; + font-family: tahoma, sans-serif; +} + +.maintab_first +{ + background: url(images/maintab_first.gif) left bottom no-repeat; + width: 10px; +} +.maintab_back +{ + background: url(images/maintab_back.gif) left bottom repeat-x; +} +.maintab_last +{ + background: url(images/maintab_last.gif) left bottom no-repeat; + width: 8px; +} +.maintab_active_first +{ + background: url(images/maintab_active_first.gif) left bottom no-repeat; + width: 6px; +} +.maintab_active_back +{ + background: url(images/maintab_active_back.gif) left bottom repeat-x; +} +.maintab_active_last +{ + background: url(images/maintab_active_last.gif) left bottom no-repeat; + width: 8px; +} + +/* how links behave in main tab. */ +.maintab_back a:link , .maintab_back a:visited, .maintab_active_back a:link , .maintab_active_back a:visited +{ + color: white; + text-decoration: none; +} + +.maintab_back a:hover, .maintab_active_back a:hover +{ + color: #e0e0ff; + text-decoration: none; +} +/* definitions for the mirror tab */ +.mirrortab_first, .mirrortab_back, .mirrortab_last, .mirrortab_active_first, .mirrortab_active_back, .mirrortab_active_last +{ + color: white; + text-transform: uppercase; + vertical-align: top; +} +.mirrortab_back, .mirrortab_active_back +{ + color: white; + text-decoration: none; + font-size: 9px; + vertical-align: bottom; + padding: 6px 6px 2px 6px; + font-family: tahoma, sans-serif; +} + +.mirrortab_first +{ + background: url(images/mirrortab_first.gif) no-repeat; + width: 10px; +} +.mirrortab_back +{ + background: url(images/mirrortab_back.gif) repeat-x; +} +.mirrortab_last +{ + background: url(images/mirrortab_last.gif) no-repeat; + width: 6px; +} +.mirrortab_active_first +{ + background: url(images/mirrortab_active_first.gif) no-repeat; + width: 6px; +} +.mirrortab_active_back +{ + background: url(images/mirrortab_active_back.gif) repeat-x; +} +.mirrortab_active_last +{ + background: url(images/mirrortab_active_last.gif) no-repeat; + width: 8px; +} + +/* how links behave in mirror tab. */ +.mirrortab_back a:link , .mirrortab_back a:visited, .mirrortab_active_back a:link , .mirrortab_active_back a:visited +{ + color: white; + text-decoration: none; +} + +.mirrortab_back a:hover, .mirrortab_active_back a:hover +{ + color: #e0e0ff; + text-decoration: none; +} + +/* The AJAX notifier */ +#ajax_in_progress +{ + background: #32CD32; + color: white; + text-align: center; + font-weight: bold; + font-size: 18pt; + padding: 3px; + width: 100%; + position: fixed; + top: 0; + left: 0; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/wireless.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/wireless.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,30 @@ +.catbg, tr.catbg td +{ + background-color: #6d92aa; + color: #ffffff; +} +.titlebg, .titlebg a, .titlebg a:link, .titlebg a:visited +{ + background-color: #b6dbff; + color: #000000; + text-decoration: none; +} +.windowbg, tr.windowbg td +{ + background-color: #ffffff; + color: #000000; +} +.windowbg2, tr.windowbg2 td +{ + background-color: #c0c0c0; + color: #000000; +} +.new, a:link.new, a:visited.new +{ + background-color: #2f2fc0; + color: #ffffff; +} +.updated +{ + color: #ff0000; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/xml_board.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/xml_board.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,113 @@ +var cur_topic_id, cur_msg_id, buff_subject, cur_subject_div, in_edit_mode = 0; +var hide_prefixes = Array(); + +function modify_topic(topic_id, first_msg_id, cur_session_id) +{ + if (!window.XMLHttpRequest) + return; + if (typeof(window.opera) != "undefined") + { + var test = new XMLHttpRequest(); + if (typeof(test.setRequestHeader) != "function") + return; + } + + if (in_edit_mode == 1) + { + if (cur_topic_id == topic_id) + return; + else + modify_topic_cancel(); + } + + in_edit_mode = 1; + mouse_on_div = 1; + cur_topic_id = topic_id; + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + + getXMLDocument(smf_scripturl + "?action=quotefast;quote=" + first_msg_id + ";sesc=" + cur_session_id + ";modify;xml", onDocReceived_modify_topic); +} + +function onDocReceived_modify_topic(XMLDoc) +{ + cur_msg_id = XMLDoc.getElementsByTagName("message")[0].getAttribute("id"); + + cur_subject_div = document.getElementById('msg_' + cur_msg_id.substr(4)); + buff_subject = getInnerHTML(cur_subject_div); + + // Here we hide any other things they want hiding on edit. + set_hidden_topic_areas('none'); + + modify_topic_show_edit(XMLDoc.getElementsByTagName("subject")[0].childNodes[0].nodeValue); + + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + +function modify_topic_cancel() +{ + setInnerHTML(cur_subject_div, buff_subject); + set_hidden_topic_areas(''); + + in_edit_mode = 0; + return false; +} + +function modify_topic_save(cur_session_id) +{ + if (!in_edit_mode) + return true; + + var i, x = new Array(); + x[x.length] = 'subject=' + escape(textToEntities(document.forms.quickModForm['subject'].value)).replace(/\+/g, "%2B"); + x[x.length] = 'topic=' + parseInt(document.forms.quickModForm.elements['topic'].value); + x[x.length] = 'msg=' + parseInt(document.forms.quickModForm.elements['msg'].value); + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + + sendXMLDocument(smf_scripturl + "?action=jsmodify;topic=" + parseInt(document.forms.quickModForm.elements['topic'].value) + ";sesc=" + cur_session_id + ";xml", x.join("&"), modify_topic_done); + + return false; +} + +function modify_topic_done(XMLDoc) +{ + if (!XMLDoc) + { + modify_topic_cancel(); + return true; + } + + var message = XMLDoc.getElementsByTagName("smf")[0].getElementsByTagName("message")[0]; + var subject = message.getElementsByTagName("subject")[0]; + var error = message.getElementsByTagName("error")[0]; + + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); + + if (!subject || error) + return false; + + subjectText = subject.childNodes[0].nodeValue; + + modify_topic_hide_edit(subjectText); + + set_hidden_topic_areas(''); + + in_edit_mode = 0; + + return false; +} + +// Simply restore any hidden bits during topic editing. +function set_hidden_topic_areas(set_style) +{ + for (var i = 0; i < hide_prefixes.length; i++) + { + if (document.getElementById(hide_prefixes[i] + cur_msg_id.substr(4)) != null) + document.getElementById(hide_prefixes[i] + cur_msg_id.substr(4)).style.display = set_style; + } +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/Vamp/xml_topic.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/Vamp/xml_topic.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,206 @@ +var smf_topic, smf_start, smf_show_modify, quickReplyCollapsed, buff_message; +var cur_msg_id, cur_msg_div, buff_subject, cur_subject_div, in_edit_mode = 0; + +function doQuote(messageid, cur_session_id) +{ + if (quickReplyCollapsed) + window.location.href = smf_scripturl + "?action=post;quote=" + messageid + ";topic=" + smf_topic + "." + smf_start + ";sesc=" + cur_session_id; + else + { + if (window.XMLHttpRequest) + { + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + getXMLDocument(smf_scripturl + "?action=quotefast;quote=" + messageid + ";sesc=" + cur_session_id + ";xml", onDocReceived); + } + else + reqWin(smf_scripturl + "?action=quotefast;quote=" + messageid + ";sesc=" + cur_session_id, 240, 90); + + if (navigator.appName == "Microsoft Internet Explorer") + window.location.hash = "quickreply"; + else + window.location.hash = "#quickreply"; + } +} + +function onDocReceived(XMLDoc) +{ + var text = ""; + for (var i = 0; i < XMLDoc.getElementsByTagName("quote")[0].childNodes.length; i++) + text += XMLDoc.getElementsByTagName("quote")[0].childNodes[i].nodeValue; + + replaceText(text, document.forms.postmodify.message); + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + + +function modify_msg(msg_id, cur_session_id) +{ + if (!window.XMLHttpRequest) + return; + if (typeof(window.opera) != "undefined") + { + var test = new XMLHttpRequest(); + if (typeof(test.setRequestHeader) != "function") + return; + } + if (in_edit_mode == 1) + modify_cancel(); + in_edit_mode = 1; + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + getXMLDocument(smf_scripturl + '?action=quotefast;quote=' + msg_id + ';sesc=' + cur_session_id + ';modify;xml', onDocReceived_modify); +} + +function onDocReceived_modify(XMLDoc) +{ + var text = ""; + var subject = ""; + + // Grab the message ID. + cur_msg_id = XMLDoc.getElementsByTagName("message")[0].getAttribute("id"); + + // Replace the body part. + for (var i = 0; i < XMLDoc.getElementsByTagName("message")[0].childNodes.length; i++) + text += XMLDoc.getElementsByTagName("message")[0].childNodes[i].nodeValue; + cur_msg_div = document.getElementById(cur_msg_id); + buff_message = getInnerHTML(cur_msg_div); + + // Actually create the content, with a bodge for dissapearing dollar signs. + text = text.replace(/\$/g,"{&dollarfix;$}"); + text = smf_template_body_edit.replace(/%body%/, text).replace(/%msg_id%/g, cur_msg_id.substr(4)); + text = text.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(cur_msg_div, text); + + // Replace the subject part. + cur_subject_div = document.getElementById('subject_' + cur_msg_id.substr(4)); + buff_subject = getInnerHTML(cur_subject_div); + + subject = XMLDoc.getElementsByTagName("subject")[0].childNodes[0].nodeValue; + subject = subject.replace(/\$/g,"{&dollarfix;$}"); + subject = smf_template_subject_edit.replace(/%subject%/, subject); + subject = subject.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(cur_subject_div, subject); + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + +function modify_cancel() +{ + // Roll back the HTML to its original state. + setInnerHTML(cur_msg_div, buff_message); + setInnerHTML(cur_subject_div, buff_subject); + + // No longer in edit mode, that's right. + in_edit_mode = 0; + + return false; +} + +function modify_save(cur_session_id) +{ + if (!in_edit_mode) + return true; + + var i, x = new Array(); + x[x.length] = 'subject=' + escape(textToEntities(document.forms.quickModForm['subject'].value.replace(/&#/g, "&#"))).replace(/\+/g, "%2B"); + x[x.length] = 'message=' + escape(textToEntities(document.forms.quickModForm['message'].value.replace(/&#/g, "&#"))).replace(/\+/g, "%2B"); + x[x.length] = 'topic=' + parseInt(document.forms.quickModForm.elements['topic'].value); + x[x.length] = 'msg=' + parseInt(document.forms.quickModForm.elements['msg'].value); + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + + sendXMLDocument(smf_scripturl + "?action=jsmodify;topic=" + smf_topic + ";sesc=" + cur_session_id + ";xml", x.join("&"), modify_done); + + return false; +} + +function modify_done(XMLDoc) +{ + if (!XMLDoc) + { + modify_cancel(); + return; + } + + var message = XMLDoc.getElementsByTagName("smf")[0].getElementsByTagName("message")[0]; + var body = message.getElementsByTagName("body")[0]; + var error = message.getElementsByTagName("error")[0]; + + if (body) + { + // Show new body. + var bodyText = ""; + for (i = 0; i < body.childNodes.length; i++) + bodyText += body.childNodes[i].nodeValue; + + bodyText = bodyText.replace(/\$/g,"{&dollarfix;$}"); + bodyText = smf_template_body_normal.replace(/%body%/, bodyText); + bodyText = bodyText.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(cur_msg_div, bodyText); + buff_message = bodyText; + + // Show new subject. + var subject = message.getElementsByTagName("subject")[0]; + var subject_text = subject.childNodes[0].nodeValue; + subject_text = subject_text.replace(/\$/g,"{&dollarfix;$}"); + var subject_html = smf_template_subject_normal.replace(/%msg_id%/g, cur_msg_id.substr(4)).replace(/%subject%/, subject_text); + subject_html = subject_html.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(cur_subject_div, subject_html); + buff_subject = subject_html; + + // If this is the first message, also update the topic subject. + if (subject.getAttribute("is_first") == 1) + { + var subject_top = smf_template_top_subject.replace(/%subject%/, subject_text); + subject_top = subject_top.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(document.getElementById("top_subject"), subject_top); + } + + // Show this message as "modified on x by y". + if (smf_show_modify) + { + var cur_modify_div = document.getElementById('modified_' + cur_msg_id.substr(4)); + setInnerHTML(cur_modify_div, message.getElementsByTagName("modified")[0].childNodes[0].nodeValue); + } + } + else if (error) + { + setInnerHTML(document.getElementById("error_box"), error.childNodes[0].nodeValue); + document.forms.quickModForm.message.style.border = error.getAttribute("in_body") == "1" ? "1px solid red" : ""; + document.forms.quickModForm.subject.style.border = error.getAttribute("in_subject") == "1" ? "1px solid red" : ""; + } + + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + +function showModifyButtons() +{ + var numImages = document.images.length; + for (var i = 0; i < numImages; i++) + if (document.images[i].id.substr(0, 14) == 'modify_button_') + document.images[i].style.display = ''; +} + +function expandThumb(thumbID) +{ + var img = document.getElementById('thumb_' + thumbID); + var link = document.getElementById('link_' + thumbID); + var tmp = img.src; + img.src = link.href; + link.href = tmp; + img.style.width = ''; + img.style.height = ''; + return false; +} + +function swapQuickReply() +{ + document.getElementById("quickReplyExpand").src = smf_images_url + "/" + (quickReplyCollapsed ? "collapse.gif" : "expand.gif"); + document.getElementById("quickReplyOptions").style.display = quickReplyCollapsed ? "" : "none"; + + quickReplyCollapsed = !quickReplyCollapsed; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/BoardIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/BoardIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,474 @@ + + + ', theme_linktree(), ' + '; + if (!$settings['show_sp1_info']) + echo ' + ', $txt[19], ': ', $context['common_stats']['total_members'], '  •  ', $txt[95], ': ', $context['common_stats']['total_posts'], '  •  ', $txt[64], ': ', $context['common_stats']['total_topics'], ' + ', ($settings['show_latest_member'] ? '
    ' . $txt[201] . ' ' . $context['common_stats']['latest_member']['link'] . '' . $txt[581] : ''); + echo ' + + +'; + + // Show the news fader? (assuming there are things to show...) + if ($settings['show_newsfader'] && !empty($context['fader_news_lines'])) + { + echo ' +
    + + + + + + + +
    ', $txt[102], '
    '; + + // Prepare all the javascript settings. + echo ' +
    ', $context['news_lines'][0], '
    + + +
    +
    '; + } + + /* Each category in categories is made up of: + id, href, link, name, is_collapsed (is it collapsed?), can_collapse (is it okay if it is?), + new (is it new?), collapse_href (href to collapse/expand), collapse_image (up/down iamge), + and boards. (see below.) */ + foreach ($context['categories'] as $category) + { + echo ' +
    + + + '; + + // Assuming the category hasn't been collapsed... + if (!$category['is_collapsed']) + { + /* Each board in each category's boards has: + new (is it new?), id, name, description, moderators (see below), link_moderators (just a list.), + children (see below.), link_children (easier to use.), children_new (are they new?), + topics (# of), posts (# of), link, href, and last_post. (see below.) */ + foreach ($category['boards'] as $board) + { + echo ' + + + + + + '; + } + } + + echo ' +
    '; + + // If this category even can collapse, show a link to collapse it. + if ($category['can_collapse']) + echo ' + ', $category['collapse_image'], ''; + + echo ' + ', $category['link'], ' +
    '; + + // If the board is new, show a strong indicator. + if ($board['new']) + echo '', $txt[333], ''; + // This board doesn't have new posts, but its children do. + elseif ($board['children_new']) + echo '', $txt[333], ''; + // No new posts at all! The agony!! + else + echo '', $txt[334], ''; + + echo ' + ', $board['name'], '
    + ', $board['description']; + + // Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.) + if (!empty($board['moderators'])) + echo ' +
    ', count($board['moderators']) == 1 ? $txt[298] : $txt[299], ': ', implode(', ', $board['link_moderators']), '
    '; + + // Show the "Child Boards: ". (there's a link_children but we're going to bold the new ones...) + if (!empty($board['children'])) + { + // Sort the links into an array with new boards bold so it can be imploded. + $children = array(); + /* Each child in each board's children has: + id, name, description, new (is it new?), topics (#), posts (#), href, link, and last_post. */ + foreach ($board['children'] as $child) + { + $child['link'] = '' . $child['name'] . ''; + $children[] = $child['new'] ? '' . $child['link'] . '' : $child['link']; + } + + echo ' +
    ', $txt['parent_boards'], ': ', implode(', ', $children), '
    '; + } + + // Show some basic information about the number of posts, etc. + echo ' +
    + ', $board['posts'], ' ', $txt[21], ' ', $txt['smf88'], '
    + ', $board['topics'], ' ', $txt[330], ' +
    '; + + /* The board's and children's 'last_post's have: + time, timestamp (a number that represents the time.), id (of the post), topic (topic id.), + link, href, subject, start (where they should go for the first unread post.), + and member. (which has id, name, link, href, username in it.) */ + if (!empty($board['last_post']['id'])) + echo ' + ', $txt[22], ' ', $txt[30], ' ', $board['last_post']['time'], '
    + ', $txt['smf88'], ' ', $board['last_post']['link'], ' ', $txt[525], ' ', $board['last_post']['member']['link']; + + echo ' +
    +
    '; + } + + if ($context['user']['is_logged']) + { + echo ' +
    + + + + +
    + ', $txt[333], ' + ', $txt[334], ' + '; + // Show the mark all as read button? + if ($settings['show_mark_read']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452]), ''; + echo ' +
    '; + } + + // Here's where the "Info Center" starts... + echo ' +
    +
    + + + '; + + // This is the "Recent Posts" bar. + if (!empty($settings['number_recent_posts'])) + { + echo ' + + + + + + + '; + } + + // Show information about events, birthdays, and holidays on the calendar. + if ($context['show_calendar']) + { + echo ' + + + + + + '; + } + + // Show a member bar. Not heavily ornate, but functional at least. + if ($settings['show_member_bar']) + { + echo ' + + + + + + + '; + } + + // Show YaBB SP1 style information... + if ($settings['show_sp1_info']) + { + echo ' + + + + + + + '; + } + + // "Users online" - in order of activity. + echo ' + + + + + + '; + + // If they are logged in, but SP1 style information is off... show a personal message bar. + if ($context['user']['is_logged'] && !$settings['show_sp1_info']) + { + echo ' + + + + + + '; + } + + // Show the login bar. (it's only true if they are logged out anyway.) + if ($context['show_login_bar']) + { + echo ' + + + + + + + '; + } + + echo ' +
    ', $txt[685], '
    ', $txt[214], '
    + + ', $txt[214], ' + '; + + // Only show one post. + if ($settings['number_recent_posts'] == 1) + { + // latest_post has link, href, time, subject, short_subject (shortened with...), and topic. (its id.) + echo ' + ', $txt[214], ' +
    + ', $txt[234], ' "', $context['latest_post']['link'], '" ', $txt[235], ' (', $context['latest_post']['time'], ')
    +
    '; + } + // Show lots of posts. + elseif (!empty($context['latest_posts'])) + { + echo ' + '; + /* Each post in latest_posts has: + board (with an id, name, and link.), topic (the topic's id.), poster (with id, name, and link.), + subject, short_subject (shortened with...), time, link, and href. */ + foreach ($context['latest_posts'] as $post) + echo ' + + + + + '; + echo ' +
    [', $post['board']['link'], ']', $post['link'], ' ', $txt[525], ' ', $post['poster']['link'], '', $post['time'], '
    '; + } + echo ' +
    ', $context['calendar_only_today'] ? $txt['calendar47b'] : $txt['calendar47'], '
    + + ', $txt['calendar24'], ' + + '; + + // Holidays like "Christmas", "Chanukah", and "We Love [Unknown] Day" :P. + if (!empty($context['calendar_holidays'])) + echo ' + ', $txt['calendar5'], ' ', implode(', ', $context['calendar_holidays']), '
    '; + + // People's birthdays. Like mine. And yours, I guess. Kidding. + if (!empty($context['calendar_birthdays'])) + { + echo ' + ', $context['calendar_only_today'] ? $txt['calendar3'] : $txt['calendar3b'], ' '; + /* Each member in calendar_birthdays has: + id, name (person), age (if they have one set?), is_last. (last in list?), and is_today (birthday is today?) */ + foreach ($context['calendar_birthdays'] as $member) + echo ' + ', $member['is_today'] ? '' : '', $member['name'], $member['is_today'] ? '' : '', isset($member['age']) ? ' (' . $member['age'] . ')' : '', '', $member['is_last'] ? '
    ' : ', '; + } + // Events like community get-togethers. + if (!empty($context['calendar_events'])) + { + echo ' + ', $context['calendar_only_today'] ? $txt['calendar4'] : $txt['calendar4b'], ' '; + /* Each event in calendar_events should have: + title, href, is_last, can_edit (are they allowed?), modify_href, and is_today. */ + foreach ($context['calendar_events'] as $event) + echo ' + ', $event['can_edit'] ? '* ' : '', $event['href'] == '' ? '' : '', $event['is_today'] ? '' . $event['title'] . '' : $event['title'], $event['href'] == '' ? '' : '', $event['is_last'] ? '
    ' : ', '; + + // Show a little help text to help them along ;). + if ($context['calendar_can_edit']) + echo ' + (', $txt['calendar_how_edit'], ')'; + } + echo ' +
    +
    ', $txt[331], '
    + ', $context['show_member_list'] ? '' : '', '', $txt[332], '', $context['show_member_list'] ? '' : '', ' + + ', $context['show_member_list'] ? '' . $txt[332] . '' : $txt[332], ' +
    ', $txt[200], '
    +
    ', $txt[645], '
    + + ', $txt[645], ' + + + + +
    +
    ', $txt[490], ': ', $context['common_stats']['total_topics'], '
    ', $txt[489], ': ', $context['common_stats']['total_posts'], '
    ', !empty($context['latest_post']) ? ' + ' . $txt[659] . ': "' . $context['latest_post']['link'] . '" (' . $context['latest_post']['time'] . ')
    ' : '', ' + ', $txt[234], '', $context['show_stats'] ? '
    + ' . $txt['smf223'] . '' : '', ' +
    + ', $txt[488], ': ', $context['common_stats']['total_members'], '
    + ', $txt[656], ': ', $context['common_stats']['latest_member']['link'], '
    '; + // If they are logged in, show their unread message count, etc.. + if ($context['user']['is_logged']) + echo ' + ', $txt['smf199'], ': ', $context['user']['messages'], ' ', $txt['newmessages3'], ': ', $context['user']['unread_messages'], ''; + echo ' +
    +
    ', $txt[158], '
    + ', $context['show_who'] ? '' : '', '', $txt[158], '', $context['show_who'] ? '' : '', ' + '; + + if ($context['show_who']) + echo ' + '; + + echo $context['num_guests'], ' ', $context['num_guests'] == 1 ? $txt['guest'] : $txt['guests'], ', ' . $context['num_users_online'], ' ', $context['num_users_online'] == 1 ? $txt['user'] : $txt['users']; + + // Handle hidden users and buddies. + if (!empty($context['num_users_hidden']) || ($context['show_buddies'] && !empty($context['show_buddies']))) + { + echo ' ('; + + // Show the number of buddies online? + if ($context['show_buddies']) + echo $context['num_buddies'], ' ', $context['num_buddies'] == 1 ? $txt['buddy'] : $txt['buddies']; + + // How about hidden users? + if (!empty($context['num_users_hidden'])) + echo $context['show_buddies'] ? ', ' : '', $context['num_users_hidden'] . ' ' . $txt['hidden']; + + echo ')'; + } + + if ($context['show_who']) + echo ''; + + echo ' +
    '; + + // Assuming there ARE users online... each user in users_online has an id, username, name, group, href, and link. + if (!empty($context['users_online'])) + echo ' + ', $txt[140], ':
    ', implode(', ', $context['list_users_online']); + + echo ' +
    ', $context['show_stats'] && !$settings['show_sp1_info'] ? ' + ' . $txt['smf223'] . '' : '', ' +
    +
    ', $txt[159], '
    + ', $context['allow_pm'] ? '' : '', '', $txt[159], '', $context['allow_pm'] ? '' : '', ' + + ', $txt[159], ' +
    + ', $txt[660], ' ', $context['user']['messages'], ' ', $context['user']['messages'] == 1 ? $txt[471] : $txt[153], '.... ', $txt[661], ' ', $txt[662], ' ', $txt[663], ' +
    +
    ', $txt[34], ' (' . $txt[315] . ')
    + + ', $txt[34], ' + +
    + + + + + + +
    + + + + + + + + + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/Display.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/Display.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,701 @@ + +', $context['first_new_message'] ? '' : ''; + + // Show the linktree as well as the "Who's Viewing" information. + echo ' + + + '; + if (!empty($settings['display_who_viewing'])) + { + echo ' + '; + } + + // Show the previous/next links. + echo ' + + +
    ', theme_linktree(), ''; + + // Show just numbers...? + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) == 1 ? $txt['who_member'] : $txt[19]; + // Or show the actual people viewing the topic? + else + echo empty($context['view_members_list']) ? '0 ' . $txt[19] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) or $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + + // Now show how many guests are here too. + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_topic'], ' + ', $context['previous_next'], ' +
    '; + + // Is this topic also a poll? + if ($context['is_poll']) + { + echo ' + + + + + + + + +
    + ', $txt['smf43'], ' +
    ', $txt['smf21'], ': + ', $context['poll']['question']; + if (!empty($context['poll']['expire_time'])) + echo ' +  (', ($context['poll']['is_expired'] ? $txt['poll_expired_on'] : $txt['poll_expires_on']), ': ', $context['poll']['expire_time'], ')'; + + // Are they not allowed to vote but allowed to view the options? + if ($context['poll']['show_results'] || !$context['allow_vote']) + { + echo ' + + + + + ', $context['allow_poll_view'] ? ' + + + ' : '', ' +
    + '; + + // Show each option with its corresponding percentage bar. + foreach ($context['poll']['options'] as $option) + echo ' + + ', $context['allow_poll_view'] ? ' + ' : '', ' + '; + + echo ' +
    ', $option['option'], '' . $option['bar'] . ' ' . $option['votes'] . ' (' . $option['percent'] . '%)
    +
    '; + + // If they are allowed to revote - show them a link! + if ($context['allow_change_vote']) + echo ' + ', $txt['poll_change_vote'], '
    '; + + // If we're viewing the results... maybe we want to go back and vote? + if ($context['poll']['show_results'] && $context['allow_vote']) + echo ' + ', $txt['poll_return_vote'], '
    '; + + // If they're allowed to lock the poll, show a link! + if ($context['poll']['lock']) + echo ' + ', !$context['poll']['is_locked'] ? $txt['smf30'] : $txt['smf30b'], '
    '; + + // If they're allowed to edit the poll... guess what... show a link! + if ($context['poll']['edit']) + echo ' + ', $txt['smf39'], ''; + + echo ' +
    ' . $txt['smf24'] . ': ' . $context['poll']['total_votes'] . '

    '; + } + // They are allowed to vote! Go to it! + else + { + echo ' +
    + + + + + + + + + +
    '; + + // Show a warning if they are allowed more than one option. + if ($context['poll']['allowed_warning']) + echo ' + ', $context['poll']['allowed_warning'], ' +
    '; + + // Show each option with its button - a radio likely. + foreach ($context['poll']['options'] as $option) + echo ' +
    '; + + echo ' +
    '; + + // Allowed to view the results? (without voting!) + if ($context['allow_poll_view']) + echo ' + ', $txt['smf29'], '
    '; + + // Show a link for locking the poll as well... + if ($context['poll']['lock']) + echo ' + ', (!$context['poll']['is_locked'] ? $txt['smf30'] : $txt['smf30b']), '
    '; + + // Want to edit it? Click right here...... + if ($context['poll']['edit']) + echo ' + ', $txt['smf39'], ''; + + echo ' +
    + +
    '; + } + + echo ' +
    '; + } + + // Does this topic have some events linked to it? + if (!empty($context['linked_calendar_events'])) + { + echo ' + + + + + + + +
    + ', $txt['calendar_linked_events'], ' +
    +
      '; + foreach ($context['linked_calendar_events'] as $event) + { + echo ' +
    • + ', ($event['can_edit'] ? '* ' : ''), '', $event['title'], ': ', $event['start_date'], ($event['start_date'] != $event['end_date'] ? ' - ' . $event['end_date'] : ''), ' +
    • '; + } + echo ' +
    +
    '; + } + + // Show the page index... "Pages: [1]". + echo ' + + + + +
    + + + + + +
    + ', $txt[139], ': ', $context['page_index']; + + // Show a "go down" link? + if (!empty($modSettings['topbottomEnable'])) + echo $context['menu_separator'], '', $settings['use_image_buttons'] ? '' . $txt['topbottom5'] . '' : $txt['topbottom5'], ''; + + echo ' + '; + + $buttonArray = array(); + if ($context['can_reply']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[146] . '' : $txt[146]) . ''; + if ($context['can_mark_notify']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[131] . '' : $txt[131]) . ''; + + // This is a special case; if they can see mark unread, put it at the top... otherwise show add poll. + if ($context['user']['is_logged'] && $settings['show_mark_read']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt['mark_unread'] . '' : $txt['mark_unread']) . ''; + elseif ($context['can_add_poll']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt['add_poll'] . '' : $txt['add_poll']) . ''; + + if ($context['can_send_topic']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[707] . '' : $txt[707]) . ''; + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[465] . '' : $txt[465]) . ''; + + echo implode($context['menu_separator'], $buttonArray); + + echo '
    +
    '; + + // Show the topic information - icon, subject, etc. + echo ' + + + + + +
    + ', $txt[29], ' + + ', $txt[118], ': ', $context['subject'], '  (', $txt[641], ' ', $context['num_views'], ' ', $txt[642], ') +
    '; + +// if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $context['can_remove_post']) + echo ' +
    '; + + echo ' +'; + + // Get all the messages... + while ($message = $context['get_message']()) + { + echo ' + '; + } + echo ' + +
    '; + + // Show the message anchor and a "new" anchor if this message is new. + if ($message['id'] != $context['first_message']) + echo ' + ', $message['first_new'] ? '' : ''; + + echo ' + + +
    '; + + // Show information about the poster of this message. + echo ' + + + + + '; + + // Now for the attachments, signature, ip logged, etc... + echo ' + + + +
    + ', $message['member']['link'], ' +
    '; + + // Show the member's custom title, if they have one. + if (isset($message['member']['title']) && $message['member']['title'] != '') + echo ' + ', $message['member']['title'], '
    '; + + // Show the member's primary group (like 'Administrator') if they have one. + if (isset($message['member']['group']) && $message['member']['group'] != '') + echo ' + ', $message['member']['group'], '
    '; + + // Don't show these things for guests. + if (!$message['member']['is_guest']) + { + // Show the post group if and only if they have no other group or the option is on, and they are in a post group. + if ((empty($settings['hide_post_group']) || $message['member']['group'] == '') && $message['member']['post_group'] != '') + echo ' + ', $message['member']['post_group'], '
    '; + echo ' + ', $message['member']['group_stars'], '
    '; + + // Is karma display enabled? Total or +/-? + if ($modSettings['karmaMode'] == '1') + echo ' +
    + ', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '
    '; + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    + ', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '
    '; + + // Is this user allowed to modify this member's karma? + if ($message['member']['karma']['allow']) + echo ' + ', $modSettings['karmaApplaudLabel'], ' + ', $modSettings['karmaSmiteLabel'], '
    '; + + // Show online and offline buttons? + if (!empty($modSettings['onlineEnable']) && !$message['member']['is_guest']) + echo ' + ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $message['member']['online']['text'] . '' : $message['member']['online']['text'], $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? ' ' . $message['member']['online']['text'] . '' : '', '

    '; + + // Show the member's gender icon? + if (!empty($settings['show_gender']) && $message['member']['gender']['image'] != '') + echo ' + ', $txt[231], ': ', $message['member']['gender']['image'], '
    '; + + // Show how many posts they have made. + echo ' + ', $txt[26], ': ', $message['member']['posts'], '
    +
    '; + + // Show avatars, images, etc.? + if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image'])) + echo ' +
    ', $message['member']['avatar']['image'], '

    '; + + // Show their personal text? + if (!empty($settings['show_blurb']) && $message['member']['blurb'] != '') + echo ' + ', $message['member']['blurb'], '
    +
    '; + + // This shows the popular messaging icons. + echo ' + ', $message['member']['icq']['link'], ' + ', $message['member']['msn']['link'], ' + ', $message['member']['aim']['link'], ' + ', $message['member']['yim']['link'], '
    '; + + // Show the profile, website, email address, and personal message buttons. + if ($settings['show_profile_buttons']) + { + // Don't show the profile button if you're not allowed to view the profile. + if ($message['member']['can_view_profile']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[27] . '' : $txt[27]), ''; + + // Don't show an icon if they haven't specified a website. + if ($message['member']['website']['url'] != '') + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[515] . '' : $txt[515]), ''; + + // Don't show the email address if they want it hidden. + if (empty($message['member']['hide_email'])) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[69] . '' : $txt[69]), ''; + + // Since we know this person isn't a guest, you *can* message them. + if ($context['can_send_pm']) + echo ' + ', $settings['use_image_buttons'] ? '' . $message['member']['online']['label'] . '' : $message['member']['online']['label'], ''; + } + } + // Otherwise, show the guest's email. + elseif (empty($message['member']['hide_email'])) + echo ' +
    +
    + ', ($settings['use_image_buttons'] ? '' . $txt[69] . '' : $txt[69]), ''; + + // Done with the information about the poster... on to the post itself. + echo ' +
    +
    + + + + +
    + '; + + // If this is the first post, (#0) just say when it was posted - otherwise give the reply #. + echo ' +
    « ', !empty($message['counter']) ? $txt[146] . ' #' . $message['counter'] : '', ' ', $txt[30], ': ', $message['time'], ' »
    '; + + // Can they reply? Have they turned on quick reply? + if ($context['can_reply'] && !empty($options['display_quick_reply'])) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[145] . '' : $txt[145]), ''; + // So... quick reply is off, but they *can* reply? + elseif ($context['can_reply']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[145] . '' : $txt[145]), ''; + + // Can the user modify the contents of this post? + if ($message['can_modify']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[66] . '' : $txt[17]), ''; + + // How about... even... remove it entirely?! + if ($message['can_remove']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[121] . '' : $txt[31]), ''; + + // What about splitting it off the rest of the topic? + if ($context['can_split']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt['smf251'] . '' : $txt['smf251']), ''; + + // Show a checkbox for quick moderation? + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $message['can_remove']) + echo ' + '; + + // Show the post itself, finally! + echo ' +
    +
    +
    ', $message['body'], '
    ', $message['can_modify'] ? ' + ' : '' , ' +
    + + + + + +
    '; + + // Assuming there are attachments... + if (!empty($message['attachment'])) + { + echo ' +
    +
    '; + foreach ($message['attachment'] as $attachment) + { + if ($attachment['is_image']) + { + if ($attachment['thumbnail']['has_thumb']) + echo ' +
    '; + else + echo ' +
    '; + } + echo ' + * ' . $attachment['name'] . ' (', $attachment['size'], ($attachment['is_image'] ? ', ' . $attachment['real_width'] . 'x' . $attachment['real_height'] . ' - ' . $txt['attach_viewed'] : ' - ' . $txt['attach_downloaded']) . ' ' . $attachment['downloads'] . ' ' . $txt['attach_times'] . '.)
    '; + } + + echo ' +
    '; + } + + echo ' +
    '; + + // Show "« Last Edit: Time by Person »" if this post was edited. + if ($settings['show_modify'] && !empty($message['modified']['name'])) + echo ' + « ', $txt[211], ': ', $message['modified']['time'], ' ', $txt[525], ' ', $message['modified']['name'], ' »'; + + echo ' + '; + + // Maybe they want to report this post to the moderator(s)? + if ($context['can_report_moderator']) + echo ' + ', $txt['rtm1'], '  '; + echo ' + '; + + // Show the IP to this user for this post - because you can moderate? + if ($context['can_moderate_forum'] && !empty($message['member']['ip'])) + echo ' + ', $message['member']['ip'], ' (?)'; + // Or, should we show it because this is you? + elseif ($message['can_see_ip']) + echo ' + ', $message['member']['ip'], ''; + // Okay, are you at least logged in? Then we can show something about why IPs are logged... + elseif (!$context['user']['is_guest']) + echo ' + ', $txt[511], ''; + // Otherwise, you see NOTHING! + else + echo ' + ', $txt[511]; + + echo ' +
    '; + + // Show the member's signature? + if (!empty($message['member']['signature']) && empty($options['show_no_signatures'])) + echo ' +
    +
    ', $message['member']['signature'], '
    '; + + echo ' +
    +
    +
    + + + + + + +
    + + + + + +
    + ', $txt[139], ': ', $context['page_index'], (!empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '' . ($settings['use_image_buttons'] ? '' . $txt['topbottom4'] . '' : $txt['topbottom4']) . '' : ''), ' + '; + + $buttonArray = array(); + if ($context['can_reply']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[146] . '' : $txt[146]) . ''; + if ($context['can_mark_notify']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[131] . '' : $txt[131]) . ''; + + // Another special case, similar to above but reversed. Show "add poll" unless they can't - then show unread here too. + if ($context['can_add_poll']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt['add_poll'] . '' : $txt['add_poll']) . ''; + elseif ($context['user']['is_logged'] && $settings['show_mark_read']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt['mark_unread'] . '' : $txt['mark_unread']) . ''; + + if ($context['can_send_topic']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[707] . '' : $txt[707]) . ''; + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[465] . '' : $txt[465]) . ''; + + echo implode($context['menu_separator'], $buttonArray); + + echo '  +
    +
    '; + + if ($context['show_spellchecking']) + echo ' +'; + +echo ' + + + + '; + if ($settings['linktree_inline']) + echo ' + '; + echo ' + + +
    ', theme_linktree(), ' ', $context['previous_next'], '
    +
    ', theme_show_mod_buttons(), '
    '; + + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $context['can_remove_post']) + echo ' + + '; + echo ' +
    '; + + echo ' +
    +
    + ' . $txt[160] . ': +   + +
    +
    +
    '; + + if ($context['can_reply'] && !empty($options['display_quick_reply'])) + { + echo ' + + + + + + + + + +
    + ', $txt['quick_reply_1'], '
    '; + } + if ($context['show_spellchecking']) + echo ' +
    '; + +} + +function theme_show_mod_buttons() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + $moderationButtons = array(); + + if ($context['can_move']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt[132] . '' : $txt[132]) . ''; + if ($context['can_delete']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt[63] . '' : $txt[63]) . ''; + if ($context['can_lock']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . (empty($context['is_locked']) ? $txt['smf279'] : $txt['smf280']) . '' : (empty($context['is_locked']) ? $txt['smf279'] : $txt['smf280'])) . ''; + if ($context['can_sticky']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . (empty($context['is_sticky']) ? $txt['smf277'] : $txt['smf278']) . '' : (empty($context['is_sticky']) ? $txt['smf277'] : $txt['smf278'])) . ''; + if ($context['can_merge']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt['smf252'] . '' : $txt['smf252']) . ''; + if ($context['can_remove_poll']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt['poll_remove'] . '' : $txt['poll_remove']) . ''; + + if ($context['calendar_post']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt['calendar37'] . '' : $txt['calendar37']) . ''; + + if ($context['can_remove_post'] && !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1) + $moderationButtons[] = $settings['use_image_buttons'] ? '' : '' . $txt['quickmod_delete_selected'] . ''; + + return implode($context['menu_separator'], $moderationButtons); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/MessageIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/MessageIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,417 @@ + + + ', theme_linktree(), ''; + if (!empty($settings['display_who_viewing'])) + { + echo ' + '; + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) == 1 ? $txt['who_member'] : $txt[19]; + else + echo empty($context['view_members_list']) ? '0 ' . $txt[19] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) or $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_board'], ''; + } + echo ' + + '; + + if (isset($context['boards']) && (!empty($options['show_children']) || $context['start'] == 0)) + { + echo ' +
    + + + '; + foreach ($context['boards'] as $board) + { + echo ' + + + + + + '; + } + echo ' +
    ', $txt['parent_boards'], '
    '; + + // Is this board itself new? + if ($board['new']) + echo '', $txt[333], ''; + // Is one of this board's children new, then? + elseif ($board['children_new']) + echo '', $txt[333], ''; + // I guess it's not new at all. + else + echo '', $txt[334], ''; + + echo ' + ', $board['name'], '
    + ', $board['description']; + + if (!empty($board['moderators'])) + echo ' +
    ', count($board['moderators']) == 1 ? $txt[298] : $txt[299], ': ', implode(', ', $board['link_moderators']), '
    '; + + if (!empty($board['children'])) + { + $children = array(); + foreach ($board['children'] as $child) + { + $child['link'] = '' . $child['name'] . ''; + $children[] = $child['new'] ? '' . $child['link'] . '' : $child['link']; + } + + echo ' +
    ', $txt['parent_boards'], ': ', implode(', ', $children), '
    '; + } + + echo ' +
    + ', $board['posts'], ' ', $txt[21], ' ', $txt['smf88'], '
    + ', $board['topics'], ' ', $txt[330], ' +
    '; + + if (!empty($board['last_post']['id'])) + echo ' + ', $txt[22], ' ', $txt[30], ' ', $board['last_post']['time'], '
    + ', $txt['smf88'], ' ', $board['last_post']['link'], ' ', $txt[525], ' ', $board['last_post']['member']['link']; + + echo ' +
    '; + } + + if (!empty($options['show_board_desc']) && $context['description'] != '') + { + echo ' + + + + +
    + ', $context['description'], ' +
    '; + } + + if (!$context['no_topic_listing']) + { + echo ' + + + + +
    + + + + + +
    ', $txt[139], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '' . ($settings['use_image_buttons'] ? '' . $txt['topbottom5'] . '' : $txt['topbottom5']) . '' : '', '', theme_show_buttons(), '
    +
    '; + + // If Quick Moderation is enabled start the form. + if (!empty($options['display_quick_mod']) && !empty($context['topics'])) + echo ' +
    '; + + echo ' + + '; + + // Are there actually any topics to show? + if (!empty($context['topics'])) + { + echo ' + + + + + + '; + + // Show a "select all" box for quick moderation? + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1) + echo ' + '; + // If it's on in "image" mode, don't show anything but the column. + elseif (!empty($options['display_quick_mod'])) + echo ' + '; + } + // No topics.... just say, "sorry bub". + else + echo ' + '; + + echo ' + '; + + foreach ($context['topics'] as $topic) + { + echo ' + + + + + + + + '; + + // Show the quick moderation options? + if (!empty($options['display_quick_mod'])) + { + echo ' + '; + } + echo ' + '; + } + + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + { + echo ' + + + '; + } + + echo ' +
    ', $txt[70], $context['sort_by'] == 'subject' ? ' ' : '', '', $txt[109], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt[110], $context['sort_by'] == 'replies' ? ' ' : '', '', $txt[301], $context['sort_by'] == 'views' ? ' ' : '', '', $txt[111], $context['sort_by'] == 'last_post' ? ' ' : '', ' + + ', $txt[151], '
    + + + + + ', $topic['first_post']['link'], ''; + + // Is this topic new? (assuming they are logged in!) + if ($topic['new'] && $context['user']['is_logged']) + echo ' + ', $txt[302], ''; + + echo ' + ', $topic['pages'], ' + + ', $topic['first_post']['member']['link'], ' + + ', $topic['replies'], ' + + ', $topic['views'], ' + + ', $txt[111], ' + + ', $topic['last_post']['time'], '
    + ', $txt[525], ' ', $topic['last_post']['member']['link'], ' +
    '; + if ($options['display_quick_mod'] == 1) + echo ' + '; + else + { + // Check permissions on each and show only the ones they are allowed to use. + if ($topic['quick_mod']['remove']) + echo '', $txt[63], ''; + if ($topic['quick_mod']['lock']) + echo '', $txt['smf279'], ''; + if ($topic['quick_mod']['lock'] || $topic['quick_mod']['remove']) + echo '
    '; + if ($topic['quick_mod']['sticky']) + echo '', $txt['smf277'], ''; + if ($topic['quick_mod']['move']) + echo '', $txt[132], ''; + } + echo '
    + '; + + if ($context['can_move']) + { + echo ' + '; + } + + echo ' + +
    '; + + // Finish off the form - again, if Quick Moderation is being done with checkboxes. (1) + if (!empty($options['display_quick_mod']) && !empty($context['topics'])) + echo ' + +
    '; + + echo ' + + + + +
    + + + + + +
    ', $txt[139], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '' . ($settings['use_image_buttons'] ? '' . $txt['topbottom4'] . '' : $txt['topbottom4']) . '' : '', '', theme_show_buttons(), '
    +
    '; + } + + echo ' + '; + + // Show breadcrumbs at the bottom too? + if ($settings['linktree_inline']) + echo ' + + + '; + + echo ' + '; + + if (!$context['no_topic_listing']) + echo ' + + '; + + echo ' + + +
    ', theme_linktree(), '

    ', !empty($modSettings['enableParticipation']) ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ' . $txt[457] . '
    + ' . $txt[454] . '
    + ' . $txt[455] . ' +
    + ' . $txt[456] . '
    ' . ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['smf96'] . '
    ' : '') . ($modSettings['pollMode'] == '1' ? ' + ' . $txt['smf43'] : '') . ' +
    +
    + : +   + +
    +
    '; + + // Javascript for inline editing. + echo ' + +'; + +} + +function theme_show_buttons() +{ + global $context, $settings, $options, $txt, $scripturl; + + $buttonArray = array(); + + // If they are logged in, and the mark read buttons are enabled.. + if ($context['user']['is_logged'] && $settings['show_mark_read']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[300] . '' : $txt[300]) . ''; + + // If the user has permission to show the notification button... ask them if they're sure, though. + if ($context['can_mark_notify']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[131] . '' : $txt[131]) . ''; + + // Are they allowed to post new topics? + if ($context['can_post_new']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[33] . '' : $txt[33]) . ''; + + // How about new polls, can the user post those? + if ($context['can_post_poll']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt['smf20'] . '' : $txt['smf20']) . ''; + + return implode($context['menu_separator'], $buttonArray); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/Recent.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/Recent.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,302 @@ +', theme_linktree(), ' + + + + +
    + ', $txt[139], ': ', $context['page_index'], ' +
    +
    '; + + foreach ($context['posts'] as $post) + { + echo ' + + + + + + + + + + + + + +
    +
     ', $post['counter'], ' 
    +
     ', $post['category']['link'], ' / ', $post['board']['link'], ' / ', $post['link'], '
    +
     ', $txt[30], ': ', $post['time'], ' 
    +
    + ', $txt[109], ' ' . $post['first_poster']['link'] . ' - ' . $txt[22] . ' ' . $txt[525] . ' ' . $post['poster']['link'] . ' +
    +
    ' . $post['message'] . '
    +
    '; + + if ($post['can_delete']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[121] . '' : $txt[31]), ''; + if ($post['can_delete'] && ($post['can_mark_notify'] || $post['can_reply'])) + echo ' + ', $context['menu_separator']; + if ($post['can_reply']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[146] . '' : $txt[146]), '', $context['menu_separator'], ' + ', ($settings['use_image_buttons'] ? '' . $txt[145] . '' : $txt[145]), ''; + if ($post['can_reply'] && $post['can_mark_notify']) + echo ' + ', $context['menu_separator']; + if ($post['can_mark_notify']) + echo ' + ' . ($settings['use_image_buttons'] ? '' . $txt[131] . '' : $txt[131]) . ''; + + echo '
    +
    '; + } + + echo ' + + + + +
    + ', $txt[139], ': ', $context['page_index'], ' +
    '; + if ($settings['linktree_inline']) + echo ' +
    ', theme_linktree(), '
    '; +} + +function template_unread() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' + + + + +
    ', theme_linktree(), '
    + + + + + +
    + + + ', $settings['show_mark_read'] ? ' + ' : '', ' + +
    ' . $txt[139] . ': ' . $context['page_index'] . '' . ($settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452]) . '
    +
    + + + +
    + + '; + if (!empty($context['topics'])) + echo ' + + + + + + '; + else + echo ' + '; + echo ' + '; + + foreach ($context['topics'] as $topic) + { + echo ' + + + + + + + + + '; + } + + if (!empty($context['topics']) && !$context['showing_all_topics']) + echo ' + + + '; + + echo ' +
     ', $txt[70], $context['sort_by'] == 'subject' ? ' ' : '', '', $txt[109], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt[110], $context['sort_by'] == 'replies' ? ' ' : '', '', $txt[301], $context['sort_by'] == 'views' ? ' ' : '', '', $txt[111], $context['sort_by'] == 'last_post' ? ' ' : '', '', $context['showing_all_topics'] ? $txt[151] : $txt['unread_topics_visit_none'], '
    + + + ' . $topic['first_post']['link'] . ' ' . $txt[302] . ' ' . $topic['pages'] . ' +
    ' . $txt['smf88'] . ' ' . $topic['board']['link'] . '
    + ' . $topic['first_post']['member']['link'] . ' + ' . $topic['replies'] . ' + ' . $topic['views'] . ' + ', $txt[111], ' + + ', $topic['last_post']['time'], '
    + ', $txt[525], ' ', $topic['last_post']['member']['link'], ' +
    ', $txt['unread_topics_all'], '
    +
    + + + + + +
    + + + ', $settings['show_mark_read'] ? ' + ' : '', ' + +
    ' . $txt[139] . ': ' . $context['page_index'] . '' . ($settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452]) . '
    +
    + + + + + + +
    ', !empty($modSettings['enableParticipation']) ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ' . $txt[457] . '
    + ' . $txt[454] . '
    + ' . $txt[455] . ' +
    + ' . $txt[456] . '
    ' . ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['smf96'] . '
    ' : '') . ($modSettings['pollMode'] == '1' ? ' + ' . $txt['smf43'] : '') . ' +
    '; +} + +function template_replies() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' + + + + +
    ', theme_linktree(), '
    + + + + + +
    + + + + + +
    ' . $txt[139] . ': ' . $context['page_index'] . ''; + + if (isset($context['topics_to_mark']) && !empty($settings['show_mark_read'])) + echo ' + ' . ($settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452]) . ''; + + echo '
    +
    + + + +
    + + '; + if (!empty($context['topics'])) + echo ' + + + + + + '; + else + echo ' + '; + echo ' + '; + + foreach ($context['topics'] as $topic) + { + echo ' + + + + + + + + + '; + } + + echo ' +
     ', $txt[70], $context['sort_by'] == 'subject' ? ' ' : '', '', $txt[109], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt[110], $context['sort_by'] == 'replies' ? ' ' : '', '', $txt[301], $context['sort_by'] == 'views' ? ' ' : '', '', $txt[111], $context['sort_by'] == 'last_post' ? ' ' : '', '' . $txt[151] . '
    + + + ', $topic['first_post']['link'], ' ', $txt[302], ' +
    ', $topic['pages'], ' ' . $txt['smf88'] . ' ' . $topic['board']['link'] . '
    + ' . $topic['first_post']['member']['link'] . ' + ' . $topic['replies'] . ' + ' . $topic['views'] . ' + ', $txt[111], ' + + ', $topic['last_post']['time'], '
    + ', $txt[525], ' ', $topic['last_post']['member']['link'], ' +
    +
    + + + + + +
    + + + + + +
    ' . $txt[139] . ': ' . $context['page_index'] . ''; + + if (isset($context['topics_to_mark']) && !empty($settings['show_mark_read'])) + echo ' + ' . ($settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452]) . ''; + + echo '
    +
    + + + + + + +
    + ' . $txt[457] . '
    + ' . $txt[454] . '
    + ' . $txt[455] . ' +
    + ' . $txt[456] . '
    ' . ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['smf96'] . '
    ' : '') . ($modSettings['pollMode'] == '1' ? ' + ' . $txt['smf43'] : '') . ' +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/Settings.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/Settings.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,215 @@ + 'show_board_desc', + 'label' => $txt[732], + 'default' => true, + ), + array( + 'id' => 'show_children', + 'label' => $txt['show_children'], + 'default' => true, + ), + array( + 'id' => 'show_no_avatars', + 'label' => $txt['show_no_avatars'], + 'default' => true, + ), + array( + 'id' => 'show_no_signatures', + 'label' => $txt['show_no_signatures'], + 'default' => true, + ), + array( + 'id' => 'show_no_censored', + 'label' => $txt['show_no_censored'], + 'default' => true, + ), + array( + 'id' => 'return_to_post', + 'label' => $txt['return_to_post'], + 'default' => true, + ), + array( + 'id' => 'no_new_reply_warning', + 'label' => $txt['no_new_reply_warning'], + 'default' => true, + ), + array( + 'id' => 'view_newest_first', + 'label' => $txt['recent_posts_at_top'], + 'default' => true, + ), + array( + 'id' => 'view_newest_pm_first', + 'label' => $txt['recent_pms_at_top'], + 'default' => true, + ), + array( + 'id' => 'popup_messages', + 'label' => $txt['popup_messages'], + 'default' => true, + ), + array( + 'id' => 'copy_to_outbox', + 'label' => $txt['copy_to_outbox'], + 'default' => true, + ), + array( + 'id' => 'auto_notify', + 'label' => $txt['auto_notify'], + 'default' => true, + ), + array( + 'id' => 'calendar_start_day', + 'label' => $txt['calendar_start_day'], + 'options' => array( + 0 => $txt['days'][0], + 1 => $txt['days'][1], + 6 => $txt['days'][6], + ), + 'default' => true, + ), + array( + 'id' => 'display_quick_reply', + 'label' => $txt['display_quick_reply'], + 'options' => array( + 0 => $txt['display_quick_reply1'], + 1 => $txt['display_quick_reply2'], + 2 => $txt['display_quick_reply3'] + ), + 'default' => true, + ), + array( + 'id' => 'display_quick_mod', + 'label' => $txt['display_quick_mod'], + 'options' => array( + 0 => $txt['display_quick_mod_none'], + 1 => $txt['display_quick_mod_check'], + 2 => $txt['display_quick_mod_image'], + ), + 'default' => true, + ), + ); +} + +function template_settings() +{ + global $context, $settings, $options, $scripturl, $txt; + + $context['theme_settings'] = array( + array( + 'id' => 'header_logo_url', + 'label' => $txt['header_logo_url'], + 'description' => $txt['header_logo_url_desc'], + 'type' => 'text', + ), + array( + 'id' => 'number_recent_posts', + 'label' => $txt['number_recent_posts'], + 'description' => $txt['number_recent_posts_desc'], + 'type' => 'number', + ), + array( + 'id' => 'display_who_viewing', + 'label' => $txt['who_display_viewing'], + 'options' => array( + 0 => $txt['who_display_viewing_off'], + 1 => $txt['who_display_viewing_numbers'], + 2 => $txt['who_display_viewing_names'], + ), + ), + array( + 'id' => 'smiley_sets_default', + 'label' => $txt['smileys_default_set_for_theme'], + 'options' => $context['smiley_sets'], + ), + array( + 'id' => 'show_modify', + 'label' => $txt[383], + ), + array( + 'id' => 'show_member_bar', + 'label' => $txt[510], + ), + array( + 'id' => 'linktree_link', + 'label' => $txt[522], + ), + array( + 'id' => 'show_profile_buttons', + 'label' => $txt[523], + ), + array( + 'id' => 'show_mark_read', + 'label' => $txt[618], + ), + array( + 'id' => 'linktree_inline', + 'label' => $txt['smf105'], + 'description' => $txt['smf106'], + ), + array( + 'id' => 'show_sp1_info', + 'label' => $txt['smf200'], + ), + array( + 'id' => 'allow_no_censored', + 'label' => $txt['allow_no_censored'], + ), + array( + 'id' => 'show_bbc', + 'label' => $txt[740], + ), + array( + 'id' => 'additional_options_collapsable', + 'label' => $txt['additional_options_collapsable'], + ), + array( + 'id' => 'enable_news', + 'label' => $txt[379], + ), + array( + 'id' => 'show_newsfader', + 'label' => $txt[387], + ), + array( + 'id' => 'newsfader_time', + 'label' => $txt[739], + 'type' => 'number', + ), + array( + 'id' => 'show_user_images', + 'label' => $txt[384], + ), + array( + 'id' => 'show_blurb', + 'label' => $txt[385], + ), + array( + 'id' => 'show_latest_member', + 'label' => $txt[382], + ), + array( + 'id' => 'use_image_buttons', + 'label' => $txt[521], + ), + array( + 'id' => 'show_gender', + 'label' => $txt[386], + ), + array( + 'id' => 'hide_post_group', + 'label' => $txt['hide_post_group'], + 'description' => $txt['hide_post_group_desc'], + ), + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/Female.gif Binary file forum/Themes/babylon/images/Female.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/Male.gif Binary file forum/Themes/babylon/images/Male.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/aim.gif Binary file forum/Themes/babylon/images/aim.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bar.gif Binary file forum/Themes/babylon/images/bar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/bbc_bg.gif Binary file forum/Themes/babylon/images/bbc/bbc_bg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/bbc_hoverbg.gif Binary file forum/Themes/babylon/images/bbc/bbc_hoverbg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/bold.gif Binary file forum/Themes/babylon/images/bbc/bold.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/center.gif Binary file forum/Themes/babylon/images/bbc/center.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/code.gif Binary file forum/Themes/babylon/images/bbc/code.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/divider.gif Binary file forum/Themes/babylon/images/bbc/divider.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/email.gif Binary file forum/Themes/babylon/images/bbc/email.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/face.gif Binary file forum/Themes/babylon/images/bbc/face.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/flash.gif Binary file forum/Themes/babylon/images/bbc/flash.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/ftp.gif Binary file forum/Themes/babylon/images/bbc/ftp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/glow.gif Binary file forum/Themes/babylon/images/bbc/glow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/hr.gif Binary file forum/Themes/babylon/images/bbc/hr.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/img.gif Binary file forum/Themes/babylon/images/bbc/img.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/italicize.gif Binary file forum/Themes/babylon/images/bbc/italicize.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/left.gif Binary file forum/Themes/babylon/images/bbc/left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/list.gif Binary file forum/Themes/babylon/images/bbc/list.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/move.gif Binary file forum/Themes/babylon/images/bbc/move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/pre.gif Binary file forum/Themes/babylon/images/bbc/pre.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/quote.gif Binary file forum/Themes/babylon/images/bbc/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/right.gif Binary file forum/Themes/babylon/images/bbc/right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/shadow.gif Binary file forum/Themes/babylon/images/bbc/shadow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/size.gif Binary file forum/Themes/babylon/images/bbc/size.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/strike.gif Binary file forum/Themes/babylon/images/bbc/strike.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/sub.gif Binary file forum/Themes/babylon/images/bbc/sub.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/sup.gif Binary file forum/Themes/babylon/images/bbc/sup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/table.gif Binary file forum/Themes/babylon/images/bbc/table.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/td.gif Binary file forum/Themes/babylon/images/bbc/td.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/tele.gif Binary file forum/Themes/babylon/images/bbc/tele.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/tr.gif Binary file forum/Themes/babylon/images/bbc/tr.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/underline.gif Binary file forum/Themes/babylon/images/bbc/underline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bbc/url.gif Binary file forum/Themes/babylon/images/bbc/url.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/bdaycake.gif Binary file forum/Themes/babylon/images/bdaycake.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/blank.gif Binary file forum/Themes/babylon/images/blank.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/board.gif Binary file forum/Themes/babylon/images/board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/box_bg.gif Binary file forum/Themes/babylon/images/box_bg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/buddy_useroff.gif Binary file forum/Themes/babylon/images/buddy_useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/buddy_useron.gif Binary file forum/Themes/babylon/images/buddy_useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/catbg.jpg Binary file forum/Themes/babylon/images/catbg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/catbg2.jpg Binary file forum/Themes/babylon/images/catbg2.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/collapse.gif Binary file forum/Themes/babylon/images/collapse.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/construction.gif Binary file forum/Themes/babylon/images/construction.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/addpoll.gif Binary file forum/Themes/babylon/images/dutch/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/admin.gif Binary file forum/Themes/babylon/images/dutch/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/admin_lock.gif Binary file forum/Themes/babylon/images/dutch/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/admin_move.gif Binary file forum/Themes/babylon/images/dutch/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/admin_rem.gif Binary file forum/Themes/babylon/images/dutch/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/admin_remove_poll.gif Binary file forum/Themes/babylon/images/dutch/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/admin_sticky.gif Binary file forum/Themes/babylon/images/dutch/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/calendar.gif Binary file forum/Themes/babylon/images/dutch/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/calendarpe.gif Binary file forum/Themes/babylon/images/dutch/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/delete.gif Binary file forum/Themes/babylon/images/dutch/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/delete_selected.gif Binary file forum/Themes/babylon/images/dutch/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/go_down.gif Binary file forum/Themes/babylon/images/dutch/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/go_up.gif Binary file forum/Themes/babylon/images/dutch/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/help.gif Binary file forum/Themes/babylon/images/dutch/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/home.gif Binary file forum/Themes/babylon/images/dutch/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/im_delete.gif Binary file forum/Themes/babylon/images/dutch/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/im_inbox.gif Binary file forum/Themes/babylon/images/dutch/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/im_new.gif Binary file forum/Themes/babylon/images/dutch/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/im_outbox.gif Binary file forum/Themes/babylon/images/dutch/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/im_reload.gif Binary file forum/Themes/babylon/images/dutch/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/im_reply.gif Binary file forum/Themes/babylon/images/dutch/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/keystats.gif Binary file forum/Themes/babylon/images/dutch/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/linktocal.gif Binary file forum/Themes/babylon/images/dutch/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/login.gif Binary file forum/Themes/babylon/images/dutch/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/logout.gif Binary file forum/Themes/babylon/images/dutch/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/markread.gif Binary file forum/Themes/babylon/images/dutch/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/markunread.gif Binary file forum/Themes/babylon/images/dutch/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/memberlist.gif Binary file forum/Themes/babylon/images/dutch/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/merge.gif Binary file forum/Themes/babylon/images/dutch/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/modify.gif Binary file forum/Themes/babylon/images/dutch/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/new.gif Binary file forum/Themes/babylon/images/dutch/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/new_poll.gif Binary file forum/Themes/babylon/images/dutch/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/new_topic.gif Binary file forum/Themes/babylon/images/dutch/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/newsbox.gif Binary file forum/Themes/babylon/images/dutch/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/notify.gif Binary file forum/Themes/babylon/images/dutch/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/notify_sm.gif Binary file forum/Themes/babylon/images/dutch/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/print.gif Binary file forum/Themes/babylon/images/dutch/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/profile.gif Binary file forum/Themes/babylon/images/dutch/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/quote.gif Binary file forum/Themes/babylon/images/dutch/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/register.gif Binary file forum/Themes/babylon/images/dutch/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/reply.gif Binary file forum/Themes/babylon/images/dutch/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/reply_sm.gif Binary file forum/Themes/babylon/images/dutch/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/search.gif Binary file forum/Themes/babylon/images/dutch/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/sendtopic.gif Binary file forum/Themes/babylon/images/dutch/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/split.gif Binary file forum/Themes/babylon/images/dutch/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/userinfo.gif Binary file forum/Themes/babylon/images/dutch/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/dutch/who.gif Binary file forum/Themes/babylon/images/dutch/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/email_sm.gif Binary file forum/Themes/babylon/images/email_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/addpoll.gif Binary file forum/Themes/babylon/images/english/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/admin.gif Binary file forum/Themes/babylon/images/english/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/admin_lock.gif Binary file forum/Themes/babylon/images/english/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/admin_move.gif Binary file forum/Themes/babylon/images/english/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/admin_rem.gif Binary file forum/Themes/babylon/images/english/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/admin_remove_poll.gif Binary file forum/Themes/babylon/images/english/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/admin_sticky.gif Binary file forum/Themes/babylon/images/english/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/calendar.gif Binary file forum/Themes/babylon/images/english/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/calendarpe.gif Binary file forum/Themes/babylon/images/english/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/delete.gif Binary file forum/Themes/babylon/images/english/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/delete_selected.gif Binary file forum/Themes/babylon/images/english/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/go_down.gif Binary file forum/Themes/babylon/images/english/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/go_up.gif Binary file forum/Themes/babylon/images/english/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/help.gif Binary file forum/Themes/babylon/images/english/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/home.gif Binary file forum/Themes/babylon/images/english/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/im_delete.gif Binary file forum/Themes/babylon/images/english/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/im_inbox.gif Binary file forum/Themes/babylon/images/english/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/im_new.gif Binary file forum/Themes/babylon/images/english/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/im_outbox.gif Binary file forum/Themes/babylon/images/english/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/im_preferences.gif Binary file forum/Themes/babylon/images/english/im_preferences.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/im_reload.gif Binary file forum/Themes/babylon/images/english/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/im_reply.gif Binary file forum/Themes/babylon/images/english/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/keystats.gif Binary file forum/Themes/babylon/images/english/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/linktocal.gif Binary file forum/Themes/babylon/images/english/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/login.gif Binary file forum/Themes/babylon/images/english/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/logout.gif Binary file forum/Themes/babylon/images/english/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/markread.gif Binary file forum/Themes/babylon/images/english/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/markunread.gif Binary file forum/Themes/babylon/images/english/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/memberlist.gif Binary file forum/Themes/babylon/images/english/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/merge.gif Binary file forum/Themes/babylon/images/english/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/modify.gif Binary file forum/Themes/babylon/images/english/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/new.gif Binary file forum/Themes/babylon/images/english/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/new_poll.gif Binary file forum/Themes/babylon/images/english/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/new_topic.gif Binary file forum/Themes/babylon/images/english/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/newsbox.gif Binary file forum/Themes/babylon/images/english/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/notify.gif Binary file forum/Themes/babylon/images/english/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/notify_sm.gif Binary file forum/Themes/babylon/images/english/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/print.gif Binary file forum/Themes/babylon/images/english/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/profile.gif Binary file forum/Themes/babylon/images/english/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/quote.gif Binary file forum/Themes/babylon/images/english/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/register.gif Binary file forum/Themes/babylon/images/english/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/reply.gif Binary file forum/Themes/babylon/images/english/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/reply_sm.gif Binary file forum/Themes/babylon/images/english/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/search.gif Binary file forum/Themes/babylon/images/english/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/sendtopic.gif Binary file forum/Themes/babylon/images/english/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/split.gif Binary file forum/Themes/babylon/images/english/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/userinfo.gif Binary file forum/Themes/babylon/images/english/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/english/who.gif Binary file forum/Themes/babylon/images/english/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/expand.gif Binary file forum/Themes/babylon/images/expand.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/filter.gif Binary file forum/Themes/babylon/images/filter.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/addpoll.gif Binary file forum/Themes/babylon/images/german/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/admin.gif Binary file forum/Themes/babylon/images/german/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/admin_lock.gif Binary file forum/Themes/babylon/images/german/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/admin_move.gif Binary file forum/Themes/babylon/images/german/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/admin_rem.gif Binary file forum/Themes/babylon/images/german/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/admin_remove_poll.gif Binary file forum/Themes/babylon/images/german/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/admin_sticky.gif Binary file forum/Themes/babylon/images/german/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/calendar.gif Binary file forum/Themes/babylon/images/german/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/calendarpe.gif Binary file forum/Themes/babylon/images/german/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/delete.gif Binary file forum/Themes/babylon/images/german/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/delete_selected.gif Binary file forum/Themes/babylon/images/german/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/go_down.gif Binary file forum/Themes/babylon/images/german/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/go_up.gif Binary file forum/Themes/babylon/images/german/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/help.gif Binary file forum/Themes/babylon/images/german/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/home.gif Binary file forum/Themes/babylon/images/german/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/im_delete.gif Binary file forum/Themes/babylon/images/german/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/im_inbox.gif Binary file forum/Themes/babylon/images/german/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/im_new.gif Binary file forum/Themes/babylon/images/german/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/im_outbox.gif Binary file forum/Themes/babylon/images/german/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/im_reload.gif Binary file forum/Themes/babylon/images/german/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/im_reply.gif Binary file forum/Themes/babylon/images/german/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/keystats.gif Binary file forum/Themes/babylon/images/german/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/linktocal.gif Binary file forum/Themes/babylon/images/german/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/login.gif Binary file forum/Themes/babylon/images/german/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/logout.gif Binary file forum/Themes/babylon/images/german/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/markread.gif Binary file forum/Themes/babylon/images/german/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/markunread.gif Binary file forum/Themes/babylon/images/german/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/memberlist.gif Binary file forum/Themes/babylon/images/german/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/merge.gif Binary file forum/Themes/babylon/images/german/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/modify.gif Binary file forum/Themes/babylon/images/german/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/new.gif Binary file forum/Themes/babylon/images/german/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/new_poll.gif Binary file forum/Themes/babylon/images/german/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/new_topic.gif Binary file forum/Themes/babylon/images/german/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/newsbox.gif Binary file forum/Themes/babylon/images/german/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/notify.gif Binary file forum/Themes/babylon/images/german/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/notify_sm.gif Binary file forum/Themes/babylon/images/german/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/print.gif Binary file forum/Themes/babylon/images/german/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/profile.gif Binary file forum/Themes/babylon/images/german/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/quote.gif Binary file forum/Themes/babylon/images/german/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/register.gif Binary file forum/Themes/babylon/images/german/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/reply.gif Binary file forum/Themes/babylon/images/german/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/reply_sm.gif Binary file forum/Themes/babylon/images/german/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/search.gif Binary file forum/Themes/babylon/images/german/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/sendtopic.gif Binary file forum/Themes/babylon/images/german/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/split.gif Binary file forum/Themes/babylon/images/german/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/userinfo.gif Binary file forum/Themes/babylon/images/german/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/german/who.gif Binary file forum/Themes/babylon/images/german/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/h_powered-mysql.gif Binary file forum/Themes/babylon/images/h_powered-mysql.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/h_powered-php.gif Binary file forum/Themes/babylon/images/h_powered-php.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/h_valid-css.gif Binary file forum/Themes/babylon/images/h_valid-css.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/h_valid-xhtml10.gif Binary file forum/Themes/babylon/images/h_valid-xhtml10.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/helplogo.jpg Binary file forum/Themes/babylon/images/helplogo.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/helptopics.gif Binary file forum/Themes/babylon/images/helptopics.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/angry.gif Binary file forum/Themes/babylon/images/icons/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/assist.gif Binary file forum/Themes/babylon/images/icons/assist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/bullet_grin.gif Binary file forum/Themes/babylon/images/icons/bullet_grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/calendar.gif Binary file forum/Themes/babylon/images/icons/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/cheesy.gif Binary file forum/Themes/babylon/images/icons/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/clip.gif Binary file forum/Themes/babylon/images/icons/clip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/config_sm.gif Binary file forum/Themes/babylon/images/icons/config_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/delete.gif Binary file forum/Themes/babylon/images/icons/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/exclamation.gif Binary file forum/Themes/babylon/images/icons/exclamation.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/folder_open.gif Binary file forum/Themes/babylon/images/icons/folder_open.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/grin.gif Binary file forum/Themes/babylon/images/icons/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/im_newmsg.gif Binary file forum/Themes/babylon/images/icons/im_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/info.gif Binary file forum/Themes/babylon/images/icons/info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/lamp.gif Binary file forum/Themes/babylon/images/icons/lamp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/last_post.gif Binary file forum/Themes/babylon/images/icons/last_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/linktree_main.gif Binary file forum/Themes/babylon/images/icons/linktree_main.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/linktree_side.gif Binary file forum/Themes/babylon/images/icons/linktree_side.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/login.gif Binary file forum/Themes/babylon/images/icons/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/login_sm.gif Binary file forum/Themes/babylon/images/icons/login_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/members.gif Binary file forum/Themes/babylon/images/icons/members.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/modify_inline.gif Binary file forum/Themes/babylon/images/icons/modify_inline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/moved.gif Binary file forum/Themes/babylon/images/icons/moved.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/notify_sm.gif Binary file forum/Themes/babylon/images/icons/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/online.gif Binary file forum/Themes/babylon/images/icons/online.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/package_installed.gif Binary file forum/Themes/babylon/images/icons/package_installed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/package_old.gif Binary file forum/Themes/babylon/images/icons/package_old.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/pm_read.gif Binary file forum/Themes/babylon/images/icons/pm_read.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/pm_replied.gif Binary file forum/Themes/babylon/images/icons/pm_replied.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/profile_sm.gif Binary file forum/Themes/babylon/images/icons/profile_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/question.gif Binary file forum/Themes/babylon/images/icons/question.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/quick_lock.gif Binary file forum/Themes/babylon/images/icons/quick_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/quick_move.gif Binary file forum/Themes/babylon/images/icons/quick_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/quick_remove.gif Binary file forum/Themes/babylon/images/icons/quick_remove.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/quick_sticky.gif Binary file forum/Themes/babylon/images/icons/quick_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/recycled.gif Binary file forum/Themes/babylon/images/icons/recycled.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/sad.gif Binary file forum/Themes/babylon/images/icons/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/smiley.gif Binary file forum/Themes/babylon/images/icons/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/thumbdown.gif Binary file forum/Themes/babylon/images/icons/thumbdown.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/thumbup.gif Binary file forum/Themes/babylon/images/icons/thumbup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/wink.gif Binary file forum/Themes/babylon/images/icons/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/wireless.gif Binary file forum/Themes/babylon/images/icons/wireless.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icons/xx.gif Binary file forum/Themes/babylon/images/icons/xx.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/icq.gif Binary file forum/Themes/babylon/images/icq.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/im_off.gif Binary file forum/Themes/babylon/images/im_off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/im_on.gif Binary file forum/Themes/babylon/images/im_on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/im_sm_newmsg.gif Binary file forum/Themes/babylon/images/im_sm_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/im_sm_prefs.gif Binary file forum/Themes/babylon/images/im_sm_prefs.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/ip.gif Binary file forum/Themes/babylon/images/ip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/message_sm.gif Binary file forum/Themes/babylon/images/message_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/msntalk.gif Binary file forum/Themes/babylon/images/msntalk.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/new_none.gif Binary file forum/Themes/babylon/images/new_none.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/new_some.gif Binary file forum/Themes/babylon/images/new_some.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/off.gif Binary file forum/Themes/babylon/images/off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/on.gif Binary file forum/Themes/babylon/images/on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/on2.gif Binary file forum/Themes/babylon/images/on2.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/poll_left.gif Binary file forum/Themes/babylon/images/poll_left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/poll_middle.gif Binary file forum/Themes/babylon/images/poll_middle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/poll_right.gif Binary file forum/Themes/babylon/images/poll_right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/angry.gif Binary file forum/Themes/babylon/images/post/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/cheesy.gif Binary file forum/Themes/babylon/images/post/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/exclamation.gif Binary file forum/Themes/babylon/images/post/exclamation.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/grin.gif Binary file forum/Themes/babylon/images/post/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/lamp.gif Binary file forum/Themes/babylon/images/post/lamp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/moved.gif Binary file forum/Themes/babylon/images/post/moved.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/question.gif Binary file forum/Themes/babylon/images/post/question.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/recycled.gif Binary file forum/Themes/babylon/images/post/recycled.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/sad.gif Binary file forum/Themes/babylon/images/post/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/smiley.gif Binary file forum/Themes/babylon/images/post/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/thumbdown.gif Binary file forum/Themes/babylon/images/post/thumbdown.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/thumbup.gif Binary file forum/Themes/babylon/images/post/thumbup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/wink.gif Binary file forum/Themes/babylon/images/post/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/wireless.gif Binary file forum/Themes/babylon/images/post/wireless.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/post/xx.gif Binary file forum/Themes/babylon/images/post/xx.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/powered-mysql.gif Binary file forum/Themes/babylon/images/powered-mysql.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/powered-php.gif Binary file forum/Themes/babylon/images/powered-php.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/selected.gif Binary file forum/Themes/babylon/images/selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/smflogo.gif Binary file forum/Themes/babylon/images/smflogo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/smiley_select_spot.gif Binary file forum/Themes/babylon/images/smiley_select_spot.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/sort_down.gif Binary file forum/Themes/babylon/images/sort_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/sort_up.gif Binary file forum/Themes/babylon/images/sort_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/addpoll.gif Binary file forum/Themes/babylon/images/spanish/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/admin.gif Binary file forum/Themes/babylon/images/spanish/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/admin_lock.gif Binary file forum/Themes/babylon/images/spanish/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/admin_move.gif Binary file forum/Themes/babylon/images/spanish/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/admin_rem.gif Binary file forum/Themes/babylon/images/spanish/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/admin_remove_poll.gif Binary file forum/Themes/babylon/images/spanish/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/admin_sticky.gif Binary file forum/Themes/babylon/images/spanish/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/calendar.gif Binary file forum/Themes/babylon/images/spanish/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/calendarpe.gif Binary file forum/Themes/babylon/images/spanish/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/delete.gif Binary file forum/Themes/babylon/images/spanish/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/delete_selected.gif Binary file forum/Themes/babylon/images/spanish/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/go_down.gif Binary file forum/Themes/babylon/images/spanish/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/go_up.gif Binary file forum/Themes/babylon/images/spanish/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/help.gif Binary file forum/Themes/babylon/images/spanish/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/home.gif Binary file forum/Themes/babylon/images/spanish/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/im_delete.gif Binary file forum/Themes/babylon/images/spanish/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/im_inbox.gif Binary file forum/Themes/babylon/images/spanish/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/im_new.gif Binary file forum/Themes/babylon/images/spanish/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/im_outbox.gif Binary file forum/Themes/babylon/images/spanish/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/im_reload.gif Binary file forum/Themes/babylon/images/spanish/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/im_reply.gif Binary file forum/Themes/babylon/images/spanish/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/keystats.gif Binary file forum/Themes/babylon/images/spanish/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/linktocal.gif Binary file forum/Themes/babylon/images/spanish/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/login.gif Binary file forum/Themes/babylon/images/spanish/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/logout.gif Binary file forum/Themes/babylon/images/spanish/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/markread.gif Binary file forum/Themes/babylon/images/spanish/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/markunread.gif Binary file forum/Themes/babylon/images/spanish/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/memberlist.gif Binary file forum/Themes/babylon/images/spanish/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/merge.gif Binary file forum/Themes/babylon/images/spanish/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/modify.gif Binary file forum/Themes/babylon/images/spanish/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/new.gif Binary file forum/Themes/babylon/images/spanish/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/new_poll.gif Binary file forum/Themes/babylon/images/spanish/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/new_topic.gif Binary file forum/Themes/babylon/images/spanish/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/newsbox.gif Binary file forum/Themes/babylon/images/spanish/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/notify.gif Binary file forum/Themes/babylon/images/spanish/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/notify_sm.gif Binary file forum/Themes/babylon/images/spanish/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/print.gif Binary file forum/Themes/babylon/images/spanish/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/profile.gif Binary file forum/Themes/babylon/images/spanish/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/quote.gif Binary file forum/Themes/babylon/images/spanish/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/register.gif Binary file forum/Themes/babylon/images/spanish/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/reply.gif Binary file forum/Themes/babylon/images/spanish/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/reply_sm.gif Binary file forum/Themes/babylon/images/spanish/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/search.gif Binary file forum/Themes/babylon/images/spanish/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/sendtopic.gif Binary file forum/Themes/babylon/images/spanish/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/split.gif Binary file forum/Themes/babylon/images/spanish/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/userinfo.gif Binary file forum/Themes/babylon/images/spanish/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/spanish/who.gif Binary file forum/Themes/babylon/images/spanish/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/split_deselect.gif Binary file forum/Themes/babylon/images/split_deselect.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/split_select.gif Binary file forum/Themes/babylon/images/split_select.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/star.gif Binary file forum/Themes/babylon/images/star.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/staradmin.gif Binary file forum/Themes/babylon/images/staradmin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/stargmod.gif Binary file forum/Themes/babylon/images/stargmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/starmod.gif Binary file forum/Themes/babylon/images/starmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/stats_board.gif Binary file forum/Themes/babylon/images/stats_board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/stats_history.gif Binary file forum/Themes/babylon/images/stats_history.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/stats_info.gif Binary file forum/Themes/babylon/images/stats_info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/stats_posters.gif Binary file forum/Themes/babylon/images/stats_posters.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/stats_replies.gif Binary file forum/Themes/babylon/images/stats_replies.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/stats_views.gif Binary file forum/Themes/babylon/images/stats_views.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/thumbnail.gif Binary file forum/Themes/babylon/images/thumbnail.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/hot_poll.gif Binary file forum/Themes/babylon/images/topic/hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/hot_poll_locked.gif Binary file forum/Themes/babylon/images/topic/hot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/hot_poll_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/hot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/hot_poll_sticky.gif Binary file forum/Themes/babylon/images/topic/hot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/hot_post.gif Binary file forum/Themes/babylon/images/topic/hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/hot_post_locked.gif Binary file forum/Themes/babylon/images/topic/hot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/hot_post_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/hot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/hot_post_sticky.gif Binary file forum/Themes/babylon/images/topic/hot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_hot_poll.gif Binary file forum/Themes/babylon/images/topic/my_hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_hot_poll_locked.gif Binary file forum/Themes/babylon/images/topic/my_hot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_hot_poll_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/my_hot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_hot_poll_sticky.gif Binary file forum/Themes/babylon/images/topic/my_hot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_hot_post.gif Binary file forum/Themes/babylon/images/topic/my_hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_hot_post_locked.gif Binary file forum/Themes/babylon/images/topic/my_hot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_hot_post_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/my_hot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_hot_post_sticky.gif Binary file forum/Themes/babylon/images/topic/my_hot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_normal_poll.gif Binary file forum/Themes/babylon/images/topic/my_normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_normal_poll_locked.gif Binary file forum/Themes/babylon/images/topic/my_normal_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_normal_poll_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/my_normal_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_normal_poll_sticky.gif Binary file forum/Themes/babylon/images/topic/my_normal_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_normal_post.gif Binary file forum/Themes/babylon/images/topic/my_normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_normal_post_locked.gif Binary file forum/Themes/babylon/images/topic/my_normal_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_normal_post_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/my_normal_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_normal_post_sticky.gif Binary file forum/Themes/babylon/images/topic/my_normal_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_veryhot_poll.gif Binary file forum/Themes/babylon/images/topic/my_veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_veryhot_poll_locked.gif Binary file forum/Themes/babylon/images/topic/my_veryhot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_veryhot_poll_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/my_veryhot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_veryhot_poll_sticky.gif Binary file forum/Themes/babylon/images/topic/my_veryhot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_veryhot_post.gif Binary file forum/Themes/babylon/images/topic/my_veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_veryhot_post_locked.gif Binary file forum/Themes/babylon/images/topic/my_veryhot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_veryhot_post_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/my_veryhot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/my_veryhot_post_sticky.gif Binary file forum/Themes/babylon/images/topic/my_veryhot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/normal_poll.gif Binary file forum/Themes/babylon/images/topic/normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/normal_poll_locked.gif Binary file forum/Themes/babylon/images/topic/normal_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/normal_poll_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/normal_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/normal_poll_sticky.gif Binary file forum/Themes/babylon/images/topic/normal_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/normal_post.gif Binary file forum/Themes/babylon/images/topic/normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/normal_post_locked.gif Binary file forum/Themes/babylon/images/topic/normal_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/normal_post_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/normal_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/normal_post_sticky.gif Binary file forum/Themes/babylon/images/topic/normal_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/veryhot_poll.gif Binary file forum/Themes/babylon/images/topic/veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/veryhot_poll_locked.gif Binary file forum/Themes/babylon/images/topic/veryhot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/veryhot_poll_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/veryhot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/veryhot_poll_sticky.gif Binary file forum/Themes/babylon/images/topic/veryhot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/veryhot_post.gif Binary file forum/Themes/babylon/images/topic/veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/veryhot_post_locked.gif Binary file forum/Themes/babylon/images/topic/veryhot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/veryhot_post_locked_sticky.gif Binary file forum/Themes/babylon/images/topic/veryhot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/topic/veryhot_post_sticky.gif Binary file forum/Themes/babylon/images/topic/veryhot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/upshrink.gif Binary file forum/Themes/babylon/images/upshrink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/upshrink2.gif Binary file forum/Themes/babylon/images/upshrink2.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/useroff.gif Binary file forum/Themes/babylon/images/useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/useron.gif Binary file forum/Themes/babylon/images/useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/valid-css.gif Binary file forum/Themes/babylon/images/valid-css.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/valid-xhtml10.gif Binary file forum/Themes/babylon/images/valid-xhtml10.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/www.gif Binary file forum/Themes/babylon/images/www.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/www_sm.gif Binary file forum/Themes/babylon/images/www_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/images/yim.gif Binary file forum/Themes/babylon/images/yim.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/index.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/index.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,538 @@ + + + + ', empty($context['robot_no_index']) ? '' : ' + ', ' + + + + ', $context['page_title'], ''; + + // The ?fin11 part of this link is just here to make sure browsers don't cache it wrongly. + echo ' + + '; + + /* Internet Explorer 4/5 and Opera 6 just don't do font sizes properly. (they are big...) + Thus, in Internet Explorer 4, 5, and Opera 6 this will show fonts one size smaller than usual. + Note that this is affected by whether IE 6 is in standards compliance mode.. if not, it will also be big. + Standards compliance mode happens when you use xhtml... */ + if ($context['browser']['needs_size_fix']) + echo ' + '; + + // Show all the relative links, such as help, search, contents, and the like. + echo ' + + + '; + + // If RSS feeds are enabled, advertise the presence of one. + if (!empty($modSettings['xmlnews_enable'])) + echo ' + '; + + // If we're viewing a topic, these should be the previous and next topics, respectively. + if (!empty($context['current_topic'])) + echo ' + + '; + + // If we're in a board, or a topic for that matter, the index will be the board's index. + if (!empty($context['current_board'])) + echo ' + '; + + // We'll have to use the cookie to remember the header... + if ($context['user']['is_guest']) + $options['collapse_header'] = !empty($_COOKIE['upshrink']); + + // Output any remaining HTML headers. (from mods, maybe?) + echo $context['html_headers'], ' + + + +'; + + // Because of the way width/padding are calculated, we have to tell Internet Explorer 4 and 5 that the content should be 100% wide. (or else it will assume about 108%!) + echo ' +
    '; + + // The logo and the three info boxes. + echo ' + + + + + + + + +
    '; + + // This part is the logo and forum name. You should be able to change this to whatever you want... + echo ' + '; + if (empty($settings['header_logo_url'])) + echo ' + ', $context['forum_name'], ''; + else + echo ' + ', $context['forum_name'], ''; + + echo ' +
    + + *'; + + // Show the menu here, according to the menu sub template. + template_menu(); + + echo ' +
    '; + + // The main content should go here. A table is used because IE 6 just can't handle a div. + echo ' + + +
    '; +} + +function template_main_below() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo '
    '; + + // Show the "Powered by" and "Valid" logos, as well as the copyright. Remember, the copyright must be somewhere! + echo ' + +
    + + + + + + + +
    + ', $txt['powered_by_mysql'], ' + ', $txt['powered_by_php'], ' + + ', theme_copyright(), ' + + ', $txt['valid_xhtml'], ' + ', $txt['valid_css'], ' +
    '; + + // Show the load time? + if ($context['show_load_time']) + echo ' + ', $txt['smf301'], $context['load_time'], $txt['smf302'], $context['load_queries'], $txt['smf302b'], ''; + + echo ' +
    '; + + // This is an interesting bug in Internet Explorer AND Safari. Rather annoying, it makes overflows just not tall enough. + if (($context['browser']['is_ie'] && !$context['browser']['is_ie4']) || $context['browser']['is_mac_ie'] || $context['browser']['is_safari'] || $context['browser']['is_firefox']) + { + // The purpose of this code is to fix the height of overflow: auto div blocks, because IE can't figure it out for itself. + echo ' + '; + } + + // The following will be used to let the user know that some AJAX process is running + echo ' + + +'; +} + +// Show a linktree. This is that thing that shows "My Community | General Category | General Discussion".. +function theme_linktree() +{ + global $context, $settings, $options; + + // Folder style or inline? Inline has a smaller font. + echo ''; + + // Each tree item has a URL and name. Some may have extra_before and extra_after. + foreach ($context['linktree'] as $link_num => $tree) + { + // Show the | | |-[] Folders. + if (!$settings['linktree_inline']) + { + if ($link_num > 0) + echo str_repeat('| ', $link_num - 1), '|-'; + echo '+  '; + } + + // Show something before the link? + if (isset($tree['extra_before'])) + echo $tree['extra_before']; + + // Show the link, including a URL if it should have one. + echo '', $settings['linktree_link'] && isset($tree['url']) ? '' . $tree['name'] . '' : $tree['name'], ''; + + // Show something after the link...? + if (isset($tree['extra_after'])) + echo $tree['extra_after']; + + // Don't show a separator for the last one. + if ($link_num != count($context['linktree']) - 1) + echo $settings['linktree_inline'] ? '  |  ' : '
    '; + } + + echo '
    '; +} + +// Show the menu up top. Something like [home] [help] [profile] [logout]... +function template_menu() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Show the [home] and [help] buttons. + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[103] . '' : $txt[103]), '', $context['menu_separator'], ' + ', ($settings['use_image_buttons'] ? '' . $txt[119] . '' : $txt[119]), '', $context['menu_separator']; + + // How about the [search] button? + if ($context['allow_search']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[182] . '' : $txt[182]), '', $context['menu_separator']; + + // Is the user allowed to administrate at all? ([admin]) + if ($context['allow_admin']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[2] . '' : $txt[2]), '', $context['menu_separator']; + + // Edit Profile... [profile] + if ($context['allow_edit_profile']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[79] . '' : $txt[467]), '', $context['menu_separator']; + + // The [calendar]! + if ($context['allow_calendar']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt['calendar24'] . '' : $txt['calendar24']), '', $context['menu_separator']; + + // If the user is a guest, show [login] and [register] buttons. + if ($context['user']['is_guest']) + { + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[34] . '' : $txt[34]), '', $context['menu_separator'], ' + ', ($settings['use_image_buttons'] ? '' . $txt[97] . '' : $txt[97]), ''; + } + // Otherwise, they might want to [logout]... + else + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[108] . '' : $txt[108]), ''; +} + +// Generate a strip of buttons, out of buttons. +function template_button_strip($button_strip, $direction = 'top', $force_reset = false, $custom_td = '') +{ + global $settings, $buttons, $context, $txt, $scripturl; + + if (empty($button_strip)) + return ''; + + // Create the buttons... + foreach ($button_strip as $key => $value) + { + if (isset($value['test']) && empty($context[$value['test']])) + { + unset($button_strip[$key]); + continue; + } + elseif (!isset($buttons[$key]) || $force_reset) + $buttons[$key] = '' . ($settings['use_image_buttons'] ? '' . $txt[$value['text']] . '' : $txt[$value['text']]) . ''; + + $button_strip[$key] = $buttons[$key]; + } + + echo ' + ', implode($context['menu_separator'], $button_strip) , ''; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/languages/Settings.dutch.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/languages/Settings.dutch.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,8 @@ +
    Thanks go to Babylonking and Alienine.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/languages/Settings.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/languages/Settings.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,7 @@ +
    Thanks go to Babylonking and Alienine.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/languages/Settings.german.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/languages/Settings.german.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,8 @@ +
    Thanks go to Babylonking and Alienine.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/languages/Settings.spanish.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/languages/Settings.spanish.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,8 @@ +
    Thanks go to Babylonking and Alienine.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/license.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/license.txt Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,73 @@ +Definitions +----------------------------------------------------------------------------- +i. This Package is defined as all of the files within any archive file or any +group of files released in conjunction by Simple Machines, Lewis Media, or a +derived or modified work based on such files. + +ii. A Modification, or a Mod, is defined as instructions, to be performed +manually or in an automated manner, that alter any part of this Package. + +iii. A Modified Package is defined as this Package or a derivative of it with +one or more Modification applied to it. + +iv. Distribution is defined as allowing one or more other people to in any +way download or receive a copy of this Package, a Modified Package, or a +derivative of this Package. + +v. The Software is defined as an installed copy of this Package, a Modified +Package, or a derivative of this Package. + +vi. The Simple Machines Website is defined as http://www.simplemachines.org/. + +Agreement +----------------------------------------------------------------------------- +1. Permission is hereby granted to use, copy, modify and/or distribute this +Package, provided that: + a. All copyright notices within source files and as generated by the +Software as output are retained, unchanged. + b. Any Distribution of this Package, whether as a Modified Package or +not, includes this file and is released under the terms of this Agreement. +This clause is not dependent upon any measure of changes made to this +Package. + c. This Package, Modified Packages, and derivative works may not be +sold or released under any paid license. Copying fees for the transport of +this Package, support fees for installation or other services, and hosting +fees for hosting the Software may, however, be imposed. + d. Any Distribution of this Package, whether as a Modified Package +or not, requires express written consent from Lewis Media. + +2. You may make Modifications to this Package or a derivative of it, and +distribute your Modifications in a form that is separate from the Package, +such as patches. The following restrictions apply to Modifications: + a. A Modification must not alter or remove any copyright notices in +the Software or Package, generated or otherwise. + b. When a Modification to the Package is released, a non-exclusive +royalty-free right is granted to Lewis Media to distribute the Modification +in future versions of the Package provided such versions remain available +under the terms of this Agreement in addition to any other license(s) of the +initial developer. + c. Any Distribution of a Modified Package or derivative requires +express written consent from Lewis Media. + +3. Permission is hereby also granted to distribute programs which depend on +this Package, provided that you do not distribute any Modified Package +without express written consent. + +4. Lewis Media reserves the right to change the terms of this Agreement at +any time, although those changes are not retroactive to past releases. +Changes to this document will be announced via email using the Simple +Machines email notification list. Failure to receive notification of a change +does not make those changes invalid. A current copy of this Agreement can be +found on the Simple Machines Website. + +5. This Agreement will terminate automatically if you fail to comply with the +limitations described herein. Upon termination, you must destroy all copies +of this Package, the Software, and any derivatives within 48 hours. + +----------------------------------------------------------------------------- +THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY WARRANTY. ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHORS BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY +OUT OF THE USE OR MISUSE OF THIS PACKAGE. \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/style.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/style.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,289 @@ +/* Normal, standard links. */ +a:link +{ + color: #000000; + text-decoration: underline; +} +a:visited, a:hover +{ + color: #323232; + text-decoration: underline; +} + +/* Navigation links - for the link tree. */ +.nav, .nav:link, .nav:visited +{ + color: #000000; + text-decoration: none; +} +a.nav:hover +{ + font-weight: bold; + color: #cc3333; + text-decoration: underline; +} + +/* Tables should show empty cells. */ +table +{ + empty-cells: show; +} + +/* By default (td, body..) use Tahoma in black. */ +body, td, th +{ + color: #000000; + font-size: small; + font-family: tahoma, sans-serif; +} + +/* The main body of the entire forum. */ +body +{ + background-color: white; + margin: 0px; + padding: 0px; +} + +/* Input boxes - just a bit smaller than normal so they align well. */ +input, textarea, button +{ + font-size: 9pt; + color: #000000; + font-family: tahoma, sans-serif; +} + +/* All input elements that are checkboxes or radio buttons. */ +input.check +{ +} + +/* Selects are a bit smaller, because it makes them look even better 8). */ +select +{ + font-size: 8pt; + font-weight: normal; + color: #000000; + font-family: tahoma, sans-serif; +} + +/* Standard horizontal rule.. ([hr], etc.) */ +hr, .hrcolor +{ + height: 1px; + border: 0; + color: #666666; + background-color: #666666; +} + +/* A quote, perhaps from another post. */ +.quote +{ + color: #000000; + background-color: #C3B8D0; + border: 1px solid #000000; + margin: 1px; + padding: 1px; + font-size: x-small; + line-height: 1.4em; +} + +/* A code block - maybe even PHP ;). */ +.code +{ + color: #000000; + background-color: #cccccc; + font-family: "courier new", "times new roman", monospace; + font-size: x-small; + line-height: 1.3em; + /* Put a nice border around it. */ + border: 1px solid #000000; + margin: 1px auto 1px auto; + padding: 1px; + width: 99%; + /* Don't wrap its contents, and show scrollbars. */ + white-space: nowrap; + overflow: auto; + /* Stop after about 24 lines, and just show a scrollbar. */ + max-height: 24em; +} + +/* The "Quote:" and "Code:" header parts... */ +.quoteheader, .codeheader +{ + color: #000000; + text-decoration: none; + font-style: normal; + font-weight: bold; + font-size: x-small; + line-height: 1.2em; +} + +/* Generally, those [?] icons. This makes your cursor a help icon. */ +.help +{ + cursor: help; +} + +/* /me uses this a lot. (emote, try typing /me in a post.) */ +.meaction +{ + color: red; +} + +/* The main post box - this makes it as wide as possible. */ +.editor +{ + width: 96%; +} + +/* Highlighted text - such as search results. */ +.highlight +{ + background-color: yellow; + font-weight: bold; + color: black; +} + +/* Alternating backgrounds for posts, and several other sections of the forum. */ +.windowbg +{ + color: #000000; + background-color: #E1E1E1; +} +.windowbg2 +{ + color: #000000; + background-color: #F0F0F0; +} + +/* These are used primarily for titles, but also for headers (the row that says what everything in the table is.) */ +.titlebg, tr.titlebg th, tr.titlebg td, .titlebg a:link, .titlebg a:visited, .titlebg2, tr.titlebg2 th, tr.titlebg2 td, .titlebg2 a:link, .titlebg2 a:visited +{ + color: white; + font-style: normal; + background-color: #6B8EAE; +} +.titlebg a:hover, .titlebg2 a:hover +{ + color: #dfdfdf; +} + +/* This is used for categories, page indexes, and several other areas in the forum. */ +.catbg, .catbg3 +{ + background-image: url(images/catbg.jpg); +} + +/* This is used for a category that has new posts in it... to make it light up. */ +.catbg2 +{ + background-image: url(images/catbg2.jpg); +} + +.catbg, .catbg2, .catbg3 +{ + font-weight: bold; + background-color: silver; + color: #000000; +} + +/* This is used for tables that have a grid/border background color (such as the topic listing.) */ +.bordercolor +{ + background-color: #828181; +} + +/* This is used on tables that should just have a border around them. */ +.tborder +{ + border: 1px solid #828181; + background-color: #FFFFFF; + padding: 2px; +} + +/* Default font sizes: small (8pt), normal (10pt), and large (14pt). */ +.smalltext +{ + font-size: x-small; + font-family: tahoma, sans-serif; +} +.normaltext +{ + font-size: small; +} +.largetext +{ + font-size: large; +} + +/* This is the white header area where the title, menu bars, and header boxes are. */ +#headerarea +{ + background-color: white; + border-bottom: 1px solid gray; +} +/* This is the main area of the forum, the part that's gray. */ +#bodyarea +{ + background-color: #D4D4D4; +} +/* And this is the bottom, where the copyright is, etc. */ +#footerarea +{ + color: black; + background-color: white; + border-top: 1px solid gray; +} + +/* This is for the special header boxes on the top (user info, key stats, news box.) */ +.headertitles +{ + background-color: #6B8EAE; + border: 1px solid #6B8EAE; + height: 12px; +} +.headerbodies +{ + border: 1px solid #7A7777; + background-color: #EFEFEF; + background-repeat: repeat-x; + background-position: bottom; +} + +/* Posts and personal messages displayed throughout the forum. */ +.post, .personalmessage +{ + width: 100%; + overflow: auto; + line-height: 1.3em; +} + +/* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ +.signature +{ + width: 100%; + overflow: auto; + padding-bottom: 3px; + line-height: 1.3em; +} +/* No image should have a border when linked */ +a img{ + border: 0; +} + +/* The AJAX notifier */ +#ajax_in_progress +{ + background: #32CD32; + color: white; + text-align: center; + font-weight: bold; + font-size: 18pt; + padding: 3px; + width: 100%; + position: fixed; + top: 0; + left: 0; +} + diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/babylon/theme_info.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/babylon/theme_info.xml Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,15 @@ + + + + Babylon Theme + + info@simplemachines.org + + http://www.simplemachines.org/ + + main + + index + + + diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/BoardIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/BoardIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,465 @@ + + + ', theme_linktree(), ' + '; + if (!$settings['show_sp1_info']) + echo ' + ', $txt[19], ': ', $context['common_stats']['total_members'], '  •  ', $txt[95], ': ', $context['common_stats']['total_posts'], '  •  ', $txt[64], ': ', $context['common_stats']['total_topics'], ' + ', ($settings['show_latest_member'] ? '
    ' . $txt[201] . ' ' . $context['common_stats']['latest_member']['link'] . '' . $txt[581] : ''); + echo ' + + +'; + + // Show the news fader? (assuming there are things to show...) + if ($settings['show_newsfader'] && !empty($context['fader_news_lines'])) + { + echo ' +
    +
    ', $txt[102], '
    +
    + + + + +
    '; + + // Prepare all the javascript settings. + echo ' +
    ', $context['news_lines'][0], '
    + + +
    '; + } + + // Show the "Board name Topics Posts Last Post" header. + echo ' + + + + + + + '; + + /* Each category in categories is made up of: + id, href, link, name, is_collapsed (is it collapsed?), can_collapse (is it okay if it is?), + new (is it new?), collapse_href (href to collapse/expand), collapse_image (up/down iamge), + and boards. (see below.) */ + foreach ($context['categories'] as $category) + { + // Show the category's name, and let them collapse it... if they feel like it. + echo ' + + + '; + + // Only if it's NOT collapsed.. + if (!$category['is_collapsed']) + { + /* Each board in each category's boards has: + new (is it new?), id, name, description, moderators (see below), link_moderators (just a list.), + children (see below.), link_children (easier to use.), children_new (are they new?), + topics (# of), posts (# of), link, href, and last_post. (see below.) */ + foreach ($category['boards'] as $board) + { + echo ' + + + + + '; + + /* The board's and children's 'last_post's have: + time, timestamp (a number that represents the time.), id (of the post), topic (topic id.), + link, href, subject, start (where they should go for the first unread post.), + and member. (which has id, name, link, href, username in it.) */ + echo ' + + '; + } + } + } + + // Show the "New Posts" and "No New Posts" legend. + if ($context['user']['is_logged']) + { + echo ' + + + + '; + } + + echo ' +
    ', $txt[20], '', $txt[330], '', $txt[21], '', $txt[22], '
    '; + + // If this category even can collapse, show a link to collapse it. + if ($category['can_collapse']) + echo ' + ', $category['collapse_image'], ''; + + echo ' + ', $category['link'], ' +
    ' . $txt[333] . ' + + ', $board['link'], '
    + ', $board['description']; + + // Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.) + if (!empty($board['moderators'])) + echo '
    + ', count($board['moderators']) == 1 ? $txt[298] : $txt[299], ': ', implode(', ', $board['link_moderators']), '
    '; + + // Show the "Child Boards: ". (there's a link_children but we're going to bold the new ones...) + if (!empty($board['children'])) + { + // Sort the links into an array with new boards bold so it can be imploded. + $children = array(); + /* Each child in each board's children has: + id, name, description, new (is it new?), topics (#), posts (#), href, link, and last_post. */ + foreach ($board['children'] as $child) + $children[] = $child['new'] ? '' . $child['link'] . '' : $child['link']; + + echo ' +
    + ', $txt['parent_boards'], ': ', implode(', ', $children), '
    '; + } + + echo ' +
    ', $board['topics'], '', $board['posts'], ' + + ', $board['last_post']['time'], '
    + ', $txt['smf88'], ' ', $board['last_post']['link'], '
    + ', $txt[525], ' ', $board['last_post']['member']['link'], ' +
    +
    + ' . $txt[333] . '  ' . $txt[334] . ' + '; + // Show the mark all as read button? + if ($settings['show_mark_read']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452]), ''; + echo ' +
    '; + + // Here's where the "Info Center" starts... + echo ' +
    +
    + + + + '; + + // This is the "Recent Posts" bar. + if (!empty($settings['number_recent_posts'])) + { + echo ' + + + + + + + '; + } + + // Show information about events, birthdays, and holidays on the calendar. + if ($context['show_calendar']) + { + echo ' + + + + + + '; + } + + // Show a member bar. Not heavily ornate, but functional at least. + if ($settings['show_member_bar']) + { + echo ' + + + + + + + '; + } + + // Show YaBB SP1 style information... + if ($settings['show_sp1_info']) + { + echo ' + + + + + + + '; + } + + // "Users online" - in order of activity. + echo ' + + + + + + '; + + // If they are logged in, but SP1 style information is off... show a personal message bar. + if ($context['user']['is_logged'] && !$settings['show_sp1_info']) + { + echo ' + + + + + + '; + } + + // Show the login bar. (it's only true if they are logged out anyway.) + if ($context['show_login_bar']) + { + echo ' + + + + + + + '; + } + + echo ' +
    ', $txt[685], '
    ', $txt[214], '
    + + ', $txt[214], ' + '; + + // Only show one post. + if ($settings['number_recent_posts'] == 1) + { + // latest_post has link, href, time, subject, short_subject (shortened with...), and topic. (its id.) + echo ' + ', $txt[214], '
    + + ', $txt[234], ' "', $context['latest_post']['link'], '" ', $txt[235], ' (', $context['latest_post']['time'], ')
    +
    '; + } + // Show lots of posts. + elseif (!empty($context['latest_posts'])) + { + echo ' + '; + /* Each post in latest_posts has: + board (with an id, name, and link.), topic (the topic's id.), poster (with id, name, and link.), + subject, short_subject (shortened with...), time, link, and href. */ + foreach ($context['latest_posts'] as $post) + echo ' + + + + + '; + echo ' +
    [', $post['board']['link'], ']', $post['link'], ' ', $txt[525], ' ', $post['poster']['link'], '', $post['time'], '
    '; + } + echo ' +
    ', $context['calendar_only_today'] ? $txt['calendar47b'] : $txt['calendar47'], '
    + + ', $txt['calendar24'], ' + + '; + + // Holidays like "Christmas", "Chanukah", and "We Love [Unknown] Day" :P. + if (!empty($context['calendar_holidays'])) + echo ' + ', $txt['calendar5'], ' ', implode(', ', $context['calendar_holidays']), '
    '; + + // People's birthdays. Like mine. And yours, I guess. Kidding. + if (!empty($context['calendar_birthdays'])) + { + echo ' + ', $context['calendar_only_today'] ? $txt['calendar3'] : $txt['calendar3b'], ' '; + /* Each member in calendar_birthdays has: + id, name (person), age (if they have one set?), is_last. (last in list?), and is_today (birthday is today?) */ + foreach ($context['calendar_birthdays'] as $member) + echo ' + ', $member['is_today'] ? '' : '', $member['name'], $member['is_today'] ? '' : '', isset($member['age']) ? ' (' . $member['age'] . ')' : '', '', $member['is_last'] ? '
    ' : ', '; + } + // Events like community get-togethers. + if (!empty($context['calendar_events'])) + { + echo ' + ', $context['calendar_only_today'] ? $txt['calendar4'] : $txt['calendar4b'], ' '; + /* Each event in calendar_events should have: + title, href, is_last, can_edit (are they allowed?), modify_href, and is_today. */ + foreach ($context['calendar_events'] as $event) + echo ' + ', $event['can_edit'] ? '* ' : '', $event['href'] == '' ? '' : '', $event['is_today'] ? '' . $event['title'] . '' : $event['title'], $event['href'] == '' ? '' : '', $event['is_last'] ? '
    ' : ', '; + + // Show a little help text to help them along ;). + if ($context['calendar_can_edit']) + echo ' + (', $txt['calendar_how_edit'], ')'; + } + echo ' +
    +
    ', $txt[331], '
    + ', $context['show_member_list'] ? '' : '', '', $txt[332], '', $context['show_member_list'] ? '' : '', ' + + ', $context['show_member_list'] ? '' . $txt[332] . '' : $txt[332], '
    + ', $txt[200], ' +
    ', $txt[645], '
    + + ', $txt[645], ' + + + + +
    + ', $txt[490], ': ', $context['common_stats']['total_topics'], '      ', $txt[489], ': ', $context['common_stats']['total_posts'], '
    + ', !empty($context['latest_post']) ? $txt[659] . ': + "' . $context['latest_post']['link'] . '" (' . $context['latest_post']['time'] . ')
    ' : '', ' + ', $txt[234], '', $context['show_stats'] ? '
    + ' . $txt['smf223'] . '' : '', ' +
    + ', $txt[488], ': ', $context['show_member_list'] ? '' . $context['common_stats']['total_members'] . '' : $context['common_stats']['total_members'], '
    + ', $txt[656], ': ', $context['common_stats']['latest_member']['link'], '
    '; + // If they are logged in, show their unread message count, etc.. + if ($context['user']['is_logged'] && $context['allow_pm']) + echo ' + ', $txt['smf199'], ': ', $context['user']['messages'], ' ', $txt['newmessages3'], ': ', $context['user']['unread_messages'], ''; + echo ' +
    +
    ', $txt[158], '
    + ', $context['show_who'] ? '' : '', '', $txt[158], '', $context['show_who'] ? '' : '', ' + '; + + if ($context['show_who']) + echo ' + '; + + echo $context['num_guests'], ' ', $context['num_guests'] == 1 ? $txt['guest'] : $txt['guests'], ', ' . $context['num_users_online'], ' ', $context['num_users_online'] == 1 ? $txt['user'] : $txt['users']; + + // Handle hidden users and buddies. + if (!empty($context['num_users_hidden']) || ($context['show_buddies'] && !empty($context['show_buddies']))) + { + echo ' ('; + + // Show the number of buddies online? + if ($context['show_buddies']) + echo $context['num_buddies'], ' ', $context['num_buddies'] == 1 ? $txt['buddy'] : $txt['buddies']; + + // How about hidden users? + if (!empty($context['num_users_hidden'])) + echo $context['show_buddies'] ? ', ' : '', $context['num_users_hidden'] . ' ' . $txt['hidden']; + + echo ')'; + } + + if ($context['show_who']) + echo ''; + + echo ' + '; + + // Assuming there ARE users online... each user in users_online has an id, username, name, group, href, and link. + if (!empty($context['users_online'])) + echo ' + ', $txt[140], ':
    ', implode(', ', $context['list_users_online']); + + echo ' +
    ', $context['show_stats'] && !$settings['show_sp1_info'] ? ' + ' . $txt['smf223'] . '' : '', ' +
    +
    ', $txt[159], '
    + ', $context['allow_pm'] ? '' : '', '', $txt[159], '', $context['allow_pm'] ? '' : '', ' + + ', $txt[159], '
    + + ', $txt[660], ' ', $context['user']['messages'], ' ', $context['user']['messages'] == 1 ? $txt[471] : $txt[153], '.... ', $txt[661], $context['allow_pm'] ? ' ' . $txt[662] . '' : '', ' ', $txt[663], ' + +
    + ', $txt[34], ' (' . $txt[315] . ') +
    + + ', $txt[34], ' + +
    + + + + + + +
    + + + + + + + + + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/Display.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/Display.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,681 @@ + +', $context['first_new_message'] ? '' : ''; + + // Show the linktree as well as the "Who's Viewing" information. + echo ' + + + '; + if (!empty($settings['display_who_viewing'])) + { + echo ' + '; + } + + // Show the previous/next links. + echo ' + + +
    ', theme_linktree(), ''; + + // Show just numbers...? + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) == 1 ? $txt['who_member'] : $txt[19]; + // Or show the actual people viewing the topic? + else + echo empty($context['view_members_list']) ? '0 ' . $txt[19] : implode(', ', $context['view_members_list']) . (empty($context['view_num_hidden']) || $context['can_moderate_forum'] ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + + // Now show how many guests are here too. + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_topic'], ' + ', $context['previous_next'], ' +
    '; + + // Show the page index... "Pages: [1]". + echo ' + + + + +
    + + + + + +
    + ', $txt[139], ': ', $context['page_index']; + + // Show a "go down" link? + if (!empty($modSettings['topbottomEnable'])) + echo $context['menu_separator'], '', $settings['use_image_buttons'] ? '' . $txt['topbottom5'] . '' : $txt['topbottom5'], ''; + + echo ' + ', theme_show_main_buttons(), '
    +
    '; + + // Is this topic also a poll? + if ($context['is_poll']) + { + echo ' + + + + + + + + +
    + ', $txt['smf43'], ' +
    ', $txt['smf21'], ': + ', $context['poll']['question']; + + // Are they not allowed to vote but allowed to view the options? + if ($context['poll']['show_results'] || !$context['allow_vote']) + { + echo ' + + + + + ', $context['allow_poll_view'] ? ' + + + ' : '', ' +
    + '; + + // Show each option with its corresponding percentage bar. + foreach ($context['poll']['options'] as $option) + echo ' + + ', $option['option'], '', $context['allow_poll_view'] ? ' + + ' : '', ' + '; + + echo ' +
     ' . $option['bar'] . $option['votes'] . ' (' . $option['percent'] . '%)
    +
    '; + + // If they are allowed to revote - show them a link! + if ($context['allow_change_vote']) + echo ' + ', $txt['poll_change_vote'], '
    '; + + // If we're viewing the results... maybe we want to go back and vote? + if ($context['poll']['show_results'] && $context['allow_vote']) + echo ' + ', $txt['poll_return_vote'], '
    '; + + // If they're allowed to lock the poll, show a link! + if ($context['poll']['lock']) + echo ' + ', !$context['poll']['is_locked'] ? $txt['smf30'] : $txt['smf30b'], '
    '; + + // If they're allowed to edit the poll... guess what... show a link! + if ($context['poll']['edit']) + echo ' + ', $txt['smf39'], ''; + + echo ' +
    ' . $txt['smf24'] . ': ' . $context['poll']['total_votes'] . '

    '; + } + // They are allowed to vote! Go to it! + else + { + echo ' +
    + + + + + + + + + +
    '; + + // Show a warning if they are allowed more than one option. + if ($context['poll']['allowed_warning']) + echo ' + ', $context['poll']['allowed_warning'], ' +
    '; + + // Show each option with its button - a radio likely. + foreach ($context['poll']['options'] as $option) + echo ' +
    '; + + echo ' +
    '; + + // Allowed to view the results? (without voting!) + if ($context['allow_poll_view']) + echo ' + ', $txt['smf29'], '
    '; + + // Show a link for locking the poll as well... + if ($context['poll']['lock']) + echo ' + ', (!$context['poll']['is_locked'] ? $txt['smf30'] : $txt['smf30b']), '
    '; + + // Want to edit it? Click right here...... + if ($context['poll']['edit']) + echo ' + ', $txt['smf39'], ''; + + echo ' +
    + +
    '; + } + + echo ' +
    '; + } + + // Does this topic have some events linked to it? + if (!empty($context['linked_calendar_events'])) + { + echo ' + + + + + + + +
    + ', $txt['calendar_linked_events'], ' +
    +
      '; + foreach ($context['linked_calendar_events'] as $event) + { + echo ' +
    • + ', ($event['can_edit'] ? '* ' : ''), $event['title'], ': ', $event['start_date'], ($event['start_date'] != $event['end_date'] ? ' - ' . $event['end_date'] : ''); + echo ' +
    • '; + } + echo ' +
    +
    '; + } + + // Show the topic information - icon, subject, etc. + echo ' + + + + + +
    + + ', $txt[29], ' + + ', $txt[118], ': ', $context['subject'], '  (', $txt[641], ' ', $context['num_views'], ' ', $txt[642], ') +
    '; + + //if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $context['can_remove_post']) + echo ' +
    '; + + echo ' +'; + + // Get all the messages... + while ($message = $context['get_message']()) + { + echo ' + '; + } + echo ' +
    '; + + // Show the message anchor and a "new" anchor if this message is new. + if ($message['id'] != $context['first_message']) + echo ' + ', $message['first_new'] ? '' : ''; + + echo ' + + +
    '; + + // Show information about the poster of this message. + echo ' + + + + + '; + + // Now for the attachments, signature, ip logged, etc... + echo ' + + + +
    + ', $message['member']['link'], '
    + '; + + // Show the member's custom title, if they have one. + if (isset($message['member']['title']) && $message['member']['title'] != '') + echo ' + ', $message['member']['title'], '
    '; + + // Show the member's primary group (like 'Administrator') if they have one. + if (isset($message['member']['group']) && $message['member']['group'] != '') + echo ' + ', $message['member']['group'], '
    '; + + // Don't show these things for guests. + if (!$message['member']['is_guest']) + { + // Show the post group if and only if they have no other group or the option is on, and they are in a post group. + if ((empty($settings['hide_post_group']) || $message['member']['group'] == '') && $message['member']['post_group'] != '') + echo ' + ', $message['member']['post_group'], '
    '; + echo ' + ', $message['member']['group_stars'], '
    '; + + // Is karma display enabled? Total or +/-? + if ($modSettings['karmaMode'] == '1') + echo ' +
    + ', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '
    '; + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    + ', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '
    '; + + // Is this user allowed to modify this member's karma? + if ($message['member']['karma']['allow']) + echo ' + ', $modSettings['karmaApplaudLabel'], ' + ', $modSettings['karmaSmiteLabel'], '
    '; + + // Show online and offline buttons? + if (!empty($modSettings['onlineEnable']) && !$message['member']['is_guest']) + echo ' + ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $message['member']['online']['text'] . '' : $message['member']['online']['text'], $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? ' ' . $message['member']['online']['text'] . '' : '', '

    '; + + // Show the member's gender icon? + if (!empty($settings['show_gender']) && $message['member']['gender']['image'] != '') + echo ' + ', $txt[231], ': ', $message['member']['gender']['image'], '
    '; + + // Show how many posts they have made. + echo ' + ', $txt[26], ': ', $message['member']['posts'], '
    +
    '; + + // Show avatars, images, etc.? + if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image'])) + echo ' + ', $message['member']['avatar']['image'], '
    '; + + // Show their personal text? + if (!empty($settings['show_blurb']) && $message['member']['blurb'] != '') + echo ' + ', $message['member']['blurb'], '
    +
    '; + + // This shows the popular messaging icons. + echo ' + ', $message['member']['icq']['link'], ' + ', $message['member']['msn']['link'], ' + ', $message['member']['yim']['link'], ' + ', $message['member']['aim']['link'], '
    '; + + // Show the profile, website, email address, and personal message buttons. + if ($settings['show_profile_buttons']) + { + // Don't show the profile button if you're not allowed to view the profile. + if ($message['member']['can_view_profile']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[27] . '' : $txt[27]), ''; + + // Don't show an icon if they haven't specified a website. + if ($message['member']['website']['url'] != '') + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[515] . '' : $txt[515]), ''; + + // Don't show the email address if they want it hidden. + if (empty($message['member']['hide_email'])) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[69] . '' : $txt[69]), ''; + + // Since we know this person isn't a guest, you *can* message them. + if ($context['can_send_pm']) + echo ' + ', $settings['use_image_buttons'] ? '' . $message['member']['online']['label'] . '' : $message['member']['online']['label'], ''; + } + } + // Otherwise, show the guest's email. + elseif (empty($message['member']['hide_email'])) + echo ' +
    +
    + ', ($settings['use_image_buttons'] ? '' . $txt[69] . '' : $txt[69]), ''; + + // Done with the information about the poster... on to the post itself. + echo ' +
    +
    + + + + +
    + '; + + // If this is the first post, (#0) just say when it was posted - otherwise give the reply #. + echo ' + « ', !empty($message['counter']) ? $txt[146] . ' #' . $message['counter'] : '', ' ', $txt[30], ': ', $message['time'], ' »'; + + // Can they reply? Have they turned on quick reply? + if ($context['can_reply'] && !empty($options['display_quick_reply'])) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[145] . '' : $txt[145]), ''; + // So... quick reply is off, but they *can* reply? + elseif ($context['can_reply']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[145] . '' : $txt[145]), ''; + + // Can the user modify the contents of this post? + if ($message['can_modify']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[66] . '' : $txt[17]), ''; + + // How about... even... remove it entirely?! + if ($message['can_remove']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[121] . '' : $txt[31]), ''; + + // What about splitting it off the rest of the topic? + if ($context['can_split']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt['smf251'] . '' : $txt['smf251']), ''; + + // Show a checkbox for quick moderation? + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $message['can_remove']) + echo ' + '; + + // Show the post itself, finally! + echo ' +
    +
    +
    ', $message['body'], '
    ', $message['can_modify'] ? ' + ' : '' , ' +
    + + + + + +
    '; + + // Assuming there are attachments... + if (!empty($message['attachment'])) + { + echo ' +
    '; + + foreach ($message['attachment'] as $attachment) + { + if ($attachment['is_image']) + { + if ($attachment['thumbnail']['has_thumb']) + echo ' +
    '; + else + echo ' +
    '; + } + echo ' + * ' . $attachment['name'] . ' (', $attachment['size'], ($attachment['is_image'] ? ', ' . $attachment['real_width'] . 'x' . $attachment['real_height'] . ' - ' . $txt['attach_viewed'] : ' - ' . $txt['attach_downloaded']) . ' ' . $attachment['downloads'] . ' ' . $txt['attach_times'] . '.)
    '; + } + } + + echo ' +
    '; + + // Show "« Last Edit: Time by Person »" if this post was edited. + if ($settings['show_modify'] && !empty($message['modified']['name'])) + echo ' + « ', $txt[211], ': ', $message['modified']['time'], ' ', $txt[525], ' ', $message['modified']['name'], ' »'; + + echo ' + '; + + // Maybe they want to report this post to the moderator(s)? + if ($context['can_report_moderator']) + echo ' + ', $txt['rtm1'], '  '; + echo ' + '; + + // Show the IP to this user for this post - because you can moderate? + if ($context['can_moderate_forum'] && !empty($message['member']['ip'])) + echo ' + ', $message['member']['ip'], ' (?)'; + // Or, should we show it because this is you? + elseif ($message['can_see_ip']) + echo ' + ', $message['member']['ip'], ''; + // Okay, are you at least logged in? Then we can show something about why IPs are logged... + elseif (!$context['user']['is_guest']) + echo ' + ', $txt[511], ''; + // Otherwise, you see NOTHING! + else + echo ' + ', $txt[511]; + + echo ' +
    '; + + // Show the member's signature? + if (!empty($message['member']['signature']) && empty($options['show_no_signatures'])) + echo ' +
    +
    ', $message['member']['signature'], '
    '; + + echo ' +
    +
    +
    + +
    + + + + +
    + + + + + +
    + ', $txt[139], ': ', $context['page_index'], (!empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '' . ($settings['use_image_buttons'] ? '' . $txt['topbottom4'] . '' : $txt['topbottom4']) . '' : ''), ' + + ', theme_show_main_buttons(), '  +
    +
    +
    '; + + if ($context['show_spellchecking']) + echo ' +'; + + echo' + + + + '; + if ($settings['linktree_inline']) + echo ' + '; + echo ' + + + + +
    ', theme_linktree(), ' ', $context['previous_next'], '
    + ', theme_show_mod_buttons(), ' +
    '; + + // This button has an identity crisis: it wishes it were an image. (kinda like the characters in The Wizard of Oz.) + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $context['can_remove_post']) + echo ' + '; + echo ' +
    '; + + echo ' + + + + +
    +
    + ' . $txt[160] . ': +   + +
    +
    '; + + if ($context['can_reply'] && !empty($options['display_quick_reply'])) + { + echo ' + + + + + + + + + +
    + ', $txt['quick_reply_1'], '
    '; + } + if ($context['show_spellchecking']) + echo ' +
    '; + +} + +function theme_show_main_buttons() +{ + global $context, $settings, $options, $txt, $scripturl; + + $buttonArray = array(); + if ($context['can_reply']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[146] . '' : $txt[146]) . ''; + if ($context['can_add_poll']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt['add_poll'] . '' : $txt['add_poll']) . ''; + if ($context['can_mark_notify']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[131] . '' : $txt[131]) . ''; + if ($context['can_send_topic']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[707] . '' : $txt[707]) . ''; + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[465] . '' : $txt[465]) . ''; + + return implode($context['menu_separator'], $buttonArray); +} + +function theme_show_mod_buttons() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + $moderationButtons = array(); + + if ($context['can_move']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt[132] . '' : $txt[132]) . ''; + if ($context['can_delete']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt[63] . '' : $txt[63]) . ''; + if ($context['can_lock']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . (empty($context['is_locked']) ? $txt['smf279'] : $txt['smf280']) . '' : (empty($context['is_locked']) ? $txt['smf279'] : $txt['smf280'])) . ''; + if ($context['can_sticky']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . (empty($context['is_sticky']) ? $txt['smf277'] : $txt['smf278']) . '' : (empty($context['is_sticky']) ? $txt['smf277'] : $txt['smf278'])) . ''; + if ($context['can_merge']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt['smf252'] . '' : $txt['smf252']) . ''; + if ($context['can_remove_poll']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt['poll_remove'] . '' : $txt['poll_remove']) . ''; + + if ($context['calendar_post']) + $moderationButtons[] = '' . ($settings['use_image_buttons'] ? '' . $txt['calendar37'] . '' : $txt['calendar37']) . ''; + + if ($context['can_remove_post'] && !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1) + $moderationButtons[] = $settings['use_image_buttons'] ? '' : '' . $txt['quickmod_delete_selected'] . ''; + + return implode($context['menu_separator'], $moderationButtons); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/MessageIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/MessageIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,338 @@ + + + ', theme_linktree(), ''; + if (!empty($settings['display_who_viewing'])) + { + echo ' + '; + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) == 1 ? $txt['who_member'] : $txt[19]; + else + echo empty($context['view_members_list']) ? '0 ' . $txt[19] : implode(', ', $context['view_members_list']) . (empty($context['view_num_hidden']) || $context['can_moderate_forum'] ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_board'], ''; + } + echo ' + +'; + + if (isset($context['boards']) && (!empty($options['show_children']) || $context['start'] == 0)) + { + echo ' + + + + + + + '; + foreach ($context['boards'] as $board) + { + echo ' + + + + + + + '; + } + echo ' +
    ', $txt[20], '', $txt[330], '', $txt[21], '', $txt[22], '
    ', $board['new'] ? '' . $txt[333] . '' : '' . $txt[334] . '', ' + + ' . $board['link'] . '
    + ' . $board['description']; + + if (!empty($board['moderators'])) + echo '
    + ', count($board['moderators']) == 1 ? $txt[298] : $txt[299], ': ', implode(', ', $board['link_moderators']), ''; + + if (!empty($board['children'])) + { + $children = array(); + foreach ($board['children'] as $child) + { + if ($child['new']) + $children[] = '' . $child['link'] . ''; + else + $children[] = $child['link']; + } + + echo ' +
    +
    ', $txt['parent_boards'], ': ', implode(', ', $children), ''; + } + + echo ' +
    + ', $board['topics'], ' + + ', $board['posts'], ' + + + ', $board['last_post']['time'], '
    + ', $txt['smf88'], ' ', $board['last_post']['link'], '
    + ', $txt[525], ' ', $board['last_post']['member']['link'], ' +
    +
    +
    '; + } + + if (!empty($options['show_board_desc']) && $context['description'] != '') + { + echo ' + + + + +
    + ', $context['description'], ' +
    '; + } + + if (!$context['no_topic_listing']) + { + echo ' + + + + +
    + + + + + +
    ', $txt[139], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '' . ($settings['use_image_buttons'] ? '' . $txt['topbottom5'] . '' : $txt['topbottom5']) . '' : '', '', theme_show_buttons(), '
    +
    '; + + // If Quick Moderation is enabled (and set to checkboxes - 1) start the form. + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + echo ' +
    '; + + echo ' + + '; + if (!empty($context['topics'])) + { + echo ' + + + + + + '; + if (!empty($options['display_quick_mod'])) + echo ' + '; + } + else + echo ' + '; + echo ' + '; + + foreach ($context['topics'] as $topic) + { + echo ' + + + + + + + + '; + + // Show the quick moderation options? + if (!empty($options['display_quick_mod'])) + { + echo ' + '; + } + echo ' + '; + } + + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + { + echo ' + + + '; + } + + echo ' +
    ', $txt[70], $context['sort_by'] == 'subject' ? ' ' : '', '', $txt[109], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt[110], $context['sort_by'] == 'replies' ? ' ' : '', '', $txt[301], $context['sort_by'] == 'views' ? ' ' : '', '', $txt[111], $context['sort_by'] == 'last_post' ? ' ' : '', '', $options['display_quick_mod'] != 1 ? ' ' : ' + + ', '', $txt[151], '
    + + + ', $topic['first_post']['link'], ' ', $topic['new'] && $context['user']['is_logged'] ? '' . $txt[302] . '' : '', ' ', $topic['pages'], ' + ', $topic['first_post']['member']['link'], ' + ', $topic['replies'], ' + ', $topic['views'], ' + ', $topic['last_post']['time'], '
    ', $txt[525], ' ', $topic['last_post']['member']['link'], '
    '; + if ($options['display_quick_mod'] == 1) + echo ' + '; + else + { + if ($topic['quick_mod']['remove']) + echo '', $txt[63], ''; + if ($topic['quick_mod']['lock']) + echo '', $txt['smf279'], ''; + if ($topic['quick_mod']['lock'] || $topic['quick_mod']['remove']) + echo '
    '; + if ($topic['quick_mod']['sticky']) + echo '', $txt['smf277'], ''; + if ($topic['quick_mod']['move']) + echo '', $txt[132], ''; + } + echo '
    + '; + + if ($context['can_move']) + { + echo ' + '; + } + + echo ' + +
    '; + + // Finish off the form - again, if Quick Moderation is being done with checkboxes. (1) + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + echo ' + +
    '; + + echo ' + + + + +
    + + + + + +
    ', $txt[139], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '' . ($settings['use_image_buttons'] ? '' . $txt['topbottom4'] . '' : $txt['topbottom4']) . '' : '', '', theme_show_buttons(), '
    +
    '; + } + + echo ' +'; + if ($settings['linktree_inline']) + echo ' + + + '; + + echo ' + '; + + if (!$context['no_topic_listing']) + { + echo ' + + '; + } + + echo ' + + +
    ', theme_linktree(), '

    ', !empty($modSettings['enableParticipation']) ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ' . $txt[457] . '
    + ' . $txt[454] . '
    + ' . $txt[455] . ' +
    + ' . $txt[456] . '
    ' . ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['smf96'] . '
    ' : '') . ($modSettings['pollMode'] == '1' ? ' + ' . $txt['smf43'] : '') . ' +
    +
    + : +   + +
    +
    '; +} + +function theme_show_buttons() +{ + global $context, $settings, $options, $txt, $scripturl; + + $buttonArray = array(); + + // If they are logged in, and the mark read buttons are enabled.. + if ($context['user']['is_logged'] && $settings['show_mark_read']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[300] . '' : $txt[300]) . ''; + + // If the user has permission to show the notification button... ask them if they're sure, though. + if ($context['can_mark_notify']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[131] . '' : $txt[131]) . ''; + + // Are they allowed to post new topics? + if ($context['can_post_new']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt[33] . '' : $txt[33]) . ''; + + // How about new polls, can the user post those? + if ($context['can_post_poll']) + $buttonArray[] = '' . ($settings['use_image_buttons'] ? '' . $txt['smf20'] . '' : $txt['smf20']) . ''; + + return implode($context['menu_separator'], $buttonArray); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/Post.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/Post.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1129 @@ +'; + + echo ' + + +
    + + + + +
    + ', theme_linktree(), ' +
    '; + + echo ' + '; + + if ($context['make_event'] && (!$context['event']['new'] || !empty($context['current_board']))) + echo ' + '; + + echo ' + + + + + + + +
    ' . $context['page_title'] . '
    ' . (isset($context['current_topic']) ? ' + ' : '') . ' + '; + + // If an error occurred, explain what happened. + echo ' + + + '; + + // If it's locked, show a message to warn the replyer. + echo ' + + + '; + + // Guests have to put in their name and email... + if (isset($context['name']) && isset($context['email'])) + { + echo ' + + + + '; + + if (empty($modSettings['guest_post_no_email'])) + echo ' + + + + '; + } + + // Are you posting a calendar event? + if ($context['make_event']) + { + echo ' + + + + + + + '; + + // If events can span more than one day then allow the user to select how long it should last. + if (!empty($modSettings['cal_allowspan'])) + { + echo ' + + + + '; + } + + // If this is a new event let the user specify which board they want the linked post to be put into. + if ($context['event']['new'] && $context['is_new_post']) + { + echo ' + + + + '; + } + } + + // Now show the subject box for this post. + echo ' + + + + + + + + '; + + // If this is a poll then display all the poll options! + if ($context['make_poll']) + { + echo ' + + + + + + + + + + + + + + + + + + + + + + + + '; + } + + // The below function prints the BBC, smileys and the message itself out. + theme_postbox($context['message']); + + // If this message has been edited in the past - display when it was. + if (isset($context['last_modified'])) + echo ' + + + + '; + + // If the admin has enabled the hiding of the additional options - show a link and image for it. + if (!empty($settings['additional_options_collapsable'])) + echo ' + + + '; + + // Display the check boxes for all the standard options - if they are available to the user! + echo ' + + + + '; + + // If this post already has attachments on it - give information about them. + if (!empty($context['current_attachments'])) + { + echo ' + + + + '; + } + + // Is the user allowed to post any additional ones? If so give them the boxes to do it! + if ($context['can_post_attachment']) + { + echo ' + + + + '; + } + + // Finally, the submit buttons - and some javascript to hide the additional options on load. + echo ' + + + + + + +
    +
    + ', $txt['error_while_submitting'], ' +
    +
    + ', empty($context['post_error']['messages']) ? '' : implode('
    ', $context['post_error']['messages']), ' +
    +
    + ', $txt['smf287'], ' +
    + ', $txt[68], ': + + +
    + ', $txt[69], ': + + +
    + ', $txt['calendar12'], ' + + +
    + ', $txt['calendar10'], '  +   + ', $txt['calendar9'], '  +   + ', $txt['calendar11'], '  + +
    ', $txt['calendar54'], ' + +
    ', $txt['calendar13'], ' + +
    + ', $txt[70], ': + + +
    + ', $txt[71], ': + + + +
    + ', $txt['smf21'], ': + + +
    '; + + // Loop through all the choices and print them out. + foreach ($context['choices'] as $choice) + { + echo ' + : '; + + if (!$choice['is_last']) + echo '
    '; + } + + echo ' + (', $txt['poll_add_option'], ') +
    ', $txt['poll_options'], ': ', $txt['poll_options5'], '
    ', $txt['poll_options1a'], ' ', $txt['poll_options1b'], '
    + ', $txt['poll_options2'], '
    + ', $txt['poll_options3'], '
    + ', $txt['poll_options4'], '
    +
    +
    + ', $txt[211], ': + + ', $context['last_modified'], ' +
    + + ', $txt['post_additionalopt'], ' +
    +
    + + + + + + + + + + + ', ' + + ', $context['can_announce'] && $context['is_first_post'] ? ' + + + + ' : '', ' +
    ', $context['can_notify'] ? '' : '', '', $context['can_lock'] ? '' : '', '
    ', $context['can_sticky'] ? '' : '', '
    ', $context['can_move'] ? '' : '', '
    +
    +
    + ', $txt['smf119b'], ': + + + ', $txt['smf130'], ':
    '; + foreach ($context['current_attachments'] as $attachment) + echo ' + ', $attachment['name'], '
    '; + echo ' +
    +
    + ', $txt['smf119'], ': + + '; + + // Show more boxes only if they aren't approaching their limit. + if ($context['num_allowed_attachments'] > 1) + echo ' + + (', $txt['more_attachments'], ')
    + '; + else + echo ' +
    '; + + // Show some useful information such as allowed extensions, maximum size and amount of attachments allowed. + if (!empty($modSettings['attachmentCheckExtensions'])) + echo ' + ', $txt['smf120'], ': ', $context['allowed_extensions'], '
    '; + echo ' + ', $txt['smf121'], ': ', $modSettings['attachmentSizeLimit'], ' ' . $txt['smf211'], !empty($modSettings['attachmentNumPerPostLimit']) ? ', ' . $txt['maxAttachPerPost'] . ': ' . $modSettings['attachmentNumPerPostLimit'] : '', ' +
    '; + if (!empty($settings['additional_options_collapsable']) && !$context['show_additional_options']) + echo ' + '; + echo ' +
    ' . $txt['smf16'] . '

    + + '; + + // Option to delete an event if user is editing one. + if ($context['make_event'] && !$context['event']['new']) + echo ' + '; + + // Spell check button if the option is enabled. + if ($context['show_spellchecking']) + echo ' + '; + + echo ' +
    +
    '; + + // Assuming this isn't a new topic pass across the number of replies when the topic was created. + if (isset($context['num_replies'])) + echo ' + '; + + echo ' + + + +
    '; + + // A hidden form to post data to the spell checking window. + if ($context['show_spellchecking']) + echo ' +
    '; + + // If the user is replying to a topic show the previous posts. + if (isset($context['previous_posts']) && count($context['previous_posts']) > 0) + { + echo ' +

    + + + + + + + +
    + + + + +
    ' . $txt[468] . '
    + + '; + foreach ($context['previous_posts'] as $post) + echo ' + + + + + + + + '; + echo ' +
    + ', $txt[279], ': ', $post['poster'], ' + + ', $txt[280], ': ', $post['time'], $post['is_new'] ? ' ' . $txt['preview_new'] . '' : '', ' +
    + ' . $txt[260] . ' +
    +
    ' . $post['message'] . '
    +
    +
    '; + } +} + +function template_postbox(&$message) +{ + global $context, $settings, $options, $txt; + + if ($context['show_bbc']) + { + echo ' + + + ' . $txt[252] . ': + + '; + + $context['bbc_tags'] = array(); + $context['bbc_tags'][] = array( + 'bold' => array('code' => 'b', 'before' => '[b]', 'after' => '[/b]', 'description' => $txt[253]), + 'italicize' => array('code' => 'i', 'before' => '[i]', 'after' => '[/i]', 'description' => $txt[254]), + 'underline' => array('code' => 'u', 'before' => '[u]', 'after' => '[/u]', 'description' => $txt[255]), + 'strike' => array('code' => 's', 'before' => '[s]', 'after' => '[/s]', 'description' => $txt[441]), + array(), + 'glow' => array('code' => 'glow', 'before' => '[glow=red,2,300]', 'after' => '[/glow]', 'description' => $txt[442]), + 'shadow' => array('code' => 'shadow', 'before' => '[shadow=red,left]', 'after' => '[/shadow]', 'description' => $txt[443]), + 'move' => array('code' => 'move', 'before' => '[move]', 'after' => '[/move]', 'description' => $txt[439]), + array(), + 'pre' => array('code' => 'pre', 'before' => '[pre]', 'after' => '[/pre]', 'description' => $txt[444]), + 'left' => array('code' => 'left', 'before' => '[left]', 'after' => '[/left]', 'description' => $txt[445]), + 'center' => array('code' => 'center', 'before' => '[center]', 'after' => '[/center]', 'description' => $txt[256]), + 'right' => array('code' => 'right', 'before' => '[right]', 'after' => '[/right]', 'description' => $txt[446]), + array(), + 'hr' => array('code' => 'hr', 'before' => '[hr]', 'description' => $txt[531]), + array(), + 'size' => array('code' => 'size', 'before' => '[size=10pt]', 'after' => '[/size]', 'description' => $txt[532]), + 'face' => array('code' => 'font', 'before' => '[font=Verdana]', 'after' => '[/font]', 'description' => $txt[533]), + ); + $context['bbc_tags'][] = array( + 'flash' => array('code' => 'flash', 'before' => '[flash=200,200]', 'after' => '[/flash]', 'description' => $txt[433]), + 'img' => array('code' => 'img', 'before' => '[img]', 'after' => '[/img]', 'description' => $txt[435]), + 'url' => array('code' => 'url', 'before' => '[url]', 'after' => '[/url]', 'description' => $txt[257]), + 'email' => array('code' => 'email', 'before' => '[email]', 'after' => '[/email]', 'description' => $txt[258]), + 'ftp' => array('code' => 'ftp', 'before' => '[ftp]', 'after' => '[/ftp]', 'description' => $txt[434]), + array(), + 'table' => array('code' => 'table', 'before' => '[table]', 'after' => '[/table]', 'description' => $txt[436]), + 'tr' => array('code' => 'td', 'before' => '[tr]', 'after' => '[/tr]', 'description' => $txt[449]), + 'td' => array('code' => 'td', 'before' => '[td]', 'after' => '[/td]', 'description' => $txt[437]), + array(), + 'sup' => array('code' => 'sup', 'before' => '[sup]', 'after' => '[/sup]', 'description' => $txt[447]), + 'sub' => array('code' => 'sub', 'before' => '[sub]', 'after' => '[/sub]', 'description' => $txt[448]), + 'tele' => array('code' => 'tt', 'before' => '[tt]', 'after' => '[/tt]', 'description' => $txt[440]), + array(), + 'code' => array('code' => 'code', 'before' => '[code]', 'after' => '[/code]', 'description' => $txt[259]), + 'quote' => array('code' => 'quote', 'before' => '[quote]', 'after' => '[/quote]', 'description' => $txt[260]), + array(), + 'list' => array('code' => 'list', 'before' => '[list]\n[li]', 'after' => '[/li]\n[li][/li]\n[/list]', 'description' => $txt[261]), + ); + + foreach ($context['bbc_tags'] as $i => $row) + { + foreach ($row as $image => $tag) + { + // Is this tag disabled? + if (!empty($tag['code']) && !empty($context['disabled_tags'][$tag['code']])) + continue; + + if (isset($tag['before'])) + echo '' . $tag['description'] . ''; + } + + if ($i != count($context['bbc_tags']) - 1) + echo '
    '; + } + + echo ' + + + '; + } + // Now start printing all of the smileys. + if (!empty($context['smileys']['postform'])) + { + echo ' + + + '; + + // Show each row of smileys ;). + foreach ($context['smileys']['postform'] as $smiley_row) + { + foreach ($smiley_row['smileys'] as $smiley) + echo ' + ', $smiley['description'], ''; + + // If this isn't the last row, show a break. + if (empty($smiley_row['last'])) + echo '
    '; + } + + // If the smileys popup is to be shown... show it! + if (!empty($context['smileys']['popup'])) + echo ' + [', $txt['more_smileys'], ']'; + + echo ' + + '; + } + + // Show an extra link for additional smileys (if there are any). + if (!empty($context['smileys']['popup'])) + { + echo ' + '; + } + + echo ' + + + ' . $txt[72] . ':
    + + + + + '; +} + +function template_spellcheck() +{ + global $context, $settings, $options, $txt; + + echo ' + + + ', $txt['spell_check'], ' + + + + + + + + + +
    +
     
    + + + +
    + ', $txt['spellcheck_change_to'], '
    + +
    + ', $txt['spellcheck_suggest'], '
    + +
    +
    + + + + +
    +
    + +'; +} + +function template_quotefast() +{ + global $context, $settings, $options, $txt; + + echo ' + + + + ', $txt['retrieving_quote'], ' + + + + ', $txt['retrieving_quote'], ' + + + +'; +} + +function template_announce() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    + + + + + + + + + + + + +
    ', $txt['announce_title'], '
    ', $txt['announce_desc'], '
    + ', $txt['announce_this_topic'], ' ', $context['topic_subject'], '
    +
    '; + + foreach ($context['groups'] as $group) + echo ' + (', $group['member_count'], ')
    '; + + echo ' +
    + +
    + +
    + + + + +
    '; +} + +function template_announcement_send() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    + + + + + + + + +
    + ', $txt['announce_sending'], ' ', $context['topic_subject'], ' +
    ', $context['percentage_done'], '% ', $txt['announce_done'], '
    + +
    + + + + + + +
    + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/Recent.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/Recent.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,279 @@ +', theme_linktree(), ' + + + + +
    + ', $txt[139], ': ', $context['page_index'], ' +
    +
    '; + + foreach ($context['posts'] as $post) + { + echo ' + + + + + + + + + + + + + +
    +
     ', $post['counter'], ' 
    +
     ', $post['category']['link'], ' / ', $post['board']['link'], ' / ', $post['link'], '
    +
     ', $txt[30], ': ', $post['time'], ' 
    +
    + ', $txt[109], ' ', $post['first_poster']['link'], ' - ', $txt[22], ' ', $txt[525], ' ', $post['poster']['link'], ' +
    +
    ', $post['message'], '
    +
    '; + + if ($post['can_delete']) + echo ' + ', $settings['use_image_buttons'] ? '' . $txt[121] . '' : $txt[31], ''; + if ($post['can_delete'] && ($post['can_mark_notify'] || $post['can_reply'])) + echo ' + ', $context['menu_separator']; + if ($post['can_reply']) + echo ' + ', $settings['use_image_buttons'] ? '' . $txt[146] . '' : $txt[146], '', $context['menu_separator'], ' + ', $settings['use_image_buttons'] ? '' . $txt[145] . '' : $txt[145], ''; + if ($post['can_reply'] && $post['can_mark_notify']) + echo ' + ', $context['menu_separator']; + if ($post['can_mark_notify']) + echo ' + ', $settings['use_image_buttons'] ? '' . $txt[131] . '' : $txt[131], ''; + + echo '
    +
    '; + } + + echo ' + + + + +
    + ', $txt[139], ': ', $context['page_index'], ' +
    '; + if ($settings['linktree_inline']) + echo ' +
    ', theme_linktree(), '
    '; +} + +function template_unread() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' + + + + +
    ', theme_linktree(), '
    + + + +
    +
    + + + ', $settings['show_mark_read'] ? ' + ' : '', ' + +
    ', $txt[139], ': ', $context['page_index'], '' . ($settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452]) . '
    +
    + + + '; + if (!empty($context['topics'])) + echo ' + + + + + + '; + else + echo ' + '; + echo ' + '; + + foreach ($context['topics'] as $topic) + { + echo ' + + + + + + + + + '; + } + + if (!empty($context['topics']) && !$context['showing_all_topics']) + echo ' + + + '; + + echo ' +
     ', $txt[70], $context['sort_by'] == 'subject' ? ' ' : '', '', $txt[109], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt[110], $context['sort_by'] == 'replies' ? ' ' : '', '', $txt[301], $context['sort_by'] == 'views' ? ' ' : '', '', $txt[111], $context['sort_by'] == 'last_post' ? ' ' : '', '', $context['showing_all_topics'] ? $txt[151] : $txt['unread_topics_visit_none'], '
    + + + ', $topic['first_post']['link'], ' ', $txt[302], ' ', $topic['pages'], ' +
    ', $txt['smf88'], ' ', $topic['board']['link'], '
    + ', $topic['first_post']['member']['link'], ' + ', $topic['replies'], ' + ', $topic['views'] . ' + + ', $topic['last_post']['time'], '
    + ', $txt[525], ' ', $topic['last_post']['member']['link'], ' +
    ', $txt['unread_topics_all'], '
    + +
    + + + ', $settings['show_mark_read'] ? ' + ' : '', ' + +
    ', $txt[139], ': ', $context['page_index'], '' . ($settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452]) . '
    +
    +
    + + + + + +
    ', !empty($modSettings['enableParticipation']) ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ' . $txt[457] . '
    + ' . $txt[454] . '
    + ' . $txt[455] . ' +
    + ' . $txt[456] . '
    ' . ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['smf96'] . '
    ' : '') . ($modSettings['pollMode'] == '1' ? ' + ' . $txt['smf43'] : '') . ' +
    '; +} + +function template_replies() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' + + + + +
    ', theme_linktree(), '
    + + +
    +
    + + + + + +
    ' . $txt[139] . ': ' . $context['page_index'] . ''; + if (isset($context['topics_to_mark']) && !empty($settings['show_mark_read'])) + echo ' + ' . ($settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452]) . ''; + echo ' +
    +
    + + + '; + if (!empty($context['topics'])) + echo ' + + + + + + '; + else + echo ' + '; + echo ' + '; + + foreach ($context['topics'] as $topic) + { + echo ' + + + + + + + + + '; + } + + echo ' +
     ', $txt[70], $context['sort_by'] == 'subject' ? ' ' : '', '', $txt[109], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt[110], $context['sort_by'] == 'replies' ? ' ' : '', '', $txt[301], $context['sort_by'] == 'views' ? ' ' : '', '', $txt[111], $context['sort_by'] == 'last_post' ? ' ' : '', '' . $txt[151] . '
    + + + ', $topic['first_post']['link'], ' ', $txt[302], ' ', $topic['pages'], ' +
    ', $txt['smf88'], ' ', $topic['board']['link'], '
    + ', $topic['first_post']['member']['link'], ' + ', $topic['replies'], ' + ', $topic['views'], ' + ', $topic['last_post']['time'], '
    ', $txt[525], ' ', $topic['last_post']['member']['link'], '
    + +
    + + + + + +
    ', $txt[139], ': ', $context['page_index'], ''; + + if (isset($context['topics_to_mark']) && !empty($settings['show_mark_read'])) + echo ' + ', $settings['use_image_buttons'] ? '' . $txt[452] . '' : $txt[452], ''; + + echo ' +
    +
    +
    + + + + + + +
    + ', $txt[457], '
    + ', $txt[454], '
    + ', $txt[455], ' +
    + ', $txt[456], '
    ', $modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['smf96'] . '
    ' : '', $modSettings['pollMode'] == '1' ? ' + ' . $txt['smf43'] : '', ' +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/Female.gif Binary file forum/Themes/classic/images/Female.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/Male.gif Binary file forum/Themes/classic/images/Male.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/aim.gif Binary file forum/Themes/classic/images/aim.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bar.gif Binary file forum/Themes/classic/images/bar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/bold.gif Binary file forum/Themes/classic/images/bbc/bold.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/center.gif Binary file forum/Themes/classic/images/bbc/center.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/code.gif Binary file forum/Themes/classic/images/bbc/code.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/email.gif Binary file forum/Themes/classic/images/bbc/email.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/face.gif Binary file forum/Themes/classic/images/bbc/face.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/flash.gif Binary file forum/Themes/classic/images/bbc/flash.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/ftp.gif Binary file forum/Themes/classic/images/bbc/ftp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/glow.gif Binary file forum/Themes/classic/images/bbc/glow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/hr.gif Binary file forum/Themes/classic/images/bbc/hr.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/img.gif Binary file forum/Themes/classic/images/bbc/img.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/italicize.gif Binary file forum/Themes/classic/images/bbc/italicize.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/left.gif Binary file forum/Themes/classic/images/bbc/left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/list.gif Binary file forum/Themes/classic/images/bbc/list.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/move.gif Binary file forum/Themes/classic/images/bbc/move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/pre.gif Binary file forum/Themes/classic/images/bbc/pre.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/quote.gif Binary file forum/Themes/classic/images/bbc/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/right.gif Binary file forum/Themes/classic/images/bbc/right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/shadow.gif Binary file forum/Themes/classic/images/bbc/shadow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/size.gif Binary file forum/Themes/classic/images/bbc/size.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/strike.gif Binary file forum/Themes/classic/images/bbc/strike.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/sub.gif Binary file forum/Themes/classic/images/bbc/sub.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/sup.gif Binary file forum/Themes/classic/images/bbc/sup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/table.gif Binary file forum/Themes/classic/images/bbc/table.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/td.gif Binary file forum/Themes/classic/images/bbc/td.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/tele.gif Binary file forum/Themes/classic/images/bbc/tele.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/tr.gif Binary file forum/Themes/classic/images/bbc/tr.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/underline.gif Binary file forum/Themes/classic/images/bbc/underline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bbc/url.gif Binary file forum/Themes/classic/images/bbc/url.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/bdaycake.gif Binary file forum/Themes/classic/images/bdaycake.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/blank.gif Binary file forum/Themes/classic/images/blank.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/board.gif Binary file forum/Themes/classic/images/board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/board_select_spot.gif Binary file forum/Themes/classic/images/board_select_spot.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/board_select_spot_child.gif Binary file forum/Themes/classic/images/board_select_spot_child.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/buddy_useroff.gif Binary file forum/Themes/classic/images/buddy_useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/buddy_useron.gif Binary file forum/Themes/classic/images/buddy_useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/catbg.gif Binary file forum/Themes/classic/images/catbg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/catbg.jpg Binary file forum/Themes/classic/images/catbg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/collapse.gif Binary file forum/Themes/classic/images/collapse.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/construction.gif Binary file forum/Themes/classic/images/construction.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/addpoll.gif Binary file forum/Themes/classic/images/dutch/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/admin.gif Binary file forum/Themes/classic/images/dutch/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/admin_lock.gif Binary file forum/Themes/classic/images/dutch/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/admin_move.gif Binary file forum/Themes/classic/images/dutch/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/admin_rem.gif Binary file forum/Themes/classic/images/dutch/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/admin_remove_poll.gif Binary file forum/Themes/classic/images/dutch/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/admin_sticky.gif Binary file forum/Themes/classic/images/dutch/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/calendar.gif Binary file forum/Themes/classic/images/dutch/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/calendarpe.gif Binary file forum/Themes/classic/images/dutch/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/delete.gif Binary file forum/Themes/classic/images/dutch/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/delete_selected.gif Binary file forum/Themes/classic/images/dutch/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/email.gif Binary file forum/Themes/classic/images/dutch/email.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/go_down.gif Binary file forum/Themes/classic/images/dutch/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/go_up.gif Binary file forum/Themes/classic/images/dutch/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/help.gif Binary file forum/Themes/classic/images/dutch/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/home.gif Binary file forum/Themes/classic/images/dutch/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/im_delete.gif Binary file forum/Themes/classic/images/dutch/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/im_inbox.gif Binary file forum/Themes/classic/images/dutch/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/im_new.gif Binary file forum/Themes/classic/images/dutch/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/im_outbox.gif Binary file forum/Themes/classic/images/dutch/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/im_reload.gif Binary file forum/Themes/classic/images/dutch/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/im_reply.gif Binary file forum/Themes/classic/images/dutch/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/linktocal.gif Binary file forum/Themes/classic/images/dutch/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/login.gif Binary file forum/Themes/classic/images/dutch/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/logout.gif Binary file forum/Themes/classic/images/dutch/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/markread.gif Binary file forum/Themes/classic/images/dutch/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/markunread.gif Binary file forum/Themes/classic/images/dutch/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/merge.gif Binary file forum/Themes/classic/images/dutch/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/modify.gif Binary file forum/Themes/classic/images/dutch/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/new.gif Binary file forum/Themes/classic/images/dutch/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/new_none.gif Binary file forum/Themes/classic/images/dutch/new_none.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/new_poll.gif Binary file forum/Themes/classic/images/dutch/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/new_some.gif Binary file forum/Themes/classic/images/dutch/new_some.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/new_topic.gif Binary file forum/Themes/classic/images/dutch/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/notify.gif Binary file forum/Themes/classic/images/dutch/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/notify_sm.gif Binary file forum/Themes/classic/images/dutch/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/print.gif Binary file forum/Themes/classic/images/dutch/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/profile.gif Binary file forum/Themes/classic/images/dutch/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/quote.gif Binary file forum/Themes/classic/images/dutch/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/register.gif Binary file forum/Themes/classic/images/dutch/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/reply.gif Binary file forum/Themes/classic/images/dutch/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/reply_sm.gif Binary file forum/Themes/classic/images/dutch/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/search.gif Binary file forum/Themes/classic/images/dutch/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/sendtopic.gif Binary file forum/Themes/classic/images/dutch/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/dutch/split.gif Binary file forum/Themes/classic/images/dutch/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/email_sm.gif Binary file forum/Themes/classic/images/email_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/addpoll.gif Binary file forum/Themes/classic/images/english/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/admin.gif Binary file forum/Themes/classic/images/english/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/admin_lock.gif Binary file forum/Themes/classic/images/english/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/admin_move.gif Binary file forum/Themes/classic/images/english/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/admin_rem.gif Binary file forum/Themes/classic/images/english/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/admin_remove_poll.gif Binary file forum/Themes/classic/images/english/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/admin_sticky.gif Binary file forum/Themes/classic/images/english/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/calendar.gif Binary file forum/Themes/classic/images/english/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/calendarpe.gif Binary file forum/Themes/classic/images/english/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/delete.gif Binary file forum/Themes/classic/images/english/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/delete_selected.gif Binary file forum/Themes/classic/images/english/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/email.gif Binary file forum/Themes/classic/images/english/email.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/go_down.gif Binary file forum/Themes/classic/images/english/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/go_up.gif Binary file forum/Themes/classic/images/english/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/help.gif Binary file forum/Themes/classic/images/english/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/home.gif Binary file forum/Themes/classic/images/english/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/im_delete.gif Binary file forum/Themes/classic/images/english/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/im_inbox.gif Binary file forum/Themes/classic/images/english/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/im_new.gif Binary file forum/Themes/classic/images/english/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/im_outbox.gif Binary file forum/Themes/classic/images/english/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/im_reload.gif Binary file forum/Themes/classic/images/english/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/im_reply.gif Binary file forum/Themes/classic/images/english/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/linktocal.gif Binary file forum/Themes/classic/images/english/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/login.gif Binary file forum/Themes/classic/images/english/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/logout.gif Binary file forum/Themes/classic/images/english/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/markread.gif Binary file forum/Themes/classic/images/english/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/markunread.gif Binary file forum/Themes/classic/images/english/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/merge.gif Binary file forum/Themes/classic/images/english/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/modify.gif Binary file forum/Themes/classic/images/english/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/new.gif Binary file forum/Themes/classic/images/english/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/new_none.gif Binary file forum/Themes/classic/images/english/new_none.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/new_poll.gif Binary file forum/Themes/classic/images/english/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/new_some.gif Binary file forum/Themes/classic/images/english/new_some.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/new_topic.gif Binary file forum/Themes/classic/images/english/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/notify.gif Binary file forum/Themes/classic/images/english/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/notify_sm.gif Binary file forum/Themes/classic/images/english/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/print.gif Binary file forum/Themes/classic/images/english/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/profile.gif Binary file forum/Themes/classic/images/english/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/quote.gif Binary file forum/Themes/classic/images/english/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/register.gif Binary file forum/Themes/classic/images/english/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/reply.gif Binary file forum/Themes/classic/images/english/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/reply_sm.gif Binary file forum/Themes/classic/images/english/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/search.gif Binary file forum/Themes/classic/images/english/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/sendtopic.gif Binary file forum/Themes/classic/images/english/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/english/split.gif Binary file forum/Themes/classic/images/english/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/expand.gif Binary file forum/Themes/classic/images/expand.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/filter.gif Binary file forum/Themes/classic/images/filter.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/addpoll.gif Binary file forum/Themes/classic/images/german/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/admin.gif Binary file forum/Themes/classic/images/german/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/admin_lock.gif Binary file forum/Themes/classic/images/german/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/admin_move.gif Binary file forum/Themes/classic/images/german/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/admin_rem.gif Binary file forum/Themes/classic/images/german/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/admin_remove_poll.gif Binary file forum/Themes/classic/images/german/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/admin_sticky.gif Binary file forum/Themes/classic/images/german/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/calendar.gif Binary file forum/Themes/classic/images/german/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/calendarpe.gif Binary file forum/Themes/classic/images/german/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/delete.gif Binary file forum/Themes/classic/images/german/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/delete_selected.gif Binary file forum/Themes/classic/images/german/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/email.gif Binary file forum/Themes/classic/images/german/email.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/go_down.gif Binary file forum/Themes/classic/images/german/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/go_up.gif Binary file forum/Themes/classic/images/german/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/help.gif Binary file forum/Themes/classic/images/german/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/home.gif Binary file forum/Themes/classic/images/german/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/im_delete.gif Binary file forum/Themes/classic/images/german/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/im_inbox.gif Binary file forum/Themes/classic/images/german/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/im_new.gif Binary file forum/Themes/classic/images/german/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/im_outbox.gif Binary file forum/Themes/classic/images/german/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/im_reload.gif Binary file forum/Themes/classic/images/german/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/im_reply.gif Binary file forum/Themes/classic/images/german/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/linktocal.gif Binary file forum/Themes/classic/images/german/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/login.gif Binary file forum/Themes/classic/images/german/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/logout.gif Binary file forum/Themes/classic/images/german/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/markread.gif Binary file forum/Themes/classic/images/german/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/markunread.gif Binary file forum/Themes/classic/images/german/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/merge.gif Binary file forum/Themes/classic/images/german/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/modify.gif Binary file forum/Themes/classic/images/german/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/new.gif Binary file forum/Themes/classic/images/german/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/new_none.gif Binary file forum/Themes/classic/images/german/new_none.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/new_poll.gif Binary file forum/Themes/classic/images/german/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/new_some.gif Binary file forum/Themes/classic/images/german/new_some.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/new_topic.gif Binary file forum/Themes/classic/images/german/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/notify.gif Binary file forum/Themes/classic/images/german/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/notify_sm.gif Binary file forum/Themes/classic/images/german/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/print.gif Binary file forum/Themes/classic/images/german/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/profile.gif Binary file forum/Themes/classic/images/german/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/quote.gif Binary file forum/Themes/classic/images/german/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/register.gif Binary file forum/Themes/classic/images/german/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/reply.gif Binary file forum/Themes/classic/images/german/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/reply_sm.gif Binary file forum/Themes/classic/images/german/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/search.gif Binary file forum/Themes/classic/images/german/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/sendtopic.gif Binary file forum/Themes/classic/images/german/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/german/split.gif Binary file forum/Themes/classic/images/german/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/helplogo.jpg Binary file forum/Themes/classic/images/helplogo.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/helptopics.gif Binary file forum/Themes/classic/images/helptopics.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/assist.gif Binary file forum/Themes/classic/images/icons/assist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/bullet_grin.gif Binary file forum/Themes/classic/images/icons/bullet_grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/calendar.gif Binary file forum/Themes/classic/images/icons/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/clip.gif Binary file forum/Themes/classic/images/icons/clip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/config_sm.gif Binary file forum/Themes/classic/images/icons/config_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/delete.gif Binary file forum/Themes/classic/images/icons/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/folder_open.gif Binary file forum/Themes/classic/images/icons/folder_open.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/im_newmsg.gif Binary file forum/Themes/classic/images/icons/im_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/info.gif Binary file forum/Themes/classic/images/icons/info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/linktree_main.gif Binary file forum/Themes/classic/images/icons/linktree_main.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/linktree_side.gif Binary file forum/Themes/classic/images/icons/linktree_side.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/login.gif Binary file forum/Themes/classic/images/icons/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/login_sm.gif Binary file forum/Themes/classic/images/icons/login_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/members.gif Binary file forum/Themes/classic/images/icons/members.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/modify_inline.gif Binary file forum/Themes/classic/images/icons/modify_inline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/notify_sm.gif Binary file forum/Themes/classic/images/icons/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/online.gif Binary file forum/Themes/classic/images/icons/online.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/package_installed.gif Binary file forum/Themes/classic/images/icons/package_installed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/package_old.gif Binary file forum/Themes/classic/images/icons/package_old.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/pm_read.gif Binary file forum/Themes/classic/images/icons/pm_read.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/pm_replied.gif Binary file forum/Themes/classic/images/icons/pm_replied.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/profile_sm.gif Binary file forum/Themes/classic/images/icons/profile_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/quick_lock.gif Binary file forum/Themes/classic/images/icons/quick_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/quick_move.gif Binary file forum/Themes/classic/images/icons/quick_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/quick_remove.gif Binary file forum/Themes/classic/images/icons/quick_remove.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icons/quick_sticky.gif Binary file forum/Themes/classic/images/icons/quick_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/icq.gif Binary file forum/Themes/classic/images/icq.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/im_off.gif Binary file forum/Themes/classic/images/im_off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/im_on.gif Binary file forum/Themes/classic/images/im_on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/im_sm_newmsg.gif Binary file forum/Themes/classic/images/im_sm_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/im_sm_prefs.gif Binary file forum/Themes/classic/images/im_sm_prefs.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/ip.gif Binary file forum/Themes/classic/images/ip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/message_sm.gif Binary file forum/Themes/classic/images/message_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/msntalk.gif Binary file forum/Themes/classic/images/msntalk.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/mysql.gif Binary file forum/Themes/classic/images/mysql.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/off.gif Binary file forum/Themes/classic/images/off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/on.gif Binary file forum/Themes/classic/images/on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/php.gif Binary file forum/Themes/classic/images/php.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/poll_left.gif Binary file forum/Themes/classic/images/poll_left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/poll_middle.gif Binary file forum/Themes/classic/images/poll_middle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/poll_right.gif Binary file forum/Themes/classic/images/poll_right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/angry.gif Binary file forum/Themes/classic/images/post/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/cheesy.gif Binary file forum/Themes/classic/images/post/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/exclamation.gif Binary file forum/Themes/classic/images/post/exclamation.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/grin.gif Binary file forum/Themes/classic/images/post/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/lamp.gif Binary file forum/Themes/classic/images/post/lamp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/moved.gif Binary file forum/Themes/classic/images/post/moved.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/question.gif Binary file forum/Themes/classic/images/post/question.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/recycled.gif Binary file forum/Themes/classic/images/post/recycled.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/sad.gif Binary file forum/Themes/classic/images/post/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/smiley.gif Binary file forum/Themes/classic/images/post/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/thumbdown.gif Binary file forum/Themes/classic/images/post/thumbdown.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/thumbup.gif Binary file forum/Themes/classic/images/post/thumbup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/wink.gif Binary file forum/Themes/classic/images/post/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/wireless.gif Binary file forum/Themes/classic/images/post/wireless.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/post/xx.gif Binary file forum/Themes/classic/images/post/xx.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/selected.gif Binary file forum/Themes/classic/images/selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/selogo.gif Binary file forum/Themes/classic/images/selogo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/smflogo.gif Binary file forum/Themes/classic/images/smflogo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/smiley_select_spot.gif Binary file forum/Themes/classic/images/smiley_select_spot.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/sort_down.gif Binary file forum/Themes/classic/images/sort_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/sort_up.gif Binary file forum/Themes/classic/images/sort_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/addpoll.gif Binary file forum/Themes/classic/images/spanish/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/admin.gif Binary file forum/Themes/classic/images/spanish/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/admin_lock.gif Binary file forum/Themes/classic/images/spanish/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/admin_move.gif Binary file forum/Themes/classic/images/spanish/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/admin_rem.gif Binary file forum/Themes/classic/images/spanish/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/admin_remove_poll.gif Binary file forum/Themes/classic/images/spanish/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/admin_sticky.gif Binary file forum/Themes/classic/images/spanish/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/calendar.gif Binary file forum/Themes/classic/images/spanish/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/calendarpe.gif Binary file forum/Themes/classic/images/spanish/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/delete.gif Binary file forum/Themes/classic/images/spanish/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/delete_selected.gif Binary file forum/Themes/classic/images/spanish/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/email.gif Binary file forum/Themes/classic/images/spanish/email.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/go_down.gif Binary file forum/Themes/classic/images/spanish/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/go_up.gif Binary file forum/Themes/classic/images/spanish/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/help.gif Binary file forum/Themes/classic/images/spanish/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/home.gif Binary file forum/Themes/classic/images/spanish/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/im_delete.gif Binary file forum/Themes/classic/images/spanish/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/im_inbox.gif Binary file forum/Themes/classic/images/spanish/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/im_new.gif Binary file forum/Themes/classic/images/spanish/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/im_outbox.gif Binary file forum/Themes/classic/images/spanish/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/im_reload.gif Binary file forum/Themes/classic/images/spanish/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/im_reply.gif Binary file forum/Themes/classic/images/spanish/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/linktocal.gif Binary file forum/Themes/classic/images/spanish/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/login.gif Binary file forum/Themes/classic/images/spanish/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/logout.gif Binary file forum/Themes/classic/images/spanish/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/markread.gif Binary file forum/Themes/classic/images/spanish/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/markunread.gif Binary file forum/Themes/classic/images/spanish/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/merge.gif Binary file forum/Themes/classic/images/spanish/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/modify.gif Binary file forum/Themes/classic/images/spanish/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/new.gif Binary file forum/Themes/classic/images/spanish/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/new_none.gif Binary file forum/Themes/classic/images/spanish/new_none.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/new_poll.gif Binary file forum/Themes/classic/images/spanish/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/new_some.gif Binary file forum/Themes/classic/images/spanish/new_some.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/new_topic.gif Binary file forum/Themes/classic/images/spanish/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/notify.gif Binary file forum/Themes/classic/images/spanish/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/notify_sm.gif Binary file forum/Themes/classic/images/spanish/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/print.gif Binary file forum/Themes/classic/images/spanish/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/profile.gif Binary file forum/Themes/classic/images/spanish/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/quote.gif Binary file forum/Themes/classic/images/spanish/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/register.gif Binary file forum/Themes/classic/images/spanish/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/reply.gif Binary file forum/Themes/classic/images/spanish/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/reply_sm.gif Binary file forum/Themes/classic/images/spanish/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/search.gif Binary file forum/Themes/classic/images/spanish/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/sendtopic.gif Binary file forum/Themes/classic/images/spanish/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/spanish/split.gif Binary file forum/Themes/classic/images/spanish/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/split_deselect.gif Binary file forum/Themes/classic/images/split_deselect.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/split_select.gif Binary file forum/Themes/classic/images/split_select.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/star.gif Binary file forum/Themes/classic/images/star.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/staradmin.gif Binary file forum/Themes/classic/images/staradmin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/stargmod.gif Binary file forum/Themes/classic/images/stargmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/starmod.gif Binary file forum/Themes/classic/images/starmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/stats_board.gif Binary file forum/Themes/classic/images/stats_board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/stats_history.gif Binary file forum/Themes/classic/images/stats_history.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/stats_info.gif Binary file forum/Themes/classic/images/stats_info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/stats_posters.gif Binary file forum/Themes/classic/images/stats_posters.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/stats_replies.gif Binary file forum/Themes/classic/images/stats_replies.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/stats_views.gif Binary file forum/Themes/classic/images/stats_views.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/thumbnail.gif Binary file forum/Themes/classic/images/thumbnail.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/hot_poll.gif Binary file forum/Themes/classic/images/topic/hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/hot_poll_locked.gif Binary file forum/Themes/classic/images/topic/hot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/hot_poll_locked_sticky.gif Binary file forum/Themes/classic/images/topic/hot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/hot_poll_sticky.gif Binary file forum/Themes/classic/images/topic/hot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/hot_post.gif Binary file forum/Themes/classic/images/topic/hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/hot_post_locked.gif Binary file forum/Themes/classic/images/topic/hot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/hot_post_locked_sticky.gif Binary file forum/Themes/classic/images/topic/hot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/hot_post_sticky.gif Binary file forum/Themes/classic/images/topic/hot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_hot_poll.gif Binary file forum/Themes/classic/images/topic/my_hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_hot_poll_locked.gif Binary file forum/Themes/classic/images/topic/my_hot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_hot_poll_locked_sticky.gif Binary file forum/Themes/classic/images/topic/my_hot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_hot_poll_sticky.gif Binary file forum/Themes/classic/images/topic/my_hot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_hot_post.gif Binary file forum/Themes/classic/images/topic/my_hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_hot_post_locked.gif Binary file forum/Themes/classic/images/topic/my_hot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_hot_post_locked_sticky.gif Binary file forum/Themes/classic/images/topic/my_hot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_hot_post_sticky.gif Binary file forum/Themes/classic/images/topic/my_hot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_normal_poll.gif Binary file forum/Themes/classic/images/topic/my_normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_normal_poll_locked.gif Binary file forum/Themes/classic/images/topic/my_normal_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_normal_poll_locked_sticky.gif Binary file forum/Themes/classic/images/topic/my_normal_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_normal_poll_sticky.gif Binary file forum/Themes/classic/images/topic/my_normal_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_normal_post.gif Binary file forum/Themes/classic/images/topic/my_normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_normal_post_locked.gif Binary file forum/Themes/classic/images/topic/my_normal_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_normal_post_locked_sticky.gif Binary file forum/Themes/classic/images/topic/my_normal_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_normal_post_sticky.gif Binary file forum/Themes/classic/images/topic/my_normal_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_veryhot_poll.gif Binary file forum/Themes/classic/images/topic/my_veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_veryhot_poll_locked.gif Binary file forum/Themes/classic/images/topic/my_veryhot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_veryhot_poll_locked_sticky.gif Binary file forum/Themes/classic/images/topic/my_veryhot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_veryhot_poll_sticky.gif Binary file forum/Themes/classic/images/topic/my_veryhot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_veryhot_post.gif Binary file forum/Themes/classic/images/topic/my_veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_veryhot_post_locked.gif Binary file forum/Themes/classic/images/topic/my_veryhot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_veryhot_post_locked_sticky.gif Binary file forum/Themes/classic/images/topic/my_veryhot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/my_veryhot_post_sticky.gif Binary file forum/Themes/classic/images/topic/my_veryhot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/normal_poll.gif Binary file forum/Themes/classic/images/topic/normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/normal_poll_locked.gif Binary file forum/Themes/classic/images/topic/normal_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/normal_poll_locked_sticky.gif Binary file forum/Themes/classic/images/topic/normal_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/normal_poll_sticky.gif Binary file forum/Themes/classic/images/topic/normal_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/normal_post.gif Binary file forum/Themes/classic/images/topic/normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/normal_post_locked.gif Binary file forum/Themes/classic/images/topic/normal_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/normal_post_locked_sticky.gif Binary file forum/Themes/classic/images/topic/normal_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/normal_post_sticky.gif Binary file forum/Themes/classic/images/topic/normal_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/veryhot_poll.gif Binary file forum/Themes/classic/images/topic/veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/veryhot_poll_locked.gif Binary file forum/Themes/classic/images/topic/veryhot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/veryhot_poll_locked_sticky.gif Binary file forum/Themes/classic/images/topic/veryhot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/veryhot_poll_sticky.gif Binary file forum/Themes/classic/images/topic/veryhot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/veryhot_post.gif Binary file forum/Themes/classic/images/topic/veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/veryhot_post_locked.gif Binary file forum/Themes/classic/images/topic/veryhot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/veryhot_post_locked_sticky.gif Binary file forum/Themes/classic/images/topic/veryhot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/topic/veryhot_post_sticky.gif Binary file forum/Themes/classic/images/topic/veryhot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/useroff.gif Binary file forum/Themes/classic/images/useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/useron.gif Binary file forum/Themes/classic/images/useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/valid-css.gif Binary file forum/Themes/classic/images/valid-css.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/valid-xhtml10.gif Binary file forum/Themes/classic/images/valid-xhtml10.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/www.gif Binary file forum/Themes/classic/images/www.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/www_sm.gif Binary file forum/Themes/classic/images/www_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/images/yim.gif Binary file forum/Themes/classic/images/yim.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/index.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/index.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,360 @@ + + + + ', empty($context['robot_no_index']) ? '' : ' + ', ' + + + + ', $context['page_title'], ' + + '; + + /* Internet Explorer 4/5 and Opera 6 just don't do font sizes properly. (they are big...) + Thus, in Internet Explorer 4, 5, and Opera 6 this will show fonts one size smaller than usual. + Note that this is affected by whether IE 6 is in standards compliance mode.. if not, it will also be big. + Standards compliance mode happens when you use xhtml... */ + if ($context['browser']['needs_size_fix']) + echo ' + '; + + // Show all the relative links, such as help, search, contents, and the like. + echo ' + + + '; + + // If RSS feeds are enabled, advertise the presence of one. + if (!empty($modSettings['xmlnews_enable'])) + echo ' + '; + + // If we're viewing a topic, these should be the previous and next topics, respectively. + if (!empty($context['current_topic'])) + echo ' + + '; + + // If we're in a board, or a topic for that matter, the index will be the board's index. + if (!empty($context['current_board'])) + echo ' + '; + + // Output any remaining HTML headers. (from mods, maybe?) + echo $context['html_headers'], ' + +'; + + // The logo, user information, news, and menu. + echo ' + + + + + + + + '; + + // Show a random news item? (or you could pick one from news_lines...) + if (!empty($settings['enable_news'])) + echo ' + + + '; + + echo ' +
    '; + + // If the user is logged in, display stuff like their name, new messages, etc. + if ($context['user']['is_logged']) + { + echo ' + ', $txt['hello_member'], ' ', $context['user']['name'], '', $context['allow_pm'] ? ', ' . $txt[152] . ' ' . $context['user']['messages'] . ' ' . ($context['user']['messages'] != 1 ? $txt[153] : $txt[471]) . '' . $txt['newmessages4'] . ' ' . $context['user']['unread_messages'] . ' ' . ($context['user']['unread_messages'] == 1 ? $txt['newmessages0'] : $txt['newmessages1']) : '', '.'; + + // Are there any members waiting for approval? + if (!empty($context['unapproved_members'])) + echo '
    + ', $context['unapproved_members'] == 1 ? $txt['approve_thereis'] : $txt['approve_thereare'], ' ', $context['unapproved_members'] == 1 ? $txt['approve_member'] : $context['unapproved_members'] . ' ' . $txt['approve_members'], ' ', $txt['approve_members_waiting']; + + // Is the forum in maintenance mode? + if ($context['in_maintenance'] && $context['user']['is_admin']) + echo '
    + ', $txt[616], ''; + } + // Otherwise they're a guest - so politely ask them to register or login. + else + echo ' + ', $txt['welcome_guest']; + + echo ' +
    ', $context['current_time'], ' +
    '; + + // Show the menu here, according to the menu sub template. + template_menu(); + + echo ' +
    + ', $txt[102], ': ', $context['random_news_line'], ' +
    + +
    + + +
    '; +} + +function template_main_below() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    '; + + // Show a vB style login for quick login? + if ($context['show_quick_login']) + { + echo ' + + +
    + + +

    + + + +
    + ', $txt['smf52'], ' + +
    +
    '; + } + + // Don't show a login box, just a break. + else + echo ' +
    '; + + // Show the "Powered by" and "Valid" logos, as well as the copyright. Remember, the copyright must be somewhere! + echo ' +
    + + + + + + + +
    + ', $txt['powered_by_mysql'], ' + ', $txt['powered_by_php'], ' + + ', theme_copyright(), ' + + ', $txt['valid_xhtml'], ' + ', $txt['valid_css'], ' +
    '; + + // Show the load time? + if ($context['show_load_time']) + echo ' +
    + ', $txt['smf301'], $context['load_time'], $txt['smf302'], $context['load_queries'], $txt['smf302b'], ' +
    '; + + // The following will be used to let the user know that some AJAX process is running + echo ' + '; + + // And then we're done! + echo ' + +'; +} + +// Show a linktree. This is that thing that shows "My Community | General Category | General Discussion".. +function theme_linktree() +{ + global $context, $settings, $options; + + // Folder style or inline? Inline has a smaller font. + echo ''; + + // Each tree item has a URL and name. Some may have extra_before and extra_after. + foreach ($context['linktree'] as $link_num => $tree) + { + // Show the | | |-[] Folders. + if (!$settings['linktree_inline']) + { + if ($link_num > 0) + echo str_repeat('| ', $link_num - 1), '|-'; + echo '+  '; + } + + // Show something before the link? + if (isset($tree['extra_before'])) + echo $tree['extra_before']; + + // Show the link, including a URL if it should have one. + echo '', $settings['linktree_link'] && isset($tree['url']) ? '' . $tree['name'] . '' : $tree['name'], ''; + + // Show something after the link...? + if (isset($tree['extra_after'])) + echo $tree['extra_after']; + + // Don't show a separator for the last one. + if ($link_num != count($context['linktree']) - 1) + echo $settings['linktree_inline'] ? '  |  ' : '
    '; + } + + echo '
    '; +} + +// Show the menu up top. Something like [home] [help] [profile] [logout]... +function template_menu() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Show the [home] and [help] buttons. + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[103] . '' : $txt[103]), '', $context['menu_separator'], ' + ', ($settings['use_image_buttons'] ? '' . $txt[119] . '' : $txt[119]), '', $context['menu_separator']; + + // How about the [search] button? + if ($context['allow_search']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[182] . '' : $txt[182]), '', $context['menu_separator']; + + // Is the user allowed to administrate at all? ([admin]) + if ($context['allow_admin']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[2] . '' : $txt[2]), '', $context['menu_separator']; + + // Edit Profile... [profile] + if ($context['allow_edit_profile']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[79] . '' : $txt[467]), '', $context['menu_separator']; + + // The [calendar]! + if ($context['allow_calendar']) + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt['calendar24'] . '' : $txt['calendar24']), '', $context['menu_separator']; + + // If the user is a guest, show [login] and [register] buttons. + if ($context['user']['is_guest']) + { + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[34] . '' : $txt[34]), '', $context['menu_separator'], ' + ', ($settings['use_image_buttons'] ? '' . $txt[97] . '' : $txt[97]), ''; + } + // Otherwise, they might want to [logout]... + else + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt[108] . '' : $txt[108]), ''; +} + +// Generate a strip of buttons, out of buttons. +function template_button_strip($button_strip, $direction = 'top', $force_reset = false, $custom_td = '') +{ + global $settings, $buttons, $context, $txt, $scripturl; + + if (empty($button_strip)) + return ''; + + // Create the buttons... + foreach ($button_strip as $key => $value) + { + if (isset($value['test']) && empty($context[$value['test']])) + { + unset($button_strip[$key]); + continue; + } + elseif (!isset($buttons[$key]) || $force_reset) + $buttons[$key] = '' . ($settings['use_image_buttons'] ? '' . $txt[$value['text']] . '' : $txt[$value['text']]) . ''; + + $button_strip[$key] = $buttons[$key]; + } + + echo ' + ', implode($context['menu_separator'], $button_strip) , ''; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/languages/Settings.dutch.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/languages/Settings.dutch.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,7 @@ +
    Auteur: het YaBB SE Team.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/languages/Settings.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/languages/Settings.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,7 @@ +
    Author: The YaBB SE Team.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/languages/Settings.german.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/languages/Settings.german.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,7 @@ +
    Autor: Das YaBB SE Team.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/languages/Settings.spanish.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/languages/Settings.spanish.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,7 @@ +
    Autor: El equipo YaBB SE.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/license.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/license.txt Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,73 @@ +Definitions +----------------------------------------------------------------------------- +i. This Package is defined as all of the files within any archive file or any +group of files released in conjunction by Simple Machines, Lewis Media, or a +derived or modified work based on such files. + +ii. A Modification, or a Mod, is defined as instructions, to be performed +manually or in an automated manner, that alter any part of this Package. + +iii. A Modified Package is defined as this Package or a derivative of it with +one or more Modification applied to it. + +iv. Distribution is defined as allowing one or more other people to in any +way download or receive a copy of this Package, a Modified Package, or a +derivative of this Package. + +v. The Software is defined as an installed copy of this Package, a Modified +Package, or a derivative of this Package. + +vi. The Simple Machines Website is defined as http://www.simplemachines.org/. + +Agreement +----------------------------------------------------------------------------- +1. Permission is hereby granted to use, copy, modify and/or distribute this +Package, provided that: + a. All copyright notices within source files and as generated by the +Software as output are retained, unchanged. + b. Any Distribution of this Package, whether as a Modified Package or +not, includes this file and is released under the terms of this Agreement. +This clause is not dependent upon any measure of changes made to this +Package. + c. This Package, Modified Packages, and derivative works may not be +sold or released under any paid license. Copying fees for the transport of +this Package, support fees for installation or other services, and hosting +fees for hosting the Software may, however, be imposed. + d. Any Distribution of this Package, whether as a Modified Package +or not, requires express written consent from Lewis Media. + +2. You may make Modifications to this Package or a derivative of it, and +distribute your Modifications in a form that is separate from the Package, +such as patches. The following restrictions apply to Modifications: + a. A Modification must not alter or remove any copyright notices in +the Software or Package, generated or otherwise. + b. When a Modification to the Package is released, a non-exclusive +royalty-free right is granted to Lewis Media to distribute the Modification +in future versions of the Package provided such versions remain available +under the terms of this Agreement in addition to any other license(s) of the +initial developer. + c. Any Distribution of a Modified Package or derivative requires +express written consent from Lewis Media. + +3. Permission is hereby also granted to distribute programs which depend on +this Package, provided that you do not distribute any Modified Package +without express written consent. + +4. Lewis Media reserves the right to change the terms of this Agreement at +any time, although those changes are not retroactive to past releases. +Changes to this document will be announced via email using the Simple +Machines email notification list. Failure to receive notification of a change +does not make those changes invalid. A current copy of this Agreement can be +found on the Simple Machines Website. + +5. This Agreement will terminate automatically if you fail to comply with the +limitations described herein. Upon termination, you must destroy all copies +of this Package, the Software, and any derivatives within 48 hours. + +----------------------------------------------------------------------------- +THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY WARRANTY. ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHORS BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY +OUT OF THE USE OR MISUSE OF THIS PACKAGE. \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/style.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/style.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,223 @@ +/* Normal, standard links. */ +a:link, a:visited +{ + color: #005177; + background-color: transparent; + text-decoration: none; +} +a:hover +{ + color: #cc3333; + background-color: transparent; + text-decoration: none; +} + +/* Navigation links - for the link tree. */ +.nav, .nav:link, .nav:visited +{ + color: #000000; + background-color: transparent; + text-decoration: none; +} +a.nav:hover +{ + font-weight: bold; + color: #cc3333; + background-color: transparent; + text-decoration: underline; +} + +/* Tables should show empty cells too. */ +table +{ + empty-cells: show; +} + +/* By default (td, body..) use Verdana in black. */ +body, td, th +{ + color: #000000; + font-size: small; + font-family: verdana, sans-serif; +} + +/* Input boxes - just a bit smaller than normal so they align well. */ +input, textarea +{ + font-size: 9pt; + color: #000000; + font-family: verdana, sans-serif; + background-color: #afc6db; +} + +/* Checkboxes shouldn't have a background color. */ +input.check +{ + background-color: transparent; +} + +/* Selects are a bit smaller, because it makes them look even better 8). */ +select +{ + font-size: 8pt; + font-weight: normal; + color: #000000; + font-family: verdana, sans-serif; + background-color: #afc6db; +} + +/* Standard horizontal rule.. */ +hr +{ + color: #6394bd; + background-color: transparent; +} +/* A more colorful hr.. */ +.hrcolor +{ + height: 1px; + border: 0; + color: #6394bd; + background-color: #6394bd; +} + +/* A quote, perhaps from another post. */ +.quote +{ + color: #000000; + background-color: #dee7ef; + border: 1px solid black; + margin: 1px; + padding: 1px; + font-size: x-small; +} + +/* A code block - maybe even PHP ;). */ +.code +{ + color: #000000; + background-color: #cccccc; + border: 1px solid black; + margin: 1px; + padding: 1px; + font-size: x-small; + line-height: 1.3em; +} + +/* The "Quote:" and "Code:" header parts... */ +.quoteheader, .codeheader +{ + color: #000000; + text-decoration: none; + font-style: normal; + font-weight: bold; + font-size: x-small; + line-height: 1.2em; +} + +/* Generally, those [?] icons. */ +.help +{ + cursor: help; + background-color: transparent; +} + +/* /me uses this a lot. */ +.meaction +{ + color: red; + background-color: transparent; +} + +/* The main post box - this makes it as wide as possible. */ +.editor +{ + width: 100%; +} + +/* Highlighted text - such as search results. */ +.highlight +{ + background-color: yellow; + font-weight: bold; + color: black; +} + +/* Alternating backgrounds... */ +.windowbg +{ + color: #000000; + background-color: #afc6db; +} +.windowbg2 +{ + color: #000000; + background-color: #f8f8f8; +} + +/* Titles - such as table headers. */ +.titlebg, tr.titlebg th, tr.titlebg td, .titlebg a:link, .titlebg a:visited, .titlebg2, tr.titlebg2 th, tr.titlebg2 td, .titlebg2 a:link, .titlebg2 a:visited +{ + font-weight: bold; + font-style: normal; + color: #ffffff; + background-color: #6e94b7; +} +.titlebg a:hover, .titlebg2 a:hover +{ + color: #ffffff; + text-decoration: underline; +} + +/* The category headers, page indexes, and such things. */ +.catbg, .catbg3 +{ + font-weight: bold; + background-color: #afc6db; + background-image: url(images/catbg.gif); + color: #000000; +} + +/* The borders around things. */ +.bordercolor +{ + background-color: #6394bd; +} +.tborder +{ + border: 1px solid #6394bd; +} + +/* Default font sizes. */ +.smalltext +{ + font-size: x-small; +} +.normaltext +{ + font-size: small; +} +.largetext +{ + font-size: large; +} +/* No image should have a border when linked */ +a img{ + border: 0; +} + +/* The AJAX notifier */ +#ajax_in_progress +{ + background: #32CD32; + color: white; + text-align: center; + font-weight: bold; + font-size: 18pt; + padding: 3px; + width: 100%; + position: fixed; + top: 0; + left: 0; +} + diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/classic/theme_info.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/classic/theme_info.xml Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,15 @@ + + + + Classic YaBB SE Theme + + info@simplemachines.org + + http://www.simplemachines.org/ + + main + + index + + + diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/BoardIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/BoardIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,513 @@ + + ', $txt['members'], ': ', $context['common_stats']['total_members'], '  •  ', $txt['posts_made'], ': ', $context['common_stats']['total_posts'], '  •  ', $txt['topics'], ': ', $context['common_stats']['total_topics'], ' + ', ($settings['show_latest_member'] ? '
    ' . $txt['welcome_member'] . ' ' . $context['common_stats']['latest_member']['link'] . '' . $txt['newest_member'] : '') , ' +

    '; + + // Show the news fader? (assuming there are things to show...) + if ($settings['show_newsfader'] && !empty($context['fader_news_lines'])) + { + echo ' +
    +

    + + ', $txt['news'], ' +

    +
    +
    ', $context['news_lines'][0], '
    +
    +
    + + '; + } + + /* Each category in categories is made up of: + id, href, link, name, is_collapsed (is it collapsed?), can_collapse (is it okay if it is?), + new (is it new?), collapse_href (href to collapse/expand), collapse_image (up/down image), + and boards. (see below.) */ + foreach ($context['categories'] as $category) + { + // If theres no parent boards we can see, avoid showing an empty category (unless its collapsed) + if (empty($category['boards']) && !$category['is_collapsed']) + continue; + + echo ' +
    +

    '; + + if (!$context['user']['is_guest'] && !empty($category['show_unread'])) + echo ' + ', $txt['view_unread_category'], ''; + + // If this category even can collapse, show a link to collapse it. + if ($category['can_collapse']) + echo ' + ', $category['collapse_image'], ' '; + + echo $category['link']; + + echo ' +

    '; + + // Assuming the category hasn't been collapsed... + if (!$category['is_collapsed']) + { + echo ' + '; + + /* Each board in each category's boards has: + new (is it new?), id, name, description, moderators (see below), link_moderators (just a list.), + children (see below.), link_children (easier to use.), children_new (are they new?), + topics (# of), posts (# of), link, href, and last_post. (see below.) */ + foreach ($category['boards'] as $board) + { + echo ' + + + '; + + // If the board or children is new, show an indicator. + if ($board['new'] || $board['children_new']) + echo ' + ', $txt['new_posts'], ''; + // Is it a redirection board? + elseif ($board['is_redirect']) + echo ' + *'; + // No new posts at all! The agony!! + else + echo ' + ', $txt['old_posts'], ''; + + echo ' + + + + + ', comma_format($board['posts']), ' ', $board['is_redirect'] ? $txt['redirects'] : $txt['posts'], '
    + ', $board['is_redirect'] ? '' : comma_format($board['topics']) . ' ' . $txt['board_topics'], ' + + '; + + /* The board's and children's 'last_post's have: + time, timestamp (a number that represents the time.), id (of the post), topic (topic id.), + link, href, subject, start (where they should go for the first unread post.), + and member. (which has id, name, link, href, username in it.) */ + if (!empty($board['last_post']['id'])) + echo ' + ', $txt['last_post'], ' ', $txt['by'], ' ', $board['last_post']['member']['link'] , '
    + ', $txt['in'], ' ', $board['last_post']['link'], '
    + ', $txt['on'], ' ', $board['last_post']['time']; + echo ' + +
    '; + + // Show the "Child Boards: ". (there's a link_children but we're going to bold the new ones...) + if (!empty($board['children'])) + { + // Sort the links into an array with new boards bold so it can be imploded. + $children = array(); + /* Each child in each board's children has: + id, name, description, new (is it new?), topics (#), posts (#), href, link, and last_post. */ + foreach ($board['children'] as $child) + { + if (!$child['is_redirect']) + $child['link'] = '' . $child['name'] . ''; + else + $child['link'] = '' . $child['name'] . ''; + + // Has it posts awaiting approval? + if ($child['can_approve_posts'] && ($child['unapproved_posts'] || $child['unapproved_topics'])) + $child['link'] .= ' (!)'; + + $children[] = $child['new'] ? '' . $child['link'] . '' : $child['link']; + } + echo ' + + + '; + } + } + echo ' +
    +

    ', $board['name'], ''; + + // Has it outstanding posts for approval? + if ($board['can_approve_posts'] && ($board['unapproved_posts'] || $board['unapproved_topics'])) + echo ' + (!)'; + + echo ' +

    +

    ', $board['description'] , '

    '; + + // Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.) + if (!empty($board['moderators'])) + echo ' +

    ', count($board['moderators']) == 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $board['link_moderators']), '

    '; + + // Show some basic information about the number of posts, etc. + echo ' +
    ', $txt['parent_boards'], ': ', implode(', ', $children), '
    '; + } + echo ' +
    '; + } + + if ($context['user']['is_logged']) + { + echo ' +
    +
    +
    + ', $txt['new_posts'], ' + ', $txt['old_posts'], ' +
    '; + + // Mark read button. + $mark_read_button = array( + 'markread' => array('text' => 'mark_as_read', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=all;' . $context['session_var'] . '=' . $context['session_id']), + ); + + // Show the mark all as read button? + if ($settings['show_mark_read'] && !empty($context['categories'])) + template_button_strip($mark_read_button, 'top'); + + echo ' +
    +
    '; + } + + template_info_center(); +} + +function template_info_center() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // Here's where the "Info Center" starts... + echo ' +
    +

    + + ', sprintf($txt['info_center_title'], $context['forum_name_html_safe']), ' +

    + +
    '; +} +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/Display.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/Display.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,843 @@ + + ', $txt['report_sent'], ' +'; + } + + // Show the anchor for the top and for the first message. If the first message is new, say so. + echo ' + +', $context['first_new_message'] ? '' : ''; + + // Is this topic also a poll? + if ($context['is_poll']) + { + echo ' +
    +

    + ', $txt['poll'], ' +

    +

    + ', $context['poll']['question'], ' +

    +
    '; + + // Are they not allowed to vote but allowed to view the options? + if ($context['poll']['show_results'] || !$context['allow_vote']) + { + echo ' +
    '; + + // Show each option with its corresponding percentage bar. + foreach ($context['poll']['options'] as $option) + echo ' +
    ', $option['option'], '
    +
    ', $context['allow_poll_view'] ? $option['bar'] . ' ' . $option['votes'] . ' (' . $option['percent'] . '%)' : '', '
    '; + + echo ' +
    '; + + if ($context['allow_poll_view']) + echo ' +

    ', $txt['poll_total_voters'], ': ', $context['poll']['total_votes'], '

    '; + + } + // They are allowed to vote! Go to it! + else + { + echo ' +
    '; + + // Show a warning if they are allowed more than one option. + if ($context['poll']['allowed_warning']) + echo ' +

    ', $context['poll']['allowed_warning'], '

    '; + + echo ' +
      '; + + // Show each option with its button - a radio likely. + foreach ($context['poll']['options'] as $option) + echo ' +
    • ', $option['vote_button'], '
    • '; + + echo ' +
    + +
    + + +
    +
    '; + } + + // Is the clock ticking? + if (!empty($context['poll']['expire_time'])) + echo ' +

    ', ($context['poll']['is_expired'] ? $txt['poll_expired_on'] : $txt['poll_expires_on']), ': ', $context['poll']['expire_time'], '

    '; + + echo ' +
    +
    +
    '; + + // Build the poll moderation button array. + $poll_buttons = array( + 'vote' => array('test' => 'allow_return_vote', 'text' => 'poll_return_vote', 'image' => 'poll_options.gif', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start']), + 'results' => array('test' => 'show_view_results_button', 'text' => 'poll_results', 'image' => 'poll_results.gif', '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.gif', 'lang' => true, 'url' => $scripturl . '?action=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.gif', '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.gif', '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.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['poll_remove_warn'] . '\');"', 'url' => $scripturl . '?action=removepoll;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + template_button_strip($poll_buttons); + + echo ' +

    '; + } + + // Does this topic have some events linked to it? + if (!empty($context['linked_calendar_events'])) + { + echo ' +
    +

    ', $txt['calendar_linked_events'], '

    +
    +
      '; + + foreach ($context['linked_calendar_events'] as $event) + echo ' +
    • + ', ($event['can_edit'] ? '* ' : ''), '', $event['title'], ': ', $event['start_date'], ($event['start_date'] != $event['end_date'] ? ' - ' . $event['end_date'] : ''), ' +
    • '; + + echo ' +
    +
    +
    '; + } + + // Build the normal button array. + $normal_buttons = array( + 'reply' => array('test' => 'can_reply', 'text' => 'reply', 'image' => 'reply.gif', 'lang' => true, 'url' => $scripturl . '?action=post;topic=' . $context['current_topic'] . '.' . $context['start'] . ';last_msg=' . $context['topic_last_message']), + 'add_poll' => array('test' => 'can_add_poll', 'text' => 'add_poll', 'image' => 'add_poll.gif', 'lang' => true, 'url' => $scripturl . '?action=editpoll;add;topic=' . $context['current_topic'] . '.' . $context['start']), + 'notify' => array('test' => 'can_mark_notify', 'text' => ($context['is_marked_notify'] ? 'unnotify' : 'notify'), 'image' => ($context['is_marked_notify'] ? 'un' : ''). 'notify.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . ($context['is_marked_notify'] ? $txt['notification_disable_topic'] : $txt['notification_enable_topic']) . '\');"', '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.gif', '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']), + 'send' => array('test' => 'can_send_topic', 'text' => 'send_topic', 'image' => 'sendtopic.gif', 'lang' => true, 'url' => $scripturl . '?action=emailuser;sa=sendtopic;topic=' . $context['current_topic'] . '.0'), + 'print' => array('text' => 'print', 'image' => 'print.gif', 'lang' => true, 'custom' => 'rel="new_win nofollow"', 'url' => $scripturl . '?action=printpage;topic=' . $context['current_topic'] . '.0'), + ); + + // Allow adding new buttons easily. + call_integration_hook('integrate_display_buttons', array(&$normal_buttons)); + + // Show the page index... "Pages: [1]". + echo ' +
    + +
    ', $txt['pages'], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '   ' . $txt['go_down'] . '' : '', '
    + +
    '; + + // Show the topic information - icon, subject, etc. + echo ' +
    +

    + + ', $txt['author'], ' + ', $txt['topic'], ': ', $context['subject'], '  (', $txt['read'], ' ', $context['num_views'], ' ', $txt['times'], ') +

    '; + + if (!empty($settings['display_who_viewing'])) + { + echo ' +
    '; + + // Show just numbers...? + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) == 1 ? $txt['who_member'] : $txt['members']; + // Or show the actual people viewing the topic? + else + echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) || $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + + // Now show how many guests are here too. + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_topic'], ' +
    '; + } + + echo ' +
    '; + + // These are some cache image buttons we may want. + $reply_button = create_button('quote.gif', 'reply', 'quote', 'align="middle"'); + $modify_button = create_button('modify.gif', 'modify', 'modify', 'align="middle"'); + $remove_button = create_button('delete.gif', 'remove', 'remove', 'align="middle"'); + $split_button = create_button('split.gif', 'split', 'split', 'align="middle"'); + $approve_button = create_button('approve.gif', 'approve', 'approve', 'align="middle"'); + $restore_message_button = create_button('restore_topic.gif', 'restore_message', 'restore_message', 'align="middle"'); + + $ignoredMsgs = array(); + $removableMessageIDs = array(); + + // Get all the messages... + while ($message = $context['get_message']()) + { + $is_first_post = !isset($is_first_post) ? true : false; + $ignoring = false; + if ($message['can_remove']) + $removableMessageIDs[] = $message['id']; + + echo ' +
    '; + + // Are we ignoring this message? + if (!empty($message['is_ignored'])) + { + $ignoring = true; + $ignoredMsgs[] = $message['id']; + } + + // Show the message anchor and a "new" anchor if this message is new. + if ($message['id'] != $context['first_message']) + echo ' + ', $message['first_new'] ? '' : ''; + + echo ' +
    '; + + // Show information about the poster of this message. + echo ' +
    +

    ', $message['member']['link'], '

    +
      '; + + // Show the member's custom title, if they have one. + if (isset($message['member']['title']) && $message['member']['title'] != '') + echo ' +
    • ', $message['member']['title'], '
    • '; + + // Show the member's primary group (like 'Administrator') if they have one. + if (isset($message['member']['group']) && $message['member']['group'] != '') + echo ' +
    • ', $message['member']['group'], '
    • '; + + // Don't show these things for guests. + if (!$message['member']['is_guest']) + { + // Show the post group if and only if they have no other group or the option is on, and they are in a post group. + if ((empty($settings['hide_post_group']) || $message['member']['group'] == '') && $message['member']['post_group'] != '') + echo ' +
    • ', $message['member']['post_group'], '
    • '; + echo ' +
    • ', $message['member']['group_stars'], '
    • '; + + // Is karma display enabled? Total or +/-? + if ($modSettings['karmaMode'] == '1') + echo ' +
    • ', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '
    • '; + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    • ', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '
    • '; + + // Is this user allowed to modify this member's karma? + if ($message['member']['karma']['allow']) + echo ' +
    • + ', $modSettings['karmaApplaudLabel'], ' + ', $modSettings['karmaSmiteLabel'], ' +
    • '; + + // Show online and offline buttons? + if (!empty($modSettings['onlineEnable'])) + echo ' +
    • ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $message['member']['online']['text'] . '' : $message['member']['online']['text'], $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? ' ' . $message['member']['online']['text'] . '' : '', '
    • '; + + // Show the member's gender icon? + if (!empty($settings['show_gender']) && $message['member']['gender']['image'] != '' && !isset($context['disabled_fields']['gender'])) + echo ' +
    • ', $txt['gender'], ': ', $message['member']['gender']['image'], '
    • '; + + // Show how many posts they have made. + if (!isset($context['disabled_fields']['posts'])) + echo ' +
    • ', $txt['member_postcount'], ': ', $message['member']['posts'], '
    • '; + + // Any custom fields for standard placement? + if (!empty($message['member']['custom_fields'])) + { + foreach ($message['member']['custom_fields'] as $custom) + if (empty($custom['placement']) && !empty($custom['value'])) + echo ' +
    • ', $custom['title'], ': ', $custom['value'], '
    • '; + } + + // Show avatars, images, etc.? + if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image'])) + echo ' +
    • ', $message['member']['avatar']['image'], '
    • '; + + // Show their personal text? + if (!empty($settings['show_blurb']) && $message['member']['blurb'] != '') + echo ' +
    • ', $message['member']['blurb'], '
    • '; + + // Any custom fields to show as icons? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 1 || empty($custom['value'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    • +
        '; + } + echo ' +
      • ', $custom['value'], '
      • '; + } + if ($shown) + echo ' +
      +
    • '; + } + + // This shows the popular messaging icons. + if ($message['member']['has_messenger'] && $message['member']['can_view_profile']) + echo ' +
    • +
        + ', !isset($context['disabled_fields']['icq']) && !empty($message['member']['icq']['link']) ? '
      • ' . $message['member']['icq']['link'] . '
      • ' : '', ' + ', !isset($context['disabled_fields']['msn']) && !empty($message['member']['msn']['link']) ? '
      • ' . $message['member']['msn']['link'] . '
      • ' : '', ' + ', !isset($context['disabled_fields']['aim']) && !empty($message['member']['aim']['link']) ? '
      • ' . $message['member']['aim']['link'] . '
      • ' : '', ' + ', !isset($context['disabled_fields']['yim']) && !empty($message['member']['yim']['link']) ? '
      • ' . $message['member']['yim']['link'] . '
      • ' : '', ' +
      +
    • '; + + // Show the profile, website, email address, and personal message buttons. + if ($settings['show_profile_buttons']) + { + echo ' +
    • + +
    • '; + } + + // Are we showing the warning status? + if ($message['member']['can_see_warning']) + echo ' +
    • ', $context['can_issue_warning'] ? '' : '', '', $txt['user_warn_' . $message['member']['warning_status']], '', $context['can_issue_warning'] ? '' : '', '', $txt['warn_' . $message['member']['warning_status']], '
    • '; + } + // Otherwise, show the guest's email. + elseif (!empty($message['member']['email']) && in_array($message['member']['show_email'], array('yes', 'yes_permission_override', 'no_through_forum'))) + echo ' +
    • ', ($settings['use_image_buttons'] ? '' . $txt['email'] . '' : $txt['email']), '
    • '; + + // Done with the information about the poster... on to the post itself. + echo ' +
    +
    +
    +
    +
    +
    +
    + ', $message['subject'], ' +
    +
    « ', !empty($message['counter']) ? $txt['reply_noun'] . ' #' . $message['counter'] : '', ' ', $txt['on'], ': ', $message['time'], ' »
    +
    +
    '; + + // If this is the first post, (#0) just say when it was posted - otherwise give the reply #. + if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg']) + echo ' +
      '; + + // Maybe we can approve it, maybe we should? + if ($message['can_approve']) + echo ' +
    • ', $approve_button, '
    • '; + + // Can they reply? Have they turned on quick reply? + if ($context['can_quote'] && !empty($options['display_quick_reply'])) + echo ' +
    • ', $reply_button, '
    • '; + + // So... quick reply is off, but they *can* reply? + elseif ($context['can_quote']) + echo ' +
    • ', $reply_button, '
    • '; + + // Can the user modify the contents of this post? + if ($message['can_modify']) + echo ' +
    • ', $modify_button, '
    • '; + + // How about... even... remove it entirely?! + if ($message['can_remove']) + echo ' +
    • ', $remove_button, '
    • '; + + // What about splitting it off the rest of the topic? + if ($context['can_split'] && !empty($context['real_num_replies'])) + echo ' +
    • ', $split_button, '
    • '; + + // Can we restore topics? + if ($context['can_restore_msg']) + echo ' +
    • ', $restore_message_button, '
    • '; + + // Show a checkbox for quick moderation? + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $message['can_remove']) + echo ' + '; + + if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg']) + echo ' +
    '; + + echo ' +
    '; + + // Ignoring this user? Hide the post. + if ($ignoring) + echo ' +
    + ', $txt['ignoring_user'], ' + +
    '; + + // Show the post itself, finally! + echo ' +
    +
    '; + + if (!$message['approved'] && $message['member']['id'] != 0 && $message['member']['id'] == $context['user']['id']) + echo ' +
    + ', $txt['post_awaiting_approval'], ' +
    '; + echo ' +
    ', $message['body'], '
    +
    ', $message['can_modify'] ? ' + ' : ''; + + // Assuming there are attachments... + if (!empty($message['attachment'])) + { + // Now for the attachments, signature, ip logged, etc... + echo ' + '; + } + + echo ' +
    +
    +
    '; + + // Show "« Last Edit: Time by Person »" if this post was edited. + if ($settings['show_modify'] && !empty($message['modified']['name'])) + echo ' + « ', $txt['last_edit'], ': ', $message['modified']['time'], ' ', $txt['by'], ' ', $message['modified']['name'], ' »'; + + echo ' +
    +
    '; + + // Maybe they want to report this post to the moderator(s)? + if ($context['can_report_moderator']) + echo ' + ', $txt['report_to_mod'], '  '; + + // Can we issue a warning because of this post? Remember, we can't give guests warnings. + if ($context['can_issue_warning'] && !$message['is_message_author'] && !$message['member']['is_guest']) + echo ' + ', $txt['issue_warning_post'], ''; + echo ' + '; + + // Show the IP to this user for this post - because you can moderate? + if ($context['can_moderate_forum'] && !empty($message['member']['ip'])) + echo ' + ', $message['member']['ip'], ' (?)'; + // Or, should we show it because this is you? + elseif ($message['can_see_ip']) + echo ' + ', $message['member']['ip'], ''; + // Okay, are you at least logged in? Then we can show something about why IPs are logged... + elseif (!$context['user']['is_guest']) + echo ' + ', $txt['logged'], ''; + // Otherwise, you see NOTHING! + else + echo ' + ', $txt['logged']; + + echo ' +
    '; + + // Are there any custom profile fields for above the signature? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 2 || empty($custom['value'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    +
      ', $message['member']['signature'], '
    '; + + echo ' +
    +
    +
    '; + } + + echo ' +
    '; + echo ' +
    +'; + + echo ' +
    + +
    ', $txt['pages'], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '   ' . $txt['go_up'] . '' : '', '
    +
    ', $context['previous_next'], '
    +
    '; + + // Show the lower breadcrumbs. + theme_linktree(); + + $mod_buttons = array( + 'move' => array('test' => 'can_move', 'text' => 'move_topic', 'image' => 'admin_move.gif', 'lang' => true, 'url' => $scripturl . '?action=movetopic;topic=' . $context['current_topic'] . '.0'), + 'delete' => array('test' => 'can_delete', 'text' => 'remove_topic', 'image' => 'admin_rem.gif', '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.gif', 'lang' => true, 'url' => $scripturl . '?action=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.gif', 'lang' => true, 'url' => $scripturl . '?action=sticky;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), + 'merge' => array('test' => 'can_merge', 'text' => 'merge', 'image' => 'merge.gif', '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.gif', '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']) + $mod_buttons[] = array('text' => 'restore_topic', 'image' => '', 'lang' => true, 'url' => $scripturl . '?action=restoretopic;topics=' . $context['current_topic'] . ';' . $context['session_var'] . '=' . $context['session_id']); + + // Allow adding new mod buttons easily. + call_integration_hook('integrate_mod_buttons', array(&$mod_buttons)); + + echo ' +
    ', template_button_strip($mod_buttons, 'bottom', array('id' => 'moderationbuttons_strip')), '
    '; + + // Show the jumpto box, or actually...let Javascript do it. + echo ' +
    +
     
    +

    '; + + if ($context['can_reply'] && !empty($options['display_quick_reply'])) + { + echo ' + +
    '; + + echo ' +

    + + + + + ', $txt['quick_reply'], ' +

    + +
    '; + } + + if ($context['show_spellchecking']) + echo ' +
    +'; + + echo ' + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/GenericMenu.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/GenericMenu.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,375 @@ + +
    + '; + + // What one are we rendering? + $context['cur_menu_id'] = isset($context['cur_menu_id']) ? $context['cur_menu_id'] + 1 : 1; + $menu_context = &$context['menu_data_' . $context['cur_menu_id']]; + + // For every section that appears on the sidebar... + $firstSection = true; + foreach ($menu_context['sections'] as $section) + { + // Show the section header - and pump up the line spacing for readability. + echo ' +
    +
    +

    '; + + if ($firstSection && !empty($menu_context['can_toggle_drop_down'])) + { + echo ' + ', $section['title'],' + ! + '; + } + + else + { + echo ' + ', $section['title']; + } + + echo ' +

    +
    +
      '; + + // For every area of this section show a link to that area (bold if it's currently selected.) + foreach ($section['areas'] as $i => $area) + { + // Not supposed to be printed? + if (empty($area['label'])) + continue; + + echo ' +
    • '; + + // Is this the current area, or just some area? + if ($i == $menu_context['current_area']) + { + echo ' + ', $area['label'], ''; + + if (empty($context['tabs'])) + $context['tabs'] = isset($area['subsections']) ? $area['subsections'] : array(); + } + else + echo ' + ', $area['label'], ''; + + echo ' +
    • '; + } + + echo ' +
    +
    '; + + $firstSection = false; + } + + // This is where the actual "main content" area for the admin section starts. + echo ' +
    +
    '; + + // If there are any "tabs" setup, this is the place to shown them. + //!!! Clean this up! + if (!empty($context['tabs']) && empty($context['force_disable_tabs'])) + template_generic_menu_tabs($menu_context); +} + +// Part of the sidebar layer - closes off the main bit. +function template_generic_menu_sidebar_below() +{ + global $context, $settings, $options; + + echo ' +
    +
    '; +} + +// This contains the html for the side bar of the admin center, which is used for all admin pages. +function template_generic_menu_dropdown_above() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Which menu are we rendering? + $context['cur_menu_id'] = isset($context['cur_menu_id']) ? $context['cur_menu_id'] + 1 : 1; + $menu_context = &$context['menu_data_' . $context['cur_menu_id']]; + + if (!empty($menu_context['can_toggle_drop_down'])) + echo ' + '; + + echo ' +
    +
    '; + + // This is the main table - we need it so we can keep the content to the right of it. + echo ' +
    '; + + // It's possible that some pages have their own tabs they wanna force... + if (!empty($context['tabs'])) + template_generic_menu_tabs($menu_context); +} + +// Part of the admin layer - used with admin_above to close the table started in it. +function template_generic_menu_dropdown_below() +{ + global $context, $settings, $options; + + echo ' +
    '; +} + +// Some code for showing a tabbed view. +function template_generic_menu_tabs(&$menu_context) +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Handy shortcut. + $tab_context = &$menu_context['tab_data']; + + // Right to left tabs should be in reverse order. + if ($context['right_to_left']) + $tab_context['tabs'] = array_reverse($tab_context['tabs'], true); + + // Exactly how many tabs do we have? + foreach ($context['tabs'] as $id => $tab) + { + // Can this not be accessed? + if (!empty($tab['disabled'])) + { + $tab_context['tabs'][$id]['disabled'] = true; + continue; + } + + // Did this not even exist - or do we not have a label? + if (!isset($tab_context['tabs'][$id])) + $tab_context['tabs'][$id] = array('label' => $tab['label']); + elseif (!isset($tab_context['tabs'][$id]['label'])) + $tab_context['tabs'][$id]['label'] = $tab['label']; + + // Has a custom URL defined in the main admin structure? + if (isset($tab['url']) && !isset($tab_context['tabs'][$id]['url'])) + $tab_context['tabs'][$id]['url'] = $tab['url']; + // Any additional paramaters for the url? + if (isset($tab['add_params']) && !isset($tab_context['tabs'][$id]['add_params'])) + $tab_context['tabs'][$id]['add_params'] = $tab['add_params']; + // Has it been deemed selected? + if (!empty($tab['is_selected'])) + $tab_context['tabs'][$id]['is_selected'] = true; + // Does it have its own help? + if (!empty($tab['help'])) + $tab_context['tabs'][$id]['help'] = $tab['help']; + // Is this the last one? + if (!empty($tab['is_last']) && !isset($tab_context['override_last'])) + $tab_context['tabs'][$id]['is_last'] = true; + } + + // Find the selected tab + foreach ($tab_context['tabs'] as $sa => $tab) + if (!empty($tab['is_selected']) || (isset($menu_context['current_subsection']) && $menu_context['current_subsection'] == $sa)) + { + $selected_tab = $tab; + $tab_context['tabs'][$sa]['is_selected'] = true; + } + + echo ' +
    +

    '; + + // Show a help item? + if (!empty($selected_tab['help']) || !empty($tab_context['help'])) + echo ' + ', $txt['help'], ' '; + + echo ' + ', $tab_context['title'], ' +

    '; + + // Shall we use the tabs? + if (!empty($settings['use_tabs'])) + { + echo ' +
    + ', !empty($selected_tab['description']) ? $selected_tab['description'] : $tab_context['description'], ' +
    '; + + echo ' +
    +
    +
    +
      '; + + // Print out all the items in this tab. + foreach ($tab_context['tabs'] as $sa => $tab) + { + if (!empty($tab['disabled'])) + continue; + + if (!empty($tab['is_selected'])) + { + echo ' +
    • + + + ', $tab['label'], ' + + +
    • '; + } + else + echo ' + + + ', $tab['label'], ' + + '; + } + + // the end of tabs + echo ' +
    +

    +
    '; + } + // ...if not use the old style + else + { + echo ' +
    '; + + // Print out all the items in this tab. + foreach ($tab_context['tabs'] as $sa => $tab) + { + if (!empty($tab['disabled'])) + continue; + + if (!empty($tab['is_selected'])) + { + echo ' + * ', $tab['label'], ''; + } + else + echo ' + ', $tab['label'], ''; + + if (empty($tab['is_last'])) + echo ' | '; + } + + echo ' +
    +
    + ', isset($selected_tab['description']) ? $selected_tab['description'] : $tab_context['description'], ' +
    + + '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/Memberlist.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/Memberlist.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,203 @@ + array('text' => 'view_all_members', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=all', 'active' => true), + 'mlist_search' => array('text' => 'mlist_search', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist;sa=search'), + ); + + echo ' +
    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    + ', template_button_strip($memberlist_buttons, 'bottom'), ' +
    '; + + echo ' +
    +

    + ', $txt['members_list'], ''; + if (!isset($context['old_search'])) + echo ' + ', $context['letter_links'], ''; + echo ' +

    + + + '; + + // Display each of the column headers of the table. + foreach ($context['columns'] as $column) + { + // We're not able (through the template) to sort the search results right now... + if (isset($context['old_search'])) + echo ' + '; + // This is a selected column, so underline it or some such. + elseif ($column['selected']) + echo ' + '; + // This is just some column... show the link and be done with it. + else + echo ' + '; + } + echo ' + + + '; + + // Assuming there are members loop through each one displaying their data. + if (!empty($context['members'])) + { + foreach ($context['members'] as $member) + { + echo ' + + + + '; + + if (!isset($context['disabled_fields']['website'])) + echo ' + '; + + // ICQ? + if (!isset($context['disabled_fields']['icq'])) + echo ' + '; + + // AIM? + if (!isset($context['disabled_fields']['aim'])) + echo ' + '; + + // YIM? + if (!isset($context['disabled_fields']['yim'])) + echo ' + '; + + // MSN? + if (!isset($context['disabled_fields']['msn'])) + echo ' + '; + + // Group and date. + echo ' + + '; + + if (!isset($context['disabled_fields']['posts'])) + echo ' + + '; + + echo ' + '; + } + } + // No members? + else + echo ' + + + '; + + // Show the page numbers again. (makes 'em easier to find!) + echo ' + +
    + ', $column['label'], ' + ' . $column['label'] . ' + ', $column['link'], '
    + ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $member['online']['text'] . '' : $member['online']['label'], $context['can_send_pm'] ? '' : '', ' + ', $member['link'], '', $member['show_email'] == 'no' ? '' : '' . $txt['email'] . '', '', $member['website']['url'] != '' ? '' . $member['website']['title'] . '' : '', '', $member['icq']['link'], '', $member['aim']['link'], '', $member['yim']['link'], '', $member['msn']['link'], '', empty($member['group']) ? $member['post_group'] : $member['group'], '', $member['registered_date'], '', $member['posts'], ' + ', $member['posts'] > 0 ? '' : '', ' +
    ', $txt['search_no_results'], '
    +
    '; + + echo ' +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + + // If it is displaying the result of a search show a "search again" link to edit their criteria. + if (isset($context['old_search'])) + echo ' + '; + echo ' +
    +
    '; +} + +// A page allowing people to search the member list. +function template_search() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Build the memberlist button array. + $membersearch_buttons = array( + 'view_all_members' => array('text' => 'view_all_members', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist;sa=all'), + 'mlist_search' => array('text' => 'mlist_search', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist;sa=search', 'active' => true), + ); + + // Start the submission form for the search! + echo ' +
    +
    +
    + ', template_button_strip($membersearch_buttons, 'right'), ' +
    +
    +

    + ', !empty($settings['use_buttons']) ? '' : '', $txt['mlist_search'], ' +

    '; + + // Display the input boxes for the form. + echo ' +
    + + + ', $txt['search_for'], ': + + '; + + $count = 0; + foreach ($context['search_fields'] as $id => $title) + { + echo ' +
    '; + // Halfway through? + if (round(count($context['search_fields']) / 2) == ++$count) + echo ' +
    + '; + } + echo ' + +
    +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/MessageIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/MessageIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,506 @@ +'; + + if (!empty($context['boards']) && (!empty($options['show_children']) || $context['start'] == 0)) + { + echo ' +
    + + + + '; + + foreach ($context['boards'] as $board) + { + echo ' + + + '; + + // If the board or children is new, show an indicator. + if ($board['new'] || $board['children_new']) + echo ' + ', $txt['new_posts'], ''; + // Is it a redirection board? + elseif ($board['is_redirect']) + echo ' + *'; + // No new posts at all! The agony!! + else + echo ' + ', $txt['old_posts'], ''; + + echo ' + + + + + ', comma_format($board['posts']), ' ', $board['is_redirect'] ? $txt['redirects'] : $txt['posts'], '
    + ', $board['is_redirect'] ? '' : comma_format($board['topics']) . ' ' . $txt['board_topics'], ' + + '; + + /* The board's and children's 'last_post's have: + time, timestamp (a number that represents the time.), id (of the post), topic (topic id.), + link, href, subject, start (where they should go for the first unread post.), + and member. (which has id, name, link, href, username in it.) */ + if (!empty($board['last_post']['id'])) + echo ' + ', $txt['last_post'], ' ', $txt['by'], ' ', $board['last_post']['member']['link'] , '
    + ', $txt['in'], ' ', $board['last_post']['link'], '
    + ', $txt['on'], ' ', $board['last_post']['time']; + echo ' + +
    '; + + // Show the "Child Boards: ". (there's a link_children but we're going to bold the new ones...) + if (!empty($board['children'])) + { + // Sort the links into an array with new boards bold so it can be imploded. + $children = array(); + /* Each child in each board's children has: + id, name, description, new (is it new?), topics (#), posts (#), href, link, and last_post. */ + foreach ($board['children'] as $child) + { + if (!$child['is_redirect']) + $child['link'] = '' . $child['name'] . ''; + else + $child['link'] = '' . $child['name'] . ''; + + // Has it posts awaiting approval? + if ($child['can_approve_posts'] && ($child['unapproved_posts'] || $child['unapproved_topics'])) + $child['link'] .= ' (!)'; + + $children[] = $child['new'] ? '' . $child['link'] . '' : $child['link']; + } + echo ' + + + '; + } + } + echo ' +
    ', $txt['parent_boards'], '
    +

    ', $board['name'], ''; + + // Has it outstanding posts for approval? + if ($board['can_approve_posts'] && ($board['unapproved_posts'] || $board['unapproved_topics'])) + echo ' + (!)'; + + echo ' +

    +

    ', $board['description'] , '

    '; + + // Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.) + if (!empty($board['moderators'])) + echo ' +

    ', count($board['moderators']) == 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $board['link_moderators']), '

    '; + + // Show some basic information about the number of posts, etc. + echo ' +
    ', $txt['parent_boards'], ': ', implode(', ', $children), '
    +
    '; + } + + if (!empty($options['show_board_desc']) && $context['description'] != '') + { + echo ' +
    +
    ', $context['description'], '
    +
    '; + } + + // Create the button set... + $normal_buttons = array( + 'new_topic' => array('test' => 'can_post_new', 'text' => 'new_topic', 'image' => 'new_topic.gif', 'lang' => true, 'url' => $scripturl . '?action=post;board=' . $context['current_board'] . '.0'), + 'post_poll' => array('test' => 'can_post_poll', 'text' => 'new_poll', 'image' => 'new_poll.gif', 'lang' => true, 'url' => $scripturl . '?action=post;board=' . $context['current_board'] . '.0;poll'), + 'notify' => array('test' => 'can_mark_notify', 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', 'image' => ($context['is_marked_notify'] ? 'un' : '') . 'notify.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . ($context['is_marked_notify'] ? $txt['notification_disable_board'] : $txt['notification_enable_board']) . '\');"', 'url' => $scripturl . '?action=notifyboard;sa=' . ($context['is_marked_notify'] ? 'off' : 'on') . ';board=' . $context['current_board'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), + 'markread' => array('text' => 'mark_read_short', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=board;board=' . $context['current_board'] . '.0;' . $context['session_var'] . '=' . $context['session_id']), + ); + + // They can only mark read if they are logged in and it's enabled! + if (!$context['user']['is_logged'] || !$settings['show_mark_read']) + unset($normal_buttons['markread']); + + // Allow adding new buttons easily. + call_integration_hook('integrate_messageindex_buttons', array(&$normal_buttons)); + + if (!$context['no_topic_listing']) + { + echo ' +
    +
    ', $txt['pages'], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '  ' . $txt['go_down'] . '' : '', '
    + ', template_button_strip($normal_buttons, 'bottom'), ' +
    '; + + // If Quick Moderation is enabled start the form. + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] > 0 && !empty($context['topics'])) + echo ' +
    '; + + echo ' +
    + '; + + // Are there actually any topics to show? + if (!empty($context['topics'])) + { + echo ' + + + + + + + + '; + + // Show a "select all" box for quick moderation? + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] == 1) + echo ' + '; + // If it's on in "image" mode, don't show anything but the column. + elseif (!empty($context['can_quick_mod'])) + echo ' + '; + echo ' + + '; + } + echo ' + '; + + if (!empty($settings['display_who_viewing'])) + { + echo ' + + + '; + } + + // If this person can approve items and we have some awaiting approval tell them. + if (!empty($context['unapproved_posts_message'])) + { + echo ' + + + '; + } + + // No topics.... just say, "sorry bub". + if (empty($context['topics'])) + echo ' + + + '; + + foreach ($context['topics'] as $topic) + { + // Do we want to separate the sticky and lock status out? + if (!empty($settings['separate_sticky_lock']) && strpos($topic['class'], 'sticky') !== false) + $topic['class'] = substr($topic['class'], 0, strrpos($topic['class'], '_sticky')); + if (!empty($settings['separate_sticky_lock']) && strpos($topic['class'], 'locked') !== false) + $topic['class'] = substr($topic['class'], 0, strrpos($topic['class'], '_locked')); + + // Is this topic pending approval, or does it have any posts pending approval? + if ($context['can_approve_posts'] && $topic['unapproved_posts']) + $color_class = !$topic['approved'] ? 'approvetbg' : 'approvebg'; + // Sticky topics should get a different color, too. + elseif ($topic['is_sticky'] && !empty($settings['separate_sticky_lock'])) + $color_class = 'windowbg3'; + // Last, but not least: regular topics. + else + $color_class = 'windowbg'; + + // Some columns require a different shade of the color class. + $alternate_class = 'windowbg2'; + + echo ' + + + + + + + + '; + + // Show the quick moderation options? + if (!empty($context['can_quick_mod'])) + { + echo ' + '; + } + echo ' + '; + } + + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + { + echo ' + + + '; + } + + echo ' + +
     ', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', '', $txt['started_by'], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt['replies'], $context['sort_by'] == 'replies' ? ' ' : '', '', $txt['views'], $context['sort_by'] == 'views' ? ' ' : '', '', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', ' + +  
    '; + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) == 1 ? $txt['who_member'] : $txt['members']; + else + echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) or $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_board'], ' +
    + ! ', $context['unapproved_posts_message'], ' +
    ', $txt['msg_alert_none'], '
    + + + + '; + + if (!empty($settings['separate_sticky_lock'])) + echo ' + ', $topic['is_locked'] ? '' : '', ' + ', $topic['is_sticky'] ? '' : ''; + + echo ' + ', $topic['is_sticky'] ? '' : '', '', $topic['first_post']['link'], (!$context['can_approve_posts'] && !$topic['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), '', $topic['is_sticky'] ? '' : ''; + + // Is this topic new? (assuming they are logged in!) + if ($topic['new'] && $context['user']['is_logged']) + echo ' + ', $txt['new'], ''; + + echo ' + ', $topic['pages'], ' + + ', $topic['first_post']['member']['link'], ' + + ', $topic['replies'], ' + + ', $topic['views'], ' + + ', $txt['last_post'], ' + + ', $topic['last_post']['time'], '
    + ', $txt['by'], ' ', $topic['last_post']['member']['link'], ' +
    +
    '; + if ($options['display_quick_mod'] == 1) + echo ' + '; + else + { + // Check permissions on each and show only the ones they are allowed to use. + if ($topic['quick_mod']['remove']) + echo '', $txt['remove_topic'], ''; + + if ($topic['quick_mod']['lock']) + echo '', $txt['set_lock'], ''; + + if ($topic['quick_mod']['lock'] || $topic['quick_mod']['remove']) + echo '
    '; + + if ($topic['quick_mod']['sticky']) + echo '', $txt['set_sticky'], ''; + + if ($topic['quick_mod']['move']) + echo '', $txt['move_topic'], ''; + } + echo ' +
    + '; + + // Show a list of boards they can move the topic to. + if ($context['can_move']) + { + echo ' + '; + } + + echo ' + +
    +
    + '; + + // Finish off the form - again. + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] > 0 && !empty($context['topics'])) + echo ' + +
    '; + + echo ' +
    + ', template_button_strip($normal_buttons, 'top'), ' +
    ' . $txt['pages'] . ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '  ' . $txt['go_up'] . '' : '', '
    +
    '; + } + + // Show breadcrumbs at the bottom too. + echo ' +
    ', theme_linktree(), '
    '; + + echo ' +
    +
    +

     

    '; + + if (!$context['no_topic_listing']) + echo ' +
    +
      + ', !empty($modSettings['enableParticipation']) && $context['user']['is_logged'] ? ' +
    • ' . $txt['participation_caption'] . '
    • ' : '', ' +
    • ' . $txt['normal_topic'] . '
    • +
    • ' . sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']) . '
    • +
    • ' . sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']) . '
    • +
    +
    +
    +
      +
    • ' . $txt['locked_topic'] . '
    • ' . ($modSettings['enableStickyTopics'] == '1' ? ' +
    • ' . $txt['sticky_topic'] . '
    • ' : '') . ($modSettings['pollMode'] == '1' ? ' +
    • ' . $txt['poll'] : '') . '
    • +
    +
    '; + + echo ' + +
    +
    '; + + // Javascript for inline editing. + echo ' + +'; +} + +function theme_show_buttons() +{ + global $context, $settings, $options, $txt, $scripturl; + + $buttonArray = array(); + + // If they are logged in, and the mark read buttons are enabled.. + if ($context['user']['is_logged'] && $settings['show_mark_read']) + $buttonArray[] = '' . $txt['mark_read_short'] . ''; + + // If the user has permission to show the notification button... ask them if they're sure, though. + if ($context['can_mark_notify']) + $buttonArray[] = '' . $txt[$context['is_marked_notify'] ? 'unnotify' : 'notify'] . ''; + + // Are they allowed to post new topics? + if ($context['can_post_new']) + $buttonArray[] = '' . $txt['new_topic'] . ''; + + // How about new polls, can the user post those? + if ($context['can_post_poll']) + $buttonArray[] = '' . $txt['new_poll'] . ''; + + // Right to left menu should be in reverse order. + if ($context['right_to_left']) + $buttonArray = array_reverse($buttonArray, true); + + return implode('  |  ', $buttonArray); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/PersonalMessage.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/PersonalMessage.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1763 @@ +'; + + // Show the capacity bar, if available. + if (!empty($context['limit_bar'])) + { + echo ' + + + + + + +
    ', $txt['pm_capacity'], ': +
    +
    +
    +
    90 ? ' class="alert"' : '', '> + ', $context['limit_bar']['text'], ' +
    '; + } + + // Message sent? Show a small indication. + if (isset($context['pm_sent'])) + echo ' +
    + ', $txt['pm_sent'], ' +
    '; +} + +// Just the end of the index bar, nothing special. +function template_pm_below() +{ + global $context, $settings, $options; + + echo ' + '; +} + +function template_folder() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // The every helpful javascript! + echo ' + '; + + echo ' +
    '; + + // If we are not in single display mode show the subjects on the top! + if ($context['display_mode'] != 1) + { + template_subject_list(); + echo '
    '; + } + + // Got some messages to display? + if ($context['get_pmessage']('message', true)) + { + // Show a few buttons if we are in conversation mode and outputting the first message. + if ($context['display_mode'] == 2) + { + // Build the normal button array. + $conversation_buttons = array( + 'reply' => array('text' => 'reply_to_all', 'image' => 'reply.gif', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=send;f=' . $context['folder'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';pmsg=' . $context['current_pm'] . ';u=all'), + 'delete' => array('text' => 'delete_conversation', 'image' => 'delete.gif', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=pmactions;pm_actions[' . $context['current_pm'] . ']=delete;conversation;f=' . $context['folder'] . ';start=' . $context['start'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'], 'custom' => 'onclick="return confirm(\'' . addslashes($txt['remove_message']) . '?\');"'), + ); + + // Show the conversation buttons. + echo ' +
    '; + + template_button_strip($conversation_buttons, 'right'); + + echo ' +
    '; + } + + echo ' +
    '; + + // Show the helpful titlebar - generally. + if ($context['display_mode'] != 1) + echo ' +
    +

    + ', $txt['author'], ' + ', $txt[$context['display_mode'] == 0 ? 'messages' : 'conversation'], ' +

    +
    '; + + // Cache some handy buttons. + $quote_button = create_button('quote.gif', 'reply_quote', 'quote', 'align="middle"'); + $reply_button = create_button('im_reply.gif', 'reply', 'reply', 'align="middle"'); + $reply_all_button = create_button('im_reply_all.gif', 'reply_to_all', 'reply_to_all', 'align="middle"'); + $forward_button = create_button('quote.gif', 'reply_quote', 'reply_quote', 'align="middle"'); + $delete_button = create_button('delete.gif', 'remove_message', 'remove', 'align="middle"'); + + while ($message = $context['get_pmessage']('message')) + { + $is_first_post = !isset($is_first_post) ? true : false; + + // Show information about the poster of this message. + echo ' +
    +
    +
    +

    ', $message['member']['link'], '

    +
      '; + + // Show the member's custom title, if they have one. + if (isset($message['member']['title']) && $message['member']['title'] != '') + echo ' +
    • ', $message['member']['title'], '
    • '; + + // Show the member's primary group (like 'Administrator') if they have one. + if (isset($message['member']['group']) && $message['member']['group'] != '') + echo ' +
    • ', $message['member']['group'], '
    • '; + + // Don't show these things for guests. + if (!$message['member']['is_guest']) + { + // Show the post group if and only if they have no other group or the option is on, and they are in a post group. + if ((empty($settings['hide_post_group']) || $message['member']['group'] == '') && $message['member']['post_group'] != '') + echo ' +
    • ', $message['member']['post_group'], '
    • '; + echo ' +
    • ', $message['member']['group_stars'], '
    • '; + + // Is karma display enabled? Total or +/-? + if ($modSettings['karmaMode'] == '1') + echo ' +
    • ', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '
    • '; + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    • ', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '
    • '; + + // Is this user allowed to modify this member's karma? + if ($message['member']['karma']['allow']) + echo ' +
    • + ', $modSettings['karmaApplaudLabel'], ' + ', $modSettings['karmaSmiteLabel'], ' +
    • '; + + // Show online and offline buttons? + if (!empty($modSettings['onlineEnable'])) + echo ' +
    • ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $message['member']['online']['text'] . '' : $message['member']['online']['text'], $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? ' ' . $message['member']['online']['text'] . '' : '', '
    • '; + + // Show the member's gender icon? + if (!empty($settings['show_gender']) && $message['member']['gender']['image'] != '' && !isset($context['disabled_fields']['gender'])) + echo ' +
    • ', $txt['gender'], ': ', $message['member']['gender']['image'], '
    • '; + + // Show how many posts they have made. + if (!isset($context['disabled_fields']['posts'])) + echo ' +
    • ', $txt['member_postcount'], ': ', $message['member']['posts'], '
    • '; + + // Any custom fields for standard placement? + if (!empty($message['member']['custom_fields'])) + { + foreach ($message['member']['custom_fields'] as $custom) + if (empty($custom['placement']) && !empty($custom['value'])) + echo ' +
    • ', $custom['title'], ': ', $custom['value'], '
    • '; + } + + // Show avatars, images, etc.? + if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image'])) + echo ' +
    • ', $message['member']['avatar']['image'], '
    • '; + + // Show their personal text? + if (!empty($settings['show_blurb']) && $message['member']['blurb'] != '') + echo ' +
    • ', $message['member']['blurb'], '
    • '; + + // Any custom fields to show as icons? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 1 || empty($custom['value'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    • +
        '; + } + echo ' +
      • ', $custom['value'], '
      • '; + } + if ($shown) + echo ' +
      +
    • '; + } + + // This shows the popular messaging icons. + if ($message['member']['has_messenger'] && $message['member']['can_view_profile']) + echo ' +
    • +
        + ', !isset($context['disabled_fields']['icq']) && !empty($message['member']['icq']['link']) ? '
      • ' . $message['member']['icq']['link'] . '
      • ' : '', ' + ', !isset($context['disabled_fields']['msn']) && !empty($message['member']['msn']['link']) ? '
      • ' . $message['member']['msn']['link'] . '
      • ' : '', ' + ', !isset($context['disabled_fields']['aim']) && !empty($message['member']['aim']['link']) ? '
      • ' . $message['member']['aim']['link'] . '
      • ' : '', ' + ', !isset($context['disabled_fields']['yim']) && !empty($message['member']['yim']['link']) ? '
      • ' . $message['member']['yim']['link'] . '
      • ' : '', ' +
      +
    • '; + + // Show the profile, website, email address, and personal message buttons. + if ($settings['show_profile_buttons']) + { + echo ' +
    • + +
    • '; + } + + // Are we showing the warning status? + if ($message['member']['can_see_warning']) + echo ' +
    • ', $context['can_issue_warning'] ? '' : '', '', $txt['user_warn_' . $message['member']['warning_status']], '', $context['can_issue_warning'] ? '' : '', '', $txt['warn_' . $message['member']['warning_status']], '
    • '; + } + + // Done with the information about the poster... on to the post itself. + echo ' +
    +
    +
    +
    +
    +
    + ', $message['subject'], ' +
    '; + + // Show who the message was sent to. + echo ' +
    + « ', $txt['sent_to'], ': '; + + // People it was sent directly to.... + if (!empty($message['recipients']['to'])) + echo implode(', ', $message['recipients']['to']); + // Otherwise, we're just going to say "some people"... + elseif ($context['folder'] != 'sent') + echo '(', $txt['pm_undisclosed_recipients'], ')'; + + echo ' + ', $txt['on'], ': ', $message['time'], ' » +
    '; + + // If we're in the sent items, show who it was sent to besides the "To:" people. + if (!empty($message['recipients']['bcc'])) + echo ' +
    « ', $txt['pm_bcc'], ': ', implode(', ', $message['recipients']['bcc']), ' »
    '; + + if (!empty($message['is_replied_to'])) + echo ' +
    « ', $txt['pm_is_replied_to'], ' »
    '; + + echo ' +
    +
      '; + + // Show reply buttons if you have the permission to send PMs. + if ($context['can_send_pm']) + { + // You can't really reply if the member is gone. + if (!$message['member']['is_guest']) + { + // Were than more than one recipient you can reply to? (Only shown when not in conversation mode.) + if ($message['number_recipients'] > 1 && $context['display_mode'] != 2) + echo ' +
    • ', $reply_all_button, '
    • '; + + echo ' +
    • ', $reply_button, '
    • +
    • ', $quote_button, '
    • '; + } + // This is for "forwarding" - even if the member is gone. + else + echo ' +
    • ', $forward_button, '
    • '; + } + echo ' +
    • ', $delete_button, '
    • '; + + if (empty($context['display_mode'])) + echo ' +
    • '; + + echo ' +
    +
    +
    +
    + ', $message['body'], ' +
    '; + + if (!empty($modSettings['enableReportPM']) && $context['folder'] != 'sent') + echo ' + '; + + // Are there any custom profile fields for above the signature? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 2 || empty($custom['value'])) + continue; + if (!$shown) + { + $shown = true; + echo ' +
    +
      ', $message['member']['signature'], '
    '; + + // Add an extra line at the bottom if we have labels enabled. + if ($context['folder'] != 'sent' && !empty($context['currently_using_labels']) && $context['display_mode'] != 2) + { + echo ' +
    '; + + // Add the label drop down box. + if (!empty($context['currently_using_labels'])) + { + echo ' + + '; + } + + echo ' +
    '; + } + + echo ' +
    +
    +
    '; + } + + echo ' +
    '; + + if (empty($context['display_mode'])) + echo ' +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
    +
    '; + + // Show a few buttons if we are in conversation mode and outputting the first message. + elseif ($context['display_mode'] == 2 && isset($conversation_buttons)) + template_button_strip($conversation_buttons); + + echo ' +
    '; + } + + // Individual messages = buttom list! + if ($context['display_mode'] == 1) + { + template_subject_list(); + echo '
    '; + } + + echo ' + +
    '; +} + +// Just list all the personal message subjects - to make templates easier. +function template_subject_list() +{ + global $context, $options, $settings, $modSettings, $txt, $scripturl; + + echo ' + + + + + + + + '; + + if (!$context['show_delete']) + echo ' + + + '; + + $next_alternate = 0; + while ($message = $context['get_pmessage']('subject')) + { + echo ' + + + + + + + '; + $next_alternate = !$next_alternate; + } + + echo ' +
    ', $txt['pm_change_view'], '', $txt['date'], $context['sort_by'] == 'date' ? ' ' : '', '', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', '', ($context['from_or_to'] == 'from' ? $txt['from'] : $txt['to']), $context['sort_by'] == 'name' ? ' ' : '', '
    ', $txt['msg_alert_none'], '
    + + ', $message['is_replied_to'] ? '' . $txt['pm_replied'] . '' : '' . $txt['pm_read'] . '', '', $message['time'], '', ($context['display_mode'] != 0 && $context['current_pm'] == $message['id'] ? '*' : ''), '', $message['subject'], '', $message['is_unread'] ? ' ' . $txt['new'] . '' : '', '', ($context['from_or_to'] == 'from' ? $message['member']['link'] : (empty($message['recipients']['to']) ? '' : implode(', ', $message['recipients']['to']))), '
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
     '; + + if ($context['show_delete']) + { + if (!empty($context['currently_using_labels']) && $context['folder'] != 'sent') + { + echo ' + + '; + } + + echo ' + '; + } + + echo ' +
    +
    '; +} + +function template_search() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' + +
    +
    +

    ', $txt['pm_search_title'], '

    +
    '; + + if (!empty($context['search_errors'])) + { + echo ' +
    + ', implode('
    ', $context['search_errors']['messages']), ' +
    '; + } + + if ($context['simple_search']) + { + echo ' + '; + } + + // Advanced search! + else + { + echo ' + '; + + // Do we have some labels setup? If so offer to search by them! + if ($context['currently_using_labels']) + { + echo ' +
    + +
    + +
      '; + + foreach ($context['search_labels'] as $label) + echo ' +
    • + +
    • '; + + echo ' +
    +

    + +

    +
    + +
    '; + } + + echo ' +
    + +
    '; + } + + echo ' + +
    '; +} + +function template_search_results() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // This splits broadly into two types of template... complete results first. + if (!empty($context['search_params']['show_complete'])) + { + echo ' + + + + + + + +
    ', $txt['pm_search_results'], '
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + } + else + { + echo ' + + + + + + + + + + + + '; + } + + $alternate = true; + // Print each message out... + foreach ($context['personal_messages'] as $message) + { + // We showing it all? + if (!empty($context['search_params']['show_complete'])) + { + // !!! This still needs to be made pretty. + echo ' +
    +
    ', $txt['pm_search_results'], '
    ', $txt['pages'], ': ', $context['page_index'], '
    ', $txt['date'], '', $txt['subject'], '', $txt['from'], '
    + + + + + + + + + + + + +
    +
    + ', $message['counter'], '  ', $message['subject'], ' +
    +
    + ', $txt['search_on'], ': ', $message['time'], ' +
    +
    ', $txt['from'], ': ', $message['member']['link'], ', ', $txt['to'], ': '; + + // Show the recipients. + // !!! This doesn't deal with the sent item searching quite right for bcc. + if (!empty($message['recipients']['to'])) + echo implode(', ', $message['recipients']['to']); + // Otherwise, we're just going to say "some people"... + elseif ($context['folder'] != 'sent') + echo '(', $txt['pm_undisclosed_recipients'], ')'; + + echo ' +
    ', $message['body'], '
    '; + + if ($context['can_send_pm']) + { + $quote_button = create_button('quote.gif', 'reply_quote', 'reply_quote', 'align="middle"'); + $reply_button = create_button('im_reply.gif', 'reply', 'reply', 'align="middle"'); + + // You can only reply if they are not a guest... + if (!$message['member']['is_guest']) + echo ' + ', $quote_button , '', $context['menu_separator'], ' + ', $reply_button , ' ', $context['menu_separator']; + // This is for "forwarding" - even if the member is gone. + else + echo ' + ', $quote_button , '', $context['menu_separator']; + } + + echo ' +
    '; + } + // Otherwise just a simple list! + else + { + // !!! No context at all of the search? + echo ' + + ', $message['time'], ' + ', $message['link'], ' + ', $message['member']['link'], ' + '; + } + + $alternate = !$alternate; + } + + // Finish off the page... + if (!empty($context['search_params']['show_complete'])) + { + // No results? + if (empty($context['personal_messages'])) + echo ' + + + + +
    ', $txt['pm_search_none_found'], '
    '; + else + echo ' +
    '; + + echo ' + + + + +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + } + else + { + if (empty($context['personal_messages'])) + echo ' + + ', $txt['pm_search_none_found'], ' + '; + + echo ' + + ', $txt['pages'], ': ', $context['page_index'], ' + + '; + } +} + +function template_send() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // Show which messages were sent successfully and which failed. + if (!empty($context['send_log'])) + { + echo ' +
    +

    ', $txt['pm_send_report'], '

    +
    +
    + +
    '; + if (!empty($context['send_log']['sent'])) + foreach ($context['send_log']['sent'] as $log_entry) + echo '', $log_entry, '
    '; + if (!empty($context['send_log']['failed'])) + foreach ($context['send_log']['failed'] as $log_entry) + echo '', $log_entry, '
    '; + echo ' +
    + +
    +
    '; + } + + // Show the preview of the personal message. + if (isset($context['preview_message'])) + echo ' +
    +

    ', $context['preview_subject'], '

    +
    +
    + +
    + ', $context['preview_message'], ' +
    + +
    +
    '; + + // Main message editing box. + echo ' +
    +

    + ', $txt['new_message'], ' ', $txt['new_message'], ' +

    +
    '; + + echo ' +
    +
    + +
    '; + + // If there were errors for sending the PM, show them. + if (!empty($context['post_error']['messages'])) + { + echo ' +
    + ', $txt['error_while_submitting'], ' +
      '; + + foreach ($context['post_error']['messages'] as $error) + echo ' +
    • ', $error, '
    • '; + + echo ' +
    +
    '; + } + + echo ' +
    '; + + // To and bcc. Include a button to search for members. + echo ' +
    + ', $txt['pm_to'], ': +
    '; + + // Autosuggest will be added by the JavaScript later on. + echo ' +
    + '; + + // A link to add BCC, only visible with JavaScript enabled. + echo ' + '; + + // A div that'll contain the items found by the autosuggest. + echo ' +
    '; + + echo ' +
    '; + + // This BCC row will be hidden by default if JavaScript is enabled. + echo ' +
    + ', $txt['pm_bcc'], ': +
    +
    + +
    +
    '; + + // The subject of the PM. + echo ' +
    + ', $txt['subject'], ': +
    +
    + +
    +
    '; + + // Showing BBC? + if ($context['show_bbc']) + { + echo ' +
    '; + } + + // What about smileys? + if (!empty($context['smileys']['postform']) || !empty($context['smileys']['popup'])) + echo ' +
    '; + + // Show BBC buttons, smileys and textbox. + echo ' + ', template_control_richedit($context['post_box_name'], 'smileyBox_message', 'bbcBox_message'); + + // Require an image to be typed to save spamming? + if ($context['require_verification']) + { + echo ' +
    + ', $txt['pm_visual_verification_label'], ': + ', template_control_verification($context['visual_verification_id'], 'all'), ' +
    '; + } + + // Send, Preview, spellcheck buttons. + echo ' +

    +

    + ', $context['browser']['is_firefox'] ? $txt['shortcuts_firefox'] : $txt['shortcuts'], ' +

    +

    + ', template_control_richedit_buttons($context['post_box_name']), ' +

    + + + + + + +
    + +
    +
    '; + + // Show the message you're replying to. + if ($context['reply']) + echo ' +
    +
    +
    +

    ', $txt['subject'], ': ', $context['quoted_message']['subject'], '

    +
    +
    +

    + ', $txt['from'], ': ', $context['quoted_message']['member']['name'], ' + ', $txt['on'], ': ', $context['quoted_message']['time'], ' +

    +
    +
    + +
    + ', $context['quoted_message']['body'], ' +
    + +
    '; + + echo ' + + + '; +} + +// This template asks the user whether they wish to empty out their folder/messages. +function template_ask_delete() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    ', ($context['delete_all'] ? $txt['delete_message'] : $txt['delete_all']), '

    +
    +
    + +
    +

    ', $txt['delete_all_confirm'], '


    + ', $txt['yes'], ' - ', $txt['no'], ' +
    + +
    '; +} + +// This template asks the user what messages they want to prune. +function template_prune() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['pm_prune'], '

    +
    +
    + +
    +

    ', $txt['pm_prune_desc1'], ' ', $txt['pm_prune_desc2'], '

    +
    + +
    +
    + +
    + +
    '; +} + +// Here we allow the user to setup labels, remove labels and change rules for labels (i.e, do quite a bit) +function template_labels() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['pm_manage_labels'], '

    +
    +
    + ', $txt['pm_labels_desc'], ' +
    + + + + + + + '; + if (count($context['labels']) < 2) + echo ' + + + '; + else + { + $alternate = true; + foreach ($context['labels'] as $label) + { + if ($label['id'] == -1) + continue; + + echo ' + + + + '; + + $alternate = !$alternate; + } + } + echo ' + +
    +
    + ', $txt['pm_label_name'], ' +
    ', $txt['pm_labels_no_exist'], '
    + +
    '; + + if (!count($context['labels']) < 2) + echo ' +
    + + +
    '; + + echo ' + +
    +
    +
    +

    ', $txt['pm_label_add_new'], '

    +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    +
    + +
    +
    + +
    + +
    '; +} + +// Template for reporting a personal message. +function template_report_message() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    + +
    +

    ', $txt['pm_report_title'], '

    +
    +
    + ', $txt['pm_report_desc'], ' +
    +
    + +
    +
    '; + + // If there is more than one admin on the forum, allow the user to choose the one they want to direct to. + // !!! Why? + if ($context['admin_count'] > 1) + { + echo ' +
    + ', $txt['pm_report_admins'], ': +
    +
    + +
    '; + } + + echo ' +
    + ', $txt['pm_report_reason'], ': +
    +
    + +
    +
    + +
    + +
    + +
    '; +} + +// Little template just to say "Yep, it's been submitted" +function template_report_message_complete() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +

    ', $txt['pm_report_title'], '

    +
    +
    + +
    +

    ', $txt['pm_report_done'], '

    + ', $txt['pm_report_return'], ' +
    + +
    '; +} + +// Manage rules. +function template_rules() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['pm_manage_rules'], '

    +
    +
    + ', $txt['pm_manage_rules_desc'], ' +
    + + + + + + + + '; + + if (empty($context['rules'])) + echo ' + + + '; + + $alternate = false; + foreach ($context['rules'] as $rule) + { + echo ' + + + + '; + $alternate = !$alternate; + } + + echo ' + +
    + ', $txt['pm_rule_title'], ' + '; + + if (!empty($context['rules'])) + echo ' + '; + + echo ' +
    + ', $txt['pm_rules_none'], ' +
    + ', $rule['name'], ' + + +
    +
    + [', $txt['pm_add_rule'], ']'; + + if (!empty($context['rules'])) + echo ' + [', $txt['pm_apply_rules'], ']'; + + if (!empty($context['rules'])) + echo ' + + '; + + echo ' +
    +
    '; + +} + +// Template for adding/editing a rule. +function template_add_rule() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' + '; + + echo ' +
    +
    +

    ', $context['rid'] == 0 ? $txt['pm_add_rule'] : $txt['pm_edit_rule'], '

    +
    +
    + +
    +
    +
    + ', $txt['pm_rule_name'], ':
    + ', $txt['pm_rule_name_desc'], ' +
    +
    + +
    +
    +
    + ', $txt['pm_rule_criteria'], ''; + + // Add a dummy criteria to allow expansion for none js users. + $context['rule']['criteria'][] = array('t' => '', 'v' => ''); + + // For each criteria print it out. + $isFirst = true; + foreach ($context['rule']['criteria'] as $k => $criteria) + { + if (!$isFirst && $criteria['t'] == '') + echo '
    '; + else + echo '
    '; + + echo ' + + + + + + + '; + + // If this is the dummy we add a means to hide for non js users. + if ($isFirst) + $isFirst = false; + elseif ($criteria['t'] == '') + echo '
    '; + } + + echo ' +
    + +

    + ', $txt['pm_rule_logic'], ': + +
    +
    + ', $txt['pm_rule_actions'], ''; + + // As with criteria - add a dummy action for "expansion". + $context['rule']['actions'][] = array('t' => '', 'v' => ''); + + // Print each action. + $isFirst = true; + foreach ($context['rule']['actions'] as $k => $action) + { + if (!$isFirst && $action['t'] == '') + echo '
    '; + else + echo '
    '; + + echo ' + + + + '; + + if ($isFirst) + $isFirst = false; + elseif ($action['t'] == '') + echo ' +
    '; + } + + echo ' +
    + +
    +
    + +
    +
    +

    ', $txt['pm_rule_description'], '

    +
    +
    +
    ', $txt['pm_rule_js_disabled'], '
    +
    +
    + + +
    +
    '; + + // Now setup all the bits! + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/Recent.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/Recent.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,423 @@ + +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
    '; + + foreach ($context['posts'] as $post) + { + // This is far from ideal, but oh well - create buttons for the post. + $button_set = array(); + + if ($post['can_delete']) + $button_set['delete'] = array('text' => 'remove', 'image' => 'delete.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['remove_message'] . '?\');"', 'url' => $scripturl . '?action=deletemsg;msg=' . $post['id'] . ';topic=' . $post['topic'] . ';recent;' . $context['session_var'] . '=' . $context['session_id']); + if ($post['can_reply']) + $button_set['reply'] = array('text' => 'reply', 'image' => 'reply_sm.gif', 'lang' => true, 'url' => $scripturl . '?action=post;topic=' . $post['topic'] . '.' . $post['start']); + if ($post['can_quote']) + $button_set['quote'] = array('text' => 'reply_quote', 'image' => 'quote.gif', 'lang' => true, 'url' => $scripturl . '?action=post;topic=' . $post['topic'] . '.' . $post['start'] . ';quote=' . $post['id']); + if ($post['can_mark_notify']) + $button_set['notify'] = array('text' => 'notify_replies', 'image' => 'notify_sm.gif', 'lang' => true, 'url' => $scripturl . '?action=notify;topic=' . $post['topic'] . '.' . $post['start']); + + echo ' + + + + + + + + + + '; + + // Are we using tabs? + if (!empty($settings['use_tabs'])) + { + echo ' +
    +
     ', $post['counter'], ' 
    +
     ', $post['category']['link'], ' / ', $post['board']['link'], ' / ', $post['link'], '
    +
     ', $txt['on'], ': ', $post['time'], ' 
    +
    + ', $txt['started_by'], ' ' . $post['first_poster']['link'] . ' - ' . $txt['last_post'] . ' ' . $txt['by'] . ' ' . $post['poster']['link'] . ' +
    +
    ' . $post['message'] . '
    +
    '; + + if (!empty($button_set)) + echo ' +
    + ', template_button_strip($button_set, 'top'), ' +
    '; + } + else + { + if (!empty($button_set)) + echo ' + + +
    + ', template_button_strip($button_set, 'top'), ' +
    + + '; + + echo ' + '; + } + + echo ' +
    '; + } + + echo ' +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
    + '; +} + +function template_unread() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + $showCheckboxes = !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $settings['show_mark_read']; + + if ($showCheckboxes) + echo ' +
    +
    + + + '; + + if ($settings['show_mark_read']) + { + // Generate the button strip. + $mark_read = array( + 'markread' => array('text' => !empty($context['no_board_limits']) ? 'mark_as_read' : 'mark_read_short', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=' . (!empty($context['no_board_limits']) ? 'all' : 'board' . $context['querystring_board_limits']) . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + if ($showCheckboxes) + $mark_read['markselectread'] = array( + 'text' => 'quick_mod_markread', + 'image' => 'markselectedread.gif', + 'lang' => true, + 'url' => 'javascript:document.quickModForm.submit();', + ); + } + + echo ' +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + + if (!empty($mark_read) && !empty($settings['use_tabs'])) + template_button_strip($mark_read, 'bottom'); + + echo ' +
    '; + + echo ' + + +
    + + '; + if (!empty($context['topics'])) + { + echo ' + + '; + if ($showCheckboxes) + echo ' + '; + } + else + echo ' + '; + echo ' + '; + + foreach ($context['topics'] as $topic) + { + // Do we want to separate the sticky and lock status out? + if (!empty($settings['separate_sticky_lock']) && strpos($topic['class'], 'sticky') !== false) + $topic['class'] = substr($topic['class'], 0, strrpos($topic['class'], '_sticky')); + if (!empty($settings['separate_sticky_lock']) && strpos($topic['class'], 'locked') !== false) + $topic['class'] = substr($topic['class'], 0, strrpos($topic['class'], '_locked')); + + echo ' + + + + + + '; + if ($showCheckboxes) + echo ' + '; + + echo ' + '; + } + + if (!empty($context['topics']) && !$context['showing_all_topics']) + echo ' + + + '; + + if (empty($settings['use_tabs']) && !empty($mark_read)) + echo ' + + + '; + + echo ' +
      + ', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', ' + + ', $txt['started_by'], $context['sort_by'] == 'starter' ? ' ' : '', ' + + ', $txt['replies'], $context['sort_by'] == 'replies' ? ' ' : '', ' + + ', $txt['views'], $context['sort_by'] == 'views' ? ' ' : '', ' + + ', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', ' + + + ', $context['showing_all_topics'] ? $txt['msg_alert_none'] : $txt['unread_topics_visit_none'], '
    + + + + ', $topic['is_locked'] && !empty($settings['separate_sticky_lock']) ? ' + ' : '', $topic['is_sticky'] && !empty($settings['separate_sticky_lock']) ? ' + ' : '', $topic['first_post']['link'], ' ', $txt['new'], ' ', $topic['pages'], ' ', $txt['in'], ' ', $topic['board']['link'], ' + ', $topic['first_post']['member']['link'], ' + ', $topic['replies'], ' + ', $topic['views'], ' + ', $txt['last_post'], ' + + ', $topic['last_post']['time'], '
    + ', $txt['by'], ' ', $topic['last_post']['member']['link'], ' +
    +
    + +
    ', $txt['unread_topics_all'], '
    +
    + ', template_button_strip($mark_read, 'top'), ' +
    +
    +
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + + if (!empty($settings['use_tabs']) && !empty($mark_read)) + template_button_strip($mark_read, 'top'); + + echo ' +
    +
    '; + + if ($showCheckboxes) + echo ' +
    '; + + echo ' +
    +
    +
    +
      + ', !empty($modSettings['enableParticipation']) && $context['user']['is_logged'] ? ' +
    • ' . $txt['participation_caption'] . '
    • ' : '', ' +
    • ' . $txt['normal_topic'] . '
    • +
    • ' . sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']) . '
    • +
    • ' . sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']) . '
    • +
    +
    +
    +
      +
    • ' . $txt['locked_topic'] . '
    • ' . ($modSettings['enableStickyTopics'] == '1' ? ' +
    • ' . $txt['sticky_topic'] . '
    • ' : '') . ($modSettings['pollMode'] == '1' ? ' +
    • ' . $txt['poll'] : '') . '
    • +
    +
    +
    +
    '; +} + +function template_replies() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + $showCheckboxes = !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $settings['show_mark_read']; + + if ($showCheckboxes) + echo ' +
    +
    + + + '; + + if (isset($context['topics_to_mark']) && !empty($settings['show_mark_read'])) + { + // Generate the button strip. + $mark_read = array( + 'markread' => array('text' => 'mark_as_read', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=unreadreplies;topics=' . $context['topics_to_mark'] . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + if ($showCheckboxes) + $mark_read['markselectread'] = array( + 'text' => 'quick_mod_markread', + 'image' => 'markselectedread.gif', + 'lang' => true, + 'url' => 'javascript:document.quickModForm.submit();', + ); + } + if (!empty($settings['use_tabs'])) + { + echo ' +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + if (!empty($mark_read)) + template_button_strip($mark_read, 'bottom'); + + echo ' +
    '; + } + + echo ' + + +
    + + '; + if (!empty($context['topics'])) + { + echo ' + + + + + + '; + if ($showCheckboxes) + echo ' + '; + } + else + echo ' + '; + echo ' + '; + + foreach ($context['topics'] as $topic) + { + // separate lock and sticky again? + if (!empty($settings['separate_sticky_lock']) && strpos($topic['class'], 'sticky') !== false) + $topic['class'] = substr($topic['class'], 0, strrpos($topic['class'], '_sticky')); + if (!empty($settings['separate_sticky_lock']) && strpos($topic['class'], 'locked') !== false) + $topic['class'] = substr($topic['class'], 0, strrpos($topic['class'], '_locked')); + + echo ' + + + + + + + + '; + if ($showCheckboxes) + echo ' + '; + + echo ' + '; + } + if (empty($settings['use_tabs']) && !empty($mark_read)) + echo ' + + + '; + + echo ' +
     ', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', '', $txt['started_by'], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt['replies'], $context['sort_by'] == 'replies' ? ' ' : '', '', $txt['views'], $context['sort_by'] == 'views' ? ' ' : '', '', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', ' + + ' . $txt['msg_alert_none'] . '
    + + + ', $topic['is_locked'] && !empty($settings['separate_sticky_lock']) ? '' : '', ' + ', $topic['is_sticky'] && !empty($settings['separate_sticky_lock']) ? '' : '', ' ', $topic['first_post']['link'], ' ', $txt['new'], ' ', $topic['pages'], ' + ', $txt['in'], ' ', $topic['board']['link'], ' + ', $topic['first_post']['member']['link'], ' + ', $topic['replies'], ' + ', $topic['views'], ' + ', $txt['last_post'], ' + + ', $topic['last_post']['time'], '
    + ', $txt['by'], ' ', $topic['last_post']['member']['link'], ' +
    +
    + +
    +
    + ', template_button_strip($mark_read, 'top'), ' +
    +
    +
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + + if (!empty($settings['use_tabs']) && !empty($mark_read)) + template_button_strip($mark_read, 'top'); + + echo ' +
    +
    '; + + if ($showCheckboxes) + echo ' +
    '; + + echo ' +
    +
    +
    +
      + ', !empty($modSettings['enableParticipation']) && $context['user']['is_logged'] ? ' +
    • ' . $txt['participation_caption'] . '
    • ' : '', ' +
    • ' . $txt['normal_topic'] . '
    • +
    • ' . sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']) . '
    • +
    • ' . sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']) . '
    • +
    +
    +
    +
      +
    • ' . $txt['locked_topic'] . '
    • ' . ($modSettings['enableStickyTopics'] == '1' ? ' +
    • ' . $txt['sticky_topic'] . '
    • ' : '') . ($modSettings['pollMode'] == '1' ? ' +
    • ' . $txt['poll'] : '') . '
    • +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/Settings.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/Settings.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,272 @@ + 'show_board_desc', + 'label' => $txt['board_desc_inside'], + 'default' => true, + ), + array( + 'id' => 'show_children', + 'label' => $txt['show_children'], + 'default' => true, + ), + array( + 'id' => 'use_sidebar_menu', + 'label' => $txt['use_sidebar_menu'], + 'default' => true, + ), + array( + 'id' => 'show_no_avatars', + 'label' => $txt['show_no_avatars'], + 'default' => true, + ), + array( + 'id' => 'show_no_signatures', + 'label' => $txt['show_no_signatures'], + 'default' => true, + ), + array( + 'id' => 'show_no_censored', + 'label' => $txt['show_no_censored'], + 'default' => true, + ), + array( + 'id' => 'return_to_post', + 'label' => $txt['return_to_post'], + 'default' => true, + ), + array( + 'id' => 'no_new_reply_warning', + 'label' => $txt['no_new_reply_warning'], + 'default' => true, + ), + array( + 'id' => 'view_newest_first', + 'label' => $txt['recent_posts_at_top'], + 'default' => true, + ), + array( + 'id' => 'view_newest_pm_first', + 'label' => $txt['recent_pms_at_top'], + 'default' => true, + ), + array( + 'id' => 'posts_apply_ignore_list', + 'label' => $txt['posts_apply_ignore_list'], + 'default' => false, + ), + array( + 'id' => 'wysiwyg_default', + 'label' => $txt['wysiwyg_default'], + 'default' => false, + ), + array( + 'id' => 'popup_messages', + 'label' => $txt['popup_messages'], + 'default' => true, + ), + array( + 'id' => 'copy_to_outbox', + 'label' => $txt['copy_to_outbox'], + 'default' => true, + ), + array( + 'id' => 'pm_remove_inbox_label', + 'label' => $txt['pm_remove_inbox_label'], + 'default' => true, + ), + array( + 'id' => 'auto_notify', + 'label' => $txt['auto_notify'], + 'default' => true, + ), + array( + 'id' => 'topics_per_page', + 'label' => $txt['topics_per_page'], + 'options' => array( + 0 => $txt['per_page_default'], + 5 => 5, + 10 => 10, + 25 => 25, + 50 => 50, + ), + 'default' => true, + ), + array( + 'id' => 'messages_per_page', + 'label' => $txt['messages_per_page'], + 'options' => array( + 0 => $txt['per_page_default'], + 5 => 5, + 10 => 10, + 25 => 25, + 50 => 50, + ), + 'default' => true, + ), + array( + 'id' => 'calendar_start_day', + 'label' => $txt['calendar_start_day'], + 'options' => array( + 0 => $txt['days'][0], + 1 => $txt['days'][1], + 6 => $txt['days'][6], + ), + 'default' => true, + ), + array( + 'id' => 'display_quick_reply', + 'label' => $txt['display_quick_reply'], + 'options' => array( + 0 => $txt['display_quick_reply1'], + 1 => $txt['display_quick_reply2'], + 2 => $txt['display_quick_reply3'] + ), + 'default' => true, + ), + array( + 'id' => 'display_quick_mod', + 'label' => $txt['display_quick_mod'], + 'options' => array( + 0 => $txt['display_quick_mod_none'], + 1 => $txt['display_quick_mod_check'], + 2 => $txt['display_quick_mod_image'], + ), + 'default' => true, + ), + ); +} + +function template_settings() +{ + global $context, $settings, $options, $scripturl, $txt; + + $context['theme_settings'] = array( + array( + 'id' => 'header_logo_url', + 'label' => $txt['header_logo_url'], + 'description' => $txt['header_logo_url_desc'], + 'type' => 'text', + ), + array( + 'id' => 'smiley_sets_default', + 'label' => $txt['smileys_default_set_for_theme'], + 'options' => $context['smiley_sets'], + 'type' => 'text', + ), + array( + 'id' => 'forum_width', + 'label' => $txt['forum_width'], + 'description' => $txt['forum_width_desc'], + 'type' => 'text', + 'size' => 8, + ), + '', + array( + 'id' => 'show_mark_read', + 'label' => $txt['enable_mark_as_read'], + ), + array( + 'id' => 'allow_no_censored', + 'label' => $txt['allow_no_censored'], + ), + array( + 'id' => 'enable_news', + 'label' => $txt['enable_random_news'], + ), + array( + 'id' => 'use_image_buttons', + 'label' => $txt['admin_image_text'], + ), + '', + array( + 'id' => 'show_newsfader', + 'label' => $txt['news_fader'], + ), + array( + 'id' => 'newsfader_time', + 'label' => $txt['admin_fader_delay'], + 'type' => 'number', + ), + array( + 'id' => 'number_recent_posts', + 'label' => $txt['number_recent_posts'], + 'description' => $txt['number_recent_posts_desc'], + 'type' => 'number', + ), + array( + 'id' => 'show_stats_index', + 'label' => $txt['show_stats_index'], + ), + array( + 'id' => 'show_latest_member', + 'label' => $txt['latest_members'], + ), + array( + 'id' => 'show_group_key', + 'label' => $txt['show_group_key'], + ), + array( + 'id' => 'display_who_viewing', + 'label' => $txt['who_display_viewing'], + 'options' => array( + 0 => $txt['who_display_viewing_off'], + 1 => $txt['who_display_viewing_numbers'], + 2 => $txt['who_display_viewing_names'], + ), + 'type' => 'number', + ), + '', + array( + 'id' => 'show_modify', + 'label' => $txt['last_modification'], + ), + array( + 'id' => 'show_profile_buttons', + 'label' => $txt['show_view_profile_button'], + ), + array( + 'id' => 'show_user_images', + 'label' => $txt['user_avatars'], + ), + array( + 'id' => 'show_blurb', + 'label' => $txt['user_text'], + ), + array( + 'id' => 'show_gender', + 'label' => $txt['gender_images'], + ), + array( + 'id' => 'hide_post_group', + 'label' => $txt['hide_post_group'], + 'description' => $txt['hide_post_group_desc'], + ), + '', + array( + 'id' => 'show_bbc', + 'label' => $txt['admin_bbc'], + ), + array( + 'id' => 'additional_options_collapsable', + 'label' => $txt['additional_options_collapsable'], + ), + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/Stats.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/Stats.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,308 @@ + + + ', $context['page_title'], ' + + + ', $txt['general_stats'], ' + + + + + + + + + + + + + + + + + + + + + + + + + '; + if (!empty($modSettings['hitStats'])) + echo ' + + + '; + echo ' + +
    ', $txt['total_members'], ':', $context['show_member_list'] ? '' . $context['num_members'] . '' : $context['num_members'], '
    ', $txt['total_posts'], ':', $context['num_posts'], '
    ', $txt['total_topics'], ':', $context['num_topics'], '
    ', $txt['total_cats'], ':', $context['num_categories'], '
    ', $txt['users_online'], ':', $context['users_online'], '
    ', $txt['most_online'], ':', $context['most_members_online']['number'], ' - ', $context['most_members_online']['date'], '
    ', $txt['users_online_today'], ':', $context['online_today'], '
    ', $txt['num_hits'], ':', $context['num_hits'], '
    + + + + + + + + + + + + + + + + + + + + + + + + + '; + if (!empty($modSettings['hitStats'])) + echo ' + + + '; + echo ' + +
    ', $txt['average_members'], ':', $context['average_members'], '
    ', $txt['average_posts'], ':', $context['average_posts'], '
    ', $txt['average_topics'], ':', $context['average_topics'], '
    ', $txt['total_boards'], ':', $context['num_boards'], '
    ', $txt['latest_member'], ':', $context['common_stats']['latest_member']['link'], '
    ', $txt['average_online'], ':', $context['average_online'], '
    ', $txt['gender_ratio'], ':', $context['gender']['ratio'], '
    ', $txt['average_hits'], ':', $context['average_hits'], '
    + + + ', $txt['top_posters'], ' + ', $txt['top_boards'], ' + + + + '; + foreach ($context['top_posters'] as $poster) + echo ' + + + + + '; + echo ' +
    ', $poster['link'], '', $poster['num_posts'] > 0 ? '' : ' ', '', $poster['num_posts'], '
    + + + + '; + foreach ($context['top_boards'] as $board) + echo ' + + + + + '; + echo ' +
    ', $board['link'], '', $board['num_posts'] > 0 ? '' : ' ', '', $board['num_posts'], '
    + + + ', $txt['top_topics_replies'], ' + ', $txt['top_topics_views'], ' + + + + '; + foreach ($context['top_topics_replies'] as $topic) + echo ' + + + + + '; + echo ' +
    ', $topic['link'], '', $topic['num_replies'] > 0 ? '' : ' ', '', $topic['num_replies'], '
    + + + + '; + foreach ($context['top_topics_views'] as $topic) + echo ' + + + + + '; + echo ' +
    ', $topic['link'], '', $topic['num_views'] > 0 ? '' : ' ', '', $topic['num_views'], '
    + + + ', $txt['top_starters'], ' + ', $txt['most_time_online'], ' + + + + '; + foreach ($context['top_starters'] as $poster) + echo ' + + + + + '; + echo ' +
    ', $poster['link'], '', $poster['num_topics'] > 0 ? '' : ' ', '', $poster['num_topics'], '
    + + + + '; + foreach ($context['top_time_online'] as $poster) + echo ' + + + + + '; + echo ' +
    ', $poster['link'], '', $poster['time_online'] > 0 ? '' : ' ', '', $poster['time_online'], '
    + + + ', $txt['forum_history'], ' + + + '; + + if (!empty($context['yearly'])) + { + echo ' + + + + + + + '; + + if (!empty($modSettings['hitStats'])) + echo ' + '; + echo ' + '; + + foreach ($context['yearly'] as $id => $year) + { + echo ' + + + + + + '; + if (!empty($modSettings['hitStats'])) + echo ' + '; + echo ' + '; + + foreach ($year['months'] as $month) + { + echo ' + + + + + + '; + if (!empty($modSettings['hitStats'])) + echo ' + '; + echo ' + '; + + if ($month['expanded']) + { + foreach ($month['days'] as $day) + { + echo ' + + + + + + '; + if (!empty($modSettings['hitStats'])) + echo ' + '; + echo ' + '; + } + } + } + } + + echo ' +
    ', $txt['yearly_summary'], '', $txt['stats_new_topics'], '', $txt['stats_new_posts'], '', $txt['stats_new_members'], '', $txt['smf_stats_14'], '', $txt['page_views'], '
    + * ', $year['year'], ' + ', $year['new_topics'], '', $year['new_posts'], '', $year['new_members'], '', $year['most_members_online'], '', $year['hits'], '
    + ', $month['month'], ' ', $month['year'], ' + ', $month['new_topics'], '', $month['new_posts'], '', $month['new_members'], '', $month['most_members_online'], '', $month['hits'], '
    ', $day['year'], '-', $day['month'], '-', $day['day'], '', $day['new_topics'], '', $day['new_posts'], '', $day['new_members'], '', $day['most_members_online'], '', $day['hits'], '
    + + + + '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/css/ie6.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/css/ie6.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,63 @@ +/* special styles for IE6 */ + +.main_menu li.active a +{ + background: none; + padding-right: 0; +} +.main_menu li.active +{ + background: url(../images/maintab_active_last.gif) no-repeat bottom right; + padding-right: 8px; +} + +* html #poll_options ul.horizlist dl.options dd, * html #poll_options ul.horizlist dl.options dt +{ + margin: 0; + padding: 0; +} +/* the tabled definition lists */ +dl.settings dd, #creator dd, dl.stats dd, dl.register_form dd, #poll_options dl.options dd +{ + float: none; + width: auto; +} + +.modbuttons .buttonlist_bottom ul, .modbuttons .buttonlist ul, .floatright .buttonlist ul, .floatright .buttonlist_bottom ul, .readbuttons .buttonlist ul, .readbuttons .buttonlist_bottom ul +{ + float: right; +} + +/* Profile template */ +#detailedinfo div.content dl +{ + height: 0.1%; +} +.infocenter_section div.sectionbody +{ + height: 30px; +} + +#forumposts .postarea +{ + margin-left: 0; + margin-right: 0; + float: right; +} +.signature +{ + padding: 0 0 0.8em 0; +} +#quickReplyOptions form textarea +{ + width: 98%; +} +code.bbc_code +{ + white-space: normal; +} + +#ip_list li.header +{ + height: .1%; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/css/ie7.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/css/ie7.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,22 @@ +/* special styles for IE7 */ +/* the tabled definition lists */ +dl.settings dd, dl.stats dd, dl.register_form dd, #poll_options dl.options dd +{ + float: none; + width: auto; +} + +.signature +{ + padding: 0 0 0.8em 0; +} + +#quickReplyOptions form textarea +{ + width: 98%; +} + +code.bbc_code +{ + white-space: normal; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/css/index.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/css/index.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,3523 @@ +/* Styles for the general looks for the Core theme. +------------------------------------------------------- */ + +/* Normal, standard links. */ +a:link, a:visited +{ + color: #476c8e; + text-decoration: none; +} +a:hover +{ + text-decoration: underline; +} + +/* Tables should show empty cells. */ +table +{ + empty-cells: show; +} + +/* Set a fontsize that will look the same in all browsers. */ +body +{ + background: #e5e5e8; + font: 95%/90% Verdana, Helvetica, sans-serif; + margin: 0; + padding: 12px 0 4px 0; +} + +/* Help popups require a different styling of the body element. */ +body#help_popup +{ + width: auto; + padding: 1em; + min-width: 0; +} + +/* use dark grey for the text, leaving #000 for headers etc */ +body, td, th, tr +{ + color: #444; +} + +/* lets give all forms zero padding/margins */ +form +{ + padding: 0; + margin: 0; +} + +/* We can style the different types of input buttons to be uniform throughout different browsers and their color themes. + .button_submit - covers input[type=submit], input[type=button], button[type=submit] and button[type=button] in all browsers + .button_reset - covers input[type=reset] and button[type=reset] throughout all browsers + .input_check - covers input[type=checkbox] throughout all browsers + .input_radio - covers input[type=radio] throughout all browsers + .input_text - covers input[type=text] throughout all browsers + .input_file - covers input[type=file] throughout all browsers +*/ + +input, button, select, textarea +{ + font: 90%/105% verdana, Helvetica, sans-serif; + color: #000; +} + +/* The font size of textareas should be just a little bit larger. */ +textarea +{ + font: 100%/130% verdana, Helvetica, sans-serif; +} + +/* All input elements that are checkboxes or radio buttons shouldn't have a border around them. */ +input.input_check, input.input_radio +{ + border: none; + background: none; +} + +/* Standard horizontal rule.. ([hr], etc.) */ +hr, .hrcolor +{ + height: 1px; + border: 0; + color: #666; + background-color: #666; +} + +/* By default set the color on these tags as #000. */ +h1, h2, h3, h4, h5, h6 +{ + color: #000; + font-size: 1em; + margin: 0; + padding: 0; +} +.content fieldset +{ + border: 2px groove #fff; + padding: 1em; + margin: 0 0 0.3em 0; +} +/* No image should have a border when linked. */ +a img +{ + border: 0; +} + +/* Define strong as bold, and em as italics */ +strong +{ + font-weight: bold; +} + +em +{ + font-style: italic; +} +/* Alternative for u tag */ +.underline +{ + text-decoration: underline; +} + +/* Common classes for easy styling. +------------------------------------------------------- */ + +.floatright +{ + float: right; +} +.floatleft +{ + float: left; +} + +.flow_auto +{ + overflow: auto; +} +.flow_hidden +{ + overflow: hidden; +} +.clear +{ + clear: both; +} +.clear_left +{ + clear: left; +} +.clear_right +{ + clear: right; +} + +/* Default font sizes: small (8pt), normal (10pt), and large (14pt). */ +.smalltext, tr.smalltext th +{ + font-size: 0.85em; + font-family: verdana, sans-serif; +} +.middletext +{ + font-size: 0.9em; + font-family: verdana, sans-serif; +} +.normaltext +{ + font-size: 1em; + line-height: 1.2em; +} +.largetext +{ + font-size: 1.4em; +} +.centertext +{ + margin: 0 auto; + text-align: center; +} +.righttext +{ + margin-left: auto; + margin-right: 0; + text-align: right; +} +.lefttext +{ + margin-left: 0; + margin-right: auto; + text-align: left; +} +/* some common padding styles */ +.padding +{ + padding: 0.7em; +} +.main_section, .lower_padding +{ + padding-bottom: 0.5em; +} +/* a quick reset list class. */ +ul.reset, ul.reset li +{ + padding: 0; + margin: 0; + list-style: none; +} + +/* Some BBC related styles. +------------------------------------------------------- */ + +/* A quote, perhaps from another post. */ +blockquote.bbc_standard_quote, blockquote.bbc_alternate_quote +{ + color: #000; + border: 1px solid #000; + margin: 1px; + padding: 1px; + font-size: x-small; + line-height: 1.4em; + overflow: auto; +} + +/* Alterate block quote stylings */ +blockquote.bbc_standard_quote +{ + background-color: #d7daec; +} +blockquote.bbc_alternate_quote +{ + background-color: #e7eafc; +} + +/* A code block - maybe even PHP ;). */ +code.bbc_code +{ + display: block; + font-family: "dejavu sans mono", "monaco", "lucida console", "courier new", monospace; + font-size: x-small; + background: #eef; + border: 1px solid #000; + line-height: 1.3em; + padding: 1px; + overflow: auto; + white-space: nowrap; + /* Show a scrollbar after about 24 lines. */ + max-height: 24em; +} + +/* The "Quote:" and "Code:" header parts... */ +.codeheader, .quoteheader +{ + color: #000; + text-decoration: none; + font-style: normal; + font-weight: bold; + font-size: x-small; + line-height: 1.2em; + padding: 0 0.3em; +} + +/* For links to change the code stuff... */ +.codeoperation +{ + font-weight: normal; +} + +/* Styling for BBC tags */ +.bbc_size +{ + line-height: 1.4em; +} +.bbc_color a +{ + color: inherit; +} +.bbc_img +{ + border: 0; +} +.bbc_table +{ + font: inherit; + color: inherit; +} +.bbc_table td +{ + font: inherit; + color: inherit; + vertical-align: top; +} +.bbc_u +{ + text-decoration: underline; +} +.bbc_tt +{ + font-family: "dejavu sans mono", "monaco", "lucida console", "courier new", monospace; +} + +/* Generally, those [?] icons. This makes your cursor a help icon. */ +.help +{ + cursor: help; +} + +/* /me uses this a lot. (emote, try typing /me in a post.) */ +.meaction +{ + color: red; +} + +/* Highlighted text - such as search results. */ +.highlight +{ + background-color: #ff0; + font-weight: bold; + color: #000; +} + +/* A more discreet highlight color, for selected membergroups etc. */ +.highlight2 +{ + background-color: #D1E1EF; + color: #000; +} + +/* Generic, mostly color-related, classes. +------------------------------------------------------- */ + +.titlebg, .titlebg2, tr.titlebg td, tr.titlebg2 td +{ + color: #000; + font-family: Verdana, Helvetica, sans-serif; + font-weight: bold; + background: url(../images/titlebg.jpg) #E9F0F6 repeat-x; +} +.catbg, .catbg2, tr.catbg td, tr.catbg2 td, tr.catbg th, tr.catbg2 th +{ + color: #fff; + font-family: Verdana, Helvetica, sans-serif; + font-weight: bold; + background: url(../images/catbg.jpg) #88A6C0 repeat-x; +} +.catbg, .catbg2, tr.catbg td, tr.catbg2 td, tr.catbg th, tr.catbg2 th +{ + background: url(../images/catbg2.jpg) #A1BFD9 repeat-x; +} + +/* adjust the table versions of headers */ +tr.titlebg td, tr.titlebg2 td +{ + padding: 6px; +} +tr.catbg td, tr.catbg2 td, td.catbg, td.catbg2, tr.catbg th, tr.catbg2 th, th.catbg, th.catbg2 +{ + padding: 6px; +} +tr.titlebg td a, tr.titlebg2 td a +{ + color: #000; +} +tr.catbg td a, tr.catbg2 td a, .catbg a +{ + color: #fff; +} +tr.catbg th.smalltext +{ + font-size: 0.9em; +} +/* Alternating backgrounds for posts, and several other sections of the forum. */ +.windowbg, #preview_body, .content, .roundframe +{ + color: #000; + background-color: #ecedf3; +} +.windowbg2 +{ + color: #000; + background-color: #f6f6f6; +} +.windowbg3 +{ + color: #000; + background-color: #e0e1e8; +} + +/* the page navigation area */ +.pagesection +{ + font-size: 0.85em; + padding: 0.5em 0.2em; + overflow: hidden; +} +.pagesection .pagelinks +{ + padding: 0.5em 0; +} + +/* GenericList */ +table.table_grid thead tr.catbg th.smalltext +{ + white-space: nowrap; +} + +/* Color for background of posts requiring approval */ +.approvebg +{ + color: #000; + background-color: #f6e0d4; +} +/* Color for background of *topics* requiring approval */ +.approvetbg +{ + color: #000; + background-color: #e4a17c; +} +/* sticky posts have a different background */ +.stickybg +{ + background: #e8d8cf; +} +.stickybg2 +{ + background: #f2e3d9; +} +/* locked posts too! */ +.lockedbg +{ + background: #d4dce2; + font-style: italic; +} +.lockedbg2 +{ + background: #d8e1e7; + font-style: italic; +} + +/* Posts and personal messages displayed throughout the forum. */ +.post, .personalmessage +{ + width: 100%; + overflow: auto; + line-height: 1.4em; +} + +/* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ +.signature +{ + clear: right; + padding: 1em 0 3px 0; + width: 98%; + border-top: 1px solid #666; + line-height: 1.4em; + font-size: 0.85em; +} +.custom_fields_above_signature +{ + clear: right; + padding: 1em 0 3px 0; + width: 98%; + border-top: 1px solid #666; + line-height: 1.4em; + font-size: 0.85em; +} + +/* Sometimes there will be an error when you post */ +.error +{ + color: red; +} + +/* Messages that somehow need to attract the attention. */ +.alert +{ + color: red; +} + +/* Calendar colors for birthdays, events and holidays */ +.birthday +{ + color: #920ac4; +} + +.event +{ + color: #078907; +} + +.holiday +{ + color: #000080; +} + +/* Colors for warnings */ +.warn_mute +{ + color: red; +} + +.warn_moderate +{ + color: #ffa500; +} + +.warn_watch, .success +{ + color: green; +} + +a.moderation_link, a.moderation_link:visited +{ + color: red; + font-weight: bold; +} + +.openid_login +{ + background: white url(../images/openid.gif) no-repeat; + padding-left: 18px; +} + +/* a descriptive style */ +.description +{ + padding: 1em; + font-size: 0.9em; + line-height: 1.5em; + border: 1px solid #bbb; + background: #f5f5f0; + margin: 0 0 1em 0; +} +/* an informative style */ +.information +{ + padding: 1em; + font-size: 0.9em; + line-height: 1.5em; + border: 1px solid #bbb; + background: #f0f6f0; + margin: 0 0 1em 0; +} +.information p +{ + padding: 1em; + margin: 0; +} +/* AJAX notification bar +------------------------------------------------------- */ +#ajax_in_progress +{ + background: #32cd32; + color: #fff; + text-align: center; + font-weight: bold; + font-size: 18pt; + padding: 0.4em; + width: 100%; + position: fixed; + top: 0; + left: 0; +} + +#ajax_in_progress a +{ + color: #fff; + text-decoration: underline; + font-size: smaller; + float: right; +} + +/* a general table class */ +table.table_grid +{ + border-collapse: collapse; + border: 1px solid #adadad; +} +table.table_grid td +{ + padding: 3px; + border: 1px solid #adadad; +} + +/* Lists with settings use these a lot. +------------------------------------------------------- */ +dl.settings +{ + clear: right; + overflow: auto; + margin: 0 0 10px 0; + padding: 0; +} +dl.settings dt +{ + width: 48%; + float: left; + margin: 0 0 10px 0; + padding: 0; + clear: both; +} +dl.settings dt.settings_title +{ + width: 100%; + float: none; + margin: 0 0 10px 0; + padding: 5px 0 0 0; + font-weight: bold; + clear: both; +} +dl.settings dt.windowbg +{ + width: 98%; + float: left; + margin: 0 0 3px 0; + padding: 0 0 5px 0; + clear: both; +} +dl.settings dd +{ + width: 48%; + float: left; + overflow: auto; + margin: 0 0 3px 0; + padding: 0; +} +dl.settings img +{ + margin: 0 10px 0 0; +} + +/* The main content area. +------------------------------------------------------- */ +.content, .roundframe +{ + padding: 0.5em 1.2em; + margin: 0; + border: none; + border: 1px solid #adadad; +} +.content p, .roundframe p +{ + margin: 0 0 0.5em 0; +} + +/* Styles used by the auto suggest control. +------------------------------------------------------- */ +.auto_suggest_div +{ + border: 1px solid #000; + position: absolute; + visibility: hidden; +} +.auto_suggest_item +{ + background-color: #ddd; +} +.auto_suggest_item_hover +{ + background-color: #888; + cursor: pointer; + color: #eee; +} + +/* Styles for the standard dropdown menus. +------------------------------------------------------- */ +/* Container for the new admin menu */ +#adm_container +{ + float: left; + margin-left: 10px; + padding: 0 5px 0 5px; + background: url(../images/admintab_left.gif) no-repeat; +} + +ul.admin_menu, ul.admin_menu li ul +{ + margin: 0; + padding: 0; + list-style: none; +} + +ul.admin_menu +{ + background: url(../images/admintab_right.gif) top right no-repeat; +} + +ul.admin_menu a +{ + text-decoration: none; +} + +/* First layer of menu items */ +ul.admin_menu li +{ + position: relative; + float: left; + background: url(../images/admintab_back.gif) top right repeat-x; + padding-right: 4px; +} + +ul.admin_menu li.last +{ + background: url(../images/admintab_right.gif) top right repeat-x; +} + +ul.admin_menu li.chosen +{ + background: url(../images/admintab_active_left.gif) no-repeat; + padding: 0 0 0 6px; +} + +ul.admin_menu li h4 +{ + margin: 0; + padding: 7px 5px 3px 5px; + cursor: pointer; + font-weight: normal; + font-size: x-small; + text-transform: uppercase; + color: #fff; +} + +ul.admin_menu li.last.chosen h4 +{ + background: url(../images/admintab_active_last.gif) top right no-repeat; + padding-right: 17px; +} +/* IE6 does't support multiple class selectors */ +ul.admin_menu li.last_chosen h4 +{ + background: url(../images/admintab_active_last.gif) top right no-repeat; + padding-right: 17px; +} + +ul.admin_menu li.chosen h4 +{ + background: url(../images/admintab_active_right.gif) top right no-repeat; + padding-right: 10px; +} + +/* Second layer of menu items */ + +ul.admin_menu li ul +{ + z-index: 90; + display: none; + position: absolute; + /* IE6 needs a fixed width to prevent the menu from going haywire */ + width: 19em; + border: 1px solid #808080; + border-left: 2px solid #6888a7; + background: #f8f8fb; +} + +ul.admin_menu li.chosen ul +{ + margin: 0 0 0 -6px; +} + +ul.admin_menu li ul li +{ + background: none; + width: 19em; + padding: 0; +} + +ul.admin_menu li ul li a +{ + display: block; + padding: 0.5em 2em 0.5em 0.5em; + font-size: 90%; + text-decoration: none; + background: none; + color: #000 !important; +} + +ul.admin_menu li ul li a.subsection +{ + background: url(../images/admin/subsection.gif) no-repeat 98% 50%; +} + +ul.admin_menu li ul li a.chosen +{ + font-weight: bold; +} + +ul.admin_menu li ul li a:hover +{ + background-color: #c8e2fb; + text-decoration: none; +} + +ul.admin_menu li:hover ul, ul.admin_menu li.over ul +{ + display: block; +} + +/* Third layer of menu items */ +ul.admin_menu li ul li ul, ul.admin_menu li ul li.over ul +{ + display: none; + position: absolute; + top: -999em; + border: 1px solid #a0a0a0; + border-left: 2px solid #6888a7; + background: #fff; +} + +ul.admin_menu li ul li:hover ul, ul.admin_menu li ul li.over ul +{ + display: block; + left: 18em; + top: auto; + margin: -2em 0 0 1em; +} +#adm_submenus +{ + padding: 0 0 0 2em; +} +#adm_submenus, #adm_submenus ul +{ + height: 3em; + overflow: auto; +} + +/* The dropdown menu toggle image */ +div#menu_toggle +{ + float: right; + margin: 0 10px 0 0; + background: url(../images/mirrortab_first.gif) top left no-repeat; + padding: 0 0 0 7px; +} +div#menu_toggle a +{ + display: block; + background: #e5e5e8 url(../images/mirrortab_last.gif) top right no-repeat; + padding: 8px 12px 3px 6px; +} + +/* Styles for the standard button lists. +------------------------------------------------------- */ + +.buttonlist ul +{ + background: url(../images/maintab_first.gif) no-repeat scroll left bottom; + padding: 0 0 0 10px; +} +.buttonlist ul li, .buttonlist_bottom ul li +{ + display: inline; +} +.buttonlist ul li a, .buttonlist_bottom ul li a +{ + float: left; + display: block; + color: #fff; + font-size: 0.8em; + font-family: tahoma, sans-serif; + text-transform: uppercase; + text-decoration: none; +} +.buttonlist ul li a:hover, .buttonlist_bottom ul li a:hover +{ + color: #e0e0ff; +} +.buttonlist ul li a span +{ + background: url(../images/maintab_back.gif) repeat-x bottom left; + display: block; + padding: 0.1em 0.5em 0.5em 0.5em; +} +.buttonlist ul li.last a span +{ + background: url(../images/maintab_last.gif) no-repeat bottom right; + padding: 0.1em 1em 0.5em 0.5em; +} +.buttonlist ul li.active a span em +{ + padding: 0.1em 0.5em 0.5em 0.5em; + display: block; + font-style: normal; + background: url(../images/maintab_active_back.gif) repeat-x bottom right; +} +.buttonlist ul li.active a span +{ + background: url(../images/maintab_active_first.gif) no-repeat bottom left; + padding: 0 0 0 8px; +} +.buttonlist ul li.lastactive +{ + float: left; + background: url(../images/maintab_last.gif) no-repeat bottom right; + padding: 0 8px 0 0; +} +.buttonlist ul li.active a +{ + background: url(../images/maintab_active_last.gif) no-repeat bottom right; + padding-right: 8px; +} +/* For links that are basically submit buttons. */ +.buttonlist_submit +{ + background: transparent; + color: #fff; + text-transform: uppercase; + vertical-align: top; + text-decoration: none; + font-size: 9px; + font-family: tahoma, sans-serif; + border: 0; +} +.buttonlist_submit:hover +{ + color: #e0e0ff; +} +/* ..for the "bottom" menu */ +.buttonlist_bottom ul +{ + background: url(../images/mirrortab_first.gif) no-repeat scroll left top; + padding: 0 0 0 10px; +} +.buttonlist_bottom ul li a span +{ + background: url(../images/mirrortab_back.gif) repeat-x top left; + display: block; + padding: 0.4em 0.5em 0.2em 0.5em; +} +.buttonlist_bottom ul li.last a span +{ + background: url(../images/mirrortab_last.gif) no-repeat top right; + padding: 0.4em 1em 0.2em 0.5em; +} +.buttonlist_bottom ul li.active a span em +{ + padding: 0.4em 0.5em 0.2em 0.5em; + display: block; + font-style: normal; + background: url(../images/mirrortab_active_back.gif) repeat-x top right; +} +.buttonlist_bottom ul li.active a span +{ + background: url(../images/mirrortab_active_first.gif) no-repeat top left; + padding: 0 0 0 8px; +} +.buttonlist_bottom ul li.lastactive +{ + float: left; + background: url(../images/mirrortab_last.gif) no-repeat top right; + padding: 0 8px 0 0; +} +.buttonlist_bottom ul li.active a +{ + background: url(../images/mirrortab_active_last.gif) no-repeat top right; + padding-right: 8px; +} + +/* The old-style button strips, with images */ +.oldbuttonlist +{ + text-align: right; + padding: 0.5em; +} + +/* a smaller quick-button list */ +ul.quickbuttons +{ + margin: 0.9em 11px 0 0; + clear: right; + float: right; + text-align: right; +} +ul.quickbuttons li +{ + float: left; + display: inline; + margin: 0 0 0 11px; +} +ul.quickbuttons li a +{ + padding: 0 0 0.7em 20px; + display: block; + height: 20px; + font: bold 0.85em/18px arial, sans-serif; + float: left; +} +ul.quickbuttons li.quote_button +{ + background: url(../images/buttons/quote.gif) no-repeat 0 0; +} +ul.quickbuttons li.remove_button +{ + background: url(../images/buttons/delete.gif) no-repeat 0 0; +} +ul.quickbuttons li.modify_button +{ + background: url(../images/buttons/modify.gif) no-repeat 0 0; +} +ul.quickbuttons li.approve_button +{ + background: url(../images/buttons/approve.gif) no-repeat 0 0; +} +ul.quickbuttons li.restore_button +{ + background: url(../images/buttons/restore_topic.gif) no-repeat 0 0; +} +ul.quickbuttons li.split_button +{ + background: url(../images/buttons/split.gif) no-repeat 0 0; +} +ul.quickbuttons li.reply_button +{ + background: url(../images/buttons/reply.gif) no-repeat 0 0; +} +ul.quickbuttons li.reply_all_button +{ + background: url(../images/buttons/reply.gif) no-repeat 0 0; +} +ul.quickbuttons li.notify_button +{ + background: url(../images/buttons/notify_sm.gif) no-repeat 0 0; +} +ul.quickbuttons li.inline_mod_check +{ + margin: 0 0 0 5px; +} + +.generic_tab_strip +{ + margin: 0 1em 2em; +} +.generic_tab_strip .buttonlist +{ + float: left !important; +} + +/* the navigation list */ +ul#navigation +{ + margin: 0; + font-size: 0.9em; + padding: 1em 0.4em; +} +ul#navigation li +{ + float: none; + font-size: 0.95em; + display: inline; +} + +/* Styles for the general looks for the Core theme. +------------------------------------------------------- */ + +/* this is the main container surrounding everything, use this to set forum width, font-size etc. */ +#mainframe +{ + font-size: 85%; + width: 95%; + margin: auto; +} +/* the forum name or logo */ +h1#forum_name +{ + padding: 0.6em 0 0.6em 0; + margin: 0; + font-family: Verdana, helvetica, sans-serif; + font-size: 135%; + color: #fff; +} + +/* The greeting section */ +#greeting_section +{ + padding: 0.7em 0.4em 0.7em 0.4em; + clear: both; +} +#greeting_section li +{ + font-weight: normal; +} +#greeting_section li#name +{ + padding-left: 0.5em; +} +#greeting_section li em +{ + font-style: normal; + font-weight: bold; +} + +/* user section with all relevant links */ +#user_section +{ + padding: 1px; + margin: 1px 0 0 0; + font-size: 90%; +} +#user_section ul, #user_section form +{ + padding: 0.5em 0.7em 0.5em 0.7em; +} + +/* the avatar, located to the left */ +#user_section #myavatar +{ + padding: 0.7em; + border-right: 1px solid #adadad; + margin: 0 0.5em 0 0; + float: left; +} +/* the news and search areas */ +#news_section +{ + clear: both; + font-size: 0.8em; + padding: 0.5em 1em 0.5em 1em; +} +#random_news h3 +{ + margin-right: 1em; + font-size: 0.85em; + display: inline; +} +#random_news p +{ + margin: 0; + padding: 0; + display: inline; +} + +/* The main menu. */ +.main_menu +{ + padding-left: 1em; +} +.main_menu ul +{ + list-style: none; + padding: 0; + margin: 0; + background: url(../images/maintab_first.gif) no-repeat bottom left; + padding-left: 10px; +} +.main_menu li +{ + margin: 0; + padding: 0; + display: inline; +} +.main_menu li a:link, .main_menu li a:visited +{ + float: left; + display: block; + color: #fff; + font-size: 0.8em; + font-family: tahoma, sans-serif; + text-transform: uppercase; +} +.main_menu li a:hover +{ + color: #e0e0ff; + text-decoration: none; +} +.main_menu li a span +{ + background: url(../images/maintab_back.gif) repeat-x bottom left; + display: block; + padding: 0.1em 0.5em 0.5em 0.5em; +} +.main_menu li.last a span +{ + background: url(../images/maintab_last.gif) no-repeat bottom right; + padding: 0.1em 1em 0.5em 0.5em; +} +.main_menu li.active a span em +{ + padding: 0.1em 0.5em 0.5em 0.5em; + display: block; + font-style: normal; + background: url(../images/maintab_active_back.gif) repeat-x bottom right; +} +.main_menu li.active a span +{ + background: url(../images/maintab_active_first.gif) no-repeat bottom left; + padding: 0 0 0 8px; +} +.main_menu li.last.active +{ + float: left; + background: url(../images/maintab_last.gif) no-repeat bottom right; + padding: 0 8px 0 0; +} +/* IE6 doesn't support multiple class selectors */ +.main_menu li.lastactive +{ + float: left; + padding: 0 8px 0 0; + background: url(../images/maintab_last.gif) no-repeat bottom right; +} +.main_menu li.active a +{ + background: url(../images/maintab_active_last.gif) no-repeat bottom right; + padding-right: 8px; +} + +/* the linktree */ +ul.linktree +{ + clear: both; + width: 100%; + list-style: none; + margin: 0; + padding: 1.5em 0.5em 0.5em 0.5em; + overflow: hidden; +} +ul.linktree li +{ + float: left; + padding: 0 0.5em 0 0; + font-size: 0.8em; +} +ul.linktree li a +{ + color: #000; +} +ul.linktree li a:hover +{ + color: #cc3333; +} +ul.linktree li span +{ + font-weight: bold; +} + +/* the footer area */ +#footerarea +{ + padding: 1em 0 2em 0; + text-align: center; +} +#footerarea ul +{ + margin: 0 auto 0 auto; +} +#footerarea ul li +{ + text-align: center; + display: inline; + border-right: 1px solid #888; + margin: 0; + padding: 0 4px 0 2px; +} +/* Note: It is against the license to remove, alter or otherwise hide the copyright output from SMF so please do not alter the two sections below. */ +#footerarea ul li.copyright +{ + display: block; + line-height: 0; + font-size: small; + padding: 1em; +} +#footerarea ul li.copyright, #footerarea ul li.last +{ + border-right: none; +} +/* page created in.. */ +#footerarea p +{ + clear: both; + text-align: left; + padding-left: 0.5em; +} +p#show_loadtime +{ + display: block; + text-align: center; +} +/* the upshrink buttons */ +#upshrink, #advsearch +{ + margin: 0 1ex; +} + +/* Styles for a typical table. +------------------------------------------------------- */ +table.table_list +{ + width: 100%; +} +table.table_list p +{ + padding: 0; + margin: 0; +} +table.table_list td,table.table_list th +{ + padding: 5px; +} +table.table_list tbody.header td +{ + padding: 0; +} +table.table_list tbody.content td.stats +{ + font-size: 90%; + width: 15%; + text-align: center; +} +table.table_list tbody.content td.lastpost +{ + line-height: 1.2em; + font-size: 85%; + width: 24%; +} +table.table_list tbody.content td.icon +{ + text-align: center; + width: 6%; +} + +/* Styles for headers. +------------------------------------------------------- */ +/* Styles for headers used in Curve templates. */ +h3.catbg, h3.catbg2, h3.titlebg, h4.titlebg, h4.catbg, div.titlebg, .table_list tbody.header td +{ + overflow: hidden; + line-height: 2em; + font-weight: bold; +} +h3.titlebg, h4.titlebg +{ + border-left: 1px solid #adadad; + border-right: 1px solid #adadad; +} +h3.titlebg, h4.catbg +{ + padding: 0 0.5em !important; +} +h3.catbg img.icon, div.titlebg img.icon, h3.catbg img +{ + float: left; + margin: 5px 8px 0 0; +} + +/* These are used primarily for titles, but also for headers (the row that says what everything in the table is.) */ +.titlebg, tr.titlebg th, tr.titlebg td, .titlebg2, tr.titlebg2 th, tr.titlebg2 td +{ + color: #000; + font-style: normal; + background: url(../images/titlebg.jpg) #E9F0F6 repeat-x; + border-bottom: 1px solid #9baebf; + border-top: 1px solid #fff; + padding-left: 10px; + padding-right: 10px; +} +.titlebg, .titlebg a:link, .titlebg a:visited +{ + font-weight: bold; + color: #000; + font-style: normal; +} + +.titlebg a:hover +{ + color: #404040; +} +/* same as titlebg, but used where bold text is not needed */ +.titlebg2 a:link, .titlebg2 a:visited +{ + color: #000; + font-style: normal; + text-decoration: underline; +} + +.titlebg2 a:hover +{ + text-decoration: underline; +} + +/* This is used for categories, page indexes, and several other areas in the forum. +.catbg and .catbg2 is for boardindex, while .catbg3 is for messageindex and display headers. */ +.catbg, tr.catbg td, .catbg3, tr.catbg3 td +{ + background: url(../images/catbg.jpg) #88A6C0 repeat-x; + color: #fff; + padding-left: 10px; + padding-right: 10px; +} +.catbg2, tr.catbg2 td +{ + background: url(../images/catbg2.jpg) #A1BFD9 repeat-x; + color: #fff; + padding-left: 10px; + padding-right: 10px; +} +.catbg, .catbg2, .catbg3 +{ + border-bottom: 1px solid #375576; +} +.catbg, .catbg2 +{ + font-weight: bold; +} +.catbg3, tr.catbg3 td, .catbg3 a:link, .catbg3 a:visited +{ + font-size: 95%; + color: #fff; + text-decoration: none; +} +.catbg a:link, .catbg a:visited, .catbg2 a:link, .catbg2 a:visited +{ + color: #fff; + text-decoration: none; +} +.catbg a:hover, .catbg2 a:hover, .catbg3 a:hover +{ + color: #e0e0ff; +} + +/* Styles for the board index. +------------------------------------------------- */ + +p#stats +{ + text-align: right; +} +h3#newsfader +{ + font-size: 1em; +} +#smfNewsFader +{ + font-weight: bold; + line-height: 1.4em; + padding: 1em; + font-size: 1em; + text-align: center; +} +#upshrink_ic +{ + margin-right: 2ex; + text-align: right; +} +.categoryframe +{ + margin-top: 0.4em; +} +.categoryframe h3 +{ + margin: 0; +} +table.boardsframe +{ + width: 100%; +} +table.boardsframe td.icon +{ + text-align: center; + padding: 0.5em; + width: 6%; +} +table.boardsframe td.info +{ + width: 60%; + padding: 0; +} +table.boardsframe td.info h4 +{ + padding: 0.4em 0.4em 0 0.4em; + margin: 0; +} +table.boardsframe td.info p +{ + padding: 0 0.4em 0.5em 0.4em; + margin: 0; +} +table.boardsframe td.info p.moderators +{ + font-size: 0.8em; + font-family: verdana, sans-serif; +} +table.boardsframe td.stats +{ + width: 8%; + vertical-align: middle; + text-align: center; +} +table.boardsframe td.lastpost +{ + width: 20%; + vertical-align: top; + padding: 0.5em; +} +#posticons +{ + clear: both; + width: 100%; +} +#posticons .buttonlist +{ + margin-right: 1em; + float: right; +} + +/* the newsfader */ +#smfFadeScroller +{ + text-align: center; + overflow: auto; + color: #000000; /* shouldn't be shorthand style due to JS bug in IE! */ +} + +/* Styles for the info center on the board index. +---------------------------------------------------- */ + +#infocenterframe +{ + margin-top: 2em; + clear: both; +} +/* each section in infocenter has this class */ +.infocenter_section +{ + clear: both; +} +.infocenter_section p.section +{ + display: block; + margin: 0; + width: 30px; + text-align: center; + float: left; + padding: 0.5em 0 0 0; +} +.infocenter_section div.sectionbody +{ + margin-left: 30px; + padding: 0.3em; + border-left: 1px solid #a0a0a0; + min-height: 25px; + height: auto !important; +} +/* recent posts - or just one recent post */ +dl#infocenter_recentposts +{ + float: left; + width: 100%; + padding: 0; + margin: 0; +} +dl#infocenter_recentposts dt +{ + clear: left; + float: left; + padding: 0.1em; + width: 68%; + white-space: nowrap; + overflow: hidden; +} +dl#infocenter_recentposts dd +{ + clear: right; + float: right; + padding: 0.1em; + width: 25%; + text-align: right; + white-space: nowrap; + overflow: hidden; +} +/* login form */ +form#infocenter_login ul.horizlist label +{ + white-space: nowrap; + font-size: 90%; + font-weight: bold; +} + +/* Styles for the message (topic) index. +---------------------------------------------------- */ + +#childboards table +{ + width: 100%; +} +.modbuttons +{ + clear: both; + width: 100%; +} +.buttonlist, .buttonlist_bottom +{ + margin-right: 1em; + float: right; +} +#messageindex td.icon1, #messageindex td.icon2 +{ + text-align: center; + padding: 0.5em; + width: 5%; +} +#messageindex td.subject +{ + padding: 0.5em; +} +#messageindex td.starter +{ + text-align: center; + padding: 0.5em; + width: 14%; +} +#messageindex td.replies +{ + text-align: center; + padding: 0.5em; + width: 4%; +} +#messageindex td.views +{ + text-align: center; + padding: 0.5em; + width: 4%; +} +#messageindex td.lastpost +{ + padding: 0.5em; + width: 22%; +} +#messageindex td.moderation +{ + text-align: center; + padding: 0.5em; + width: 4%; +} +#topic_icons p +{ + display: block; + padding: 0.5em 0.5em 0.1em 0.5em; + margin: 0; + border-bottom: none; + font-weight: normal !important; +} +#topic_icons ul +{ + display: block; + padding: 0.5em 1em 0.1em 1em; + margin: 0; + border-bottom: none; + font-weight: normal !important; +} +#message_index_jump_to +{ + margin: 2em 4em 0 2em; +} +.lastpost img +{ + float: right; +} + +/* Styles for the display template (topic view). +---------------------------------------------------- */ + +.linked_events +{ + clear: both; + margin: 1em 0; +} +.linked_events .edit_event +{ + color: #f00; +} +#moderationbuttons +{ + margin-left: 0.5em; +} +#postbuttons .nav, #postbuttons_lower .nav +{ + margin: 0.5em 0.5em 0 0; + text-align: right; +} +#postbuttons_lower .nav +{ + margin: 0 0.5em 0.5em 0; +} +#postbuttons, #postbuttons_lower +{ + text-align: right; +} + +/* Poll question */ +h4#pollquestion +{ + padding: 1em 0 1em 2em; +} + +/* Poll vote options */ +#poll_options ul.options +{ + border-top: 1px solid #696969; + padding: 1em 2.5em 0 2em; + margin: 0 0 1em 0; +} +#poll_options div.submitbutton +{ + clear: both; + padding: 0 0 1em 2em; +} + +#poll_options div.submitbutton.border +{ + border-bottom: 1px solid #696969; + margin: 0 0 1em 0; +} + +/* Poll results */ +#poll_options dl.options +{ + border: solid #696969; + border-width: 1px 0; + padding: 1em 2.5em 0 2em; + margin: 0 0 1em 0; +} +#poll_options dl.options dt.voted +{ + font-weight: bold; +} +#poll_options dl.options dd +{ + margin: 0.5em 0 1em 0; +} + +/* Poll notices */ +#poll_options p +{ + margin: 0 1.5em 0.2em 1.5em; + padding: 0 0.5em 0.5em 0.5em; +} + +div#pollmoderation +{ + margin: -1em 0 0 2em; + padding: 0; +} + +.approve_post +{ + margin: 2ex; + padding: 1ex; + border: 2px dashed #cc3344; + color: #000; + font-weight: bold; +} +#forumposts h3.catbg3 +{ + font-weight: normal; + padding: 0.4em; + overflow: hidden; +} +#forumposts h3.catbg3 img +{ + float: left; + vertical-align: middle; +} +#forumposts h3.catbg3 span +{ + float: left; + padding-left: 2%; +} +#forumposts h3.catbg3 span#top_subject +{ + padding-left: 9.5em; +} +.poster +{ + width: 15em; + float: left; +} +.post +{ + clear: right; +} +img.smiley +{ + vertical-align: bottom; +} +.postarea +{ + margin-left: 16em; +} +.messageicon +{ + float: left; + margin: 0 0.5em 0.5em 0; +} +.messageicon img +{ + padding: 6px 3px; +} +.keyinfo +{ + float: left; + clear: none; + width: 50%; + min-height: 3em; +} +ul.postingbuttons +{ + float: right; + padding: 0 0.5em 0 0; +} +ul.postingbuttons li +{ + float: left; + margin: 0 0.5em 0 0; +} +.modifybutton +{ + float: right; + margin: 0 0.5em 0.5em 0; + font: bold 0.85em arial, sans-serif; + color: #476c8e; +} +.attachments +{ + padding-top: 1em; + overflow: auto; +} +.attachments hr +{ + clear: both; + margin: 1em 0 1em 0; +} +.postfooter +{ + margin-left: 16em; +} +.topborder +{ + border-top: 1px solid #bbb; +} +.moderatorbar +{ + clear: right; + margin: 1em 0 0 16em; +} +#pollmoderation, #moderationbuttons_strip +{ + float: left; +} + +/* Styles for the quick reply area. +---------------------------------------------------- */ + +#quickReplyOptions #quickReplyWarning +{ + border: none; + text-align: left; + margin: 0; + width: 25%; + float: left; +} +#quickReplyOptions #quickReplyContent +{ + text-align: right; + float: left; + width: 67.5%; + padding: 1em; + border-left: 1px solid #aaa; +} + +#quickReplyOptions #quickReplyContent textarea, #quickReplyOptions #quickReplyContent input +{ + margin-bottom: .5em; +} + +#quickReplyWarning +{ + width: 20%; + float: left; + padding: 0.5em 1em; +} +#quickReplyContent +{ + width: 75%; + float: right; + padding: 0.5em 0; +} +#quickReplyOptions .roundframe +{ + overflow: hidden; +} +#quickReplyOptions form textarea +{ + height: 100px; + width: 635px; + max-width: 100%; + min-width: 100%; +} + +/* The jump to box */ +#display_jump_to +{ + clear: both; + padding: 5px; +} + +/* Separator of posts. More useful in the print stylesheet. */ +#forumposts .post_separator +{ + display: none; +} + +/* Styles for edit post section +---------------------------------------------------- */ +form#postmodify .roundframe +{ + padding: 0 12%; +} +#post_header +{ + margin-bottom: 0.5em; + padding: 0.5em; + overflow: hidden; +} +#post_header dt +{ + float: left; + margin: 0; + padding: 0; + width: 15%; + margin: .3em 0; + font-weight: bold; +} +#post_header dd +{ + float: left; + margin: 0; + padding: 0; + width: 83%; + margin: .3em 0; +} +#post_header img +{ + vertical-align: middle; +} +ul.post_options +{ + margin: 0 0 0 1em; + padding: 0; + list-style: none; + overflow: hidden; +} +ul.post_options li +{ + margin: 0.2em 0; + width: 49%; + float: left; +} +#postAdditionalOptionsHeader +{ + margin-top: 1em; +} +#postMoreOptions +{ + border-bottom: 1px solid #666; + padding: 0.5em; +} +#postAttachment, #postAttachment2 +{ + overflow: hidden; + margin: .5em 0; + padding: 0; + border-bottom: 1px solid #666; + padding: 0.5em; +} +#postAttachment dd, #postAttachment2 dd +{ + margin: .3em 0 .3em 1em; +} +#postAttachment dt, #postAttachment2 dt +{ + font-weight: bold; +} +#postAttachment3 +{ + margin-left: 1em; +} +#post_confirm_strip, #shortcuts +{ + padding: 1em 0 0 0; +} +.post_verification +{ + margin-top: .5em; +} +.post_verification #verification_control +{ + margin: .3em 0 .3em 1em; +} +/* The BBC buttons */ +#bbcBox_message +{ + margin: 1em 0 0.5em 0; +} +#bbcBox_message div +{ + margin: 0.2em 0; + vertical-align: top; +} +#bbcBox_message div img +{ + margin: 0 1px 0 0; + vertical-align: top; +} +#bbcBox_message select +{ + margin: 0 2px; +} +/* The smiley strip */ +#smileyBox_message +{ + margin: 0.75em 0 0.5em 0; +} + +/* Styles for edit event section +---------------------------------------------------- */ +#post_event .roundframe +{ + padding: 1% 12%; +} +#post_event fieldset +{ + margin-bottom: 0.5em; + border: 1px solid #c4c4c4; + padding: 0.5em; + clear: both; +} +#post_event legend +{ + font-weight: bold; + color: #000; +} +#post_event #event_main input +{ + margin: 0 0 1em 0; + float: left; +} +#post_event #event_main div.smalltext +{ + width: 33em; + float: right; +} +#post_event div.event_options +{ + float: right; +} +#post_event ul.event_main, ul.event_options +{ + padding: 0; + overflow: hidden; +} +#post_event ul.event_main li +{ + list-style-type: none; + margin: 0.2em 0; + width: 49%; + float: left; +} +#post_event ul.event_options +{ + margin: 0; + padding: 0 0 .7em .7em; +} +#post_event ul.event_options li +{ + list-style-type: none; + margin: 0; + float: left; +} +#post_event #event_main select, #post_event ul.event_options li select, #post_event ul.event_options li .input_check +{ + margin: 0 1em 0 0; +} + +/* Styles for edit poll section. +---------------------------------------------------- */ + +#edit_poll +{ + overflow: hidden; +} +#edit_poll fieldset +{ + margin: 0.5em 0; + border: 1px solid #c4c4c4; + padding: 0.5em; + clear: both; + overflow: hidden; +} +#edit_poll legend +{ + font-weight: bold; + color: #000; +} +#edit_poll fieldset input +{ + margin-left: 8.6em; +} +#edit_poll ul.poll_main li +{ + padding-left: 1em; +} +#edit_poll ul.poll_main input +{ + margin-left: 1em; +} +#edit_poll ul.poll_main, dl.poll_options +{ + overflow: hidden; + padding: 0 0 .7em .7em; + list-style: none; +} +#edit_poll ul.poll_main li +{ + margin: 0.2em 0; +} +#edit_poll dl.poll_options dt +{ + width: 33%; + padding: 0 0 0 1em; +} +#edit_poll dl.poll_options dd +{ + width: 65%; +} +#edit_poll dl.poll_options dd input +{ + margin-left: 0; +} + + +/* Styles for the recent messages section. +---------------------------------------------------- */ + +.readbuttons +{ + clear: both; + width: 100%; +} +.buttonlist, .buttonlist_bottom +{ + margin-right: 1em; + float: right; +} + +/* Styles for the move topic section. +---------------------------------------------------- */ + +#move_topic dl +{ + margin-bottom: 0; +} +.move_topic +{ + width: 710px; + margin: auto; + text-align: left; +} +div.move_topic fieldset +{ + margin: 0.5em 0; + border: 1px solid #cacdd3; + padding: 0.5em; +} + +/* Styles for the send topic section. +---------------------------------------------------- */ + +fieldset.send_topic +{ + margin-bottom: 0.5em; + border: none; + padding: 0.5em; +} +dl.send_topic +{ + margin-bottom: 0; +} +dl.send_mail dt +{ + width: 35%; +} +dl.send_mail dd +{ + width: 64%; +} + +/* Styles for the split topic section. +---------------------------------------------------- */ + +div#selected, div#not_selected +{ + width: 49%; +} +ul.split_messages li.windowbg, ul.split_messages li.windowbg2 +{ + border: 1px solid #adadad; + padding: 1em; + margin: 1px; +} +ul.split_messages li a.split_icon +{ + padding: 0 0.5em; +} +ul.split_messages div.post +{ + padding: 1em 0 0 0; + border-top: 1px solid #fff; +} + +/* Styles for the report topic section. +---------------------------------------------------- */ + +#report_topic dl +{ + margin-bottom: 0; +} +#report_topic dl.settings dt +{ + width: 20%; +} +#report_topic dl.settings dd +{ + width: 79%; +} + +/* Styles for the merge topic section. +---------------------------------------------------- */ + +ul.merge_topics li +{ + list-style-type: none; +} +dl.merge_topic dt +{ + width: 25%; +} +dl.merge_topic dd +{ + width: 74%; +} +fieldset.merge_options +{ + margin-bottom: 0.5em; +} +fieldset.merge_options legend +{ + font-weight: bold; +} +.custom_subject +{ + margin: 0.5em 0; +} + +/* Styles for the login areas. +------------------------------------------------------- */ +.login +{ + width: 540px; + margin: 0 auto; +} +.login dl +{ + overflow: auto; + clear: right; +} +.login dt, .login dd +{ + margin: 0 0 0.4em 0; + width: 44%; + padding: 0.1em; +} +.login dt +{ + float: left; + clear: both; + text-align: right; + font-weight: bold; +} +.login dd +{ + width: 54%; + float: right; + text-align: left; +} +.login p +{ + text-align: center; +} +.login h3 img +{ + float: left; + margin: 4px 0.5em 0 0; +} + +/* Styles for the registration section. +------------------------------------------------------- */ +.register_error +{ + border: 1px dashed red; + padding: 5px; + margin: 0 1ex 1ex 1ex; +} +.register_error span +{ + text-decoration: underline; +} + +/* Additional profile fields */ +dl.register_form +{ + margin: 0; + clear: right; + overflow: auto; +} + +dl.register_form dt +{ + font-weight: normal; + float: left; + clear: both; + width: 50%; + margin: 0.5em 0 0 0; +} + +dl.register_form dt strong +{ + font-weight: bold; +} + +dl.register_form dt span +{ + display: block; +} + +dl.register_form dd +{ + float: left; + width: 49%; + margin: 0.5em 0 0 0; +} + +#confirm_buttons +{ + text-align: center; + padding: 1em 0; +} + +.coppa_contact +{ + padding: 4px; + width: 32ex; + background-color: #fff; + color: #000; + margin-left: 5ex; + border: 1px solid #000; +} + +.valid_input +{ + background-color: #f5fff0; +} +.invalid_input +{ + background-color: #fff0f0; +} + +/* Styles for maintenance mode. +------------------------------------------------------- */ +#maintenance_mode +{ + width: 75%; + min-width: 520px; + text-align: left; +} +#maintenance_mode img.floatleft +{ + margin-right: 1em; +} + +/* common for all admin sections */ +h3.titlebg img +{ + vertical-align: middle; + margin-right: 0.5em; +} +tr.titlebg td +{ + padding-left: 0.7em; +} +#admin_menu +{ + min-height: 2em; + padding-left: 0; +} +#admin_content +{ + clear: left; +} +#admin_login .centertext +{ + padding: 1em; +} +#admin_login .centertext .error +{ + padding: 0 0 1em 0; +} + +/* Styles for sidebar menus. +------------------------------------------------------- */ +.left_admmenu, .left_admmenu ul, .left_admmenu li +{ + padding: 0; + margin: 0; + list-style: none; +} +#left_admsection +{ + background-color: #ecedf3; + padding: 1px; + border: 1px solid #ADADAD; + width: 160px; + float: left; + margin-right: 10px; +} +.adm_section h4.titlebg +{ + font-size: 95%; + margin-bottom: 5px; +} +#main_container +{ + position: relative; +} +.left_admmenu li +{ + padding: 0 0 0 0.5em; +} +.left_admmenu +{ + margin-bottom: 1.1em; +} +#main_admsection +{ + position: relative; + left: 0; + right: 0; + overflow: hidden; +} + +tr.windowbg td, tr.windowbg2 td, tr.approvebg td, tr.highlight2 td +{ + padding: 0.3em 0.7em; +} +#credits p +{ + padding: 0; + font-style: italic; + margin: 0; +} + +/* Styles for generic tables. +------------------------------------------------------- */ +.topic_table table +{ + width: 100%; +} +.topic_table .icon1, .topic_table .icon2, .topic_table .stats +{ + text-align: center; +} +#topic_icons +{ + margin-top: 1em; +} +#topic_icons .description +{ + margin: 0; +} +.topic_table table thead +{ + border-bottom: 1px solid #fff; +} +/* the subject column */ +.topic_table td +{ + font-size: 1em; +} +.topic_table td.subject +{ + padding: 4px; +} +.topic_table td.subject p, .topic_table td.stats, .topic_table td.lastpost +{ + font-size: 0.85em; + padding: 0; + margin: 0; +} +.topic_table td.lastpost, .topic_table td.lastpost +{ + font-size: 0.9em; + line-height: 100%; + padding: 4px; +} +.topic_table td.stickybg2 +{ + background-image: url(../images/icons/quick_sticky.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lockedbg2 +{ + background-image: url(../images/icons/quick_lock.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lastpost +{ + background-image: none; +} + +/* Styles for (fatal) errors. +------------------------------------------------- */ + +#fatal_error +{ + border: 1px solid #aaa; +} + +.errorbox +{ + padding: 1em; + border: 1px solid #cc3344; + color: #000; + background-color: #ffe4e9; + margin: 1em 0; +} +.errorbox h3 +{ + padding: 0; + margin: 0; + font-size: 1.1em; + text-decoration: underline; +} +.errorbox p +{ + margin: 1em 0 0 0; +} +.errorbox p.alert +{ + padding: 0; + margin: 0; + float: left; + width: 1em; + font-size: 1.5em; +} + +/* Styles for the profile section. +------------------------------------------------- */ + +dl +{ + overflow: auto; + margin: 0; + padding: 0; +} + +/* Fixes for the core theme */ +#profileview +{ + padding: 1px; + border: 1px solid #696969; + background-color: #ecedf3; +} +#profileview .content +{ + border: none; +} +#basicinfo .content +{ + padding: 1em; +} +#detailedinfo .content +{ + padding: 0.7em 1.2em; + border-left: 1px solid #aaa; +} + +/* The basic user info on the left */ +#basicinfo +{ + width: 20%; + float: left; +} +#detailedinfo +{ + width: 78%; + float: right; +} +#basicinfo h4 +{ + font-size: 135%; + font-weight: 100; + line-height: 105%; + white-space: pre-wrap; /* css-2.1 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + overflow: hidden; +} +#basicinfo h4 span.position +{ + font-size: 80%; + font-weight: 100; + display: block; +} +#basicinfo img.avatar +{ + display: block; + margin: 10px 0 0 0; +} +#basicinfo ul +{ + list-style-type: none; + margin: 10px 0 0 0; +} +#basicinfo ul li +{ + display: block; + float: left; + margin-right: 5px; + height: 20px; +} +#basicinfo span#userstatus +{ + display: block; + clear: both; +} +#basicinfo span#userstatus img +{ + vertical-align: middle; +} +#detailedinfo div.content dl, #tracking div.content dl +{ + clear: right; + overflow: auto; + margin: 0 0 18px 0; + padding: 0 0 15px 0; + border-bottom: 1px solid #ccc; +} +#detailedinfo div.content dt, #tracking div.content dt +{ + width: 30%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#detailedinfo div.content dd, #tracking div.content dd +{ + width: 70%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} +#detailedinfo div.content dl.noborder +{ + border-bottom: 0; +} +#detailedinfo div.content dt.clear +{ + width: 100%; +} +.signature, .custom_fields_above_signature, .attachments +{ + width: 98%; + overflow: auto; + clear: right; + border-top: 1px solid #666; +} +.signature h5 +{ + font-size: 100%; + margin-bottom: 10px; +} +#personal_picture +{ + display: block; + margin-bottom: 0.3em; +} +#avatar_server_stored div +{ + float: left; +} + +#main_admsection #basicinfo, #main_admsection #detailedinfo +{ + width: 100%; +} +#main_admsection #detailedinfo .content +{ + border: none !important; +} +#main_admsection #basicinfo +{ + border-bottom: 1px solid #ccc; +} +#main_admsection #basicinfo h4 +{ + float: left; +} +#main_admsection #basicinfo img.avatar +{ + float: right; + vertical-align: top; +} +#main_admsection #basicinfo ul +{ + clear: left; + padding-top: 10px; +} +#main_admsection #basicinfo span#userstatus +{ + clear: left; +} +#main_admsection #basicinfo p#infolinks +{ + display: none; + clear: both; +} +#main_admsection #basicinfo .botslice +{ + clear: both; +} + +/* Simple feedback messages */ +div#profile_error, div#profile_success +{ + margin: 0 0 1em 0; + padding: 1em 2em; + border: 1px solid; +} +div#profile_error +{ + border-color: red; + color: red; + background: #fee; +} + +div#profile_error span +{ + text-decoration: underline; +} + +div#profile_success +{ + border-color: green; + color: green; + background: #efe; +} + +/* Profile statistics */ +#generalstats div.content dt +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#generalstats div.content dd +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +/* Activity by time */ +.activity_stats +{ + margin: 0; + padding: 0; + list-style: none; +} +.activity_stats li +{ + width: 4.16%; + float: left; +} +.activity_stats li span +{ + display: block; + border: solid #000; + border-width: 1px 1px 0 0; + text-align: center; +} +.activity_stats li.last span +{ + border-right: none; +} +.activity_stats li div.bar +{ + margin: 0 auto; + width: 15px; +} +.activity_stats li div.bar div +{ + background: url('../images/bar.gif'); +} +.activity_stats li div.bar span +{ + position: absolute; + top: -1000em; + left: -1000em; +} + +/* Most popular boards by posts and activity */ +#popularposts +{ + width: 50%; + float: left; +} +#popularactivity +{ + width: 50%; + float: right; +} + +#popularposts div.content dt, #popularactivity div.content dt +{ + width: 65%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#popularposts div.content dd, #popularactivity div.content dd +{ + width: 35%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +.profile_pie +{ + background-image: url(../images/stats_pie.png); + float: left; + height: 20px; + width: 20px; + margin: 0 1em 0 0; + padding: 0; + text-indent: -1000em; +} + +/* View posts */ +.time +{ + float: right; +} +.counter +{ + margin: 0 0 0 0; + padding: 0.2em 0.5em 0.1em 0.2em; + font-size: 2.2em; + font-weight: bold; + color: #354c5f; + float: left; +} +.list_posts +{ + border-top: 1px solid #adadad; + padding-top: 1em; + margin-top: 0.5em; +} +div.core_posts +{ + border: 1px solid #adadad; + margin-bottom: 3px; +} +div.core_posts div.content +{ + background: none; + border: none; +} +.topic h4 +{ + margin: 3px 0; +} + +.mod_icons +{ + text-align: right; + margin-right: 1em; +} +#permissions div.tborder +{ + margin-bottom: 2em; +} +#permissions ul +{ + padding: 0; + margin: 1px 0 0 0; + border-top: 1px solid #e5e5e8; + float: left; + width: 100%; +} +#permissions div.permission_name +{ + width: 48%; + list-style: none; + border-right: 1px solid #e5e5e8; + background: #ecedf3; + margin: 0 1% 0 0; + padding: 0.7em 0.7em 0.8em 0.7em; + line-height: 1em; +} +#permissions li +{ + width: 100%; + padding: 0; + list-style: none; + margin: 0 0 1px 0; +} +#permissions li span.permission_status, #permissions li span.alert +{ + line-height: 2.9em; + font-size: 0.85em; +} + +#tracking div.content dl +{ + border-bottom: 0; + margin: 0; + padding: 0; +} + +#creator dl +{ + margin: 0; +} +#creator dt +{ + width: 40%; + float: left; + clear: both; + margin: 0 0 10px 0; +} +#creator dd +{ + float: left; + width: 60%; + margin: 0 0 10px 0; + overflow: auto; +} + +.ignoreboards +{ + margin: 0 2%; + padding: 0; + width: 45%; +} +.ignoreboards a +{ + font-weight: bold; + text-decoration: none; + border-bottom: 1px solid #c4c4c4; + padding: 0.1em 0; +} +.ignoreboards a:hover +{ + text-decoration: none; + border-bottom: 1px solid #476c8e; +} +.ignoreboards ul +{ + margin: 0; + padding: 0; +} +.ignoreboards li +{ + list-style: none; + float: left; + clear: both; +} +.ignoreboards li.category +{ + margin: 0.7em 0 0 0; + width: 100%; +} +.ignoreboards li ul +{ + margin: 0.2em 0 0 0; +} +.ignoreboards li.category ul li.board +{ + width: 93%; +} + +#theme_settings +{ + overflow: auto; + margin: 0; + padding: 0; +} + +#theme_settings li +{ + list-style: none; + margin: 10px 0; + padding: 0; +} +/*Paid Subscriptions*/ +#paid_subscription +{ + width: 100%; +} +#paid_subscription dl.settings +{ + margin-bottom: 0; +} +#paid_subscription dl.settings dd, #paid_subscription dl.settings dt +{ + margin-bottom: 4px; +} +/*pick theme*/ +#pick_theme +{ + width: 100%; + float: left; +} +/*Issue a warning*/ +#warn_body{ + width: 80%; + font-size: 0.9em; +} + +/* Styles for the statistics center. +------------------------------------------------- */ +#statistics +{ + padding-bottom: 0.5em; +} +#statistics h4.titlebg +{ + text-align: center; + margin-bottom: 5px; +} +#stats_left, #top_posters, #top_topics_replies, #top_topics_starter +{ + float: left; + width: 49.5%; +} +#stats_right, #top_boards, #top_topics_views, #most_online +{ + float: right; + width: 49.5%; +} +dl.stats +{ + clear: both; + overflow: hidden; + margin: 0; + padding: 0; +} +dl.stats dt +{ + width: 49%; + float: left; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; + clear: both; + font-size: 1em; +} +dl.stats dd +{ + text-align: right; + width: 50%; + font-size: 1em; + float: right; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; +} +.stats_bar +{ + float: left; + background-image: url(../images/bar_stats.png); + height: 16px; + font-size: 0.9em; + display: block; + text-align: left; + color: #fff; + font-weight: bold; + background-position: top center; +} +.stats_bar span +{ + padding-left: 2px; +} + +/* Styles for the personal messages section. +------------------------------------------------- */ + +#personal_messages +{ + padding: 1px; +} +#personal_messages #top_subject +{ + padding-left: 11.75em !important; +} +#personal_messages div.labels +{ + padding: 0 1em 0 0; +} +#personal_messages .capacity_bar +{ + background: #fff; + border: 1px solid #000; + height: 7px; + width: 75%; + margin: 0 auto; +} +#personal_messages .capacity_bar div +{ + border: none; + height: 7px; +} +#personal_messages .capacity_bar div.empty +{ + background: #468008; +} +#personal_messages .capacity_bar div.filled +{ + background: #EEA800; +} +#personal_messages .capacity_bar div.full +{ + background: #A53D05; +} +#personal_messages .reportlinks +{ + padding: 0.5em 1.3em; +} +#searchLabelsExpand li +{ + padding: 0.3em 0.5em; +} + +/* Styles for the calendar section. +------------------------------------------------- */ +.calendar_table +{ + margin-bottom: 0.7em; +} + +/* Used to indicate the current day in the grid. */ +.calendar_today +{ + background-color: #fff; +} + +#month_grid +{ + width: 200px; + text-align: center; + float: left; +} + +#month_grid table +{ + width: 200px; + border-collapse: collapse; + border: 1px solid #adadad; +} + +#month_grid table td, #month_grid table th +{ + border: 1px solid #adadad; +} + +#main_grid table +{ + width: 100%; + padding-bottom: 4px; + border-collapse: collapse; + border: 1px solid #adadad; +} + +#main_grid table td, #main_grid table th +{ + border: 1px solid #adadad; +} + +#main_grid table h3.catbg +{ + text-align: center; + + border-top: 1px solid #adadad; + border-bottom: none; +} + +#main_grid table h4 +{ + border: none; +} + +#main_grid table.weeklist td.windowbg +{ + text-align: center; + height: 49px; + width: 25px; + font-size: large; + padding: 0 7px; + border-bottom: 1px solid #adadad; +} + +#main_grid table.weeklist td.weekdays +{ + height: 49px; + width: 100%; + padding: 4px; + text-align: left; + vertical-align: middle; + border-right: 1px solid #adadad; + border-bottom: 1px solid #adadad; +} + +#main_grid h3.weekly +{ + text-align: center; + padding-left: 0; + font-size: large; + height: 29px; +} + +#main_grid h3 span.floatleft, #main_grid h3 span.floatright +{ + display: block; + +} + +#main_grid table th.days +{ + width: 14%; +} + +#main_grid table td.weeks +{ + vertical-align: middle; + text-align: center; +} + +#main_grid table td.days +{ + vertical-align: top; + +} + +a.modify_event +{ + color: red; +} + +span.hidelink +{ + font-style: italic; +} + +#calendar_navigation +{ + text-align: center; +} + +#calendar .buttonlist_bottom +{ + border-bottom: 1px solid #adadad; + padding: 0 0 0 1ex; + margin: 0 0 1ex 0; +} + +/* Styles for the memberlist section. +------------------------------------------------- */ +#mlist_search +{ + margin: auto; + width: 400px; +} + +/* Styles for the basic search section. +------------------------------------------------- */ +#simple_search p +{ + padding: 0.5em; +} +#simple_search, #simple_search p, #advanced_search +{ + text-align: center !important; + margin: 0; +} +#search_error +{ + font-style: italic; + padding: 0.3em 1em; +} +#search_term_input +{ + font-size: 115%; + margin: 0 0 1em; +} + +/* Styles for the advanced search section. +------------------------------------------------- */ +#searchform fieldset +{ + text-align: left; + padding: 0; + margin: 0; + border: none; +} +fieldset#advanced_search .roundframe +{ + border-bottom: none !important; +} +#advanced_search dl#search_options +{ + margin: 0 auto; + width: 600px; + padding-top: 1em; + overflow: hidden; +} +#advanced_search dt +{ + clear: both; + float: left; + padding: 0.2em; + text-align: right; + width: 20%; +} +#advanced_search dd +{ + width: 75%; + float: left; + padding: 0.2em; + margin: 0 0 0 0.5em; + text-align: left; +} +#searchform p.clear +{ + clear: both; +} + +/* Styles for the search results page. +------------------------------------------------- */ +.pagelinks +{ + padding: 0.5em; +} +.topic_table td blockquote, .topic_table td .quoteheader +{ + margin: 0.5em; +} +.search_results_posts +{ + overflow: hidden; +} +.search_results_posts .inner +{ + padding: 0.5em 1em; + overflow: hidden; +} +.search_results_posts .windowbg2 +{ + margin-top: 4px; +} +.search_results_posts .buttons +{ + padding: 5px 1em 0 0; +} + +/* Styles for the help section. +------------------------------------------------- */ + +#helpmain +{ + padding: 1em; + border: 1px solid #696969; +} +#helpmain p +{ + margin: 0 0 1.5em 0; + line-height: 1.5em; +} +#helpmain ul +{ + line-height: 1.5em; +} + +/* Depreciated stuff from the old days. +------------------------------------------------- */ + +/* This style will make sure all headers use the same padding throughout. */ +.headerpadding +{ + padding: 0.5em; +} +/* smaller padding used in paragraphs, sections etc */ +.smallpadding +{ + padding: 0.2em; +} +/* larger padding used in paragraphs, sections etc */ +.largepadding +{ + padding: 0.7em; +} + +/* A small space to the next section. */ +.marginbottom +{ + margin-bottom: 1em; +} +/* On the top too. */ +.margintop +{ + margin-top: 1em !important; +} +/* remove bold/italic styles */ +span.plainstyle +{ + font-weight: normal; + font-style: normal; +} +/* float a list horizontally */ +ul.horizlist +{ + width: 100%; +} +ul.horizlist li +{ + float: left; + padding: 0.2em 0.4em 0.2em 0.4em; + vertical-align: top; +} +/* make a inline-list */ +ul.nolist li +{ + display: inline; +} +/* Helping style to clear floated items. */ +.clearfix:after +{ + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.clearfix +{ + display: inline-block; +} + +/* Hides from IE-mac. \*/ +* html .clearfix +{ + height: 1%; +} +.clearfix +{ + display: block; +} +/* End hide from IE-mac. */ + +/* This is used for tables that have a grid/border background color (such as the topic listing.) */ +.bordercolor +{ + background-color: #adadad; + padding: 0; +} + +/* This is used on tables that should just have a border around them. */ +.tborder +{ + padding: 1px; + border: 1px solid #696969; + background-color: #fff; +} +/* If some random peep decides to use a description class within a tborder (happened to me!) */ +.tborder .description +{ + margin-bottom: 0; +} + +/* Styles for print media. +------------------------------------------------------- */ +@media print +{ + #headerarea + { + display: none; + } + + .tborder + { + border: none; + } +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/css/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/css/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/css/rtl.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/css/rtl.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1071 @@ +/* Common classes to ease styling. +------------------------------------------------------- */ + +.floatright +{ + float: left; +} +.floatleft +{ + float: right; +} +.clear_left +{ + clear: right; +} +.clear_right +{ + clear: left; +} +.righttext +{ + text-align: left; +} +.lefttext +{ + text-align: right; +} + +/* GenericList */ +.additional_row input +{ + margin-left: 1em; +} + +/* Lists with settings use these a lot. +------------------------------------------------------- */ +dl.settings dt +{ + float: right; +} +dl.settings dt.windowbg +{ + float: right; +} +dl.settings dd +{ + float: right; +} +dl.settings img +{ + margin: 0 0 0 10px; +} + +/* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ +.signature +{ + clear: left; +} +.custom_fields_above_signature +{ + clear: left; +} + +/* Styles for the standard dropdown menus. +------------------------------------------------------- */ +/* Container for the new admin menu */ +#adm_container +{ + float: right; + margin-left: 0; + margin-right: 10px; +} +.main_menu li +{ + float: right; +} + +/* the linktree */ +ul.linktree li +{ + float: right; + padding: 0 0 0 0.5em; +} + +/* First layer of menu items */ +ul.admin_menu li +{ + float: right !important; + padding-left: 4px; +} +ul.admin_menu li ul +{ + right: 0; +} +ul.admin_menu li ul li a +{ + padding: 0.5em 0.5em 0.5em 2em; +} +/* Second layer of menu items */ +ul.admin_menu li ul +{ + border-left: 1px solid #808080; + border-right: 2px solid #6888a7; +} +/* Third layer of menu items */ +ul.admin_menu li ul li:hover ul, ul.admin_menu li ul li.over ul +{ + /* now a very tricky stuff, never seen before ;-) */ + /*IE and Firefox like it right */ + right: 19em; + /*Opera needs left*/ + left: -19.2em; + top: auto; + margin: -2em 0 0 0; + border-left: 1px solid #808080; + border-right: 2px solid #6888a7; +} +ul.admin_menu li ul li a.subsection +{ + background-image: url(../images/admin/subsection2.gif); + background-position: 2% 50%; +} +/* The dropdown menu toggle image */ +div#menu_toggle +{ + float: left; + margin: 0 0 0 10px; +} +/* Styles for the standard button lists. +------------------------------------------------------- */ +/* The old-style button strips, with images */ +.oldbuttonlist +{ + text-align: left; +} + +/* Styles for the general looks for the Core theme. +------------------------------------------------------- */ +#user_section #myavatar +{ + margin: 0 0 0 0.5em; + border-right: none; + border-left: 1px solid #adadad; + float: right; +} +#footerarea p +{ + text-align: right; + padding-right: 0.5em; +} + +/* Styles for headers. +------------------------------------------------------- */ + +h3.catbg img.icon, div.titlebg img.icon, h3.catbg img +{ + float: right; + margin: 5px 0 0 8px; +} + +/* Styles for the general looks for the Core theme. +------------------------------------------------------- */ + +.main_menu ul +{ + padding-right: 1em; + float: right; +} +.main_menu +{ + overflow: hidden; +} + +/* Styles for the board index. +------------------------------------------------- */ + +p#stats +{ + text-align: left; +} +#upshrink_ic +{ + margin-right: 0; + margin-left: 2ex; + text-align: left; +} + +#posticons .buttonlist +{ + margin-right: 0; + margin-left: 1em; + float: left; +} + +/* Styles for the info center on the board index. +---------------------------------------------------- */ + +#infocenterframe +{ + margin-top: 2em; + clear: both; +} +/* each section in infocenter has this class */ +.infocenter_section p.section +{ + float: right; +} +.infocenter_section div.sectionbody +{ + margin-left: 0; + margin-right: 30px; + border-left: none; + border-right: 1px solid #a0a0a0; +} +/* recent posts - or just one recent post */ +dl#infocenter_recentposts +{ + float: right; +} +dl#infocenter_recentposts dt +{ + clear: right; + float: right; +} +dl#infocenter_recentposts dd +{ + clear: left; + float: left; + text-align: left; +} + +/* Styles for the message (topic) index. +---------------------------------------------------- */ +.buttonlist, .buttonlist_bottom +{ + margin-left: 0; + margin-right: 1em; + float: left; +} +#message_index_jump_to +{ + margin: 2em 2em 0 4em; +} +.lastpost img +{ + float: left; +} +/* Styles for the display template (topic view). +---------------------------------------------------- */ +/* a smaller quick-button list */ +ul.quickbuttons +{ + margin: 0.9em 0 0 11px; + clear: left; + float: left; + text-align: left; +} +ul.quickbuttons li +{ + float: left; + margin: 0 11px 0 0; +} +ul.quickbuttons li a +{ + padding: 0 20px 0.7em 0; + float: left; +} +ul.quickbuttons li.quote_button +{ + background: url(../images/buttons/quote.gif) no-repeat 100% 0; +} +ul.quickbuttons li.remove_button +{ + background: url(../images/buttons/delete.gif) no-repeat 100% 0; +} +ul.quickbuttons li.modify_button +{ + background: url(../images/buttons/modify.gif) no-repeat 100% 0; +} +ul.quickbuttons li.approve_button +{ + background: url(../images/buttons/approve.gif) no-repeat 100% 0; +} +ul.quickbuttons li.restore_button +{ + background: url(../images/buttons/restore_topic.gif) no-repeat 100% 0; +} +ul.quickbuttons li.split_button +{ + background: url(../images/buttons/split.gif) no-repeat 100% 0; +} +ul.quickbuttons li.reply_button +{ + background: url(../images/buttons/reply.gif) no-repeat 100% 0; +} +ul.quickbuttons li.reply_all_button +{ + background: url(../images/buttons/reply.gif) no-repeat 100% 0; +} +ul.quickbuttons li.notify_button +{ + background: url(../images/buttons/notify_sm.gif) no-repeat 100% 0; +} +ul.quickbuttons li.inline_mod_check +{ + margin: 0 5px 0 0; +} +#moderationbuttons +{ + margin-left: 0; + margin-right: 0.5em; +} +#postbuttons .nav, #postbuttons_lower .nav +{ + margin: 0.5em 0 0 0.5em; + text-align: left; +} +#postbuttons_lower .nav +{ + margin: 0 0.5em 0.5em 0; +} +#postbuttons, #postbuttons_lower +{ + text-align: right; +} +/* Poll question */ +h4#pollquestion +{ + padding: 1em 2em 1em 0; +} +#poll_options div.submitbutton +{ + clear: both; + padding: 0 2em 1em 0; +} +/* Poll results */ +#poll_options dl.options +{ + padding: 1em 2em 0 2.5em; +} +div#pollmoderation +{ + margin: -1em 2em 0 0; +} +#forumposts h3.catbg3 img +{ + float: right; +} +#forumposts h3.catbg3 span +{ + float: right; + padding-left: 0; + padding-right: 2%; +} +#forumposts h3.catbg3 span#top_subject +{ + padding-left: 0; + padding-right: 9.5em; +} +.poster +{ + float: right; +} +.post +{ + clear: left; + float: right; +} +.postarea +{ + margin-left: 0; + margin-right: 16em; +} +.messageicon +{ + float: right; +} +.keyinfo +{ + float: right; +} +ul.postingbuttons +{ + float: left; + padding: 0 0 0 0.5em; +} +ul.postingbuttons li +{ + float: right; + margin: 0 0 0 0.5em; +} +.modifybutton +{ + float: left; + margin: 0 0 0.5em 0.5em; +} +.postfooter +{ + margin-left: 0; + margin-right: 16em; +} +.moderatorbar +{ + clear: left; + margin: 1em 16em 0 0; +} +#pollmoderation, #moderationbuttons_strip +{ + float: right; +} + +/* Styles for the quick reply area. +---------------------------------------------------- */ + +#quickReplyOptions #quickReplyWarning +{ + text-align: right; + float: right; +} +#quickReplyOptions #quickReplyContent +{ + text-align: left; + float: left; + border-left: none; + border-right: 1px solid #aaa; +} +#quickReplyWarning +{ + float: right; +} + +/* Styles for edit post section +---------------------------------------------------- */ +#post_header dt +{ + float: right; +} +#post_header dd +{ + float: right; +} +ul.post_options +{ + margin: 0 1em 0 0; +} +ul.post_options li +{ + float: right; +} +#postAttachment dd, #postAttachment2 dd +{ + margin: .3em 1em .3em 0; +} +#postAttachment dt, #postAttachment2 dt +{ + font-weight: bold; +} +#postAttachment3 +{ + margin-left: 0; + margin-left: 1em; +} +.post_verification #verification_control +{ + margin: .3em 1em .3em 0; +} + +/* Styles for edit event section +---------------------------------------------------- */ +#post_event div.event_options +{ + float: left; +} +#post_event #event_main input +{ + margin: 0 0 1em 0; + float: right; +} +#post_event #event_main div.smalltext +{ + float: left; +} +#post_event ul.event_main li +{ + float: left; +} +#post_event ul.event_options +{ + padding: 0 .7em .7em 0; +} +#post_event #event_main select, #post_event ul.event_options li select, #post_event ul.event_options li .input_check +{ + margin: 0 0 0 1em; +} + +/* Styles for edit poll section. +---------------------------------------------------- */ +#edit_poll fieldset input +{ + margin-right: 7em; +} +#edit_poll ul.poll_main li +{ + padding-right: 1em; +} +#edit_poll ul.poll_main input +{ + margin-right: 1em; +} +#edit_poll div.poll_options +{ + float: right; +} +#edit_poll ul.poll_main, dl.poll_options +{ + padding: 0 .7em 0 0; +} +#edit_poll dl.poll_options dt +{ + padding: 0 1em 0 0; +} +#edit_poll dl.poll_options dd input +{ + margin-right: 0; +} + +/* Styles for the recent messages section. +---------------------------------------------------- */ +.readbuttons .buttonlist, .readbuttons .buttonlist_bottom +{ + margin-right: 0; + margin-left: 1em; + float: left; +} + +/* Styles for the move topic section. +---------------------------------------------------- */ +.move_topic +{ + text-align: right; +} + +/* Styles for the login areas. +------------------------------------------------------- */ +.login dt +{ + float: right; + text-align: left; +} +.login dd +{ + float: left; + text-align: right; +} +.login h3 img +{ + float: right; + margin: 4px 0 0 0.5em; +} + +/* Styles for the registration section. +------------------------------------------------------- */ +dl.register_form +{ + clear: left; +} + +dl.register_form dt +{ + float: right; + clear: both; +} +dl.register_form dd +{ + float: right; +} + +/* Styles for maintenance mode. +------------------------------------------------------- */ +#maintenance_mode +{ + text-align: right; +} +#maintenance_mode img.floatleft +{ + margin-right: 0; + margin-left: 1em; +} + +h3.titlebg img +{ + margin-right: 0; + margin-left: 0.5em; +} +tr.titlebg td +{ + padding-left: 0.7em; + padding-right: 0.7em; +} +#admin_menu +{ + padding-right: 0; +} +#admin_content +{ + clear: right; +} + +/* Styles for sidebar menus. +------------------------------------------------------- */ +#left_admsection +{ + float: right; + margin-right: 0; + margin-left: 10px; +} +.left_admmenu li +{ + padding: 0 0.5em 0 0; +} + +/* Styles for generic tables. +------------------------------------------------------- */ +.topic_table td.stickybg2 +{ + background-position: 2% 4px; +} +.topic_table td.lockedbg2 +{ + background-position: 2% 4px; +} + +/* Styles for (fatal) errors. +------------------------------------------------- */ +.errorbox p.alert +{ + float: right; +} + +/* Styles for the profile section. +------------------------------------------------- */ +#profileview #detailedinfo .content +{ + border-left: none; + border-right: 1px solid #aaa; +} +/* The basic user info on the left */ +#basicinfo +{ + float: right; +} +#profileview #basicinfo .content +{ + padding: 1em; +} +#detailedinfo +{ + float: left; +} +#basicinfo ul li +{ + float: right; + margin-left: 5px; + margin-right: 0; +} +#detailedinfo div.content dl, #tracking div.content dl +{ + clear: left; +} +#detailedinfo div.content dt, #tracking div.content dt +{ + float: right; +} +#detailedinfo div.content dd, #tracking div.content dd +{ + float: right; +} +.signature, .custom_fields_above_signature, .attachments +{ + clear: left; +} +#avatar_server_stored div +{ + float: right; +} +#main_admsection #basicinfo h4 +{ + float: right; +} +#main_admsection #basicinfo img.avatar +{ + float: left; +} +#main_admsection #basicinfo ul +{ + clear: right; +} +#main_admsection #basicinfo span#userstatus +{ + clear: right; +} + + +/* Profile statistics */ +#generalstats div.content dt +{ + float: right; +} +#generalstats div.content dd +{ + float: right; +} + +/* Activity by time */ +#activitytime +{ + clear: right; +} +.activity_stats li +{ + float: right; +} +.activity_stats li span +{ + border-width: 1px 0 0 1px; +} +.activity_stats li.last span +{ + border-left: none; +} + +/* Most popular boards by posts and activity */ +#popularposts +{ + float: right; +} +#popularactivity +{ + float: left; +} + +#popularposts div.content dt, #popularactivity div.content dt +{ + float: right; +} +#popularposts div.content dd, #popularactivity div.content dd +{ + float: right; +} + +.profile_pie +{ + background-image: url(../images/stats_pie_rtl.png); + float: right; + margin-right: 0; + margin-left: 1em; +} + +/* View posts */ +.time +{ + float: left; +} +.counter +{ + padding: 0.2em 0.2em 0.1em 0.5em; + float: right; +} +.mod_icons +{ + text-align: left; + margin-right: 0; + margin-left: 1em; +} +#permissions div.permission_name +{ + margin: 0 0 0 1%; +} +#ip_list li.header, #ip_list li.ip +{ + float: right; +} +#creator dt +{ + float: right; +} +#creator dd +{ + float: right; +} + +.ignoreboards ul +{ + margin: 0 1em 0 0; +} +.ignoreboards li +{ + float: right; +} + +#pick_theme +{ + float: right; +} + +/* Styles for the statistics center. +------------------------------------------------- */ +#stats_left, #top_posters, #top_topics_replies, #top_topics_starter +{ + float: right; +} +#stats_right, #top_boards, #top_topics_views, #most_online +{ + float: left; +} +dl.stats dt +{ + float: right; +} +dl.stats dd +{ + text-align: left; + float: left; +} +.stats_bar +{ + float: right; +} + +/* Styles for the personal messages section. +------------------------------------------------- */ + +#personal_messages #top_subject +{ + padding-left: 0 !important; + padding-right: 11.75em !important; +} + +/* Styles for the calendar section. +------------------------------------------------- */ +#month_grid +{ + float: right; +} + +#main_grid table.weeklist td.weekdays +{ + text-align: right; + border-right: none; + border-left: 1px solid #adadad; +} + +#calendar .buttonlist_bottom +{ + padding: 0 1ex 0 0; +} + +/* Styles for the advanced search section. +------------------------------------------------- */ +#searchform fieldset +{ + text-align: right; +} +#advanced_search dt +{ + float: right; + text-align: left; +} +#advanced_search dd +{ + float: right; + margin: 0 0.5em 0 0; + text-align: right; +} +/* Boards picker */ +#searchform fieldset div#searchBoardsExpand ul +{ + margin: 0 1em 0 0; +} +#searchform fieldset div#searchBoardsExpand li +{ + float: right; +} +#searchform fieldset p +{ + text-align: right; +} + +/* Styles for the search results page. +------------------------------------------------- */ +.search_results_posts .buttons +{ + padding: 5px 0 0 1em; +} + +/* Styles for the help section. +------------------------------------------------- */ +#helpmain ol.la +{ + padding-right: 1.5em; + padding-left: 0; +} + +/* The admin menu +------------------------------------------------- */ + +ul.admin_menu li +{ + position: relative; + float: right; + background: url(../images/admintab_back.gif) top right repeat-x; + padding-right: 4px; +} +ul.admin_menu li.last +{ + background: url(../images/admintab_back.gif) top right repeat-x; +} +ul.admin_menu li.first +{ + background: url(../images/admintab_right.gif) top right repeat-x; +} +ul.admin_menu li.first.chosen h4 +{ + background: url(../images/admintab_active_last.gif) top right repeat-x; + padding-right: 16px; +} +ul.admin_menu li.chosen +{ + background: url(../images/admintab_active_left.gif) no-repeat; + padding: 0 0 0 6px; +} + +ul.admin_menu li.last.chosen h4 +{ + background: url(../images/admintab_active_right.gif) top right no-repeat; + padding-right: 17px; +} + +ul.admin_menu li.chosen h4 +{ + background: url(../images/admintab_active_right.gif) top right no-repeat; + padding-right: 10px; +} + +.main_menu li.last.active +{ + float: right; + background: url(../images/maintab_last.gif) no-repeat bottom right; + padding: 0 8px 0 0; +} +/* IE6 doesn't support multiple class selectors */ +.main_menu li.lastactive +{ + padding: 0 8px 0 0; + background: url(../images/maintab_last.gif) no-repeat bottom right; +} + +/* float a list horizontally */ +ul.horizlist li +{ + float: right; +} +.buttonlist, .buttonlist_bottom +{ + margin-left: 1.5em !important; +} + +.generic_tab_strip .buttonlist +{ + float: left !important; +} + +/* Styles for the admincenter (reverse admin.css). +------------------------------------------------- */ +#quick_search +{ + margin-left: 5px !important; +} +.features_image +{ + float: right !important; + margin: 0 1em 0.5em 2em !important; +} +.features_switch +{ + float: left !important; +} +.features h4 +{ + padding: 1em 0.5em 0.5em 0 !important; +} +/* admin home */ +#live_news div.content dl +{ + padding: 0.5em 0.5em 0 0 !important; +} +#smfAnnouncements dd +{ + padding: 0; + margin: 0 1.5em 1em 0 !important; +} +#quick_tasks li +{ + float: right; + list-style-type: none !important; +} +.home_image +{ + float: right !important; +} +/* common admin classes */ +.additional_row input +{ + margin-left: 0; + margin-right: 2em; +} +#error_log td div.marginleft +{ + margin: 0 1ex 0 0 !important; +} + +/* Styles for the package manager. +------------------------------------------------- */ +#package_list .tborder +{ + margin: .25em 26px .25em 0 !important; +} +#package_list ol, #package_list ol li +{ + margin-left: 0 !important; + margin-right: 50px !important; +} + +/*ManageBoards*/ +.move_links +{ + padding: 0 0 0 13px !important; +} + +span.search_weight +{ + text-align: left !important; +} + +/*Manage Bans*/ +.ban_restriction +{ + margin: 0.2em 2.2em 0.2em 0 !important; +} +/* Themes */ +.is_directory +{ + padding-right: 18px !important; + background-position: 100% 0 !important; +} + + /* Styles for the moderation center. +------------------------------------------------- */ +.modblock_left +{ + float: right !important; + clear: left !important; +} +.modblock_right +{ + float: left !important; +} +ul.moderation_notes li +{ + padding: 4px 4px 4px 0 !important; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/css/webkit.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/css/webkit.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,8 @@ +/* special styles for Safari (and other Webkit based browsers like Chrome) +Webkit needs this otherwise the post goes off to the right. +Causes issues in IE browsers in (and cached search engines pages it breaks them). +*/ +.postarea .post +{ + float: left; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/Female.gif Binary file forum/Themes/core/images/Female.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/Male.gif Binary file forum/Themes/core/images/Male.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/administration.gif Binary file forum/Themes/core/images/admin/administration.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/attachment.gif Binary file forum/Themes/core/images/admin/attachment.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/ban.gif Binary file forum/Themes/core/images/admin/ban.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/boards.gif Binary file forum/Themes/core/images/admin/boards.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/calendar.gif Binary file forum/Themes/core/images/admin/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/change_menu.png Binary file forum/Themes/core/images/admin/change_menu.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/change_menu2.png Binary file forum/Themes/core/images/admin/change_menu2.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/corefeatures.gif Binary file forum/Themes/core/images/admin/corefeatures.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/current_theme.gif Binary file forum/Themes/core/images/admin/current_theme.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/engines.gif Binary file forum/Themes/core/images/admin/engines.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/feature_cd.png Binary file forum/Themes/core/images/admin/feature_cd.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/feature_cp.png Binary file forum/Themes/core/images/admin/feature_cp.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/feature_k.png Binary file forum/Themes/core/images/admin/feature_k.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/feature_ml.png Binary file forum/Themes/core/images/admin/feature_ml.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/feature_pm.png Binary file forum/Themes/core/images/admin/feature_pm.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/feature_ps.png Binary file forum/Themes/core/images/admin/feature_ps.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/feature_rg.png Binary file forum/Themes/core/images/admin/feature_rg.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/feature_sp.png Binary file forum/Themes/core/images/admin/feature_sp.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/feature_w.png Binary file forum/Themes/core/images/admin/feature_w.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/features.gif Binary file forum/Themes/core/images/admin/features.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/features_and_options.png Binary file forum/Themes/core/images/admin/features_and_options.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/forum_maintenance.png Binary file forum/Themes/core/images/admin/forum_maintenance.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/ignore.gif Binary file forum/Themes/core/images/admin/ignore.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/images/admin/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/languages.gif Binary file forum/Themes/core/images/admin/languages.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/logs.gif Binary file forum/Themes/core/images/admin/logs.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/mail.gif Binary file forum/Themes/core/images/admin/mail.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/maintain.gif Binary file forum/Themes/core/images/admin/maintain.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/membergroups.gif Binary file forum/Themes/core/images/admin/membergroups.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/members.gif Binary file forum/Themes/core/images/admin/members.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/members.png Binary file forum/Themes/core/images/admin/members.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/modifications.gif Binary file forum/Themes/core/images/admin/modifications.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/news.gif Binary file forum/Themes/core/images/admin/news.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/package_ops.gif Binary file forum/Themes/core/images/admin/package_ops.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/packages.gif Binary file forum/Themes/core/images/admin/packages.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/packages.png Binary file forum/Themes/core/images/admin/packages.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/paid.gif Binary file forum/Themes/core/images/admin/paid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/permissions.gif Binary file forum/Themes/core/images/admin/permissions.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/permissions.png Binary file forum/Themes/core/images/admin/permissions.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/post_moderation_allow.gif Binary file forum/Themes/core/images/admin/post_moderation_allow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/post_moderation_deny.gif Binary file forum/Themes/core/images/admin/post_moderation_deny.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/post_moderation_moderate.gif Binary file forum/Themes/core/images/admin/post_moderation_moderate.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/posts.gif Binary file forum/Themes/core/images/admin/posts.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/regcenter.gif Binary file forum/Themes/core/images/admin/regcenter.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/reports.gif Binary file forum/Themes/core/images/admin/reports.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/scheduled.gif Binary file forum/Themes/core/images/admin/scheduled.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/search.gif Binary file forum/Themes/core/images/admin/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/security.gif Binary file forum/Themes/core/images/admin/security.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/server.gif Binary file forum/Themes/core/images/admin/server.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/smiley.gif Binary file forum/Themes/core/images/admin/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/smilies_and_messageicons.png Binary file forum/Themes/core/images/admin/smilies_and_messageicons.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/subsection.gif Binary file forum/Themes/core/images/admin/subsection.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/subsection2.gif Binary file forum/Themes/core/images/admin/subsection2.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/support.gif Binary file forum/Themes/core/images/admin/support.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/support_and_credits.png Binary file forum/Themes/core/images/admin/support_and_credits.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/switch_off.png Binary file forum/Themes/core/images/admin/switch_off.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/switch_on.png Binary file forum/Themes/core/images/admin/switch_on.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/themes.gif Binary file forum/Themes/core/images/admin/themes.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admin/themes_and_layout.png Binary file forum/Themes/core/images/admin/themes_and_layout.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admintab_active_last.gif Binary file forum/Themes/core/images/admintab_active_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admintab_active_left.gif Binary file forum/Themes/core/images/admintab_active_left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admintab_active_right.gif Binary file forum/Themes/core/images/admintab_active_right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admintab_back.gif Binary file forum/Themes/core/images/admintab_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admintab_left.gif Binary file forum/Themes/core/images/admintab_left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/admintab_right.gif Binary file forum/Themes/core/images/admintab_right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/aim.gif Binary file forum/Themes/core/images/aim.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bar.gif Binary file forum/Themes/core/images/bar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bar_stats.png Binary file forum/Themes/core/images/bar_stats.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/bbc_bg.gif Binary file forum/Themes/core/images/bbc/bbc_bg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/bbc_hoverbg.gif Binary file forum/Themes/core/images/bbc/bbc_hoverbg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/bold.gif Binary file forum/Themes/core/images/bbc/bold.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/center.gif Binary file forum/Themes/core/images/bbc/center.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/code.gif Binary file forum/Themes/core/images/bbc/code.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/divider.gif Binary file forum/Themes/core/images/bbc/divider.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/email.gif Binary file forum/Themes/core/images/bbc/email.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/flash.gif Binary file forum/Themes/core/images/bbc/flash.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/ftp.gif Binary file forum/Themes/core/images/bbc/ftp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/glow.gif Binary file forum/Themes/core/images/bbc/glow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/hr.gif Binary file forum/Themes/core/images/bbc/hr.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/img.gif Binary file forum/Themes/core/images/bbc/img.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/images/bbc/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/italicize.gif Binary file forum/Themes/core/images/bbc/italicize.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/left.gif Binary file forum/Themes/core/images/bbc/left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/list.gif Binary file forum/Themes/core/images/bbc/list.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/move.gif Binary file forum/Themes/core/images/bbc/move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/orderlist.gif Binary file forum/Themes/core/images/bbc/orderlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/pre.gif Binary file forum/Themes/core/images/bbc/pre.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/quote.gif Binary file forum/Themes/core/images/bbc/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/resize-handle.gif Binary file forum/Themes/core/images/bbc/resize-handle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/right.gif Binary file forum/Themes/core/images/bbc/right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/shadow.gif Binary file forum/Themes/core/images/bbc/shadow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/strike.gif Binary file forum/Themes/core/images/bbc/strike.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/sub.gif Binary file forum/Themes/core/images/bbc/sub.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/sup.gif Binary file forum/Themes/core/images/bbc/sup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/table.gif Binary file forum/Themes/core/images/bbc/table.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/tele.gif Binary file forum/Themes/core/images/bbc/tele.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/toggle.gif Binary file forum/Themes/core/images/bbc/toggle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/underline.gif Binary file forum/Themes/core/images/bbc/underline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/unformat.gif Binary file forum/Themes/core/images/bbc/unformat.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/bbc/url.gif Binary file forum/Themes/core/images/bbc/url.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/blank.gif Binary file forum/Themes/core/images/blank.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/board.gif Binary file forum/Themes/core/images/board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/board_select_spot.gif Binary file forum/Themes/core/images/board_select_spot.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/board_select_spot_child.gif Binary file forum/Themes/core/images/board_select_spot_child.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buddy_useroff.gif Binary file forum/Themes/core/images/buddy_useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buddy_useron.gif Binary file forum/Themes/core/images/buddy_useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/approve.gif Binary file forum/Themes/core/images/buttons/approve.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/calendarpe.gif Binary file forum/Themes/core/images/buttons/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/close.gif Binary file forum/Themes/core/images/buttons/close.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/delete.gif Binary file forum/Themes/core/images/buttons/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/details.gif Binary file forum/Themes/core/images/buttons/details.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/ignore.gif Binary file forum/Themes/core/images/buttons/ignore.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/im_reply.gif Binary file forum/Themes/core/images/buttons/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/im_reply_all.gif Binary file forum/Themes/core/images/buttons/im_reply_all.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/images/buttons/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/merge.gif Binary file forum/Themes/core/images/buttons/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/modify.gif Binary file forum/Themes/core/images/buttons/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/notify_sm.gif Binary file forum/Themes/core/images/buttons/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/quote.gif Binary file forum/Themes/core/images/buttons/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/reply.gif Binary file forum/Themes/core/images/buttons/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/reply_sm.gif Binary file forum/Themes/core/images/buttons/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/restore_topic.gif Binary file forum/Themes/core/images/buttons/restore_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/search.gif Binary file forum/Themes/core/images/buttons/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/buttons/split.gif Binary file forum/Themes/core/images/buttons/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/cake.png Binary file forum/Themes/core/images/cake.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/catbg.jpg Binary file forum/Themes/core/images/catbg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/catbg2.jpg Binary file forum/Themes/core/images/catbg2.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/collapse.gif Binary file forum/Themes/core/images/collapse.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/construction.png Binary file forum/Themes/core/images/construction.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/email_sm.gif Binary file forum/Themes/core/images/email_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/english/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/images/english/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/english/new.gif Binary file forum/Themes/core/images/english/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/expand.gif Binary file forum/Themes/core/images/expand.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/filter.gif Binary file forum/Themes/core/images/filter.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/helptopics.gif Binary file forum/Themes/core/images/helptopics.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/assist.gif Binary file forum/Themes/core/images/icons/assist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/calendar.gif Binary file forum/Themes/core/images/icons/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/clip.gif Binary file forum/Themes/core/images/icons/clip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/config_sm.gif Binary file forum/Themes/core/images/icons/config_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/delete.gif Binary file forum/Themes/core/images/icons/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/field_check.gif Binary file forum/Themes/core/images/icons/field_check.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/field_invalid.gif Binary file forum/Themes/core/images/icons/field_invalid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/field_valid.gif Binary file forum/Themes/core/images/icons/field_valid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/im_newmsg.gif Binary file forum/Themes/core/images/icons/im_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/images/icons/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/info.gif Binary file forum/Themes/core/images/icons/info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/last_post.gif Binary file forum/Themes/core/images/icons/last_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/login.gif Binary file forum/Themes/core/images/icons/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/login_sm.gif Binary file forum/Themes/core/images/icons/login_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/members.gif Binary file forum/Themes/core/images/icons/members.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/modify_inline.gif Binary file forum/Themes/core/images/icons/modify_inline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/modify_small.gif Binary file forum/Themes/core/images/icons/modify_small.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/notify_sm.gif Binary file forum/Themes/core/images/icons/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/online.gif Binary file forum/Themes/core/images/icons/online.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/package_installed.gif Binary file forum/Themes/core/images/icons/package_installed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/package_old.gif Binary file forum/Themes/core/images/icons/package_old.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/pm_read.gif Binary file forum/Themes/core/images/icons/pm_read.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/pm_replied.gif Binary file forum/Themes/core/images/icons/pm_replied.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/profile_sm.gif Binary file forum/Themes/core/images/icons/profile_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/quick_lock.gif Binary file forum/Themes/core/images/icons/quick_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/quick_move.gif Binary file forum/Themes/core/images/icons/quick_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/quick_remove.gif Binary file forum/Themes/core/images/icons/quick_remove.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/quick_sticky.gif Binary file forum/Themes/core/images/icons/quick_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/icons/show_sticky.gif Binary file forum/Themes/core/images/icons/show_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/im_off.gif Binary file forum/Themes/core/images/im_off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/im_on.gif Binary file forum/Themes/core/images/im_on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/im_sm_newmsg.gif Binary file forum/Themes/core/images/im_sm_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/im_sm_prefs.gif Binary file forum/Themes/core/images/im_sm_prefs.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/im_switch.gif Binary file forum/Themes/core/images/im_switch.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/images/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/ip.gif Binary file forum/Themes/core/images/ip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/maintab_active_back.gif Binary file forum/Themes/core/images/maintab_active_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/maintab_active_first.gif Binary file forum/Themes/core/images/maintab_active_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/maintab_active_last.gif Binary file forum/Themes/core/images/maintab_active_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/maintab_back.gif Binary file forum/Themes/core/images/maintab_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/maintab_first.gif Binary file forum/Themes/core/images/maintab_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/maintab_first_prev.gif Binary file forum/Themes/core/images/maintab_first_prev.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/maintab_last.gif Binary file forum/Themes/core/images/maintab_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/maintab_last_prev.gif Binary file forum/Themes/core/images/maintab_last_prev.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/menubg.gif Binary file forum/Themes/core/images/menubg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/message_sm.gif Binary file forum/Themes/core/images/message_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/mirrortab_active_back.gif Binary file forum/Themes/core/images/mirrortab_active_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/mirrortab_active_first.gif Binary file forum/Themes/core/images/mirrortab_active_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/mirrortab_active_last.gif Binary file forum/Themes/core/images/mirrortab_active_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/mirrortab_back.gif Binary file forum/Themes/core/images/mirrortab_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/mirrortab_first.gif Binary file forum/Themes/core/images/mirrortab_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/mirrortab_first_prev.gif Binary file forum/Themes/core/images/mirrortab_first_prev.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/mirrortab_last.gif Binary file forum/Themes/core/images/mirrortab_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/mirrortab_last_prev.gif Binary file forum/Themes/core/images/mirrortab_last_prev.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/msntalk.gif Binary file forum/Themes/core/images/msntalk.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/new_bar.gif Binary file forum/Themes/core/images/new_bar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/new_none.gif Binary file forum/Themes/core/images/new_none.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/new_some.gif Binary file forum/Themes/core/images/new_some.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/off.gif Binary file forum/Themes/core/images/off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/on.gif Binary file forum/Themes/core/images/on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/on2.gif Binary file forum/Themes/core/images/on2.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/openid.gif Binary file forum/Themes/core/images/openid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/pm_recipient_delete.gif Binary file forum/Themes/core/images/pm_recipient_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/poll_left.gif Binary file forum/Themes/core/images/poll_left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/poll_middle.gif Binary file forum/Themes/core/images/poll_middle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/poll_right.gif Binary file forum/Themes/core/images/poll_right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/angry.gif Binary file forum/Themes/core/images/post/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/cheesy.gif Binary file forum/Themes/core/images/post/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/clip.gif Binary file forum/Themes/core/images/post/clip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/exclamation.gif Binary file forum/Themes/core/images/post/exclamation.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/grin.gif Binary file forum/Themes/core/images/post/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/images/post/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/lamp.gif Binary file forum/Themes/core/images/post/lamp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/moved.gif Binary file forum/Themes/core/images/post/moved.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/question.gif Binary file forum/Themes/core/images/post/question.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/recycled.gif Binary file forum/Themes/core/images/post/recycled.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/sad.gif Binary file forum/Themes/core/images/post/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/smiley.gif Binary file forum/Themes/core/images/post/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/thumbdown.gif Binary file forum/Themes/core/images/post/thumbdown.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/thumbup.gif Binary file forum/Themes/core/images/post/thumbup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/wink.gif Binary file forum/Themes/core/images/post/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/wireless.gif Binary file forum/Themes/core/images/post/wireless.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/post/xx.gif Binary file forum/Themes/core/images/post/xx.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/redirect.gif Binary file forum/Themes/core/images/redirect.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/selected.gif Binary file forum/Themes/core/images/selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/smflogo.gif Binary file forum/Themes/core/images/smflogo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/smiley_select_spot.gif Binary file forum/Themes/core/images/smiley_select_spot.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/sort_down.gif Binary file forum/Themes/core/images/sort_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/sort_up.gif Binary file forum/Themes/core/images/sort_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/split_deselect.gif Binary file forum/Themes/core/images/split_deselect.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/split_select.gif Binary file forum/Themes/core/images/split_select.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/star.gif Binary file forum/Themes/core/images/star.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/staradmin.gif Binary file forum/Themes/core/images/staradmin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stargmod.gif Binary file forum/Themes/core/images/stargmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/starmod.gif Binary file forum/Themes/core/images/starmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stats_board.gif Binary file forum/Themes/core/images/stats_board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stats_boards.gif Binary file forum/Themes/core/images/stats_boards.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stats_history.gif Binary file forum/Themes/core/images/stats_history.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stats_info.gif Binary file forum/Themes/core/images/stats_info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stats_pie.png Binary file forum/Themes/core/images/stats_pie.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stats_pie_rtl.png Binary file forum/Themes/core/images/stats_pie_rtl.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stats_posters.gif Binary file forum/Themes/core/images/stats_posters.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stats_replies.gif Binary file forum/Themes/core/images/stats_replies.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/stats_views.gif Binary file forum/Themes/core/images/stats_views.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/thumbnail.gif Binary file forum/Themes/core/images/thumbnail.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/titlebg.jpg Binary file forum/Themes/core/images/titlebg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topbg.jpg Binary file forum/Themes/core/images/topbg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/hot_poll.gif Binary file forum/Themes/core/images/topic/hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/hot_post.gif Binary file forum/Themes/core/images/topic/hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/hot_post_locked.gif Binary file forum/Themes/core/images/topic/hot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/hot_post_locked_sticky.gif Binary file forum/Themes/core/images/topic/hot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/hot_post_sticky.gif Binary file forum/Themes/core/images/topic/hot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/images/topic/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/my_hot_poll.gif Binary file forum/Themes/core/images/topic/my_hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/my_hot_post.gif Binary file forum/Themes/core/images/topic/my_hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/my_normal_poll.gif Binary file forum/Themes/core/images/topic/my_normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/my_normal_post.gif Binary file forum/Themes/core/images/topic/my_normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/my_veryhot_poll.gif Binary file forum/Themes/core/images/topic/my_veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/my_veryhot_post.gif Binary file forum/Themes/core/images/topic/my_veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/normal_poll.gif Binary file forum/Themes/core/images/topic/normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/normal_poll_locked.gif Binary file forum/Themes/core/images/topic/normal_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/normal_poll_locked_sticky.gif Binary file forum/Themes/core/images/topic/normal_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/normal_poll_sticky.gif Binary file forum/Themes/core/images/topic/normal_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/normal_post.gif Binary file forum/Themes/core/images/topic/normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/normal_post_locked.gif Binary file forum/Themes/core/images/topic/normal_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/normal_post_locked_sticky.gif Binary file forum/Themes/core/images/topic/normal_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/normal_post_sticky.gif Binary file forum/Themes/core/images/topic/normal_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/veryhot_poll.gif Binary file forum/Themes/core/images/topic/veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/veryhot_post.gif Binary file forum/Themes/core/images/topic/veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/veryhot_post_locked.gif Binary file forum/Themes/core/images/topic/veryhot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/veryhot_post_locked_sticky.gif Binary file forum/Themes/core/images/topic/veryhot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/topic/veryhot_post_sticky.gif Binary file forum/Themes/core/images/topic/veryhot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/upshrink.gif Binary file forum/Themes/core/images/upshrink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/upshrink2.gif Binary file forum/Themes/core/images/upshrink2.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/useroff.gif Binary file forum/Themes/core/images/useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/useron.gif Binary file forum/Themes/core/images/useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/warn.gif Binary file forum/Themes/core/images/warn.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/warning_moderate.gif Binary file forum/Themes/core/images/warning_moderate.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/warning_mute.gif Binary file forum/Themes/core/images/warning_mute.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/warning_watch.gif Binary file forum/Themes/core/images/warning_watch.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/www.gif Binary file forum/Themes/core/images/www.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/images/www_sm.gif Binary file forum/Themes/core/images/www_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/index.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/index.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,522 @@ + +'; + + // The ?fin20 part of this link is just here to make sure browsers don't cache it wrongly. + echo ' + '; + + // Some browsers need an extra stylesheet due to bugs/compatibility issues. + foreach (array('ie7', 'ie6', 'webkit') as $cssfix) + if ($context['browser']['is_' . $cssfix]) + echo ' + '; + + // RTL languages require an additional stylesheet. + if ($context['right_to_left']) + echo ' + '; + + // Here comes the JavaScript bits! + echo ' + + + '; + + echo ' + + ', !empty($context['meta_keywords']) ? ' + ' : '', ' + ', $context['page_title_html_safe'], ''; + + // Please don't index these Mr Robot. + if (!empty($context['robot_no_index'])) + echo ' + '; + + // Present a canonical url for search engines to prevent duplicate content in their indices. + if (!empty($context['canonical_url'])) + echo ' + '; + + // Show all the relative links, such as help, search, contents, and the like. + echo ' + + + '; + + // If RSS feeds are enabled, advertise the presence of one. + if (!empty($modSettings['xmlnews_enable']) && (!empty($modSettings['allow_guestAccess']) || $context['user']['is_logged'])) + echo ' + '; + + // If we're viewing a topic, these should be the previous and next topics, respectively. + if (!empty($context['current_topic'])) + echo ' + + '; + + // If we're in a board, or a topic for that matter, the index will be the board's index. + if (!empty($context['current_board'])) + echo ' + '; + + // We'll have to use the cookie to remember the header... + if ($context['user']['is_guest']) + { + $options['collapse_header'] = !empty($_COOKIE['upshrink']); + $options['collapse_header_ic'] = !empty($_COOKIE['upshrinkIC']); + } + + // Output any remaining HTML headers. (from mods, maybe?) + echo $context['html_headers']; + + echo ' + +'; +} + +function template_body_above() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    + +

    '; + + if (empty($context['header_logo_url_html_safe'])) + echo $context['forum_name_html_safe']; + else + echo ' + ', $context['forum_name_html_safe'], ''; + + echo ' +

    +
    '; + + // Display user name and time. + echo ' +
      +
    • + ', $context['current_time'], ' + +
    • '; + + if ($context['user']['is_logged']) + echo ' +
    • ', $txt['hello_member_ndt'], ' ', $context['user']['name'], '
    • '; + else + echo ' +
    • ', $txt['hello_guest'], ' ', $txt['guest'], '
    • '; + + echo ' +
    '; + + if ($context['user']['is_logged'] || !empty($context['show_login_bar'])) + echo ' + '; + + echo ' + +
    '; + + // Define the upper_section toggle in JavaScript. + echo ' + '; + + // Show the menu here, according to the menu sub template. + template_menu(); + + // Show the navigation tree. + theme_linktree(); + + // The main content should go here. + echo ' +
    '; +} + +function template_body_below() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    '; + + // Show the "Powered by" and "Valid" logos, as well as the copyright. Remember, the copyright must be somewhere! + echo ' +
    + '; + + // Show the load time? + if ($context['show_load_time']) + echo ' +

    ', $txt['page_created'], $context['load_time'], $txt['seconds_with'], $context['load_queries'], $txt['queries'], '

    '; + + echo ' +
    +
    '; +} + +function template_html_below() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +'; +} + +// Show a linktree. This is that thing that shows "My Community | General Category | General Discussion".. +function theme_linktree($force_show = false) +{ + global $context, $settings, $options, $shown_linktree; + + // If linktree is empty, just return - also allow an override. + if (empty($context['linktree']) || (!empty($context['dont_default_linktree']) && !$force_show)) + return; + + echo ' +
      '; + + // Each tree item has a URL and name. Some may have extra_before and extra_after. + foreach ($context['linktree'] as $link_num => $tree) + { + echo ' + '; + + // Show something before the link? + if (isset($tree['extra_before'])) + echo $tree['extra_before']; + + // Show the link, including a URL if it should have one. + echo $settings['linktree_link'] && isset($tree['url']) ? ' + ' . $tree['name'] . '' : '' . $tree['name'] . ''; + + // Show something after the link...? + if (isset($tree['extra_after'])) + echo $tree['extra_after']; + + // Don't show a separator for the last one. + if ($link_num != count($context['linktree']) - 1) + echo ' >'; + + echo ' + '; + } + echo ' +
    '; + + $shown_linktree = true; +} + +// Show the menu up top. Something like [home] [help] [profile] [logout]... +function template_menu() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + '; +} + +// Generate a strip of buttons. +function template_button_strip($button_strip, $direction = 'top', $strip_options = array()) +{ + global $settings, $context, $txt, $scripturl; + + if (!is_array($strip_options)) + $strip_options = array(); + + // Right to left menu should be in reverse order. + if ($context['right_to_left']) + $button_strip = array_reverse($button_strip, true); + + // Create the buttons... + $buttons = array(); + foreach ($button_strip as $key => $value) + if (!isset($value['test']) || !empty($context[$value['test']])) + $buttons[] = ' + ' . (isset($value['active']) ? '' . $txt[$value['text']] . '' : $txt[$value['text']]) . ''; + + // No buttons? No button strip either. + if (empty($buttons)) + return; + + // Make the last one, as easy as possible. + $list_item = array('
  23. ', '
  24. '); + $active_item = array('
  25. ', '
  26. '); + + $buttons[count($buttons) - 1] = str_replace($list_item, $active_item, $buttons[count($buttons) - 1]); + + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/languages/Settings.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/languages/Settings.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ +
    Author: The Simple Machines Team'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/languages/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/languages/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/license.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/license.txt Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,27 @@ +Copyright © 2011 Simple Machines. All rights reserved. + +Developed by: Simple Machines Forum Project + Simple Machines + http://www.simplemachines.org + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + 3. Neither the names of Simple Machines Forum, Simple Machines, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior written + permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +WITH THE SOFTWARE. + +This license may be viewed online at http://www.simplemachines.org/about/smf/license.php \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/scripts/theme.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/scripts/theme.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,97 @@ +// The purpose of this code is to fix the height of overflow: auto blocks, because some browsers can't figure it out for themselves. +function smf_codeBoxFix() +{ + var codeFix = document.getElementsByTagName('code'); + for (var i = codeFix.length - 1; i >= 0; i--) + { + if (is_webkit && codeFix[i].offsetHeight < 20) + codeFix[i].style.height = (codeFix[i].offsetHeight + 20) + 'px'; + + else if (is_ff && (codeFix[i].scrollWidth > codeFix[i].clientWidth || codeFix[i].clientWidth == 0)) + codeFix[i].style.overflow = 'scroll'; + + else if ('currentStyle' in codeFix[i] && codeFix[i].currentStyle.overflow == 'auto' && (codeFix[i].currentStyle.height == '' || codeFix[i].currentStyle.height == 'auto') && (codeFix[i].scrollWidth > codeFix[i].clientWidth || codeFix[i].clientWidth == 0) && (codeFix[i].offsetHeight != 0)) + codeFix[i].style.height = (codeFix[i].offsetHeight + 24) + 'px'; + } +} + +// Add a fix for code stuff? +if ((is_ie && !is_ie4) || is_webkit || is_ff) + addLoadEvent(smf_codeBoxFix); + +// Toggles the element height and width styles of an image. +function smc_toggleImageDimensions() +{ + var oImages = document.getElementsByTagName('IMG'); + for (oImage in oImages) + { + // Not a resized image? Skip it. + if (oImages[oImage].className == undefined || oImages[oImage].className.indexOf('bbc_img resized') == -1) + continue; + + oImages[oImage].style.cursor = 'pointer'; + oImages[oImage].onclick = function() { + this.style.width = this.style.height = this.style.width == 'auto' ? null : 'auto'; + }; + } +} + +// Add a load event for the function above. +addLoadEvent(smc_toggleImageDimensions); + +// Adds a button to a certain button strip. +function smf_addButton(sButtonStripId, bUseImage, oOptions) +{ + var oButtonStrip = document.getElementById(sButtonStripId); + var aItems = oButtonStrip.getElementsByTagName('li'); + + // Remove the 'last' class from the last item. + if (aItems.length > 0) + { + var oLastItem = aItems[aItems.length - 1]; + oLastItem.className = oLastItem.className.replace(/\s*last/, 'position_holder'); + } + + // Add the button. + var oButtonStripList = oButtonStrip.getElementsByTagName('ul')[0]; + var oNewButton = document.createElement('li'); + oNewButton.className = 'last'; + setInnerHTML(oNewButton, '' + oOptions.sText + ''); + + oButtonStripList.appendChild(oNewButton); +} + +// Adds hover events to list items. Used for a versions of IE that don't support this by default. +var smf_addListItemHoverEvents = function() +{ + var cssRule, newSelector; + + // Add a rule for the list item hover event to every stylesheet. + for (var iStyleSheet = 0; iStyleSheet < document.styleSheets.length; iStyleSheet ++) + for (var iRule = 0; iRule < document.styleSheets[iStyleSheet].rules.length; iRule ++) + { + oCssRule = document.styleSheets[iStyleSheet].rules[iRule]; + if (oCssRule.selectorText.indexOf('LI:hover') != -1) + { + sNewSelector = oCssRule.selectorText.replace(/LI:hover/gi, 'LI.iehover'); + document.styleSheets[iStyleSheet].addRule(sNewSelector, oCssRule.style.cssText); + } + } + + // Now add handling for these hover events. + var oListItems = document.getElementsByTagName('LI'); + for (oListItem in oListItems) + { + oListItems[oListItem].onmouseover = function() { + this.className += ' iehover'; + }; + + oListItems[oListItem].onmouseout = function() { + this.className = this.className.replace(new RegExp(' iehover\\b'), ''); + }; + } +} + +// Add hover events to list items if the browser requires it. +if (is_ie6down && 'attachEvent' in window) + window.attachEvent('onload', smf_addListItemHoverEvents); diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/core/theme_info.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/core/theme_info.xml Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,15 @@ + + + + Core Theme + + info@simplemachines.org + + http://www.simplemachines.org/ + + html,body + + index + + + diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Admin.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Admin.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2112 @@ + +
    +

    '; + + if ($context['user']['is_admin']) + echo ' + +
    + + + + +
    +
    '; + + echo $txt['admin_center'], ' +

    +
    + +
    +
    + ', $txt['hello_guest'], ' ', $context['user']['name'], '! + ', sprintf($txt['admin_main_welcome'], $txt['admin_center'], $txt['help'], $txt['help']), ' +
    +
    + '; + + // Is there an update available? + echo ' +
    '; + + echo ' +
    '; + + // Display the "live news" from simplemachines.org. + echo ' +
    +
    +

    + ', $txt['help'], ' ', $txt['live'], ' +

    +
    +
    + +
    +
    ', $txt['lfyi'], '
    +
    + +
    +
    '; + + // Show the user version information from their server. + echo ' +
    + +
    + +
    +
    + ', $txt['support_versions'], ':
    + ', $txt['support_versions_forum'], ': + ', $context['forum_version'], '
    + ', $txt['support_versions_current'], ': + ??
    + ', $context['can_admin'] ? '' . $txt['version_check_more'] . '' : '', '
    '; + + // Display all the members who can administrate the forum. + echo ' +
    + ', $txt['administrators'], ': + ', implode(', ', $context['administrators']); + // If we have lots of admins... don't show them all. + if (!empty($context['more_admins_link'])) + echo ' + (', $context['more_admins_link'], ')'; + + echo ' +
    +
    + +
    +
    +
    '; + + echo ' +
    + +
    +
      '; + + foreach ($context['quick_admin_tasks'] as $task) + echo ' +
    • + ', !empty($task['icon']) ? '' : '', ' +
      ', $task['link'], '
      + ', $task['description'],' +
    • '; + + echo ' +
    +
    + +
    +
  27. +
    '; + + // The below functions include all the scripts needed from the simplemachines.org site. The language and format are passed for internationalization. + if (empty($modSettings['disable_smf_js'])) + echo ' + + '; + + // This sets the announcements and current versions themselves ;). + echo ' + + '; +} + +// Show some support information and credits to those who helped make this. +function template_credits() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Show the user version information from their server. + echo ' + +
    +
    +

    + ', $txt['support_title'], ' +

    +
    +
    + +
    + ', $txt['support_versions'], ':
    + ', $txt['support_versions_forum'], ': + ', $context['forum_version'], '', $context['can_admin'] ? ' ' . $txt['version_check_more'] . '' : '', '
    + ', $txt['support_versions_current'], ': + ??
    '; + + // Display all the variables we have server information for. + foreach ($context['current_versions'] as $version) + echo ' + ', $version['title'], ': + ', $version['version'], '
    '; + + echo ' +
    + +
    + '; + + // Point the admin to common support resources. + echo ' +
    +

    + ', $txt['support_resources'], ' +

    +
    +
    + +
    +

    ', $txt['support_resources_p1'], '

    +

    ', $txt['support_resources_p2'], '

    +
    + +
    '; + + // Display latest support questions from simplemachines.org. + echo ' +
    +

    + ', $txt['help'], ' ', $txt['support_latest'], ' +

    +
    +
    + +
    +
    ', $txt['support_latest_fetch'], '
    +
    + +
    '; + + // The most important part - the credits :P. + echo ' +
    +

    + ', $txt['admin_credits'], ' +

    +
    +
    + +
    '; + + foreach ($context['credits'] as $section) + { + if (isset($section['pretext'])) + echo ' +

    ', $section['pretext'], '

    '; + + echo ' +
    '; + + foreach ($section['groups'] as $group) + { + if (isset($group['title'])) + echo ' +
    + ', $group['title'], ': +
    '; + + echo ' +
    ', implode(', ', $group['members']), '
    '; + } + + echo ' +
    '; + + if (isset($section['posttext'])) + echo ' +

    ', $section['posttext'], '

    '; + } + + echo ' +
    + +
    +
    +
    '; + + // This makes all the support information available to the support script... + echo ' + + + + '; + + // This sets the latest support stuff. + echo ' + '; +} + +// Displays information about file versions installed, and compares them to current version. +function template_view_versions() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    + ', $txt['admin_version_check'], ' +

    +
    +
    ', $txt['version_check_desc'], '
    + + + + + + + + + '; + + // The current version of the core SMF package. + echo ' + + + + + '; + + // Now list all the source file versions, starting with the overall version (if all match!). + echo ' + + + + + + +
    + ', $txt['admin_smffile'], ' + + ', $txt['dvc_your'], ' + + ', $txt['dvc_current'], ' +
    + ', $txt['admin_smfpackage'], ' + + ', $context['forum_version'], ' + + ?? +
    + ', $txt['dvc_sources'], ' + + ?? + + ?? +
    + + + '; + + // Loop through every source file displaying its version - using javascript. + foreach ($context['file_versions'] as $filename => $version) + echo ' + + + + + '; + + // Default template files. + echo ' + +
    + ', $filename, ' + + ', $version, ' + + ?? +
    + + + + + + + + + +
    + ', $txt['dvc_default'], ' + + ?? + + ?? +
    + + + '; + + foreach ($context['default_template_versions'] as $filename => $version) + echo ' + + + + + '; + + // Now the language files... + echo ' + +
    + ', $filename, ' + + ', $version, ' + + ?? +
    + + + + + + + + + +
    + ', $txt['dvc_languages'], ' + + ?? + + ?? +
    + + + '; + + foreach ($context['default_language_versions'] as $language => $files) + { + foreach ($files as $filename => $version) + echo ' + + + + + '; + } + + echo ' + +
    + ', $filename, '.', $language, '.php + + ', $version, ' + + ?? +
    '; + + // Finally, display the version information for the currently selected theme - if it is not the default one. + if (!empty($context['template_versions'])) + { + echo ' + + + + + + + + +
    + ', $txt['dvc_templates'], ' + + ?? + + ?? +
    + + + '; + + foreach ($context['template_versions'] as $filename => $version) + echo ' + + + + + '; + + echo ' + +
    + ', $filename, ' + + ', $version, ' + + ?? +
    '; + } + + echo ' +
    +
    '; + + /* Below is the hefty javascript for this. Upon opening the page it checks the current file versions with ones + held at simplemachines.org and works out if they are up to date. If they aren't it colors that files number + red. It also contains the function, swapOption, that toggles showing the detailed information for each of the + file categories. (sources, languages, and templates.) */ + echo ' + + + '; + +} + +// Form for stopping people using naughty words, etc. +function template_edit_censored() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // First section is for adding/removing words from the censored list. + echo ' +
    +
    +
    +

    + ', $txt['admin_censored_words'], ' +

    +
    +
    + +
    +

    ', $txt['admin_censored_where'], '

    '; + + // Show text boxes for censoring [bad ] => [good ]. + foreach ($context['censored_words'] as $vulgar => $proper) + echo ' +
    =>
    '; + + // Now provide a way to censor more words. + echo ' + +
    + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    + +
    '; + + // This table lets you test out your filters by typing in rude words and seeing what comes out. + echo ' +
    +

    + ', $txt['censor_test'], ' +

    +
    +
    + +
    +

    + + +

    +
    + +
    + + +
    +
    +
    '; +} + +// Maintenance is a lovely thing, isn't it? +function template_not_done() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    + ', $txt['not_done_title'], ' +

    +
    +
    + +
    + ', $txt['not_done_reason']; + + if (!empty($context['continue_percent'])) + echo ' +
    +
    +
    ', $context['continue_percent'], '%
    +
     
    +
    +
    '; + + if (!empty($context['substep_enabled'])) + echo ' +
    + ', $context['substep_title'], ' +
    +
    ', $context['substep_continue_percent'], '%
    +
     
    +
    +
    '; + + echo ' +
    +
    + ', $context['continue_post_data'], ' +
    +
    + +
    +
    +
    + '; +} + +// Template for showing settings (Of any kind really!) +function template_show_settings() +{ + global $context, $txt, $settings, $scripturl; + + echo ' + '; + + if (!empty($context['settings_insert_above'])) + echo $context['settings_insert_above']; + + echo ' +
    +
    '; + + // Is there a custom title? + if (isset($context['settings_title'])) + echo ' +
    +

    + ', $context['settings_title'], ' +

    +
    '; + + // Have we got some custom code to insert? + if (!empty($context['settings_message'])) + echo ' +
    ', $context['settings_message'], '
    '; + + // Now actually loop through all the variables. + $is_open = false; + foreach ($context['config_vars'] as $config_var) + { + // Is it a title or a description? + if (is_array($config_var) && ($config_var['type'] == 'title' || $config_var['type'] == 'desc')) + { + // Not a list yet? + if ($is_open) + { + $is_open = false; + echo ' +
    + + + '; + } + + // A title? + if ($config_var['type'] == 'title') + { + echo ' +
    +

    + ', ($config_var['help'] ? '' . $txt['help'] . '' : ''), ' + ', $config_var['label'], ' +

    +
    '; + } + // A description? + else + { + echo ' +

    + ', $config_var['label'], ' +

    '; + } + + continue; + } + + // Not a list yet? + if (!$is_open) + { + $is_open = true; + echo ' +
    + +
    +
    '; + } + + // Hang about? Are you pulling my leg - a callback?! + if (is_array($config_var) && $config_var['type'] == 'callback') + { + if (function_exists('template_callback_' . $config_var['name'])) + call_user_func('template_callback_' . $config_var['name']); + + continue; + } + + if (is_array($config_var)) + { + // First off, is this a span like a message? + if (in_array($config_var['type'], array('message', 'warning'))) + { + echo ' + + ', $config_var['label'], ' + '; + } + // Otherwise it's an input box of some kind. + else + { + echo ' + '; + + // Some quick helpers... + $javascript = $config_var['javascript']; + $disabled = !empty($config_var['disabled']) ? ' disabled="disabled"' : ''; + $subtext = !empty($config_var['subtext']) ? '
    ' . $config_var['subtext'] . '' : ''; + + // Show the [?] button. + if ($config_var['help']) + echo ' + ', $txt['help'], '', $subtext, ($config_var['type'] == 'password' ? '
    ' . $txt['admin_confirm_password'] . '' : ''), ' + '; + else + echo ' + ', $subtext, ($config_var['type'] == 'password' ? '
    ' . $txt['admin_confirm_password'] . '' : ''), ' + '; + + echo ' + ', + $config_var['preinput']; + + // Show a check box. + if ($config_var['type'] == 'check') + echo ' + '; + // Escape (via htmlspecialchars.) the text box. + elseif ($config_var['type'] == 'password') + echo ' +
    + '; + // Show a selection box. + elseif ($config_var['type'] == 'select') + { + echo ' + '; + } + // Text area? + elseif ($config_var['type'] == 'large_text') + echo ' + '; + // Permission group? + elseif ($config_var['type'] == 'permissions') + theme_inline_permissions($config_var['name']); + // BBC selection? + elseif ($config_var['type'] == 'bbc') + { + echo ' +
    + ', $txt['bbcTagsToUse_select'], ' +
      '; + + foreach ($context['bbc_columns'] as $bbcColumn) + { + foreach ($bbcColumn as $bbcTag) + echo ' +
    • + ', $bbcTag['show_help'] ? ' (?)' : '', ' +
    • '; + } + echo '
    + +
    '; + } + // A simple message? + elseif ($config_var['type'] == 'var_message') + echo ' + ', $config_var['var_message'], '
    '; + // Assume it must be a text box. + else + echo ' + '; + + echo isset($config_var['postinput']) ? ' + ' . $config_var['postinput'] : '', + ''; + } + } + + else + { + // Just show a separator. + if ($config_var == '') + echo ' + +
    +
    '; + else + echo ' +
    + ' . $config_var . ' +
    '; + } + } + + if ($is_open) + echo ' +
    '; + + if (empty($context['settings_save_dont_show'])) + echo ' +
    +
    + +
    '; + + if ($is_open) + echo ' +
    + + '; + + echo ' + + + +
    '; + + if (!empty($context['settings_post_javascript'])) + echo ' + '; + + if (!empty($context['settings_insert_below'])) + echo $context['settings_insert_below']; +} + +// Template for showing custom profile fields. +function template_show_custom_profile() +{ + global $context, $txt, $settings, $scripturl; + + // Standard fields. + template_show_list('standard_profile_fields'); + + echo ' +
    '; + + // Custom fields. + template_show_list('custom_profile_fields'); +} + +// Edit a profile field? +function template_edit_profile_field() +{ + global $context, $txt, $settings, $scripturl; + + // All the javascript for this page - quite a bit! + echo ' + '; + + echo ' +
    +
    +
    +

    + ', $context['page_title'], ' +

    +
    +
    + +
    +
    + ', $txt['custom_edit_general'], ' + +
    +
    + ', $txt['custom_edit_name'], ': +
    +
    + +
    +
    + ', $txt['custom_edit_desc'], ': +
    +
    + +
    +
    + ', $txt['custom_edit_profile'], ':
    + ', $txt['custom_edit_profile_desc'], ' +
    +
    + +
    +
    + ', $txt['custom_edit_registration'], ': +
    +
    + +
    +
    + ', $txt['custom_edit_display'], ': +
    +
    + +
    + +
    + ', $txt['custom_edit_placement'], ': +
    +
    + +
    +
    + ', $txt['help'], ' + ', $txt['custom_edit_enclose'], ':
    + ', $txt['custom_edit_enclose_desc'], ' +
    +
    + +
    +
    +
    +
    + ', $txt['custom_edit_input'], ' +
    +
    + ', $txt['custom_edit_picktype'], ': +
    +
    + +
    +
    + ', $txt['custom_edit_max_length'], ':
    + ', $txt['custom_edit_max_length_desc'], ' +
    +
    + +
    +
    + ', $txt['custom_edit_dimension'], ': +
    +
    + ', $txt['custom_edit_dimension_row'], ': + ', $txt['custom_edit_dimension_col'], ': +
    +
    + ', $txt['custom_edit_bbc'], ' +
    +
    + +
    +
    + ', $txt['help'], ' + ', $txt['custom_edit_options'], ':
    + ', $txt['custom_edit_options_desc'], ' +
    +
    +
    '; + + foreach ($context['field']['options'] as $k => $option) + { + echo ' + ', $k == 0 ? '' : '
    ', ''; + } + echo ' + + [', $txt['custom_edit_options_more'], '] +
    +
    +
    + ', $txt['custom_edit_default'], ': +
    +
    + +
    +
    +
    +
    + ', $txt['custom_edit_advanced'], ' +
    +
    + ', $txt['help'], ' + ', $txt['custom_edit_mask'], ':
    + ', $txt['custom_edit_mask_desc'], ' +
    +
    + +
    + + + +
    +
    + ', $txt['custom_edit_privacy'], ': + ', $txt['custom_edit_privacy_desc'], ' +
    +
    + +
    +
    + ', $txt['custom_edit_can_search'], ':
    + ', $txt['custom_edit_can_search_desc'], ' +
    +
    + +
    +
    + ', $txt['custom_edit_active'], ':
    + ', $txt['custom_edit_active_desc'], ' +
    +
    + +
    +
    +
    +
    + '; + + if ($context['fid']) + echo ' + '; + + echo ' +
    +
    + +
    + +
    +
    +
    '; + + // Get the javascript bits right! + echo ' + '; +} + +// Results page for an admin search. +function template_admin_search_results() +{ + global $context, $txt, $settings, $options, $scripturl; + + echo ' +
    +

    + +
    + + + +
    +
    +  ', sprintf($txt['admin_search_results_desc'], $context['search_term']), ' +

    +
    +
    + +
    '; + + if (empty($context['search_results'])) + { + echo ' +

    ', $txt['admin_search_results_none'], '

    '; + } + else + { + echo ' +
      '; + foreach ($context['search_results'] as $result) + { + // Is it a result from the online manual? + if ($context['search_type'] == 'online') + { + echo ' +
    1. +

      + ', $result['messages'][0]['subject'], ' +
      ', $result['category']['name'], '  /  + ', $result['board']['name'], ' / +

      +

      + ', $result['messages'][0]['body'], ' +

      +
    2. '; + } + // Otherwise it's... not! + else + { + echo ' +
    3. + ', $result['name'], ' [', isset($txt['admin_search_section_' . $result['type']]) ? $txt['admin_search_section_' . $result['type']] : $result['type'] , ']'; + + if ($result['help']) + echo ' +

      ', $result['help'], '

      '; + + echo ' +
    4. '; + } + } + echo ' +
    '; + } + + echo ' +
    + +
    +
    '; +} + +// Turn on and off certain key features. +function template_core_features() +{ + global $context, $txt, $settings, $options, $scripturl; + + echo ' + +
    '; + if ($context['is_new_install']) + { + echo ' +
    +

    + ', $txt['core_settings_welcome_msg'], ' +

    +
    +
    + ', $txt['core_settings_welcome_msg_desc'], ' +
    '; + } + + echo ' +
    +
    +

    + ', $txt['core_settings_title'], ' +

    +
    '; + + $alternate = true; + foreach ($context['features'] as $id => $feature) + { + echo ' +
    + +
    + ', $feature['title'], ' + +

    ', ($feature['enabled'] && $feature['url'] ? '' . $feature['title'] . '' : $feature['title']), '

    +

    ', $feature['desc'], '

    +
    + + +
    +
    + +
    '; + + $alternate = !$alternate; + } + + echo ' +
    + + + +
    +
    +
    +
    '; + + // Turn on the pretty javascript if we can! + echo ' + '; +} + +// Add a new language +function template_add_language() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $txt['add_language'], ' +

    +
    +
    + +
    +
    + ', $txt['add_language_smf'], ' + + '; + + if (!empty($context['smf_error'])) + echo ' +
    ', $txt['add_language_error_' . $context['smf_error']], '
    '; + + echo ' + +
    +
    + ', $context['browser']['is_ie'] ? ' ' : '', ' + +
    +
    + +
    + '; + + // Had some results? + if (!empty($context['smf_languages'])) + { + echo ' +
    ', $txt['add_language_smf_found'], '
    + + + + + + + + + + + + '; + + foreach ($context['smf_languages'] as $language) + echo ' + + + + + + + '; + + echo ' + +
    ', $txt['name'], '', $txt['add_language_smf_desc'], '', $txt['add_language_smf_version'], '', $txt['add_language_smf_utf8'], '', $txt['add_language_smf_install'], '
    ', $language['name'], '', $language['description'], '', $language['version'], '', $language['utf8'] ? $txt['yes'] : $txt['no'], '', $txt['add_language_smf_install'], '
    '; + } + + echo ' +
    +
    +
    '; +} + +// Download a new language file? +function template_download_language() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // Actually finished? + if (!empty($context['install_complete'])) + { + echo ' +
    +
    +

    + ', $txt['languages_download_complete'], ' +

    +
    +
    + +
    + ', $context['install_complete'], ' +
    + +
    +
    +
    '; + return; + } + + // An error? + if (!empty($context['error_message'])) + echo ' +
    +

    ', $context['error_message'], '

    +
    '; + + // Provide something of an introduction... + echo ' +
    +
    +
    +

    + ', $txt['languages_download'], ' +

    +
    +
    + +
    +

    + ', $txt['languages_download_note'], ' +

    +
    + ', $txt['languages_download_info'], ' +
    +
    + +
    '; + + // Show the main files. + template_show_list('lang_main_files_list'); + + // Now, all the images and the likes, hidden via javascript 'cause there are so fecking many. + echo ' +
    +
    +

    + ', $txt['languages_download_theme_files'], ' +

    +
    + + + + + + + + + + '; + + foreach ($context['files']['images'] as $theme => $group) + { + $count = 0; + echo ' + + + '; + + $alternate = false; + foreach ($group as $file) + { + echo ' + + + + + + '; + $alternate = !$alternate; + } + } + + echo ' + +
    + ', $txt['languages_download_filename'], ' + + ', $txt['languages_download_writable'], ' + + ', $txt['languages_download_exists'], ' + + ', $txt['languages_download_copy'], ' +
    + * ', isset($context['theme_names'][$theme]) ? $context['theme_names'][$theme] : $theme, ' +
    + ', $file['name'], '
    + ', $txt['languages_download_dest'], ': ', $file['destination'], ' +
    + ', ($file['writable'] ? $txt['yes'] : $txt['no']), ' + + ', $file['exists'] ? ($file['exists'] == 'same' ? $txt['languages_download_exists_same'] : $txt['languages_download_exists_different']) : $txt['no'], ' + + +
    '; + + // Do we want some FTP baby? + if (!empty($context['still_not_writable'])) + { + if (!empty($context['package_ftp']['error'])) + echo ' +
    + ', $context['package_ftp']['error'], ' +
    '; + + echo ' +
    +

    + ', $txt['package_ftp_necessary'], ' +

    +
    +
    + +
    +

    ', $txt['package_ftp_why'],'

    +
    +
    ', $txt['package_ftp_server'], ': +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    +
    +
    + +
    '; + } + + // Install? + echo ' +
    + + +
    +
    +
    +
    '; + + // The javascript for expand and collapse of sections. + echo ' + '; +} + +// Edit some language entries? +function template_modify_language_entries() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $txt['edit_languages'], ' +

    +
    '; + + // Not writable? + if ($context['lang_file_not_writable_message']) + echo ' +
    +

    ', $context['lang_file_not_writable_message'], '

    +
    '; + + echo ' +
    + ', $txt['edit_language_entries_primary'], ' +
    +
    + +
    +
    + ', $context['primary_settings']['name'], ' +
    +
    + ', $txt['languages_character_set'], ': +
    +
    + +
    +
    + ', $txt['languages_locale'], ': +
    +
    + +
    +
    + ', $txt['languages_dictionary'], ': +
    +
    + +
    +
    + ', $txt['languages_spelling'], ': +
    +
    + +
    +
    + ', $txt['languages_rtl'], ': +
    +
    + +
    +
    +
    +
    + + '; + + // English can't be deleted. + if ($context['lang_id'] != 'english') + echo ' + '; + + echo ' +
    +
    + +
    +
    + +
    +
    +

    + ', $txt['edit_language_entries'], ' +

    +
    +
    + ', $txt['edit_language_entries_file'], ': + + + +
    '; + + // Is it not writable? + if (!empty($context['entries_not_writable_message'])) + echo ' +
    + ', $context['entries_not_writable_message'], ' +
    '; + + // Already have some? + if (!empty($context['file_entries'])) + { + echo ' +
    + +
    +
    '; + + $cached = array(); + foreach ($context['file_entries'] as $entry) + { + // Do it in two's! + if (empty($cached)) + { + $cached = $entry; + continue; + } + + echo ' +
    + ', $cached['key'], ' +
    +
    + ', $entry['key'], ' +
    +
    + + +
    +
    + + +
    '; + $cached = array(); + } + + // Odd number? + if (!empty($cached)) + echo ' + +
    + ', $cached['key'], ' +
    +
    +
    +
    + + +
    +
    +
    '; + + echo ' +
    + '; + + echo ' +
    + +
    '; + } + echo ' +
    +
    +
    '; +} + +// This little beauty shows questions and answer from the captcha type feature. +function template_callback_question_answer_list() +{ + global $txt, $context; + + echo ' +
    + ', $txt['setup_verification_question'], ' +
    +
    + ', $txt['setup_verification_answer'], ' +
    '; + + foreach ($context['question_answers'] as $data) + echo ' + +
    + +
    +
    + +
    '; + + // Some blank ones. + for ($count = 0; $count < 3; $count++) + echo ' +
    + +
    +
    + +
    '; + + echo ' +
    +
    '; + + // The javascript needs to go at the end but we'll put it in this template for looks. + $context['settings_post_javascript'] .= ' + // Create a named element dynamically - thanks to: http://www.thunderguy.com/semicolon/2005/05/23/setting-the-name-attribute-in-internet-explorer/ + function createNamedElement(type, name, customFields) + { + var element = null; + + if (!customFields) + customFields = ""; + + // Try the IE way; this fails on standards-compliant browsers + try + { + element = document.createElement("<" + type + \' name="\' + name + \'" \' + customFields + ">"); + } + catch (e) + { + } + if (!element || element.nodeName != type.toUpperCase()) + { + // Non-IE browser; use canonical method to create named element + element = document.createElement(type); + element.name = name; + } + + return element; + } + + var placeHolder = document.getElementById(\'add_more_question_placeholder\'); + + function addAnotherQuestion() + { + var newDT = document.createElement("dt"); + + var newInput = createNamedElement("input", "question[]"); + newInput.type = "text"; + newInput.className = "input_text"; + newInput.size = "50"; + newInput.setAttribute("class", "verification_question"); + newDT.appendChild(newInput); + + newDD = document.createElement("dd"); + + newInput = createNamedElement("input", "answer[]"); + newInput.type = "text"; + newInput.className = "input_text"; + newInput.size = "50"; + newInput.setAttribute("class", "verification_answer"); + newDD.appendChild(newInput); + + placeHolder.parentNode.insertBefore(newDT, placeHolder); + placeHolder.parentNode.insertBefore(newDD, placeHolder); + } + document.getElementById(\'add_more_link_div\').style.display = \'\'; + '; +} + +// Repairing boards. +function template_repair_boards() +{ + global $context, $txt, $scripturl; + + echo ' +
    +
    +

    ', + $context['error_search'] ? $txt['errors_list'] : $txt['errors_fixing'] , ' +

    +
    +
    + +
    '; + + // Are we actually fixing them, or is this just a prompt? + if ($context['error_search']) + { + if (!empty($context['to_fix'])) + { + echo ' + ', $txt['errors_found'], ': +
      '; + + foreach ($context['repair_errors'] as $error) + echo ' +
    • + ', $error, ' +
    • '; + + echo ' +
    +

    + ', $txt['errors_fix'], ' +

    +

    + ', $txt['yes'], ' - ', $txt['no'], ' +

    '; + } + else + echo ' +

    ', $txt['maintain_no_errors'], '

    +

    + ', $txt['maintain_return'], ' +

    '; + + } + else + { + if (!empty($context['redirect_to_recount'])) + { + echo ' +

    + ', $txt['errors_do_recount'], ' +

    +
    + + +
    '; + } + else + { + echo ' +

    ', $txt['errors_fixed'], '

    +

    + ', $txt['maintain_return'], ' +

    '; + } + } + + echo ' +
    + +
    +
    +
    '; + + if (!empty($context['redirect_to_recount'])) + { + echo ' + '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/BoardIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/BoardIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,508 @@ + + ', $txt['members'], ': ', $context['common_stats']['total_members'], '  •  ', $txt['posts_made'], ': ', $context['common_stats']['total_posts'], '  •  ', $txt['topics'], ': ', $context['common_stats']['total_topics'], ' + ', ($settings['show_latest_member'] ? ' ' . $txt['welcome_member'] . ' ' . $context['common_stats']['latest_member']['link'] . '' . $txt['newest_member'] : '') , ' + '; + + // Show the news fader? (assuming there are things to show...) + if ($settings['show_newsfader'] && !empty($context['fader_news_lines'])) + { + echo ' +
    +
    +

    + + ', $txt['news'], ' +

    +
    + +
    + + '; + } + + echo ' +
    + '; + + /* Each category in categories is made up of: + id, href, link, name, is_collapsed (is it collapsed?), can_collapse (is it okay if it is?), + new (is it new?), collapse_href (href to collapse/expand), collapse_image (up/down image), + and boards. (see below.) */ + foreach ($context['categories'] as $category) + { + // If theres no parent boards we can see, avoid showing an empty category (unless its collapsed) + if (empty($category['boards']) && !$category['is_collapsed']) + continue; + + echo ' + + + + + '; + + // Assuming the category hasn't been collapsed... + if (!$category['is_collapsed']) + { + + echo ' + '; + /* Each board in each category's boards has: + new (is it new?), id, name, description, moderators (see below), link_moderators (just a list.), + children (see below.), link_children (easier to use.), children_new (are they new?), + topics (# of), posts (# of), link, href, and last_post. (see below.) */ + foreach ($category['boards'] as $board) + { + echo ' + + + + + + '; + // Show the "Child Boards: ". (there's a link_children but we're going to bold the new ones...) + if (!empty($board['children'])) + { + // Sort the links into an array with new boards bold so it can be imploded. + $children = array(); + /* Each child in each board's children has: + id, name, description, new (is it new?), topics (#), posts (#), href, link, and last_post. */ + foreach ($board['children'] as $child) + { + if (!$child['is_redirect']) + $child['link'] = '' . $child['name'] . ($child['new'] ? '' : '') . ''; + else + $child['link'] = '' . $child['name'] . ''; + + // Has it posts awaiting approval? + if ($child['can_approve_posts'] && ($child['unapproved_posts'] || $child['unapproved_topics'])) + $child['link'] .= ' (!)'; + + $children[] = $child['new'] ? '' . $child['link'] . '' : $child['link']; + } + echo ' + + + '; + } + } + echo ' + '; + } + echo ' + + + + + '; + } + echo ' +
    +
    +

    '; + + // If this category even can collapse, show a link to collapse it. + if ($category['can_collapse']) + echo ' + ', $category['collapse_image'], ''; + + if (!$context['user']['is_guest'] && !empty($category['show_unread'])) + echo ' + ', $txt['view_unread_category'], ''; + + echo ' + ', $category['link'], ' +

    +
    +
    + '; + + // If the board or children is new, show an indicator. + if ($board['new'] || $board['children_new']) + echo ' + ', $txt['new_posts'], ''; + // Is it a redirection board? + elseif ($board['is_redirect']) + echo ' + *'; + // No new posts at all! The agony!! + else + echo ' + ', $txt['old_posts'], ''; + + echo ' + + + ', $board['name'], ''; + + // Has it outstanding posts for approval? + if ($board['can_approve_posts'] && ($board['unapproved_posts'] || $board['unapproved_topics'])) + echo ' + (!)'; + + echo ' + +

    ', $board['description'] , '

    '; + + // Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.) + if (!empty($board['moderators'])) + echo ' +

    ', count($board['moderators']) == 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $board['link_moderators']), '

    '; + + // Show some basic information about the number of posts, etc. + echo ' +
    +

    ', comma_format($board['posts']), ' ', $board['is_redirect'] ? $txt['redirects'] : $txt['posts'], '
    + ', $board['is_redirect'] ? '' : comma_format($board['topics']) . ' ' . $txt['board_topics'], ' +

    +
    '; + + /* The board's and children's 'last_post's have: + time, timestamp (a number that represents the time.), id (of the post), topic (topic id.), + link, href, subject, start (where they should go for the first unread post.), + and member. (which has id, name, link, href, username in it.) */ + if (!empty($board['last_post']['id'])) + echo ' +

    ', $txt['last_post'], ' ', $txt['by'], ' ', $board['last_post']['member']['link'] , '
    + ', $txt['in'], ' ', $board['last_post']['link'], '
    + ', $txt['on'], ' ', $board['last_post']['time'],' +

    '; + echo ' +
    + ', $txt['parent_boards'], ': ', implode(', ', $children), ' +
    +
    '; + + if ($context['user']['is_logged']) + { + echo ' +
    '; + + // Mark read button. + $mark_read_button = array( + 'markread' => array('text' => 'mark_as_read', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=all;' . $context['session_var'] . '=' . $context['session_id']), + ); + + echo ' +
      +
    • ', $txt['new_posts'], '
    • +
    • ', $txt['old_posts'], '
    • +
    • ', $txt['redirect_board'], '
    • +
    +
    '; + + // Show the mark all as read button? + if ($settings['show_mark_read'] && !empty($context['categories'])) + echo '
    ', template_button_strip($mark_read_button, 'right'), '
    '; + } + else + { + echo ' +
    +
      +
    • ', $txt['old_posts'], '
    • +
    • ', $txt['redirect_board'], '
    • +
    +
    '; + } + + template_info_center(); +} + +function template_info_center() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // Here's where the "Info Center" starts... + echo ' + +
    +
    +

    + + ', sprintf($txt['info_center_title'], $context['forum_name_html_safe']), ' +

    +
    + +
    + '; + + // Info center collapse object. + echo ' + '; +} +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Calendar.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Calendar.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,824 @@ + +
    + ', template_show_month_grid('prev'), ' + ', template_show_month_grid('current'), ' + ', template_show_month_grid('next'), ' +
    +
    + ', $context['view_week'] ? template_show_week_grid('main') : template_show_month_grid('main'); + + // Build the calendar button array. + $calendar_buttons = array( + 'post_event' => array('test' => 'can_post', 'text' => 'calendar_post_event', 'image' => 'calendarpe.gif', 'lang' => true, 'url' => $scripturl . '?action=calendar;sa=post;month=' . $context['current_month'] . ';year=' . $context['current_year'] . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + template_button_strip($calendar_buttons, 'right'); + + // Show some controls to allow easy calendar navigation. + echo ' +
    + + + '; + + echo ' +
    +
    +
    + '; +} + +// Template for posting a calendar event. +function template_event_post() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // Start the javascript for drop down boxes... + echo ' + + +
    '; + + if (!empty($context['event']['new'])) + echo ' + '; + + // Start the main table. + echo ' +
    +
    +

    + ', $context['page_title'], ' +

    +
    '; + + if (!empty($context['post_error']['messages'])) + { + echo ' +
    +
    +
    + ', $context['error_type'] == 'serious' ? '' . $txt['error_while_submitting'] . '' : '', ' +
    +
    + ', implode('
    ', $context['post_error']['messages']), ' +
    +
    +
    '; + } + + echo ' +
    + +
    +
    + ', $txt['calendar_event_title'], ' + +
    + ', $txt['calendar_year'], ' + + ', $txt['calendar_month'], ' + + ', $txt['calendar_day'], ' + +
    +
    '; + + if (!empty($modSettings['cal_allowspan']) || $context['event']['new']) + echo ' +
    + ', $txt['calendar_event_options'], ' +
    +
      '; + + // If events can span more than one day then allow the user to select how long it should last. + if (!empty($modSettings['cal_allowspan'])) + { + echo ' +
    • + ', $txt['calendar_numb_days'], ' + +
    • '; + } + + // If this is a new event let the user specify which board they want the linked post to be put into. + if ($context['event']['new']) + { + echo ' +
    • + ', $txt['calendar_link_event'], ' + +
    • +
    • + ', $txt['calendar_post_in'], ' + +
    • '; + } + + if (!empty($modSettings['cal_allowspan']) || $context['event']['new']) + echo ' +
    +
    +
    '; + + echo ' +
    + '; + // Delete button? + if (empty($context['event']['new'])) + echo ' + '; + + echo ' + + +
    +
    + +
    +
    +
    +
    '; +} + +// Display a monthly calendar grid. +function template_show_month_grid($grid_name) +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings, $smcFunc; + + if (!isset($context['calendar_grid_' . $grid_name])) + return false; + + $calendar_data = &$context['calendar_grid_' . $grid_name]; + $colspan = !empty($calendar_data['show_week_links']) ? 8 : 7; + + if (empty($calendar_data['disable_title'])) + { + echo ' +
    +

    '; + + if (empty($calendar_data['previous_calendar']['disabled']) && $calendar_data['show_next_prev']) + echo ' + «'; + + if (empty($calendar_data['next_calendar']['disabled']) && $calendar_data['show_next_prev']) + echo ' + »'; + + if ($calendar_data['show_next_prev']) + echo ' + ', $txt['months_titles'][$calendar_data['current_month']], ' ', $calendar_data['current_year']; + else + echo ' + ', $txt['months_titles'][$calendar_data['current_month']], ' ', $calendar_data['current_year'], ''; + + echo ' +

    +
    '; + } + + echo ' + '; + + // Show each day of the week. + if (empty($calendar_data['disable_day_titles'])) + { + echo ' + '; + + if (!empty($calendar_data['show_week_links'])) + echo ' + '; + + foreach ($calendar_data['week_days'] as $day) + { + echo ' + '; + } + echo ' + '; + } + + /* Each week in weeks contains the following: + days (a list of days), number (week # in the year.) */ + foreach ($calendar_data['weeks'] as $week) + { + echo ' + '; + + if (!empty($calendar_data['show_week_links'])) + echo ' + '; + + /* Every day has the following: + day (# in month), is_today (is this day *today*?), is_first_day (first day of the week?), + holidays, events, birthdays. (last three are lists.) */ + foreach ($week['days'] as $day) + { + // If this is today, make it a different color and show a border. + echo ' + '; + } + + echo ' + '; + } + + echo ' +
     ', !empty($calendar_data['short_day_titles']) ? ($smcFunc['substr']($txt['days'][$day], 0, 1)) : $txt['days'][$day], '
    + » + '; + + // Skip it if it should be blank - it's not a day if it has no number. + if (!empty($day['day'])) + { + // Should the day number be a link? + if (!empty($modSettings['cal_daysaslink']) && $context['can_post']) + echo ' + ', $day['day'], ''; + else + echo ' + ', $day['day']; + + // Is this the first day of the week? (and are we showing week numbers?) + if ($day['is_first_day'] && $calendar_data['size'] != 'small') + echo ' - ', $txt['calendar_week'], ' ', $week['number'], ''; + + // Are there any holidays? + if (!empty($day['holidays'])) + echo ' +
    ', $txt['calendar_prompt'], ' ', implode(', ', $day['holidays']), '
    '; + + // Show any birthdays... + if (!empty($day['birthdays'])) + { + echo ' +
    + ', $txt['birthdays'], ''; + + /* Each of the birthdays has: + id, name (person), age (if they have one set?), and is_last. (last in list?) */ + $use_js_hide = empty($context['show_all_birthdays']) && count($day['birthdays']) > 15; + $count = 0; + foreach ($day['birthdays'] as $member) + { + echo ' + ', $member['name'], isset($member['age']) ? ' (' . $member['age'] . ')' : '', '', $member['is_last'] || ($count == 10 && $use_js_hide)? '' : ', '; + + // Stop at ten? + if ($count == 10 && $use_js_hide) + echo '...
    (', sprintf($txt['calendar_click_all'], count($day['birthdays'])), ')
    '; + + echo ' +
    '; + } + + // Any special posted events? + if (!empty($day['events'])) + { + echo ' +
    + ', $txt['events'], ''; + + /* The events are made up of: + title, href, is_last, can_edit (are they allowed to?), and modify_href. */ + foreach ($day['events'] as $event) + { + // If they can edit the event, show a star they can click on.... + if ($event['can_edit']) + echo ' + *'; + + echo ' + ', $event['link'], $event['is_last'] ? '' : ', '; + } + + echo ' +
    '; + } + } + + echo ' +
    '; +} + +// Or show a weekly one? +function template_show_week_grid($grid_name) +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + if (!isset($context['calendar_grid_' . $grid_name])) + return false; + + $calendar_data = &$context['calendar_grid_' . $grid_name]; + + // Loop through each month (At least one) and print out each day. + foreach ($calendar_data['months'] as $month_data) + { + echo ' +
    +

    '; + + if (empty($calendar_data['previous_calendar']['disabled']) && $calendar_data['show_next_prev'] && empty($done_title)) + echo ' + «'; + + if (empty($calendar_data['next_calendar']['disabled']) && $calendar_data['show_next_prev'] && empty($done_title)) + echo ' + »'; + + echo ' + ', $txt['months_titles'][$month_data['current_month']], ' ', $month_data['current_year'], '', empty($done_title) && !empty($calendar_data['week_number']) ? (' - ' . $txt['calendar_week'] . ' ' . $calendar_data['week_number']) : '', ' +

    +
    '; + + $done_title = true; + + echo ' + '; + + foreach ($month_data['days'] as $day) + { + echo ' + + + + + + + '; + } + + echo ' +
    +
    +

    ', $txt['days'][$day['day_of_week']], '

    +
    +
    '; + + // Should the day number be a link? + if (!empty($modSettings['cal_daysaslink']) && $context['can_post']) + echo ' + ', $day['day'], ''; + else + echo ' + ', $day['day']; + + echo ' + '; + + // Are there any holidays? + if (!empty($day['holidays'])) + echo ' +
    ', $txt['calendar_prompt'], ' ', implode(', ', $day['holidays']), '
    '; + + // Show any birthdays... + if (!empty($day['birthdays'])) + { + echo ' +
    + ', $txt['birthdays'], ''; + + /* Each of the birthdays has: + id, name (person), age (if they have one set?), and is_last. (last in list?) */ + foreach ($day['birthdays'] as $member) + echo ' + ', $member['name'], isset($member['age']) ? ' (' . $member['age'] . ')' : '', '', $member['is_last'] ? '' : ', '; + echo ' +
    '; + } + + // Any special posted events? + if (!empty($day['events'])) + { + echo ' +
    + ', $txt['events'], ''; + + /* The events are made up of: + title, href, is_last, can_edit (are they allowed to?), and modify_href. */ + foreach ($day['events'] as $event) + { + // If they can edit the event, show a star they can click on.... + if ($event['can_edit']) + echo ' + * '; + + echo ' + ', $event['link'], $event['is_last'] ? '' : ', '; + } + + echo ' +
    '; + } + + echo ' +
    '; + } +} + +function template_bcd() +{ + global $context, $scripturl; + + echo ' + + + '; + + $alt = false; + foreach ($context['clockicons'] as $t => $v) + { + echo ' + '; + + $alt = !$alt; + } + + echo ' + +
    BCD Clock
    '; + + foreach ($v as $i) + echo ' +
    '; + + echo ' +
    +

    Are you hardcore?

    + + '; +} + +function template_hms() +{ + global $context, $scripturl; + + echo ' + + '; + $alt = false; + foreach ($context['clockicons'] as $t => $v) + { + echo ' + + '; + $alt = !$alt; + } + + echo ' + + +
    Binary Clock
    '; + foreach ($v as $i) + echo ' + '; + echo ' +
    Too tough for you?
    '; + + echo ' + '; +} + +function template_omfg() +{ + global $context, $scripturl; + + echo ' + + '; + $alt = false; + foreach ($context['clockicons'] as $t => $v) + { + echo ' + + '; + $alt = !$alt; + } + + echo ' + +
    OMFG Binary Clock
    '; + foreach ($v as $i) + echo ' + '; + echo ' +
    '; + + echo ' + '; +} + +function template_thetime() +{ + global $context, $scripturl; + + echo ' + + '; + $alt = false; + foreach ($context['clockicons'] as $t => $v) + { + echo ' + + '; + $alt = !$alt; + } + + echo ' + +
    The time you requested
    '; + foreach ($v as $i) + echo ' + '; + echo ' +
    '; + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Combat.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Combat.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,40 @@ + + a img{ + border: 0; + } + '; + +// Generate a strip of buttons, out of buttons. +function template_button_strip($button_strip, $direction = 'top', $force_reset = false, $custom_td = '') +{ + global $settings, $buttons, $context, $txt, $scripturl; + + if (empty($button_strip)) + return ''; + + // Create the buttons... + foreach ($button_strip as $key => $value) + { + if (isset($value['test']) && empty($context[$value['test']])) + { + unset($button_strip[$key]); + continue; + } + elseif (!isset($buttons[$key]) || $force_reset) + $buttons[$key] = '' . ($settings['use_image_buttons'] ? '' . $txt[$value['text']] . '' : $txt[$value['text']]) . ''; + + $button_strip[$key] = $buttons[$key]; + } + + echo ' + ', implode($context['menu_separator'], $button_strip) , ''; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Compat.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Compat.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,46 @@ + $value) + { + if (!isset($value['test']) || !empty($context[$value['test']])) + $buttons[] = ' +
  28. ' . $txt[$value['text']] . '
  29. '; + } + + // No buttons? No button strip either. + if (empty($buttons)) + return; + + // Make the last one, as easy as possible. + $buttons[count($buttons) - 1] = str_replace('', '', $buttons[count($buttons) - 1]); + + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Display.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Display.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,883 @@ + + ', $txt['report_sent'], ' + '; + } + + // Show the anchor for the top and for the first message. If the first message is new, say so. + echo ' + + ', $context['first_new_message'] ? '' : ''; + + // Is this topic also a poll? + if ($context['is_poll']) + { + echo ' +
    +
    +

    + ', $txt['poll'], ' +

    +
    +
    + +
    +

    + ', $context['poll']['question'], ' +

    '; + + // Are they not allowed to vote but allowed to view the options? + if ($context['poll']['show_results'] || !$context['allow_vote']) + { + echo ' +
    '; + + // Show each option with its corresponding percentage bar. + foreach ($context['poll']['options'] as $option) + { + echo ' +
    ', $option['option'], '
    +
    '; + + if ($context['allow_poll_view']) + echo ' + ', $option['bar_ndt'], ' + ', $option['votes'], ' (', $option['percent'], '%)'; + + echo ' +
    '; + } + + echo ' +
    '; + + if ($context['allow_poll_view']) + echo ' +

    ', $txt['poll_total_voters'], ': ', $context['poll']['total_votes'], '

    '; + } + // They are allowed to vote! Go to it! + else + { + echo ' +
    '; + + // Show a warning if they are allowed more than one option. + if ($context['poll']['allowed_warning']) + echo ' +

    ', $context['poll']['allowed_warning'], '

    '; + + echo ' +
      '; + + // Show each option with its button - a radio likely. + foreach ($context['poll']['options'] as $option) + echo ' +
    • ', $option['vote_button'], '
    • '; + + echo ' +
    +
    + + +
    +
    '; + } + + // Is the clock ticking? + if (!empty($context['poll']['expire_time'])) + echo ' +

    ', ($context['poll']['is_expired'] ? $txt['poll_expired_on'] : $txt['poll_expires_on']), ': ', $context['poll']['expire_time'], '

    '; + + echo ' +
    + +
    +
    +
    '; + + // Build the poll moderation button array. + $poll_buttons = array( + 'vote' => array('test' => 'allow_return_vote', 'text' => 'poll_return_vote', 'image' => 'poll_options.gif', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start']), + 'results' => array('test' => 'show_view_results_button', 'text' => 'poll_results', 'image' => 'poll_results.gif', '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.gif', 'lang' => true, 'url' => $scripturl . '?action=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.gif', '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.gif', '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.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['poll_remove_warn'] . '\');"', 'url' => $scripturl . '?action=removepoll;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + template_button_strip($poll_buttons); + + echo ' +
    '; + } + + // Does this topic have some events linked to it? + if (!empty($context['linked_calendar_events'])) + { + echo ' +
    +
    +

    ', $txt['calendar_linked_events'], '

    +
    +
    + +
    +
      '; + + foreach ($context['linked_calendar_events'] as $event) + echo ' +
    • + ', ($event['can_edit'] ? ' ' : ''), '', $event['title'], ': ', $event['start_date'], ($event['start_date'] != $event['end_date'] ? ' - ' . $event['end_date'] : ''), ' +
    • '; + + echo ' +
    +
    + +
    +
    '; + } + + // Build the normal button array. + $normal_buttons = array( + 'reply' => array('test' => 'can_reply', 'text' => 'reply', 'image' => 'reply.gif', 'lang' => true, 'url' => $scripturl . '?action=post;topic=' . $context['current_topic'] . '.' . $context['start'] . ';last_msg=' . $context['topic_last_message'], 'active' => true), + 'add_poll' => array('test' => 'can_add_poll', 'text' => 'add_poll', 'image' => 'add_poll.gif', 'lang' => true, 'url' => $scripturl . '?action=editpoll;add;topic=' . $context['current_topic'] . '.' . $context['start']), + 'notify' => array('test' => 'can_mark_notify', 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', 'image' => ($context['is_marked_notify'] ? 'un' : '') . 'notify.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . ($context['is_marked_notify'] ? $txt['notification_disable_topic'] : $txt['notification_enable_topic']) . '\');"', '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.gif', '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']), + 'send' => array('test' => 'can_send_topic', 'text' => 'send_topic', 'image' => 'sendtopic.gif', 'lang' => true, 'url' => $scripturl . '?action=emailuser;sa=sendtopic;topic=' . $context['current_topic'] . '.0'), + 'print' => array('text' => 'print', 'image' => 'print.gif', 'lang' => true, 'custom' => 'rel="new_win nofollow"', 'url' => $scripturl . '?action=printpage;topic=' . $context['current_topic'] . '.0'), + ); + + // Allow adding new buttons easily. + call_integration_hook('integrate_display_buttons', array(&$normal_buttons)); + + // Show the page index... "Pages: [1]". + echo ' +
    + ', template_button_strip($normal_buttons, 'right'), ' + +
    '; + + // Show the topic information - icon, subject, etc. + echo ' +
    +
    +

    + + ', $txt['author'], ' + ', $txt['topic'], ': ', $context['subject'], '  (', $txt['read'], ' ', $context['num_views'], ' ', $txt['times'], ') +

    +
    '; + + if (!empty($settings['display_who_viewing'])) + { + echo ' +

    '; + + // Show just numbers...? + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) == 1 ? $txt['who_member'] : $txt['members']; + // Or show the actual people viewing the topic? + else + echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) || $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + + // Now show how many guests are here too. + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_topic'], ' +

    '; + } + + echo ' +
    '; + + $ignoredMsgs = array(); + $removableMessageIDs = array(); + $alternate = false; + + // Get all the messages... + while ($message = $context['get_message']()) + { + $ignoring = false; + $alternate = !$alternate; + if ($message['can_remove']) + $removableMessageIDs[] = $message['id']; + + // Are we ignoring this message? + if (!empty($message['is_ignored'])) + { + $ignoring = true; + $ignoredMsgs[] = $message['id']; + } + + // Show the message anchor and a "new" anchor if this message is new. + if ($message['id'] != $context['first_message']) + echo ' + ', $message['first_new'] ? '' : ''; + + echo ' +
    + +
    '; + + // Show information about the poster of this message. + echo ' +
    +

    '; + + // Show online and offline buttons? + if (!empty($modSettings['onlineEnable']) && !$message['member']['is_guest']) + echo ' + ', $context['can_send_pm'] ? '' : '', '', $message['member']['online']['text'], '', $context['can_send_pm'] ? '' : ''; + + // Show a link to the member's profile. + echo ' + ', $message['member']['link'], ' +

    +
      '; + + // Show the member's custom title, if they have one. + if (!empty($message['member']['title'])) + echo ' +
    • ', $message['member']['title'], '
    • '; + + // Show the member's primary group (like 'Administrator') if they have one. + if (!empty($message['member']['group'])) + echo ' +
    • ', $message['member']['group'], '
    • '; + + // Don't show these things for guests. + if (!$message['member']['is_guest']) + { + // Show the post group if and only if they have no other group or the option is on, and they are in a post group. + if ((empty($settings['hide_post_group']) || $message['member']['group'] == '') && $message['member']['post_group'] != '') + echo ' +
    • ', $message['member']['post_group'], '
    • '; + echo ' +
    • ', $message['member']['group_stars'], '
    • '; + + // Show avatars, images, etc.? + if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image'])) + echo ' +
    • + + ', $message['member']['avatar']['image'], ' + +
    • '; + + // Show how many posts they have made. + if (!isset($context['disabled_fields']['posts'])) + echo ' +
    • ', $txt['member_postcount'], ': ', $message['member']['posts'], '
    • '; + + // Is karma display enabled? Total or +/-? + if ($modSettings['karmaMode'] == '1') + echo ' +
    • ', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '
    • '; + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    • ', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '
    • '; + + // Is this user allowed to modify this member's karma? + if ($message['member']['karma']['allow']) + echo ' +
    • + ', $modSettings['karmaApplaudLabel'], ' + ', $modSettings['karmaSmiteLabel'], ' +
    • '; + + // Show the member's gender icon? + if (!empty($settings['show_gender']) && $message['member']['gender']['image'] != '' && !isset($context['disabled_fields']['gender'])) + echo ' +
    • ', $txt['gender'], ': ', $message['member']['gender']['image'], '
    • '; + + // Show their personal text? + if (!empty($settings['show_blurb']) && $message['member']['blurb'] != '') + echo ' +
    • ', $message['member']['blurb'], '
    • '; + + // Any custom fields to show as icons? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 1 || empty($custom['value'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    • +
        '; + } + echo ' +
      • ', $custom['value'], '
      • '; + } + if ($shown) + echo ' +
      +
    • '; + } + + // This shows the popular messaging icons. + if ($message['member']['has_messenger'] && $message['member']['can_view_profile']) + echo ' +
    • +
        + ', !empty($message['member']['icq']['link']) ? '
      • ' . $message['member']['icq']['link'] . '
      • ' : '', ' + ', !empty($message['member']['msn']['link']) ? '
      • ' . $message['member']['msn']['link'] . '
      • ' : '', ' + ', !empty($message['member']['aim']['link']) ? '
      • ' . $message['member']['aim']['link'] . '
      • ' : '', ' + ', !empty($message['member']['yim']['link']) ? '
      • ' . $message['member']['yim']['link'] . '
      • ' : '', ' +
      +
    • '; + + // Show the profile, website, email address, and personal message buttons. + if ($settings['show_profile_buttons']) + { + echo ' +
    • + +
    • '; + } + + // Any custom fields for standard placement? + if (!empty($message['member']['custom_fields'])) + { + foreach ($message['member']['custom_fields'] as $custom) + if (empty($custom['placement']) || empty($custom['value'])) + echo ' +
    • ', $custom['title'], ': ', $custom['value'], '
    • '; + } + + // Are we showing the warning status? + if ($message['member']['can_see_warning']) + echo ' +
    • ', $context['can_issue_warning'] ? '' : '', '', $txt['user_warn_' . $message['member']['warning_status']], '', $context['can_issue_warning'] ? '' : '', '', $txt['warn_' . $message['member']['warning_status']], '
    • '; + } + // Otherwise, show the guest's email. + elseif (!empty($message['member']['email']) && in_array($message['member']['show_email'], array('yes', 'yes_permission_override', 'no_through_forum'))) + echo ' + '; + + // Done with the information about the poster... on to the post itself. + echo ' +
    +
    +
    +
    +
    +
    + +
    +
    + ', $message['subject'], ' +
    +
    « ', !empty($message['counter']) ? $txt['reply_noun'] . ' #' . $message['counter'] : '', ' ', $txt['on'], ': ', $message['time'], ' »
    +
    +
    '; + + // If this is the first post, (#0) just say when it was posted - otherwise give the reply #. + if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg']) + echo ' +
      '; + + // Maybe we can approve it, maybe we should? + if ($message['can_approve']) + echo ' +
    • ', $txt['approve'], '
    • '; + + // Can they reply? Have they turned on quick reply? + if ($context['can_quote'] && !empty($options['display_quick_reply'])) + echo ' +
    • ', $txt['quote'], '
    • '; + + // So... quick reply is off, but they *can* reply? + elseif ($context['can_quote']) + echo ' +
    • ', $txt['quote'], '
    • '; + + // Can the user modify the contents of this post? + if ($message['can_modify']) + echo ' +
    • ', $txt['modify'], '
    • '; + + // How about... even... remove it entirely?! + if ($message['can_remove']) + echo ' +
    • ', $txt['remove'], '
    • '; + + // What about splitting it off the rest of the topic? + if ($context['can_split'] && !empty($context['real_num_replies'])) + echo ' +
    • ', $txt['split'], '
    • '; + + // Can we restore topics? + if ($context['can_restore_msg']) + echo ' +
    • ', $txt['restore_message'], '
    • '; + + // Show a checkbox for quick moderation? + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $message['can_remove']) + echo ' + '; + + if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg']) + echo ' +
    '; + + echo ' +
    '; + + // Ignoring this user? Hide the post. + if ($ignoring) + echo ' +
    + ', $txt['ignoring_user'], ' + +
    '; + + // Show the post itself, finally! + echo ' +
    '; + + if (!$message['approved'] && $message['member']['id'] != 0 && $message['member']['id'] == $context['user']['id']) + echo ' +
    + ', $txt['post_awaiting_approval'], ' +
    '; + echo ' +
    ', $message['body'], '
    +
    '; + + // Can the user modify the contents of this post? Show the modify inline image. + if ($message['can_modify']) + echo ' + '; + + // Assuming there are attachments... + if (!empty($message['attachment'])) + { + echo ' + '; + } + + echo ' +
    +
    +
    '; + + // Show "� Last Edit: Time by Person �" if this post was edited. + if ($settings['show_modify'] && !empty($message['modified']['name'])) + echo ' + « ', $txt['last_edit'], ': ', $message['modified']['time'], ' ', $txt['by'], ' ', $message['modified']['name'], ' »'; + + echo ' +
    + '; + + // Are there any custom profile fields for above the signature? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 2 || empty($custom['value'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    +
      '; + } + echo ' +
    • ', $custom['value'], '
    • '; + } + if ($shown) + echo ' +
    +
    '; + } + + // Show the member's signature? + if (!empty($message['member']['signature']) && empty($options['show_no_signatures']) && $context['signature_enabled']) + echo ' +
    ', $message['member']['signature'], '
    '; + + echo ' +
    +
    + +
    +
    '; + } + + echo ' +
    +
    + '; + + // Show the page index... "Pages: [1]". + echo ' +
    + ', template_button_strip($normal_buttons, 'right'), ' + + +
    '; + + // Show the lower breadcrumbs. + theme_linktree(); + + $mod_buttons = array( + 'move' => array('test' => 'can_move', 'text' => 'move_topic', 'image' => 'admin_move.gif', 'lang' => true, 'url' => $scripturl . '?action=movetopic;topic=' . $context['current_topic'] . '.0'), + 'delete' => array('test' => 'can_delete', 'text' => 'remove_topic', 'image' => 'admin_rem.gif', '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.gif', 'lang' => true, 'url' => $scripturl . '?action=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.gif', 'lang' => true, 'url' => $scripturl . '?action=sticky;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), + 'merge' => array('test' => 'can_merge', 'text' => 'merge', 'image' => 'merge.gif', '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.gif', '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']) + $mod_buttons[] = array('text' => 'restore_topic', 'image' => '', 'lang' => true, 'url' => $scripturl . '?action=restoretopic;topics=' . $context['current_topic'] . ';' . $context['session_var'] . '=' . $context['session_id']); + + // Allow adding new mod buttons easily. + call_integration_hook('integrate_mod_buttons', array(&$mod_buttons)); + + echo ' +
    ', template_button_strip($mod_buttons, 'bottom', array('id' => 'moderationbuttons_strip')), '
    '; + + // Show the jumpto box, or actually...let Javascript do it. + echo ' +
     
    '; + + if ($context['can_reply'] && !empty($options['display_quick_reply'])) + { + echo ' + +
    + + +
    '; + } + else + echo ' +
    '; + + if ($context['show_spellchecking']) + echo ' +
    + '; + + echo ' + + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Errors.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Errors.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,203 @@ + +
    +

    + ', $context['error_title'], ' +

    +
    +
    + +
    ', $context['error_message'], '
    + +
    + '; + + // Show a back button (using javascript.) + echo ' + '; +} + +function template_error_log() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    + +
    +

    + + ', $txt['help'], ' ', $txt['errlog'], ' + +

    +
    + + + + + + + '; + + if ($context['has_filter']) + echo ' + + + '; + + if (!empty($context['errors'])) + echo ' + + + '; + + foreach ($context['errors'] as $error) + { + echo ' + + + + + + + + '; + } + + if (!empty($context['errors'])) + echo ' + + + '; + else + echo ' + + + '; + + echo ' + + + +
    +   ', $txt['apply_filter_of_type'], ':'; + + $error_types = array(); + foreach ($context['error_types'] as $type => $details) + $error_types[] = ($details['is_selected'] ? ' ' : '') . '' . $details['label'] . ''; + + echo ' + ', implode(' | ', $error_types), ' +
    +   ', $txt['pages'], ': ', $context['page_index'], ' +
    + ', $txt['applying_filter'], ': ', $context['filter']['entity'], ' ', $context['filter']['value']['html'], ' (', $txt['clear_filter'], ') +
    +
    + +
    + + + ', $txt['apply_filter'], ': ', $txt['filter_only_member'], ' + ', $error['member']['link'], '
    + ', $txt['apply_filter'], ': ', $txt['filter_only_ip'], ' + ', $error['member']['ip'], '   +
      +
    + ', $txt['reverse_direction'], ' + ', $error['time'], ' +
    '; + + if ($error['member']['session'] != '') + echo ' + ', $txt['apply_filter'], ': ', $txt['filter_only_session'], ' + ', $error['member']['session'], ' +
    '; + + echo ' + ', $txt['apply_filter'], ': ', $txt['filter_only_type'], ' + ', $txt['error_type'], ': ', $error['error_type']['name'], ' +
    +
    ', $txt['apply_filter'], ': ', $txt['filter_only_url'], '
    + +
    ', $txt['apply_filter'], ': ', $txt['filter_only_message'], '
    +
    ', $error['message']['html'], '
    '; + + if (!empty($error['file'])) + echo ' +
    ', $txt['apply_filter'], ': ', $txt['filter_only_file'], '
    +
    + ', $txt['file'], ': ', $error['file']['link'], '
    + ', $txt['line'], ': ', $error['file']['line'], ' +
    '; + + echo ' +
    +
    +   +
    ', $txt['errlog_no_entries'], '
    +   ', $txt['pages'], ': ', $context['page_index'], ' +

    '; + if ($context['sort_direction'] == 'down') + echo ' + '; + echo ' + +
    '; +} + +function template_show_file() +{ + global $context, $settings; + + echo ' + + + ', $context['file_data']['file'], ' + + + + + '; + foreach ($context['file_data']['contents'] as $index => $line) + { + $line_num = $index+$context['file_data']['min']; + $is_target = $line_num == $context['file_data']['target']; + echo ' + + + + '; + } + echo ' +
    ==>' : '>', $line_num , ':', $line, '
    + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/GenericControls.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/GenericControls.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,359 @@ + +
    +
    + +
    +
    +
    + + + '; +} + +function template_control_richedit_buttons($editor_id) +{ + global $context, $settings, $options, $txt, $modSettings, $scripturl; + + $editor_context = &$context['controls']['richedit'][$editor_id]; + + echo ' + '; + + if ($editor_context['preview_type']) + echo ' + '; + + if ($context['show_spellchecking']) + echo ' + '; +} + +// What's this, verification?! +function template_control_verification($verify_id, $display_type = 'all', $reset = false) +{ + global $context, $settings, $options, $txt, $modSettings; + + $verify_context = &$context['controls']['verification'][$verify_id]; + + // Keep track of where we are. + if (empty($verify_context['tracking']) || $reset) + $verify_context['tracking'] = 0; + + // How many items are there to display in total. + $total_items = count($verify_context['questions']) + ($verify_context['show_visual'] ? 1 : 0); + + // If we've gone too far, stop. + if ($verify_context['tracking'] > $total_items) + return false; + + // Loop through each item to show them. + for ($i = 0; $i < $total_items; $i++) + { + // If we're after a single item only show it if we're in the right place. + if ($display_type == 'single' && $verify_context['tracking'] != $i) + continue; + + if ($display_type != 'single') + echo ' +
    '; + + // Do the actual stuff - image first? + if ($i == 0 && $verify_context['show_visual']) + { + if ($context['use_graphic_library']) + echo ' + ', $txt['visual_verification_description'], ''; + else + echo ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ' + ', $txt['visual_verification_description'], ''; + + if (WIRELESS) + echo '
    + '; + else + echo ' +
    + ', $txt['visual_verification_sound'], ' / ', $txt['visual_verification_request_new'], '', $display_type != 'quick_reply' ? '
    ' : '', '
    + ', $txt['visual_verification_description'], ':', $display_type != 'quick_reply' ? '
    ' : '', ' + +
    '; + } + else + { + // Where in the question array is this question? + $qIndex = $verify_context['show_visual'] ? $i - 1 : $i; + + echo ' +
    + ', $verify_context['questions'][$qIndex]['q'], ':
    + +
    '; + } + + if ($display_type != 'single') + echo ' +
    '; + + // If we were displaying just one and we did it, break. + if ($display_type == 'single' && $verify_context['tracking'] == $i) + break; + } + + // Assume we found something, always, + $verify_context['tracking']++; + + // Tell something displaying piecemeal to keep going. + if ($display_type == 'single') + return true; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/GenericList.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/GenericList.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,333 @@ + +
    '; + + // Show the title of the table (if any). + if (!empty($cur_list['title'])) + echo ' +
    +

    + ', $cur_list['title'], ' +

    +
    '; + // This is for the old style menu with the arrows "> Test | Test 1" + if (empty($settings['use_tabs']) && isset($cur_list['list_menu'], $cur_list['list_menu']['show_on']) && ($cur_list['list_menu']['show_on'] == 'both' || $cur_list['list_menu']['show_on'] == 'top')) + template_create_list_menu($cur_list['list_menu'], 'top'); + + if (isset($cur_list['additional_rows']['top_of_list'])) + template_additional_rows('top_of_list', $cur_list); + + if (isset($cur_list['additional_rows']['after_title'])) + { + echo ' +
    '; + template_additional_rows('after_title', $cur_list); + echo ' +
    '; + } + + if (!empty($cur_list['items_per_page']) || isset($cur_list['additional_rows']['bottom_of_list'])) + { + echo ' +
    '; + + // Show the page index (if this list doesn't intend to show all items). + if (!empty($cur_list['items_per_page'])) + echo ' +
    +
    ', $txt['pages'], ': ', $cur_list['page_index'], '
    +
    '; + + if (isset($cur_list['additional_rows']['above_column_headers'])) + { + echo ' +
    '; + + template_additional_rows('above_column_headers', $cur_list); + + echo ' +
    '; + } + + echo ' +
    '; + } + + echo ' + '; + + // Show the column headers. + $header_count = count($cur_list['headers']); + if (!($header_count < 2 && empty($cur_list['headers'][0]['label']))) + { + echo ' + + '; + + // Loop through each column and add a table header. + $i = 0; + foreach ($cur_list['headers'] as $col_header) + { + $i ++; + if (empty($col_header['class']) && $i == 1) + $col_header['class'] = 'first_th'; + elseif (empty($col_header['class']) && $i == $header_count) + $col_header['class'] = 'last_th'; + + echo ' + '; + } + + echo ' + + + '; + } + + // Show a nice message informing there are no items in this list. + if (empty($cur_list['rows']) && !empty($cur_list['no_items_label'])) + echo ' + + + '; + + // Show the list rows. + elseif (!empty($cur_list['rows'])) + { + $alternate = false; + foreach ($cur_list['rows'] as $id => $row) + { + echo ' + '; + + foreach ($row as $row_data) + echo ' + ', $row_data['value'], ''; + + echo ' + '; + + $alternate = !$alternate; + } + } + + echo ' + +
    ', empty($col_header['href']) ? '' : '', empty($col_header['label']) ? ' ' : $col_header['label'], empty($col_header['href']) ? '' : '', empty($col_header['sort_image']) ? '' : ' ', '
    ', $cur_list['no_items_label'], '
    '; + + if (!empty($cur_list['items_per_page']) || isset($cur_list['additional_rows']['below_table_data']) || isset($cur_list['additional_rows']['bottom_of_list'])) + { + echo ' +
    '; + + // Show the page index (if this list doesn't intend to show all items). + if (!empty($cur_list['items_per_page'])) + echo ' +
    +
    ', $txt['pages'], ': ', $cur_list['page_index'], '
    +
    '; + + if (isset($cur_list['additional_rows']['below_table_data'])) + { + echo ' +
    '; + + template_additional_rows('below_table_data', $cur_list); + + echo ' +
    '; + } + + if (isset($cur_list['additional_rows']['bottom_of_list'])) + { + echo ' +
    '; + + template_additional_rows('bottom_of_list', $cur_list); + + echo ' +
    '; + } + + echo ' +
    '; + } + + if (isset($cur_list['form'])) + { + foreach ($cur_list['form']['hidden_fields'] as $name => $value) + echo ' + '; + + echo ' +
    + '; + } + + // Tabs at the bottom. Usually bottom alligned. + if (!empty($settings['use_tabs']) && isset($cur_list['list_menu'], $cur_list['list_menu']['show_on']) && ($cur_list['list_menu']['show_on'] == 'both' || $cur_list['list_menu']['show_on'] == 'bottom')) + template_create_list_menu($cur_list['list_menu'], 'bottom'); + + if (isset($cur_list['javascript'])) + echo ' + '; +} + +function template_additional_rows($row_position, $cur_list) +{ + global $context, $settings, $options; + + foreach ($cur_list['additional_rows'][$row_position] as $row) + echo ' +
    ', $row['value'], '
    '; +} + +function template_create_list_menu($list_menu, $direction = 'top') +{ + global $context, $settings; + + /** + // This is use if you want your generic lists to have tabs. + $cur_list['list_menu'] = array( + // This is the style to use. Tabs or Buttons (Text 1 | Text 2). + // By default tabs are selected if not set. + // The main difference between tabs and buttons is that tabs get highlighted if selected. + // If style is set to buttons and use tabs is diabled then we change the style to old styled tabs. + 'style' => 'tabs', + // The posisiton of the tabs/buttons. Left or Right. By default is set to left. + 'position' => 'left', + // This is used by the old styled menu. We *need* to know the total number of columns to span. + 'columns' => 0, + // This gives you the option to show tabs only at the top, bottom or both. + // By default they are just shown at the top. + 'show_on' => 'top', + // Links. This is the core of the array. It has all the info that we need. + 'links' => array( + 'name' => array( + // This will tell use were to go when they click it. + 'href' => $scripturl . '?action=theaction', + // The name that you want to appear for the link. + 'label' => $txt['name'], + // If we use tabs instead of buttons we highlight the current tab. + // Must use conditions to determine if its selected or not. + 'is_selected' => isset($_REQUEST['name']), + ), + ), + ); + */ + + // Are we using right-to-left orientation? + $first = $context['right_to_left'] ? 'last' : 'first'; + $last = $context['right_to_left'] ? 'first' : 'last'; + + // Tabs take preference over buttons in certain cases. + if (empty($settings['use_tabs']) && $list_menu['style'] == 'button') + $list_menu['style'] = 'tabs'; + + if (!isset($list_menu['style']) || isset($list_menu['style']) && $list_menu['style'] == 'tabs') + { + if (!empty($settings['use_tabs'])) + { + echo ' + + ', $list_menu['position'] == 'right' ? ' + ' : '', ' + ', $list_menu['position'] == 'left' ? ' + ' : '', ' + +
      + + + '; + + foreach ($list_menu['links'] as $link) + { + if ($link['is_selected']) + echo ' + + + '; + else + echo ' + '; + } + + echo ' + + +
       + ', $link['label'], ' +   + ', $link['label'], ' +  
    +
     
    '; + } + else + { + echo ' + + '; + + $links = array(); + foreach ($list_menu['links'] as $link) + $links[] = ($link['is_selected'] ? '> ' : '') . '' . $link['label'] . ''; + + echo ' + ', implode(' | ', $links), ' + + '; + } + } + elseif (isset($list_menu['style']) && $list_menu['style'] == 'buttons') + { + $links = array(); + foreach ($list_menu['links'] as $link) + $links[] = '' . $link['label'] . ''; + + echo ' + + ', $list_menu['position'] == 'right' ? ' + ' : '', ' + ', $list_menu['position'] == 'left' ? ' + ' : '', ' + +
      + + + + + + +
     ', implode('  |  ', $links), ' 
    +
     
    '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/GenericMenu.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/GenericMenu.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,367 @@ + +
    '; + + // What one are we rendering? + $context['cur_menu_id'] = isset($context['cur_menu_id']) ? $context['cur_menu_id'] + 1 : 1; + $menu_context = &$context['menu_data_' . $context['cur_menu_id']]; + + // For every section that appears on the sidebar... + $firstSection = true; + foreach ($menu_context['sections'] as $section) + { + // Show the section header - and pump up the line spacing for readability. + echo ' +
    +
    +

    '; + + if ($firstSection && !empty($menu_context['can_toggle_drop_down'])) + { + echo ' + + ', $section['title'],'! + '; + } + else + { + echo ' + ', $section['title']; + } + + echo ' +

    +
    +
      '; + + // For every area of this section show a link to that area (bold if it's currently selected.) + foreach ($section['areas'] as $i => $area) + { + // Not supposed to be printed? + if (empty($area['label'])) + continue; + + echo ' +
    • '; + + // Is this the current area, or just some area? + if ($i == $menu_context['current_area']) + { + echo ' + ', $area['label'], ''; + + if (empty($context['tabs'])) + $context['tabs'] = isset($area['subsections']) ? $area['subsections'] : array(); + } + else + echo ' + ', $area['label'], ''; + + echo ' +
    • '; + } + + echo ' +
    +
    '; + + $firstSection = false; + } + + // This is where the actual "main content" area for the admin section starts. + echo ' +
    +
    '; + + // If there are any "tabs" setup, this is the place to shown them. + if (!empty($context['tabs']) && empty($context['force_disable_tabs'])) + template_generic_menu_tabs($menu_context); +} + +// Part of the sidebar layer - closes off the main bit. +function template_generic_menu_sidebar_below() +{ + global $context, $settings, $options; + + echo ' +
    +
    '; +} + +// This contains the html for the side bar of the admin center, which is used for all admin pages. +function template_generic_menu_dropdown_above() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Which menu are we rendering? + $context['cur_menu_id'] = isset($context['cur_menu_id']) ? $context['cur_menu_id'] + 1 : 1; + $menu_context = &$context['menu_data_' . $context['cur_menu_id']]; + + if (!empty($menu_context['can_toggle_drop_down'])) + echo ' + *'; + + echo ' +
    +
    '; + + // This is the main table - we need it so we can keep the content to the right of it. + echo ' +
    '; + + // It's possible that some pages have their own tabs they wanna force... + if (!empty($context['tabs'])) + template_generic_menu_tabs($menu_context); +} + +// Part of the admin layer - used with admin_above to close the table started in it. +function template_generic_menu_dropdown_below() +{ + global $context, $settings, $options; + + echo ' +
    '; +} + +// Some code for showing a tabbed view. +function template_generic_menu_tabs(&$menu_context) +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Handy shortcut. + $tab_context = &$menu_context['tab_data']; + + echo ' +
    +

    '; + + // Exactly how many tabs do we have? + foreach ($context['tabs'] as $id => $tab) + { + // Can this not be accessed? + if (!empty($tab['disabled'])) + { + $tab_context['tabs'][$id]['disabled'] = true; + continue; + } + + // Did this not even exist - or do we not have a label? + if (!isset($tab_context['tabs'][$id])) + $tab_context['tabs'][$id] = array('label' => $tab['label']); + elseif (!isset($tab_context['tabs'][$id]['label'])) + $tab_context['tabs'][$id]['label'] = $tab['label']; + + // Has a custom URL defined in the main admin structure? + if (isset($tab['url']) && !isset($tab_context['tabs'][$id]['url'])) + $tab_context['tabs'][$id]['url'] = $tab['url']; + // Any additional paramaters for the url? + if (isset($tab['add_params']) && !isset($tab_context['tabs'][$id]['add_params'])) + $tab_context['tabs'][$id]['add_params'] = $tab['add_params']; + // Has it been deemed selected? + if (!empty($tab['is_selected'])) + $tab_context['tabs'][$id]['is_selected'] = true; + // Does it have its own help? + if (!empty($tab['help'])) + $tab_context['tabs'][$id]['help'] = $tab['help']; + // Is this the last one? + if (!empty($tab['is_last']) && !isset($tab_context['override_last'])) + $tab_context['tabs'][$id]['is_last'] = true; + } + + // Find the selected tab + foreach ($tab_context['tabs'] as $sa => $tab) + { + if (!empty($tab['is_selected']) || (isset($menu_context['current_subsection']) && $menu_context['current_subsection'] == $sa)) + { + $selected_tab = $tab; + $tab_context['tabs'][$sa]['is_selected'] = true; + } + } + + // Show an icon and/or a help item? + if (!empty($selected_tab['icon']) || !empty($tab_context['icon']) || !empty($selected_tab['help']) || !empty($tab_context['help'])) + { + echo ' + '; + + if (!empty($selected_tab['icon']) || !empty($tab_context['icon'])) + echo ''; + + if (!empty($selected_tab['help']) || !empty($tab_context['help'])) + echo '', $txt['help'], ''; + + echo $tab_context['title'], ' + '; + } + else + { + echo ' + ', $tab_context['title']; + } + + echo ' +

    +
    '; + + // Shall we use the tabs? + if (!empty($settings['use_tabs'])) + { + echo ' +

    + ', !empty($selected_tab['description']) ? $selected_tab['description'] : $tab_context['description'], ' +

    '; + + // The admin tabs. + echo ' +
    +
      '; + + // Print out all the items in this tab. + foreach ($tab_context['tabs'] as $sa => $tab) + { + if (!empty($tab['disabled'])) + continue; + + if (!empty($tab['is_selected'])) + { + echo ' +
    • + ', $tab['label'], ' +
    • '; + } + else + echo ' +
    • + ', $tab['label'], ' +
    • '; + } + + // the end of tabs + echo ' +
    +

    '; + } + // ...if not use the old style + else + { + echo ' +

    '; + + // Print out all the items in this tab. + foreach ($tab_context['tabs'] as $sa => $tab) + { + if (!empty($tab['disabled'])) + continue; + + if (!empty($tab['is_selected'])) + { + echo ' + * ', $tab['label'], ''; + } + else + echo ' + ', $tab['label'], ''; + + if (empty($tab['is_last'])) + echo ' | '; + } + + echo ' +

    +

    ', isset($selected_tab['description']) ? $selected_tab['description'] : $tab_context['description'], '

    '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Help.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Help.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,185 @@ + + + + + + ', $context['page_title'], ' + + + + +
    + ', $context['help_text'], '
    +
    + ', $txt['close_window'], ' +
    + +'; +} + +function template_find_members() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' + + + ', $txt['find_members'], ' + + + + + + + +
    + +
    +
    +
    +

    ', $txt['find_members'], '

    +
    +
    + ', $txt['find_username'], ':
    +
    + ', $txt['find_wildcards'], '
    '; + + // Only offer to search for buddies if we have some! + if (!empty($context['show_buddies'])) + echo ' +
    '; + + echo ' +
    + + +
    +
    +
    +
    + +
    + +
    +
    +
    +

    ', $txt['find_results'], '

    +
    '; + + if (empty($context['results'])) + echo ' +

    ', $txt['find_no_results'], '

    '; + else + { + echo ' +
      '; + + $alternate = true; + foreach ($context['results'] as $result) + { + echo ' +
    • + ', $txt['view_profile'], ' + ', $result['name'], ' +
    • '; + + $alternate = !$alternate; + } + + echo ' +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + + echo ' +
    +
    + + + + +
    '; + + if (empty($context['results'])) + echo ' + '; + + echo ' + +'; +} + +// The main help page. +function template_manual() +{ + global $context, $scripturl, $txt; + + echo ' +
    +

    ', $txt['manual_smf_user_help'], '

    +
    +
    +
    + +
    +

    ', sprintf($txt['manual_welcome'], $context['forum_name']), '

    +

    ', $txt['manual_introduction'], '

    + +

    ', sprintf($txt['manual_docs_and_credits'], $context['wiki_url'], $scripturl . '?action=credits'), '

    +
    + +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Login.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Login.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,308 @@ + + +
    +
    '; + + // Focus on the correct input - username or password. + echo ' + '; +} + +// Tell a guest to get lost or login! +function template_kick_guest() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // This isn't that much... just like normal login but with a message at the top. + echo ' + +
    + +
    '; + + // Do the focus thing... + echo ' + '; +} + +// This is for maintenance mode. +function template_maintenance() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Display the administrator's message at the top. + echo ' + +
    + +
    '; +} + +// This is for the security stuff - makes administrators login every so often. +function template_admin_login() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Since this should redirect to whatever they were doing, send all the get data. + echo ' + + +
    + + +
    '; + + // Focus on the password box. + echo ' +'; +} + +// Activate your account manually? +function template_retry_activate() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Just ask them for their code so they can try it again... + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    + +
    '; + + // You didn't even have an ID? + if (empty($context['member_id'])) + echo ' +
    +
    ', $txt['invalid_activation_username'], ':
    +
    '; + + echo ' +
    ', $txt['invalid_activation_retry'], ':
    +
    +
    +

    +
    + +
    '; +} + +// Activate your account manually? +function template_resend() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Just ask them for their code so they can try it again... + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    + +
    +
    +
    ', $txt['invalid_activation_username'], ':
    +
    +
    +

    ', $txt['invalid_activation_new'], '

    +
    +
    ', $txt['invalid_activation_new_email'], ':
    +
    +
    ', $txt['invalid_activation_password'], ':
    +
    +
    '; + + if ($context['can_activate']) + echo ' +

    ', $txt['invalid_activation_known'], '

    +
    +
    ', $txt['invalid_activation_retry'], ':
    +
    +
    '; + + echo ' +

    +
    + +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageAttachments.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageAttachments.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,215 @@ + +'; +} + +function template_browse() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + '; + + template_show_list('file_list'); + echo ' +
    '; + +} + +function template_maintenance() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['attachment_stats'], '

    +
    +
    + +
    +
    +
    ', $txt['attachment_total'], ':
    ', $context['num_attachments'], '
    +
    ', $txt['attachment_manager_total_avatars'], ':
    ', $context['num_avatars'], '
    +
    ', $txt['attachmentdir_size' . ($context['attach_multiple_dirs'] ? '_current' : '')], ':
    ', $context['attachment_total_size'], ' ', $txt['kilobyte'], '
    +
    ', $txt['attachment_space' . ($context['attach_multiple_dirs'] ? '_current' : '')], ':
    ', isset($context['attachment_space']) ? $context['attachment_space'] . ' ' . $txt['kilobyte'] : $txt['attachmentdir_size_not_set'], '
    +
    +
    + +
    +
    +

    ', $txt['attachment_integrity_check'], '

    +
    +
    + +
    +
    +

    ', $txt['attachment_integrity_check_desc'], '

    + +
    +
    + +
    +
    +

    ', $txt['attachment_pruning'], '

    +
    +
    + +
    +
    + ', $txt['attachment_remove_old'], ' ', $txt['days_word'], '
    + ', $txt['attachment_pruning_message'], ':
    + + + + +
    +
    +
    + ', $txt['attachment_remove_size'], ' ', $txt['kilobyte'], '
    + ', $txt['attachment_pruning_message'], ':
    + + + + +
    +
    +
    + ', $txt['attachment_manager_avatars_older'], ' ', $txt['days_word'], '
    + + + + +
    +
    + +
    +
    +
    '; +} + +function template_attachment_repair() +{ + global $context, $txt, $scripturl, $settings; + + // If we've completed just let them know! + if ($context['completed']) + { + echo ' +
    +
    +

    ', $txt['repair_attachments_complete'], '

    +
    +
    + +
    + ', $txt['repair_attachments_complete_desc'], ' +
    + +
    +
    +
    '; + } + + // What about if no errors were even found? + elseif (!$context['errors_found']) + { + echo ' +
    +
    +

    ', $txt['repair_attachments_complete'], '

    +
    +
    + +
    + ', $txt['repair_attachments_no_errors'], ' +
    + +
    +
    +
    '; + } + // Otherwise, I'm sad to say, we have a problem! + else + { + echo ' +
    +
    +
    +

    ', $txt['repair_attachments'], '

    +
    +
    + +
    +

    ', $txt['repair_attachments_error_desc'], '

    '; + + // Loop through each error reporting the status + foreach ($context['repair_errors'] as $error => $number) + { + if (!empty($number)) + echo ' + +
    '; + } + + echo '
    + + +
    + +
    +
    +
    +
    '; + } +} + +function template_attachment_paths() +{ + template_show_list('attach_paths'); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageBans.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageBans.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,365 @@ + + +
    +

    + ', $context['ban']['is_new'] ? $txt['ban_add_new'] : $txt['ban_edit'] . ' \'' . $context['ban']['name'] . '\'', ' +

    +
    '; + + if ($context['ban']['is_new']) + echo ' +
    ', $txt['ban_add_notes'], '
    '; + + echo ' +
    + +
    +
    +
    +
    + ', $txt['ban_name'], ': +
    +
    + +
    +
    + ', $txt['ban_reason'], ':
    + ', $txt['ban_reason_desc'], ' +
    +
    + +
    +
    + ', $txt['ban_notes'], ':
    + ', $txt['ban_notes_desc'], ' +
    +
    + +
    +
    +
    + + ', $txt['ban_expiration'], ' + +
    + : ', $txt['ban_days'], '
    + +
    +
    + + ', $txt['ban_restriction'], ' + +
    +
    + (?)
    +
    +
    +
    +
    '; + + if (!empty($context['ban_suggestions'])) + { + echo ' +
    + + ', $txt['ban_triggers'], ' + +
    +
    + + +
    +
    + +
    '; + + if (empty($modSettings['disableHostnameLookup'])) + echo ' +
    + + +
    +
    + +
    '; + + echo ' +
    + + +
    +
    + +
    +
    + + : +
    +
    '; + + if (empty($context['ban_suggestions']['member']['id'])) + echo ' + '; + else + echo ' + ', $context['ban_suggestions']['member']['link'], ' + '; + echo ' +
    '; + + if (!empty($context['ban_suggestions']['message_ips'])) + { + echo ' +
    +
    ', $txt['ips_in_messages'], ':
    +
    '; + + foreach ($context['ban_suggestions']['message_ips'] as $ip) + echo ' +
    + +
    +
    + ', $ip, ' +
    '; + } + + if (!empty($context['ban_suggestions']['error_ips'])) + { + echo ' +
    +
    ', $txt['ips_in_errors'], '
    +
    '; + + foreach ($context['ban_suggestions']['error_ips'] as $ip) + echo ' +
    + +
    +
    + ', $ip, ' +
    '; + } + + echo ' +
    +
    '; + } + + echo ' +
    + + + + +
    +
    +
    + +
    '; + + if (!$context['ban']['is_new'] && empty($context['ban_suggestions'])) + { + echo ' +
    +
    + + + + + + '; + if (empty($context['ban_items'])) + echo ' + + + '; + else + { + foreach ($context['ban_items'] as $ban_item) + { + echo ' + + + + + + '; + } + } + + echo ' + +
    ', $txt['ban_banned_entity'], ' + ', $txt['ban_hits'], ' + ', $txt['ban_actions'], ' + +
    (', $txt['ban_no_triggers'], ')
    '; + if ($ban_item['type'] == 'ip') + echo ' ', $txt['ip'], ': ', $ban_item['ip']; + elseif ($ban_item['type'] == 'hostname') + echo ' ', $txt['hostname'], ': ', $ban_item['hostname']; + elseif ($ban_item['type'] == 'email') + echo ' ', $txt['email'], ': ', $ban_item['email']; + elseif ($ban_item['type'] == 'user') + echo ' ', $txt['username'], ': ', $ban_item['user']['link']; + echo ' + ', $ban_item['hits'], '', $txt['ban_edit_trigger'], '
    + +
    + + +
    '; + + } + + echo ' + +
    + + '; +} + +function template_ban_edit_trigger() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    + ', $context['ban_trigger']['is_new'] ? $txt['ban_add_trigger'] : $txt['ban_edit_trigger_title'], ' +

    +
    +
    + +
    +
    + + ', $txt['ban_triggers'], ' + +
    +
    + + ', $txt['ban_on_ip'], ' +
    +
    + +
    '; + if (empty($modSettings['disableHostnameLookup'])) + echo ' +
    + + ', $txt['ban_on_hostname'], ' +
    +
    + +
    '; + echo ' +
    + + ', $txt['ban_on_email'], ' +
    +
    + +
    +
    + + ', $txt['ban_on_username'], ' +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    + + + +
    +
    +
    + + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageBoards.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageBoards.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,633 @@ + +
    +

    ', $txt['boardsEdit'], '

    +
    '; + + if (!empty($context['move_board'])) + echo ' +
    +

    ', $context['move_title'], ' [', $txt['mboards_cancel_moving'], ']', '

    +
    '; + + // No categories so show a label. + if (empty($context['categories'])) + echo ' +
    + +
    + ', $txt['mboards_no_cats'], ' +
    + +
    '; + + // Loop through every category, listing the boards in each as we go. + foreach ($context['categories'] as $category) + { + // Link to modify the category. + echo ' + '; + + // Boards table header. + echo ' +
    +
    + +
    +
      '; + + if (!empty($category['move_link'])) + echo ' +
    • ', $category['move_link']['label'], '
    • '; + + $alternate = false; + + // List through every board in the category, printing its name and link to modify the board. + foreach ($category['boards'] as $board) + { + $alternate = !$alternate; + + echo ' + ', $board['name'], '', !empty($modSettings['recycle_board']) && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board['id'] ? ' ' . $txt['recycle_board'] . '' : '', ' + ', $context['can_manage_permissions'] ? '' . $txt['mboards_permissions'] . '' : '', ' + ', $txt['mboards_move'], ' + ', $txt['mboards_modify'], '
      + '; + + if (!empty($board['move_links'])) + { + $alternate = !$alternate; + + echo ' +
    • '; + + foreach ($board['move_links'] as $link) + echo ' + ', $link['label'], ''; + + echo ' +
    • '; + } + } + + // Button to add a new board. + echo ' +
    +
    + + +
    +
    + +
    +
    '; + } + echo ' + +
    '; +} + +// Template for editing/adding a category on the forum. +function template_modify_category() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Print table header. + echo ' +
    +
    + +
    +

    + ', isset($context['category']['is_new']) ? $txt['mboards_new_cat_name'] : $txt['catEdit'], ' +

    +
    +
    + +
    +
    '; + // If this isn't the only category, let the user choose where this category should be positioned down the board index. + if (count($context['category_order']) > 1) + { + echo ' +
    ', $txt['order'], ':
    +
    + +
    '; + } + // Allow the user to edit the category name and/or choose whether you can collapse the category. + echo ' +
    + ', $txt['full_name'], ':
    + ', $txt['name_on_display'], ' +
    +
    + +
    +
    + ' . $txt['collapse_enable'] . '
    + ' . $txt['collapse_desc'] . ' +
    +
    + +
    '; + + // Table footer. + echo ' +
    +
    '; + + if (isset($context['category']['is_new'])) + echo ' + '; + else + echo ' + + '; + echo ' + '; + + // If this category is empty we don't bother with the next confirmation screen. + if ($context['category']['is_empty']) + echo ' + '; + + echo ' +
    +
    + +
    +
    +
    +
    '; +} + +// A template to confirm if a user wishes to delete a category - and whether they want to save the boards. +function template_confirm_category_delete() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Print table header. + echo ' +
    +
    + +
    +

    ', $txt['mboards_delete_cat'], '

    +
    +
    + +
    +

    ', $txt['mboards_delete_cat_contains'], ':

    +
      '; + + foreach ($context['category']['children'] as $child) + echo ' +
    • ', $child, '
    • '; + + echo ' +
    +
    + +
    +
    +

    ', $txt['mboards_delete_what_do'], '

    +
    +
    + +
    +

    +
    + : + +

    + + + + +
    + +
    +
    +
    +
    '; +} + +// Below is the template for adding/editing an board on the forum. +function template_modify_board() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // The main table header. + echo ' +
    +
    + +
    +

    + ', isset($context['board']['is_new']) ? $txt['mboards_new_board_name'] : $txt['boardsEdit'], ' +

    +
    +
    + +
    +
    '; + + // Option for choosing the category the board lives in. + echo ' + +
    + ', $txt['mboards_category'], ': + +
    +
    + +
    '; + + // If this isn't the only board in this category let the user choose where the board is to live. + if ((isset($context['board']['is_new']) && count($context['board_order']) > 0) || count($context['board_order']) > 1) + { + echo ' +
    + ', $txt['order'], ': +
    +
    '; + + // The first select box gives the user the option to position it before, after or as a child of another board. + echo ' + '; + + // The second select box lists all the boards in the category. + echo ' + +
    '; + } + + // Options for board name and description. + echo ' +
    + ', $txt['full_name'], ':
    + ', $txt['name_on_display'], ' +
    +
    + +
    +
    + ', $txt['mboards_description'], ':
    + ', $txt['mboards_description_desc'], ' +
    +
    + +
    +
    + ', $txt['permission_profile'], ':
    + ', $context['can_manage_permissions'] ? sprintf($txt['permission_profile_desc'], $scripturl . '?action=admin;area=permissions;sa=profiles;' . $context['session_var'] . '=' . $context['session_id']) : strip_tags($txt['permission_profile_desc']), ' +
    +
    + +
    +
    + ', $txt['mboards_groups'], ':
    + ', $txt['mboards_groups_desc'], ' +
    +
    '; + + // List all the membergroups so the user can choose who may access this board. + foreach ($context['groups'] as $group) + echo ' +
    '; + echo ' + ', $txt['check_all'], '
    +
    +
    '; + + // Options to choose moderators, specifiy as announcement board and choose whether to count posts here. + echo ' +
    + ', $txt['mboards_moderators'], ':
    + ', $txt['mboards_moderators_desc'], '
    +
    +
    + +
    +
    +
    +
    '; + + if (empty($context['board']['is_recycle']) && empty($context['board']['topics'])) + echo ' +
    +
    + ', $txt['mboards_redirect'], ':
    + ', $txt['mboards_redirect_desc'], '
    +
    +
    + +
    +
    '; + + if (!empty($context['board']['is_recycle'])) + echo ' +
    ', $txt['mboards_redirect_disabled_recycle'], '
    '; + + if (empty($context['board']['is_recycle']) && !empty($context['board']['topics'])) + echo ' +
    + ', $txt['mboards_redirect'],'
    + ', $txt['mboards_redirect_disabled'], ' +
    '; + + if (!$context['board']['topics'] && empty($context['board']['is_recycle'])) + { + echo ' +
    +
    +
    + ', $txt['mboards_redirect_url'], ':
    + ', $txt['mboards_redirect_url_desc'], '
    +
    +
    + +
    +
    +
    '; + + if ($context['board']['redirect']) + echo ' +
    +
    +
    + ', $txt['mboards_redirect_reset'], ':
    + ', $txt['mboards_redirect_reset_desc'], '
    +
    +
    + + (', sprintf($txt['mboards_current_redirects'], $context['board']['posts']), ') +
    +
    +
    '; + } + + echo ' +
    +
    +
    + ', $txt['mboards_count_posts'], ':
    + ', $txt['mboards_count_posts_desc'], '
    +
    +
    + +
    +
    +
    '; + + // Here the user can choose to force this board to use a theme other than the default theme for the forum. + echo ' +
    +
    +
    + ', $txt['mboards_theme'], ':
    + ', $txt['mboards_theme_desc'], '
    +
    +
    + +
    +
    +
    +
    +
    +
    + ', $txt['mboards_override_theme'], ':
    + ', $txt['mboards_override_theme_desc'], '
    +
    +
    + +
    +
    +
    '; + + if (!empty($context['board']['is_recycle'])) + echo '
    ', $txt['mboards_recycle_disabled_delete'], '
    '; + + echo ' + + '; + + // If this board has no children don't bother with the next confirmation screen. + if ($context['board']['no_children']) + echo ' + '; + + if (isset($context['board']['is_new'])) + echo ' + + '; + else + echo ' + '; + + if (!isset($context['board']['is_new']) && empty($context['board']['is_recycle'])) + echo ' + ' : '>', ''; + echo ' +
    + +
    +
    +
    +
    + +'; + + // Javascript for deciding what to show. + echo ' + '; +} + +// A template used when a user is deleting a board with child boards in it - to see what they want to do with them. +function template_confirm_board_delete() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Print table header. + echo ' +
    +
    + + +
    +

    ', $txt['mboards_delete_board'], '

    +
    +
    + +
    +

    ', $txt['mboards_delete_board_contains'], '

    +
      '; + + foreach ($context['children'] as $child) + echo ' +
    • ', $child['node']['name'], '
    • '; + + echo ' +
    +
    + +
    +
    +

    ', $txt['mboards_delete_what_do'], '

    +
    +
    + +
    +

    +
    + : + +

    + + + + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageCalendar.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageCalendar.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,117 @@ +'; + + // Show a form for all the holiday information. + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +
    +
    + ', $txt['holidays_title_label'], ': +
    +
    + +
    +
    + ', $txt['calendar_year'], ' +
    +
    +   + ', $txt['calendar_month'], '  +   + ', $txt['calendar_day'], '  + +
    +
    '; + + if ($context['is_new']) + echo ' + '; + else + echo ' + + + '; + echo ' + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageMail.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageMail.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,42 @@ + +
    +

    ', $txt['mailqueue_stats'], '

    +
    +
    + +
    +
    +
    ', $txt['mailqueue_size'], '
    +
    ', $context['mail_queue_size'], '
    +
    ', $txt['mailqueue_oldest'], '
    +
    ', $context['oldest_mail'], '
    +
    +
    + +
    '; + + template_show_list('mail_queue'); + + echo ' + +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageMaintenance.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageMaintenance.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,622 @@ + + ', sprintf($txt['maintain_done'], $context['maintenance_finished']), ' + '; + + echo ' +
    +
    +

    ', $txt['maintain_optimize'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_optimize_info'], '

    + + +
    +
    + +
    + +
    +

    + ', $txt['help'], ' ', $txt['maintain_backup'], ' +

    +
    + +
    + +
    +
    +

    ', $txt['maintain_backup_info'], '

    '; + + if ($db_type == 'sqlite') + echo ' +

    '; + else + echo ' +


    +
    +

    +

    '; + + echo ' + +
    +
    + +
    '; + + // Show an option to convert to UTF-8 if we're not on UTF-8 yet. + if ($context['convert_utf8']) + { + echo ' +
    +

    ', $txt['utf8_title'], '

    +
    +
    + +
    +
    +

    ', $txt['utf8_introduction'], '

    + ', !empty($modSettings['search_index']) && $modSettings['search_index'] == 'fulltext' ? '

    ' . $txt['utf8_cannot_convert_fulltext'] . '

    ' : '', ' + + +
    +
    + +
    '; + } + + // We might want to convert entities if we're on UTF-8. + if ($context['convert_entities']) + { + echo ' +
    +

    ', $txt['entity_convert_title'], '

    +
    +
    + +
    +
    +

    ', $txt['entity_convert_introduction'], '

    + + +
    +
    + +
    '; + } + + echo ' +
    +
    '; +} + +// Template for the routine maintenance tasks. +function template_maintain_routine() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // If maintenance has finished tell the user. + if (!empty($context['maintenance_finished'])) + echo ' +
    + ', sprintf($txt['maintain_done'], $context['maintenance_finished']), ' +
    '; + + // Starts off with general maintenance procedures. + echo ' +
    +
    +

    ', $txt['maintain_version'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_version_info'], '

    + + +
    +
    + +
    +
    +

    ', $txt['maintain_errors'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_errors_info'], '

    + + +
    +
    + +
    +
    +

    ', $txt['maintain_recount'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_recount_info'], '

    + + +
    +
    + +
    +
    +

    ', $txt['maintain_logs'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_logs_info'], '

    + + +
    +
    + +
    +
    +

    ', $txt['maintain_cache'], '

    +
    +
    + +
    +
    +

    ', $txt['maintain_cache_info'], '

    + + +
    +
    + +
    +
    +
    '; +} + +// Template for the member maintenance tasks. +function template_maintain_members() +{ + global $context, $settings, $options, $txt, $scripturl; + + // If maintenance has finished tell the user. + if (!empty($context['maintenance_finished'])) + echo ' +
    + ', sprintf($txt['maintain_done'], $context['maintenance_finished']), ' +
    '; + + echo ' + +
    +
    +

    ', $txt['maintain_reattribute_posts'], '

    +
    +
    + +
    +
    +

    ', $txt['reattribute_guest_posts'], '

    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +

    + + +

    + + +
    +
    + +
    +
    +

    + + ', $txt['help'], ' ', $txt['maintain_members'], ' + +

    +
    +
    + +
    +
    +

    ', $txt['maintain_members_since1'], ' + ', $txt['maintain_members_since2'], ' ', $txt['maintain_members_since3'], '

    '; + + echo ' +

    + ', $txt['maintain_members_all'], '

    + + + +
    +
    + +
    +
    +
    + + + '; +} + +// Template for the topic maintenance tasks. +function template_maintain_topics() +{ + global $scripturl, $txt, $context, $settings, $modSettings; + + // If maintenance has finished tell the user. + if (!empty($context['maintenance_finished'])) + echo ' +
    + ', sprintf($txt['maintain_done'], $context['maintenance_finished']), ' +
    '; + + // Bit of javascript for showing which boards to prune in an otherwise hidden list. + echo ' + '; + + echo ' +
    +
    +

    ', $txt['maintain_old'], '

    +
    +
    + +
    +
    '; + + // The otherwise hidden "choose which boards to prune". + echo ' +

    + ', $txt['maintain_old_since_days1'], '', $txt['maintain_old_since_days2'], ' +

    +

    +
    +
    +
    +

    '; + + if (!empty($modSettings['enableStickyTopics'])) + echo ' +

    +
    +

    '; + + echo ' +

    + + ', $txt['maintain_old_all'], ' +

    + + + +
    +
    + +
    +
    +

    ', $txt['move_topics_maintenance'], '

    +
    +
    + +
    +
    +

    + + +

    + + +
    +
    + +
    +
    +
    '; +} + +// Simple template for showing results of our optimization... +function template_optimize() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['maintain_optimize'], '

    +
    +
    + +
    +

    + ', $txt['database_numb_tables'], '
    + ', $txt['database_optimize_attempt'], '
    '; + + // List each table being optimized... + foreach ($context['optimized_tables'] as $table) + echo ' + ', sprintf($txt['database_optimizing'], $table['name'], $table['data_freed']), '
    '; + + // How did we go? + echo ' +
    ', $context['num_tables_optimized'] == 0 ? $txt['database_already_optimized'] : $context['num_tables_optimized'] . ' ' . $txt['database_optimized']; + + echo ' +

    +

    ', $txt['maintain_return'], '

    +
    + +
    +
    +
    '; +} + +function template_convert_utf8() +{ + global $context, $txt, $settings, $scripturl; + + echo ' +
    +
    +

    ', $txt['utf8_title'], '

    +
    +
    + +
    +
    +

    ', $txt['utf8_introduction'], '

    +
    ', $txt['utf8_warning'], '
    + +
    +
    ', $txt['utf8_source_charset'], ':
    +
    +
    ', $txt['utf8_database_charset'], ':
    +
    ', $context['database_charset'], '
    +
    ', $txt['utf8_target_charset'], ':
    +
    ', $txt['utf8_utf8'], '
    +
    + + + +
    +
    + +
    +
    +
    '; +} + +function template_convert_entities() +{ + global $context, $txt, $settings, $scripturl; + + echo ' +
    +
    +

    ', $txt['entity_convert_title'], '

    +
    +
    + +
    +

    ', $txt['entity_convert_introduction'], '

    +
    + +
    +
    + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageMembergroups.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageMembergroups.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,610 @@ +
    '; + template_show_list('post_count_membergroups_list'); + +} + +function template_new_group() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['membergroups_new_group'], '

    +
    +
    + +
    +
    +
    + +
    +
    + +
    '; + if ($context['undefined_group']) + { + echo ' +
    + +
    +
    +
    + ', $txt['membergroups_edit_select_group_type'], ' +
    '; + + if ($context['allow_protected']) + echo ' +
    '; + + echo ' +
    +
    +
    +
    +
    '; + } + + if ($context['post_group'] || $context['undefined_group']) + echo ' +
    + ', $txt['membergroups_min_posts'], ': +
    +
    + +
    '; + if (!$context['post_group'] || !empty($modSettings['permission_enable_postgroups'])) + { + echo ' +
    +
    + ', $txt['membergroups_can_edit_later'], ' +
    +
    +
    + ', $txt['membergroups_select_permission_type'], ' + + +
    + + + +
    + + + + +
    +
    '; + } + echo ' +
    + ', $txt['membergroups_new_board'], ':', $context['post_group'] ? '
    + ' . $txt['membergroups_new_board_post_groups'] . '' : '', ' +
    +
    +
    + ', $txt['membergroups_new_board_desc'], ''; + foreach ($context['boards'] as $board) + echo ' +
    '; + + echo ' +
    + +
    +
    +
    +
    + +
    +
    + +
    '; + if ($context['undefined_group']) + { + echo ' + '; + } + echo ' + +
    +
    +
    '; +} + +function template_edit_group() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +
    +

    ', $txt['membergroups_edit_group'], ' - ', $context['group']['name'], ' +

    +
    +
    + +
    +
    +
    + +
    +
    + +
    '; + + if ($context['group']['id'] != 3 && $context['group']['id'] != 4) + echo ' + +
    + +
    +
    + +
    '; + + // Group type... + if ($context['group']['allow_post_group']) + { + echo ' +
    + +
    +
    +
    + ', $txt['membergroups_edit_select_group_type'], ' +
    '; + + if ($context['group']['allow_protected']) + echo ' +
    '; + + echo ' +
    +
    +
    +
    +
    '; + } + + if ($context['group']['id'] != 3 && $context['group']['id'] != 4) + echo ' +
    + +
    +
    + +
    +
    +
    + +
    +
    + +
    '; + + // Can they inherit permissions? + if ($context['group']['id'] > 1 && $context['group']['id'] != 3) + { + echo ' +
    + :
    + ', $txt['membergroups_edit_inherit_permissions_desc'], ' +
    +
    + + +
    '; + } + + if ($context['group']['allow_post_group']) + echo ' + +
    + +
    +
    + +
    '; + echo ' +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + ', $txt['membergroups_star_image_note'], ' +
    +
    + ', $txt['membergroups_images_url'], ' + + * +
    +
    +
    + ', $txt['membergroups_max_messages_note'], ' +
    +
    + +
    '; + if (!empty($context['boards'])) + { + echo ' +
    + ', $txt['membergroups_new_board'], ':', $context['group']['is_post_group'] ? '
    + ' . $txt['membergroups_new_board_post_groups'] . '' : '', ' +
    +
    +
    + ', $txt['membergroups_new_board_desc'], ''; + foreach ($context['boards'] as $board) + echo ' +
    '; + + echo ' +
    + +
    + + +
    '; + } + echo ' +
    +
    + ', $context['group']['allow_delete'] ? ' + ' : '', ' +
    +
    + +
    + +
    +
    +
    + + '; + + if ($context['group']['allow_post_group']) + echo ' + '; +} + +// Templating for viewing the members of a group. +function template_group_members() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +
    +
    + ', $txt['name'], ': +
    +
    + ', $context['group']['name'], ' ', $context['group']['stars'], ' +
    '; + //Any description to show? + if (!empty($context['group']['description'])) + echo ' +
    + ' . $txt['membergroups_members_description'] . ': +
    +
    + ', $context['group']['description'] ,' +
    '; + + echo ' +
    + ', $txt['membergroups_members_top'], ': +
    +
    + ', $context['total_members'] ,' +
    '; + // Any group moderators to show? + if (!empty($context['group']['moderators'])) + { + $moderators = array(); + foreach ($context['group']['moderators'] as $moderator) + $moderators[] = '' . $moderator['name'] . ''; + + echo ' +
    + ', $txt['membergroups_members_group_moderators'], ': +
    +
    + ', implode(', ', $moderators) ,' +
    '; + } + + echo ' +
    +
    + +
    + +
    +
    +

    ', $txt['membergroups_members_group_members'], '

    +
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    + + + + + + + + ', $txt['posts'], $context['sort_by'] == 'posts' ? ' ' : '', ''; + if (!empty($context['group']['assignable'])) + echo ' + '; + echo ' + + + '; + + if (empty($context['members'])) + echo ' + + + '; + + foreach ($context['members'] as $member) + { + echo ' + + + '; + + // Is it totally hidden? + if ($member['show_email'] == 'no') + echo ' + ', $txt['hidden'], ''; + // ... otherwise they want it hidden but it's not to this person? + elseif ($member['show_email'] == 'yes_permission_override') + echo ' + ', $member['email'], ''; + // ... otherwise it's visible - but only via an image? + elseif ($member['show_email'] == 'no_through_forum') + echo ' + ', ($settings['use_image_buttons'] ? '' . $txt['email'] . '' : $txt['email']), ''; + // ... otherwise it must be a 'yes', show it and show it fully. + else + echo ' + ', $member['email'], ''; + + echo ' + + + + ', $member['posts'], ''; + if (!empty($context['group']['assignable'])) + echo ' + '; + echo ' + '; + } + + echo ' + +
    ', $txt['name'], $context['sort_by'] == 'name' ? ' ' : '', '', $txt['email'], $context['sort_by'] == 'email' ? ' ' : '', '', $txt['membergroups_members_last_active'], $context['sort_by'] == 'active' ? ' ' : '', '', $txt['date_registered'], $context['sort_by'] == 'registered' ? ' ' : '', '
    ', $txt['membergroups_members_no_members'], '
    ', $member['name'], '', $member['last_online'], '', $member['registered'], '
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + + if (!empty($context['group']['assignable'])) + echo ' +
    '; + echo ' +
    +
    '; + + if (!empty($context['group']['assignable'])) + { + echo ' +
    +

    ', $txt['membergroups_members_add_title'], '

    +
    +
    + +
    + ', $txt['membergroups_members_add_desc'], ': + +
    + +
    + +
    '; + } + + echo ' + +
    +
    +
    '; + + if (!empty($context['group']['assignable'])) + echo ' + + '; +} + +// Allow the moderator to enter a reason to each user being rejected. +function template_group_request_reason() +{ + global $settings, $options, $context, $txt, $scripturl; + + // Show a welcome message to the user. + echo ' +
    +
    +
    +

    ', $txt['mc_groups_reason_title'], '

    +
    +
    + +
    +
    '; + + // Loop through and print out a reason box for each... + foreach ($context['group_requests'] as $request) + echo ' +
    + ', sprintf($txt['mc_groupr_reason_desc'], $request['member_link'], $request['group_link']), ': +
    +
    + + +
    '; + + echo ' +
    + + + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageMembers.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageMembers.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,332 @@ + +
    +
    +

    + ', $txt['search_for'], ' + ', $txt['wild_cards_allowed'], ' +

    +
    + +
    + +
    +
    +
    +
    +
    + ', $txt['member_id'], ': + +
    +
    + +
    +
    + ', $txt['age'], ': + +
    +
    + +
    +
    + ', $txt['member_postcount'], ': + +
    +
    + +
    +
    + ', $txt['date_registered'], ': + +
    +
    + ', $txt['date_format'], ' +
    +
    + ', $txt['viewmembers_online'], ': + +
    +
    + ', $txt['date_format'], ' +
    +
    +
    +
    +
    +
    + ', $txt['username'], ': +
    +
    + +
    +
    + ', $txt['email_address'], ': +
    +
    + +
    +
    + ', $txt['website'], ': +
    +
    + +
    +
    + ', $txt['location'], ': +
    +
    + +
    +
    + ', $txt['ip_address'], ': +
    +
    + +
    +
    + ', $txt['messenger_address'], ': +
    +
    + +
    +
    +
    +
    +
    +
    +
    + ', $txt['gender'], ' +    +    + +
    +
    +
    +
    + ', $txt['activation_status'], ' +    + +
    +
    +
    +
    + +
    +
    +
    +

    ', $txt['member_part_of_these_membergroups'], '

    +
    +
    + + + + + + + + + '; + + foreach ($context['membergroups'] as $membergroup) + echo ' + + + + + '; + + echo ' + + + + + + +
    ', $txt['membergroups'], '', $txt['primary'], '', $txt['additional'], '
    ', $membergroup['name'], ' + + + ', $membergroup['can_be_additional'] ? '' : '', ' +
    + ', $txt['check_all'], ' + + + + +
    + + + + + + + + + '; + + foreach ($context['postgroups'] as $postgroup) + echo ' + + + + '; + + echo ' + + + + + +
    + ', $txt['membergroups_postgroups'], ' +  
    + ', $postgroup['name'], ' + + +
    + ', $txt['check_all'], ' + + +
    +

    +
    + +
    +
    + +
    '; +} + +function template_admin_browse() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    '; + + template_show_list('approve_list'); + + // If we have lots of outstanding members try and make the admin's life easier. + if ($context['approve_list']['total_num_items'] > 20) + { + echo ' +
    +
    +
    +

    ', $txt['admin_browse_outstanding'], '

    +
    + + +
    + +
    +
    +
    + ', $txt['admin_browse_outstanding_days_1'], ': +
    +
    + ', $txt['admin_browse_outstanding_days_2'], '. +
    +
    + ', $txt['admin_browse_outstanding_perform'], ': +
    +
    + +
    +
    + + + + + + ', !empty($context['approve_list']['sort']['desc']) ? ' + ' : '', ' +
    + +
    + +
    '; + } + + echo ' +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageNews.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageNews.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,355 @@ + +
    + + + + + + + + + '; + + // Loop through all the current news items so you can edit/remove them. + foreach ($context['admin_current_news'] as $admin_news) + echo ' + + + '; + + // This provides an empty text box to add a news item to the site. + echo ' + + + + + + +
    ', $txt['admin_edit_news'], '', $txt['preview'], '
    + +
    +
    +
    ', $admin_news['parsed'], '
    +
    + +
    +
    + + + +
    +
    + +
    + +
    + +
    '; +} + +function template_email_members() +{ + global $context, $settings, $options, $txt, $scripturl; + + // This is some javascript for the simple/advanced toggling stuff. + echo ' + '; + + echo ' +
    +
    +
    +

    ', $txt['admin_newsletters'], '

    +
    +
    + ', $txt['admin_news_select_recipients'], ' +
    +
    + +
    +
    +
    + ', $txt['admin_news_select_group'], ':
    + ', $txt['admin_news_select_group_desc'], ' +
    +
    '; + + foreach ($context['groups'] as $group) + echo ' + (', $group['member_count'], ')
    '; + + echo ' +
    + '; + + echo ' +
    +

    +
    + +
    +
    + + + + +
    + + +
    +
    +
    +
    '; + + // Make the javascript stuff visible. + echo ' + + '; +} + +function template_email_members_compose() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $txt['help'], ' ', $txt['admin_newsletters'], ' +

    +
    +
    + ', $txt['email_variables'], ' +
    +
    + +
    +

    + +

    +

    + +

    +
      +
    • +
    • +
    • +
    +

    + +

    +
    + +
    + + + + '; + + foreach ($context['recipients'] as $key => $values) + echo ' + '; + + echo ' +
    +
    +
    '; +} + +function template_email_members_send() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $txt['help'], ' ', $txt['admin_newsletters'], ' +

    +
    +
    + +
    +

    + ', $context['percentage_done'], '% ', $txt['email_done'], ' +

    + + + + + + + + + + '; + + // All the things we must remember! + foreach ($context['recipients'] as $key => $values) + echo ' + '; + + echo ' +
    + +
    +
    +
    +
    + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManagePaid.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManagePaid.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,676 @@ +'; + + echo ' +
    +
    +
    +

    ', $txt['paid_' . $context['action_type'] . '_subscription'], '

    +
    '; + + if (!empty($context['disable_groups'])) + echo ' +
    + ', $txt['paid_mod_edit_note'], ' +
    + '; + echo ' +
    + +
    +
    +
    + ', $txt['paid_mod_name'], ': +
    +
    + +
    +
    + ', $txt['paid_mod_desc'], ': +
    +
    + +
    +
    + : +
    +
    + +
    +
    + :
    ', $txt['paid_mod_active_desc'], ' +
    +
    + +
    +
    +
    +
    +
    + ', $txt['paid_mod_prim_group'], ':
    ', $txt['paid_mod_prim_group_desc'], ' +
    +
    + +
    +
    + ', $txt['paid_mod_add_groups'], ':
    ', $txt['paid_mod_add_groups_desc'], ' +
    +
    '; + + // Put a checkbox in for each group + foreach ($context['groups'] as $id => $name) + echo ' +
    '; + + echo ' +
    +
    + ', $txt['paid_mod_reminder'], ':
    ', $txt['paid_mod_reminder_desc'], ' +
    +
    + +
    +
    + ', $txt['paid_mod_email'], ':
    ', $txt['paid_mod_email_desc'], ' +
    +
    + +
    +
    +
    + + ', $txt['paid_mod_fixed_price'], ' +
    +
    +
    +
    +
    + ', $txt['paid_cost'], ' (', str_replace('%1.2f', '', $modSettings['paid_currency_symbol']), '): +
    +
    + +
    +
    + ', $txt['paid_mod_span'], ': +
    +
    + + +
    +
    +
    +
    + + ', $txt['paid_mod_flexible_price'], ' +
    +
    +
    '; + + //!! Removed until implemented + if (!empty($sdflsdhglsdjgs)) + echo ' +
    +
    + :
    ', $txt['paid_mod_allow_partial_desc'], ' +
    +
    + +
    +
    '; + + echo ' +
    + ', $txt['paid_mod_price_breakdown'], '
    + ', $txt['paid_mod_price_breakdown_desc'], ' +
    +
    +
    + ', $txt['paid_duration'], ' +
    +
    + ', $txt['paid_cost'], ' (', preg_replace('~%[df\.\d]+~', '', $modSettings['paid_currency_symbol']), ') +
    +
    + ', $txt['paid_per_day'], ': +
    +
    + +
    +
    + ', $txt['paid_per_week'], ': +
    +
    + +
    +
    + ', $txt['paid_per_month'], ': +
    +
    + +
    +
    + ', $txt['paid_per_year'], ': +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    '; + +} + +function template_delete_subscription() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['paid_delete_subscription'], '

    +
    +
    + +
    +

    ', $txt['paid_mod_delete_warning'], '

    + + + +
    + +
    +
    +
    +
    '; + +} + +// Add or edit an existing subscriber. +function template_modify_user_subscription() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Some quickly stolen javascript from Post, could do with being more efficient :) + echo ' + '; + + echo ' +
    +
    +
    +

    + ', $txt['paid_' . $context['action_type'] . '_subscription'], ' - ', $context['current_subscription']['name'], ' + ', empty($context['sub']['username']) ? '' : ' (' . $txt['user'] . ': ' . $context['sub']['username'] . ')', ' +

    +
    +
    + +
    +
    '; + + // Do we need a username? + if ($context['action_type'] == 'add') + echo ' + +
    + ', $txt['paid_username'], ':
    + ', $txt['one_username'], ' +
    +
    + +
    '; + + echo ' +
    + ', $txt['paid_status'], ': +
    +
    + +
    +
    +
    + ', $txt['start_date_and_time'], ' +   + ', (isset($txt['calendar_month']) ? $txt['calendar_month'] : $txt['calendar_month']), '  +   + ', (isset($txt['calendar_day']) ? $txt['calendar_day'] : $txt['calendar_day']), '  + + ', $txt['hour'], ': + ', $txt['minute'], ': +
    +
    + ', $txt['end_date_and_time'], ' +   + ', (isset($txt['calendar_month']) ? $txt['calendar_month'] : $txt['calendar_month']), '  +   + ', (isset($txt['calendar_day']) ? $txt['calendar_day'] : $txt['calendar_day']), '  + + ', $txt['hour'], ': + ', $txt['minute'], ': +
    + +
    + +
    + +
    + + '; + + if (!empty($context['pending_payments'])) + { + echo ' +
    +

    ', $txt['pending_payments'], '

    +
    +
    + ', $txt['pending_payments_desc'], ' +
    +
    +

    ', $txt['pending_payments_value'], '

    +
    +
    + +
    + +
    + +
    '; + } + + echo ' +
    +
    '; +} + +// Template for a user to edit/pick their subscriptions. +function template_user_subscription() +{ + global $context, $txt, $scripturl, $modSettings; + + echo ' + +
    '; +} + +// The "choose payment" dialog. +function template_choose_payment() +{ + global $context, $txt, $modSettings, $scripturl; + + echo ' + +
    '; +} + +// The "thank you" bit... +function template_paid_done() +{ + global $context, $txt, $modSettings, $scripturl; + + echo ' + +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManagePermissions.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManagePermissions.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1192 @@ + + ', sprintf($txt['permission_cannot_edit'], $scripturl . '?action=admin;area=permissions;sa=profiles'), ' + '; + + echo ' +
    +
    '; + + if (!empty($context['profile'])) + echo ' +
    +

    ', $txt['permissions_for_profile'], ': "', $context['profile']['name'], '"

    +
    '; + + echo ' + + + + + '; + + if (empty($modSettings['permission_enable_deny'])) + echo ' + '; + else + echo ' + + '; + + echo ' + + + + + '; + + $alternate = false; + foreach ($context['groups'] as $group) + { + $alternate = !$alternate; + echo ' + + + '; + + if (empty($modSettings['permission_enable_deny'])) + echo ' + '; + else + echo ' + + '; + + echo ' + + + '; + } + + echo ' + +
    ', $txt['membergroups_name'], '', $txt['membergroups_members_top'], '', $txt['membergroups_permissions'], '', $txt['permissions_allowed'], '', $txt['permissions_denied'], '', $context['can_modify'] ? $txt['permissions_modify'] : $txt['permissions_view'], ' + ', $context['can_modify'] ? '' : '', ' +
    + ', $group['name'], $group['id'] == -1 ? ' (?)' : ($group['id'] == 0 ? ' (?)' : ($group['id'] == 1 ? ' (?)' : ($group['id'] == 3 ? ' (?)' : ''))); + + if (!empty($group['children'])) + echo ' +
    ', $txt['permissions_includes_inherited'], ': "', implode('", "', $group['children']), '"'; + + echo ' +
    ', $group['can_search'] ? $group['link'] : $group['num_members'], '', $group['num_permissions']['allowed'], '', $group['num_permissions']['allowed'], '', $group['num_permissions']['denied'], '', $group['allow_modify'] ? '' . ($context['can_modify'] ? $txt['permissions_modify'] : $txt['permissions_view']). '' : '', '', $group['allow_modify'] && $context['can_modify'] ? '' : '', '
    +
    '; + + // Advanced stuff... + if ($context['can_modify']) + { + echo ' +
    +

    + + * ', $txt['permissions_advanced_options'], ' + +

    +
    +
    + +
    +
    + ', $txt['permissions_with_selection'], ' +
    +
    + ', $txt['permissions_apply_pre_defined'], ' (?): +
    +
    + +
    +
    + ', $txt['permissions_like_group'], ': +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    '; + + // Javascript for the advanced stuff. + echo ' + '; + + if (!empty($context['profile'])) + echo ' + '; + + echo ' + '; + } + else + echo ' + '; + + echo ' +
    +
    +
    '; +} + +function template_by_board() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +

    ', $txt['permissions_boards'], '

    +
    +
    + ', $txt['permissions_boards_desc'], ' +
    +
    +

    + ', $txt['board_name'], ' + ', $txt['permission_profile'], ' +

    +
    '; + + if (!$context['edit_all']) + echo ' + '; + + foreach ($context['categories'] as $category) + { + echo ' +
    +

    ', $category['name'], '

    +
    '; + + if (!empty($category['boards'])) + echo ' +
    + +
    + +
    + +
    '; + } + + echo ' +
    '; + + if ($context['edit_all']) + echo ' + '; + else + echo ' + [', $txt['permissions_board_all'], ']'; + + echo ' + +
    +
    +
    '; +} + +// Edit permission profiles (predefined). +function template_edit_profiles() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['permissions_profile_edit'], '

    +
    + + + + + + + + + + '; + $alternate = false; + foreach ($context['profiles'] as $profile) + { + echo ' + + + + + '; + $alternate = !$alternate; + } + + echo ' + +
    ', $txt['permissions_profile_name'], '', $txt['permissions_profile_used_by'], '', $txt['delete'], '
    '; + + if (!empty($context['show_rename_boxes']) && $profile['can_edit']) + echo ' + '; + else + echo ' + ', $profile['name'], ''; + + echo ' + + ', !empty($profile['boards_text']) ? $profile['boards_text'] : $txt['permissions_profile_used_by_none'], ' + + +
    +
    + '; + + if ($context['can_edit_something']) + echo ' + '; + + echo ' + +
    +
    +
    +
    +
    +

    ', $txt['permissions_profile_new'], '

    +
    +
    + +
    +
    +
    + ', $txt['permissions_profile_name'], ': +
    +
    + +
    +
    + ', $txt['permissions_profile_copy_from'], ': +
    +
    + +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +function template_modify_group() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Cannot be edited? + if (!$context['profile']['can_modify']) + { + echo ' +
    + ', sprintf($txt['permission_cannot_edit'], $scripturl . '?action=admin;area=permissions;sa=profiles'), ' +
    '; + } + else + { + echo ' + '; + } + + echo ' +
    +
    '; + + if (!empty($modSettings['permission_enable_deny']) && $context['group']['id'] != -1) + echo ' +
    + ', $txt['permissions_option_desc'], ' +
    '; + + echo ' +
    +

    '; + if ($context['permission_type'] == 'board') + echo ' + ', $txt['permissions_local_for'], ' "', $context['group']['name'], '" ', $txt['permissions_on'], ' "', $context['profile']['name'], '"'; + else + echo ' + ', $context['permission_type'] == 'membergroup' ? $txt['permissions_general'] : $txt['permissions_board'], ' - "', $context['group']['name'], '"'; + echo ' +

    +
    +
    + +
    + ', $txt['permissions_change_view'], ': ', ($context['view_type'] == 'simple' ? '*' : ''), '', $txt['permissions_view_simple'], ' | + ', ($context['view_type'] == 'classic' ? '*' : ''), '', $txt['permissions_view_classic'], ' +
    + +
    +
    '; + + // Draw out the main bits. + if ($context['view_type'] == 'simple') + template_modify_group_simple($context['permission_type']); + else + template_modify_group_classic($context['permission_type']); + + // If this is general permissions also show the default profile. + if ($context['permission_type'] == 'membergroup') + { + echo ' +
    +
    +
    +

    ', $txt['permissions_board'], '

    +
    +
    + ', $txt['permissions_board_desc'], ' +
    +
    '; + + if ($context['view_type'] == 'simple') + template_modify_group_simple('board'); + else + template_modify_group_classic('board'); + + echo ' +
    '; + } + + if ($context['profile']['can_modify']) + echo ' +
    + +
    '; + + echo ' + +
    +
    +
    '; + +} + +// A javascript enabled clean permissions view. +function template_modify_group_simple($type) +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Simple only has one column so we only need bother ourself with that one. + $permission_data = &$context['permissions'][$type]['columns'][0]; + + // Short cut for disabling fields we can't change. + $disable_field = $context['profile']['can_modify'] ? '' : 'disabled="disabled" '; + + echo ' + + + + '; + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + echo ' + + + '; + + foreach ($permission_data as $id_group => $permissionGroup) + { + if (empty($permissionGroup['permissions'])) + continue; + + // Are we likely to have something in this group to display or is it all hidden? + $has_display_content = false; + if (!$permissionGroup['hidden']) + { + // Before we go any further check we are going to have some data to print otherwise we just have a silly heading. + foreach ($permissionGroup['permissions'] as $permission) + if (!$permission['hidden']) + $has_display_content = true; + + if ($has_display_content) + { + echo ' + + '; + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + echo ' + '; + } + } + + $alternate = false; + foreach ($permissionGroup['permissions'] as $permission) + { + // If it's hidden keep the last value. + if ($permission['hidden'] || $permissionGroup['hidden']) + { + echo ' + + + '; + } + else + { + echo ' + + + '; + + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + + echo ' + '; + } + $alternate = !$alternate; + } + + if (!$permissionGroup['hidden'] && $has_display_content) + echo ' + + + '; + } + echo ' + +
     ', $txt['permissions_option_on'], '', $txt['permissions_option_off'], '', $txt['permissions_option_deny'], '
    + + * ', $permissionGroup['name'], ' + + +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    + ', $permission['help_index'] ? '' . $txt['help'] . '' : '', ' + ', $permission['name'], '
    + '; +} + +// The SMF 1.x way of looking at permissions. +function template_modify_group_classic($type) +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + $permission_type = &$context['permissions'][$type]; + $disable_field = $context['profile']['can_modify'] ? '' : 'disabled="disabled" '; + + echo ' +
    + +
    '; + + foreach ($permission_type['columns'] as $column) + { + echo ' + '; + + foreach ($column as $permissionGroup) + { + if (empty($permissionGroup['permissions'])) + continue; + + // Are we likely to have something in this group to display or is it all hidden? + $has_display_content = false; + if (!$permissionGroup['hidden']) + { + // Before we go any further check we are going to have some data to print otherwise we just have a silly heading. + foreach ($permissionGroup['permissions'] as $permission) + if (!$permission['hidden']) + $has_display_content = true; + + if ($has_display_content) + { + echo ' + + '; + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + echo ' + '; + } + } + + $alternate = false; + foreach ($permissionGroup['permissions'] as $permission) + { + // If it's hidden keep the last value. + if ($permission['hidden'] || $permissionGroup['hidden']) + { + echo ' + + + '; + } + else + { + echo ' + + '; + + if ($permission['has_own_any']) + { + echo ' + + '; + + // Guests can't do their own thing. + if ($context['group']['id'] != -1) + { + echo ' + + '; + + if (empty($modSettings['permission_enable_deny'])) + echo ' + '; + else + echo ' + + + '; + + echo ' + '; + } + + echo ' + + '; + + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + + echo ' + '; + } + else + { + echo ' + '; + + if (empty($modSettings['permission_enable_deny']) || $context['group']['id'] == -1) + echo ' + '; + else + echo ' + + + '; + + echo ' + '; + } + } + $alternate = !$alternate; + } + + if (!$permissionGroup['hidden'] && $has_display_content) + echo ' + + + '; + } + echo ' +
    ', $permissionGroup['name'], '
    ', $txt['permissions_option_on'], '
    ', $txt['permissions_option_off'], '
    ', $txt['permissions_option_deny'], '
    '; + + if ($permission['has_own_any']) + { + // Guests can't have own permissions. + if ($context['group']['id'] != -1) + echo ' + '; + + echo ' + '; + } + else + echo ' + '; + echo ' +
    + ', $permission['show_help'] ? '' . $txt['help'] . '' : '', ' + ', $permission['name'], '
    ', $permission['own']['name'], ':
    ', $permission['any']['name'], ':
    ', $permission['name'], '
    '; + } + echo ' +
    +
    + +
    '; +} + +function template_inline_permissions() +{ + global $context, $settings, $options, $txt, $modSettings; + + echo ' +
    + ', $txt['avatar_select_permission'], ''; + if (empty($modSettings['permission_enable_deny'])) + echo ' +
      '; + else + echo ' +
      ', $txt['permissions_option_desc'], '
      +
      +
      + ', $txt['permissions_option_on'], ' + ', $txt['permissions_option_off'], ' + ', $txt['permissions_option_deny'], ' +
      +
      +
      '; + foreach ($context['member_groups'] as $group) + { + if (!empty($modSettings['permission_enable_deny'])) + echo ' +
      '; + else + echo ' +
    • '; + + if (empty($modSettings['permission_enable_deny'])) + echo ' + '; + else + echo ' + + + '; + + if (!empty($modSettings['permission_enable_deny'])) + echo ' +
    • +
      + ', $group['name'], ' +
      '; + else + echo ' + ', $group['name'], ' + '; + } + + if (empty($modSettings['permission_enable_deny'])) + echo ' +
    '; + else + echo ' + '; + + echo ' +
    + + + + '; +} + +// Edit post moderation permissions. +function template_postmod_permissions() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['permissions_post_moderation'], '

    +
    '; + + // Got advanced permissions - if so warn! + if (!empty($modSettings['permission_enable_deny'])) + echo ' +
    ', $txt['permissions_post_moderation_deny_note'], '
    '; + + echo ' +
    + ', $txt['permissions_post_moderation_select'], ': + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + '; + + foreach ($context['profile_groups'] as $group) + { + echo ' + + + + + + + + + + + + + + + '; + } + + echo ' + +
    + ', $txt['permissions_post_moderation_new_topics'], ' + + ', $txt['permissions_post_moderation_replies_own'], ' + + ', $txt['permissions_post_moderation_replies_any'], ' + + ', $txt['permissions_post_moderation_attachments'], ' +
    + ', $txt['permissions_post_moderation_group'], ' + ', $txt['permissions_post_moderation_allow'], '', $txt['permissions_post_moderation_moderate'], '', $txt['permissions_post_moderation_disallow'], '', $txt['permissions_post_moderation_allow'], '', $txt['permissions_post_moderation_moderate'], '', $txt['permissions_post_moderation_disallow'], '', $txt['permissions_post_moderation_allow'], '', $txt['permissions_post_moderation_moderate'], '', $txt['permissions_post_moderation_disallow'], '', $txt['permissions_post_moderation_allow'], '', $txt['permissions_post_moderation_moderate'], '', $txt['permissions_post_moderation_disallow'], '
    + ', $group['name'], ''; + if (!empty($group['children'])) + echo ' +
    ', $txt['permissions_includes_inherited'], ': "', implode('", "', $group['children']), '"'; + + echo ' +
    +
    + +
    +
    +

    + ', $txt['permissions_post_moderation_legend'], ':
    + ', $txt['permissions_post_moderation_allow'], ' - ', $txt['permissions_post_moderation_allow'], '
    + ', $txt['permissions_post_moderation_moderate'], ' - ', $txt['permissions_post_moderation_moderate'], '
    + ', $txt['permissions_post_moderation_disallow'], ' - ', $txt['permissions_post_moderation_disallow'], ' +

    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageScheduledTasks.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageScheduledTasks.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,95 @@ + + ', $txt['scheduled_tasks_were_run'], ' + '; + + template_show_list('scheduled_tasks'); +} + +// A template for, you guessed it, editing a task! +function template_edit_scheduled_tasks() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // Starts off with general maintenance procedures. + echo ' +
    +
    +
    +

    ', $txt['scheduled_task_edit'], '

    +
    +
    + ', sprintf($txt['scheduled_task_time_offset'], $context['server_time']), ' +
    +
    + +
    +
    +
    + ', $txt['scheduled_tasks_name'], ': +
    +
    + ', $context['task']['name'], '
    + ', $context['task']['desc'], ' +
    +
    + ', $txt['scheduled_task_edit_interval'], ': +
    +
    + ', $txt['scheduled_task_edit_repeat'], ' + + +
    +
    + ', $txt['scheduled_task_edit_start_time'], ':
    + ', $txt['scheduled_task_edit_start_time_desc'], ' +
    +
    + +
    +
    + ', $txt['scheduled_tasks_enabled'], ': +
    +
    + +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageSearch.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageSearch.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,428 @@ + +
    +
    +

    ', $txt['search_weights'], '

    +
    +
    + +
    +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_frequency'], ': +
    +
    + + ', $context['relative_weights']['search_weight_frequency'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_age'], ': +
    +
    + + ', $context['relative_weights']['search_weight_age'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_length'], ': +
    +
    + + ', $context['relative_weights']['search_weight_length'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_subject'], ': +
    +
    + + ', $context['relative_weights']['search_weight_subject'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_first_message'], ': +
    +
    + + ', $context['relative_weights']['search_weight_first_message'], '% +
    +
    + ', $txt['help'], ' + ', $txt['search_weight_sticky'], ': +
    +
    + + ', $context['relative_weights']['search_weight_sticky'], '% +
    +
    + ', $txt['search_weights_total'], ' +
    +
    + ', $context['relative_weights']['total'], ' + 100% +
    +
    + +
    +
    + +
    +
    + +
    + '; +} + +function template_select_search_method() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['search_method'], '

    +
    + +
    + +
    +
    + + '; + if (!empty($context['table_info'])) + echo ' +
    + ', $txt['search_method_messages_table_space'], ': +
    +
    + ', $context['table_info']['data_length'], ' +
    +
    + ', $txt['search_method_messages_index_space'], ': +
    +
    + ', $context['table_info']['index_length'], ' +
    '; + echo ' +
    + ', $context['double_index'] ? '
    + ' . $txt['search_double_index'] . '
    ' : '', ' +
    + ', $txt['search_index'], ' +
    +
    + ', $txt['search_index_none'], ' +
    '; + + if ($context['supports_fulltext']) + { + echo ' +
    + + ', $txt['search_method_fulltext_index'], ' +
    +
    + + '; + if (empty($context['fulltext_index']) && empty($context['cannot_create_fulltext'])) + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_no_index_exists'], ' [', $txt['search_method_fulltext_create'], ']'; + elseif (empty($context['fulltext_index']) && !empty($context['cannot_create_fulltext'])) + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_fulltext_cannot_create']; + else + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_index_already_exists'], ' [', $txt['search_method_fulltext_remove'], ']
    + ', $txt['search_index_size'], ': ', $context['table_info']['fulltext_length']; + echo ' +
    +
    '; + } + + echo ' +
    + + ', $txt['search_index_custom'], ' +
    +
    + '; + if ($context['custom_index']) + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_index_already_exists'], ' [', $txt['search_index_custom_remove'], ']
    + ', $txt['search_index_size'], ': ', $context['table_info']['custom_index_length']; + elseif ($context['partial_custom_index']) + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_index_partial'], ' [', $txt['search_index_custom_remove'], '] [', $txt['search_index_custom_resume'], ']
    + ', $txt['search_index_size'], ': ', $context['table_info']['custom_index_length']; + else + echo ' + ', $txt['search_index_label'], ': ', $txt['search_method_no_index_exists'], ' [', $txt['search_index_create_custom'], ']'; + echo ' +
    +
    '; + + foreach ($context['search_apis'] as $api) + { + if (empty($api['label']) || $api['has_template']) + continue; + + echo ' +
    + + ', $api['label'] ,' +
    '; + + if ($api['desc']) + echo ' +
    + ', $api['desc'], ' +
    '; + } + + echo ' +
    +
    +
    + ', $txt['search_method'], ' +
    + +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +function template_create_index() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +
    +

    ', $txt['search_create_index'], '

    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + + +
    + +
    + +
    +
    +
    '; +} + +function template_create_index_progress() +{ + global $context, $settings, $options, $scripturl, $txt; + echo ' +
    +
    +
    +

    ', $txt['search_create_index'], '

    +
    +
    + +
    +

    + ', $txt['search_create_index_not_ready'], ' +

    +

    + ', $txt['search_create_index_progress'], ': ', $context['percentage'], '% +

    + +
    + +
    + + + + +
    +
    +
    + '; + +} + +function template_create_index_done() +{ + global $context, $settings, $options, $scripturl, $txt; + echo ' +
    +
    +

    ', $txt['search_create_index'], '

    +
    +
    + +
    +

    ', $txt['search_create_index_done'], '

    +

    + ', $txt['search_create_index_done_link'], ' +

    +
    + +
    +
    +
    '; +} + +// Add or edit a search engine spider. +function template_spider_edit() +{ + global $context, $settings, $options, $scripturl, $txt; + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    +
    + ', $txt['add_spider_desc'], ' +
    +
    + +
    +
    +
    + ', $txt['spider_name'], ':
    + ', $txt['spider_name_desc'], ' +
    +
    + +
    +
    + ', $txt['spider_agent'], ':
    + ', $txt['spider_agent_desc'], ' +
    +
    + +
    +
    + ', $txt['spider_ip_info'], ':
    + ', $txt['spider_ip_info_desc'], ' +
    +
    + +
    +
    + + +
    + +
    +
    +
    +
    '; +} + +// Show... spider... logs... +function template_show_spider_logs() +{ + global $context, $txt, $settings, $scripturl; + + echo ' +
    '; + + // Standard fields. + template_show_list('spider_logs'); + + echo ' +
    +
    +

    ', $txt['spider_logs_delete'], '

    +
    +
    +
    + +
    +

    + ', $txt['spider_logs_delete_older'], ' + + ', $txt['spider_logs_delete_day'], ' +

    + + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ManageSmileys.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ManageSmileys.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,583 @@ +'; + + template_show_list('smiley_set_list'); + + echo ' +
    +
    +

    ', $txt['smiley_sets_latest'], '

    +
    +
    + +
    +
    ', $txt['smiley_sets_latest_fetch'], '
    +
    + +
    + +
    + '; + + if (empty($modSettings['disable_smf_js'])) + echo ' + '; + + echo ' + '; +} + +// Modifying a smiley set. +function template_modifyset() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    + ', $context['current_set']['is_new'] ? $txt['smiley_set_new'] : $txt['smiley_set_modify_existing'], ' +

    +
    '; + + // If this is an existing set, and there are still un-added smileys - offer an import opportunity. + if (!empty($context['current_set']['can_import'])) + { + echo ' +
    + ', $context['current_set']['can_import'] == 1 ? $txt['smiley_set_import_single'] : $txt['smiley_set_import_multiple'], ' ', $txt['here'], ' ', $context['current_set']['can_import'] == 1 ? $txt['smiley_set_to_import_single'] : $txt['smiley_set_to_import_multiple'], ' +
    '; + } + + echo ' +
    + +
    +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + ', $modSettings['smileys_url'], '/'; + if ($context['current_set']['id'] == 'default') + echo 'default'; + elseif (empty($context['smiley_set_dirs'])) + echo ' + '; + else + { + echo ' + '; + } + echo ' + /.. +
    +
    + : +
    +
    + +
    '; + + // If this is a new smiley set they have the option to import smileys already in the directory. + if ($context['current_set']['is_new'] && !empty($modSettings['smiley_enable'])) + echo ' +
    + : +
    +
    + +
    '; + + echo ' +
    + +
    + +
    + + +
    +
    +
    '; +} + +// Editing an individual smiley +function template_modifysmiley() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    ', $txt['smiley_modify_existing'], '

    +
    +
    + +
    +
    +
    + ', $txt['smiley_preview'], ': +
    +
    + (', $txt['smiley_preview_using'], ': ) +
    +
    + : +
    +
    + +
    +
    + : +
    +
    '; + if (empty($context['filenames'])) + echo ' + '; + else + { + echo ' + '; + } + echo ' +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + + +
    + +
    + + +
    +
    +
    + '; +} + +// Adding a new smiley. +function template_addsmiley() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' + +
    +
    +
    +

    ', $txt['smileys_add_method'], '

    +
    +
    + +
    +
      +
    • + +
    • +
    • + +
    • +
    +
    +
    +
    +
    + +
    +
    + ', $txt['smiley_preview_using'], ': +
    +
    + : +
    +
    '; + if (empty($context['filenames'])) + echo ' + '; + else + { + echo ' + '; + } + + echo ' +
    +
    +
    + + + +
    + +
    +
    +
    +

    ', $txt['smiley_new'], '

    +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + +
    + +
    + +
    +
    +
    + '; +} + +// Ordering smileys. +function template_setorder() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    '; + + foreach ($context['smileys'] as $location) + { + echo ' +
    +
    +

    ', $location['title'], '

    +
    +
    + ', $location['description'], ' +
    +
    + +
    + ', empty($context['move_smiley']) ? $txt['smileys_move_select_smiley'] : $txt['smileys_move_select_destination'], '...
    '; + foreach ($location['rows'] as $row) + { + if (!empty($context['move_smiley'])) + echo ' + ', $txt['smileys_move_here'], ''; + + foreach ($row as $smiley) + { + if (empty($context['move_smiley'])) + echo '', $smiley['description'], ''; + else + echo '', $smiley['description'], '', $txt['smileys_move_here'], ''; + } + + echo ' +
    '; + } + if (!empty($context['move_smiley'])) + echo ' + ', $txt['smileys_move_here'], ''; + echo ' +
    + +
    + +
    +
    '; + } + echo ' +
    +
    '; +} + +// Editing Message Icons +function template_editicons() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + template_show_list('message_icon_list'); +} + +// Editing an individual message icon +function template_editicon() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +
    +

    + ', $context['new_icon'] ? $txt['icons_new_icon'] : $txt['icons_edit_icon'], ' +

    +
    +
    + +
    +
    '; + if (!$context['new_icon']) + echo ' +
    + ', $txt['smiley_preview'], ': +
    +
    + ', $context['icon']['title'], ' +
    '; + echo ' +
    + :
    ', $txt['icons_filename_all_gif'], ' +
    +
    + +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    '; + + if (!$context['new_icon']) + echo ' + '; + + echo ' + + + +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Memberlist.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Memberlist.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,216 @@ + array('text' => 'view_all_members', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=all', 'active'=> true), + 'mlist_search' => array('text' => 'mlist_search', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=search'), + ); + + echo ' +
    +
    +

    + ', $txt['members_list'], ''; + if (!isset($context['old_search'])) + echo ' + ', $context['letter_links'], ''; + echo ' +

    +
    +
    + ', template_button_strip($memberlist_buttons, 'right'), ' + +
    '; + + echo ' +
    + + + '; + + // Display each of the column headers of the table. + foreach ($context['columns'] as $column) + { + // We're not able (through the template) to sort the search results right now... + if (isset($context['old_search'])) + echo ' + '; + // This is a selected column, so underline it or some such. + elseif ($column['selected']) + echo ' + '; + // This is just some column... show the link and be done with it. + else + echo ' + '; + } + echo ' + + + '; + + // Assuming there are members loop through each one displaying their data. + if (!empty($context['members'])) + { + foreach ($context['members'] as $member) + { + echo ' + + + + '; + + if (!isset($context['disabled_fields']['website'])) + echo ' + '; + + // ICQ? + if (!isset($context['disabled_fields']['icq'])) + echo ' + '; + + // AIM? + if (!isset($context['disabled_fields']['aim'])) + echo ' + '; + + // YIM? + if (!isset($context['disabled_fields']['yim'])) + echo ' + '; + + // MSN? + if (!isset($context['disabled_fields']['msn'])) + echo ' + '; + + // Group and date. + echo ' + + '; + + if (!isset($context['disabled_fields']['posts'])) + { + echo ' + + '; + } + + echo ' + '; + } + } + // No members? + else + echo ' + + + '; + + // Show the page numbers again. (makes 'em easier to find!) + echo ' + +
    + ', $column['label'], ' + ' . $column['label'] . ' + ', $column['link'], '
    + ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $member['online']['text'] . '' : $member['online']['label'], $context['can_send_pm'] ? '' : '', ' + ', $member['link'], '', $member['show_email'] == 'no' ? '' : '' . $txt['email'] . '', '', $member['website']['url'] != '' ? '' . $member['website']['title'] . '' : '', '', $member['icq']['link'], '', $member['aim']['link'], '', $member['yim']['link'], '', $member['msn']['link'], '', empty($member['group']) ? $member['post_group'] : $member['group'], '', $member['registered_date'], '', $member['posts'], ''; + + if (!empty($member['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' +
    ', $txt['search_no_results'], '
    +
    '; + + echo ' +
    + '; + + // If it is displaying the result of a search show a "search again" link to edit their criteria. + if (isset($context['old_search'])) + echo ' + '; + echo ' +
    +
    '; + +} + +// A page allowing people to search the member list. +function template_search() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Build the memberlist button array. + $memberlist_buttons = array( + 'view_all_members' => array('text' => 'view_all_members', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=all'), + 'mlist_search' => array('text' => 'mlist_search', 'image' => 'mlist.gif', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=search', 'active' => true), + ); + + // Start the submission form for the search! + echo ' +
    +
    +
    +

    + ', !empty($settings['use_buttons']) ? '' : '', $txt['mlist_search'], ' +

    +
    +
    + ', template_button_strip($memberlist_buttons, 'right'), ' +
    '; + // Display the input boxes for the form. + echo ' +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/MessageIndex.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/MessageIndex.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,466 @@ +'; + + if (!empty($context['boards']) && (!empty($options['show_children']) || $context['start'] == 0)) + { + echo ' +
    +
    +

    ', $txt['parent_boards'], '

    +
    +
    + + '; + + foreach ($context['boards'] as $board) + { + echo ' + + + + + + '; + + // Show the "Child Boards: ". (there's a link_children but we're going to bold the new ones...) + if (!empty($board['children'])) + { + // Sort the links into an array with new boards bold so it can be imploded. + $children = array(); + /* Each child in each board's children has: + id, name, description, new (is it new?), topics (#), posts (#), href, link, and last_post. */ + foreach ($board['children'] as $child) + { + if (!$child['is_redirect']) + $child['link'] = '' . $child['name'] . ($child['new'] ? '' : '') . ''; + else + $child['link'] = '' . $child['name'] . ''; + + // Has it posts awaiting approval? + if ($child['can_approve_posts'] && ($child['unapproved_posts'] | $child['unapproved_topics'])) + $child['link'] .= ' (!)'; + + $children[] = $child['new'] ? '' . $child['link'] . '' : $child['link']; + } + echo ' + '; + } + } + echo ' + +
    + '; + + // If the board or children is new, show an indicator. + if ($board['new'] || $board['children_new']) + echo ' + ', $txt['new_posts'], ''; + // Is it a redirection board? + elseif ($board['is_redirect']) + echo ' + *'; + // No new posts at all! The agony!! + else + echo ' + ', $txt['old_posts'], ''; + + echo ' + + + ', $board['name'], ''; + + // Has it outstanding posts for approval? + if ($board['can_approve_posts'] && ($board['unapproved_posts'] || $board['unapproved_topics'])) + echo ' + (!)'; + + echo ' + +

    ', $board['description'] , '

    '; + + // Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.) + if (!empty($board['moderators'])) + echo ' +

    ', count($board['moderators']) === 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $board['link_moderators']), '

    '; + + // Show some basic information about the number of posts, etc. + echo ' +
    +

    ', comma_format($board['posts']), ' ', $board['is_redirect'] ? $txt['redirects'] : $txt['posts'], '
    + ', $board['is_redirect'] ? '' : comma_format($board['topics']) . ' ' . $txt['board_topics'], ' +

    +
    '; + + /* The board's and children's 'last_post's have: + time, timestamp (a number that represents the time.), id (of the post), topic (topic id.), + link, href, subject, start (where they should go for the first unread post.), + and member. (which has id, name, link, href, username in it.) */ + if (!empty($board['last_post']['id'])) + echo ' +

    ', $txt['last_post'], ' ', $txt['by'], ' ', $board['last_post']['member']['link'], '
    + ', $txt['in'], ' ', $board['last_post']['link'], '
    + ', $txt['on'], ' ', $board['last_post']['time'],' +

    '; + + echo ' +
    ', $txt['parent_boards'], ': ', implode(', ', $children), '
    +
    +
    '; + } + + if (!empty($options['show_board_desc']) && $context['description'] != '') + echo ' +

    ', $context['description'], '

    '; + + // Create the button set... + $normal_buttons = array( + 'new_topic' => array('test' => 'can_post_new', 'text' => 'new_topic', 'image' => 'new_topic.gif', 'lang' => true, 'url' => $scripturl . '?action=post;board=' . $context['current_board'] . '.0', 'active' => true), + 'post_poll' => array('test' => 'can_post_poll', 'text' => 'new_poll', 'image' => 'new_poll.gif', 'lang' => true, 'url' => $scripturl . '?action=post;board=' . $context['current_board'] . '.0;poll'), + 'notify' => array('test' => 'can_mark_notify', 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', 'image' => ($context['is_marked_notify'] ? 'un' : ''). 'notify.gif', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . ($context['is_marked_notify'] ? $txt['notification_disable_board'] : $txt['notification_enable_board']) . '\');"', 'url' => $scripturl . '?action=notifyboard;sa=' . ($context['is_marked_notify'] ? 'off' : 'on') . ';board=' . $context['current_board'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), + 'markread' => array('text' => 'mark_read_short', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=board;board=' . $context['current_board'] . '.0;' . $context['session_var'] . '=' . $context['session_id']), + ); + + // They can only mark read if they are logged in and it's enabled! + if (!$context['user']['is_logged'] || !$settings['show_mark_read']) + unset($normal_buttons['markread']); + + // Allow adding new buttons easily. + call_integration_hook('integrate_messageindex_buttons', array(&$normal_buttons)); + + if (!$context['no_topic_listing']) + { + echo ' +
    + + ', template_button_strip($normal_buttons, 'right'), ' +
    '; + + // If Quick Moderation is enabled start the form. + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] > 0 && !empty($context['topics'])) + echo ' +
    '; + + echo ' +
    + + + '; + + // Are there actually any topics to show? + if (!empty($context['topics'])) + { + echo ' + + + '; + // Show a "select all" box for quick moderation? + if (empty($context['can_quick_mod'])) + echo ' + '; + else + echo ' + '; + + // Show a "select all" box for quick moderation? + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] == 1) + echo ' + '; + + // If it's on in "image" mode, don't show anything but the column. + elseif (!empty($context['can_quick_mod'])) + echo ' + '; + } + // No topics.... just say, "sorry bub". + else + echo ' + + + '; + + echo ' + + + '; + + if (!empty($settings['display_who_viewing'])) + { + echo ' + + + '; + } + + // If this person can approve items and we have some awaiting approval tell them. + if (!empty($context['unapproved_posts_message'])) + { + echo ' + + + '; + } + + foreach ($context['topics'] as $topic) + { + // Is this topic pending approval, or does it have any posts pending approval? + if ($context['can_approve_posts'] && $topic['unapproved_posts']) + $color_class = !$topic['approved'] ? 'approvetbg' : 'approvebg'; + // We start with locked and sticky topics. + elseif ($topic['is_sticky'] && $topic['is_locked']) + $color_class = 'stickybg locked_sticky'; + // Sticky topics should get a different color, too. + elseif ($topic['is_sticky']) + $color_class = 'stickybg'; + // Locked topics get special treatment as well. + elseif ($topic['is_locked']) + $color_class = 'lockedbg'; + // Last, but not least: regular topics. + else + $color_class = 'windowbg'; + + // Some columns require a different shade of the color class. + $alternate_class = $color_class . '2'; + + echo ' + + + + + + '; + + // Show the quick moderation options? + if (!empty($context['can_quick_mod'])) + { + echo ' + '; + } + echo ' + '; + } + + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + { + echo ' + + + '; + } + + echo ' + +
     ', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', ' / ', $txt['started_by'], $context['sort_by'] == 'starter' ? ' ' : '', '', $txt['replies'], $context['sort_by'] == 'replies' ? ' ' : '', ' / ', $txt['views'], $context['sort_by'] == 'views' ? ' ' : '', '', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', '', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', '  ', $txt['msg_alert_none'], ' 
    '; + if ($settings['display_who_viewing'] == 1) + echo count($context['view_members']), ' ', count($context['view_members']) === 1 ? $txt['who_member'] : $txt['members']; + else + echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) or $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_board'], ' +
    + ! ', $context['unapproved_posts_message'], ' +
    + + + + +
    + ', $topic['is_sticky'] ? '' : '', '', $topic['first_post']['link'], (!$context['can_approve_posts'] && !$topic['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), '', $topic['is_sticky'] ? '' : ''; + + // Is this topic new? (assuming they are logged in!) + if ($topic['new'] && $context['user']['is_logged']) + echo ' + ', $txt['new'], ''; + + echo ' +

    ', $txt['started_by'], ' ', $topic['first_post']['member']['link'], ' + ', $topic['pages'], ' +

    +
    +
    + ', $topic['replies'], ' ', $txt['replies'], ' +
    + ', $topic['views'], ' ', $txt['views'], ' +
    + ', $txt['last_post'], ' + ', $topic['last_post']['time'], '
    + ', $txt['by'], ' ', $topic['last_post']['member']['link'], ' +
    '; + if ($options['display_quick_mod'] == 1) + echo ' + '; + else + { + // Check permissions on each and show only the ones they are allowed to use. + if ($topic['quick_mod']['remove']) + echo '', $txt['remove_topic'], ''; + + if ($topic['quick_mod']['lock']) + echo '', $txt['set_lock'], ''; + + if ($topic['quick_mod']['lock'] || $topic['quick_mod']['remove']) + echo '
    '; + + if ($topic['quick_mod']['sticky']) + echo '', $txt['set_sticky'], ''; + + if ($topic['quick_mod']['move']) + echo '', $txt['move_topic'], ''; + } + echo ' +
    + '; + + // Show a list of boards they can move the topic to. + if ($context['can_move']) + { + echo ' + '; + } + + echo ' + +
    +
    + '; + + // Finish off the form - again. + if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] > 0 && !empty($context['topics'])) + echo ' + +
    '; + + echo ' +
    + ', template_button_strip($normal_buttons, 'right'), ' + +
    '; + } + + // Show breadcrumbs at the bottom too. + theme_linktree(); + + echo ' +
    +
    +

     

    '; + + if (!$context['no_topic_listing']) + echo ' +

    ', !empty($modSettings['enableParticipation']) && $context['user']['is_logged'] ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ' . $txt['normal_topic'] . '
    + ' . sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']) . '
    + ' . sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']) . ' +

    +

    + ' . $txt['locked_topic'] . '
    ' . ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['sticky_topic'] . '
    ' : '') . ($modSettings['pollMode'] == '1' ? ' + ' . $txt['poll'] : '') . ' +

    '; + + echo ' + +
    +
    +
    '; + + // Javascript for inline editing. + echo ' + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/ModerationCenter.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/ModerationCenter.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,860 @@ + +
    +

    ', $txt['moderation_center'], '

    +
    +
    + ', $txt['hello_guest'], ' ', $context['user']['name'], '! +

    + ', $txt['mc_description'], ' +

    + +
    '; + + $alternate = true; + // Show all the blocks they want to see. + foreach ($context['mod_blocks'] as $block) + { + $block_function = 'template_' . $block; + + echo ' +
    ', function_exists($block_function) ? $block_function() : '', '
    '; + + if (!$alternate) + echo ' +
    '; + + $alternate = !$alternate; + } + + echo ' + +
    '; +} + +function template_latest_news() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' +
    +

    + ', $txt['help'], ' ', $txt['mc_latest_news'], ' +

    +
    +
    + +
    +
    ', $txt['mc_cannot_connect_sm'], '
    +
    + +
    '; + + // This requires a lot of javascript... + //!!! Put this in it's own file!! + echo ' + + + + '; + +} + +// Show all the group requests the user can see. +function template_group_requests_block() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' + +
    + +
    +
      '; + + foreach ($context['group_requests'] as $request) + echo ' +
    • + ', $request['group']['name'], ' ', $txt['mc_groupr_by'], ' ', $request['member']['link'], ' +
    • '; + + // Don't have any watched users right now? + if (empty($context['group_requests'])) + echo ' +
    • + ', $txt['mc_group_requests_none'], ' +
    • '; + + echo ' +
    +
    + +
    '; +} + +// A block to show the current top reported posts. +function template_reported_posts_block() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' + +
    + +
    +
      '; + + foreach ($context['reported_posts'] as $report) + echo ' +
    • + ', $report['subject'], ' ', $txt['mc_reportedp_by'], ' ', $report['author']['link'], ' +
    • '; + + // Don't have any watched users right now? + if (empty($context['reported_posts'])) + echo ' +
    • + ', $txt['mc_recent_reports_none'], ' +
    • '; + + echo ' +
    +
    + +
    '; +} + +function template_watched_users() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' + +
    + +
    +
      '; + + foreach ($context['watched_users'] as $user) + echo ' +
    • + ', sprintf(!empty($user['last_login']) ? $txt['mc_seen'] : $txt['mc_seen_never'], $user['link'], $user['last_login']), ' +
    • '; + + // Don't have any watched users right now? + if (empty($context['watched_users'])) + echo ' +
    • + ', $txt['mc_watched_users_none'], ' +
    • '; + + echo ' +
    +
    + +
    '; +} + +// Little section for making... notes. +function template_notes() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['mc_notes'], '

    +
    +
    + +
    '; + + if (!empty($context['notes'])) + { + echo ' +
      '; + + // Cycle through the notes. + foreach ($context['notes'] as $note) + echo ' +
    • ', $note['author']['link'], ': ', $note['text'], '
    • '; + + echo ' +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + + echo ' +
    + +
    +
    + +
    +
    +
    + +
    + +
    '; +} + +function template_reported_posts() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' +
    +
    +

    + ', $context['view_closed'] ? $txt['mc_reportedp_closed'] : $txt['mc_reportedp_active'], ' +

    +
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
    '; + + // Make the buttons. + $close_button = create_button('close.gif', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', 'align="middle"'); + $details_button = create_button('details.gif', 'mc_reportedp_details', 'mc_reportedp_details', 'align="middle"'); + $ignore_button = create_button('ignore.gif', 'mc_reportedp_ignore', 'mc_reportedp_ignore', 'align="middle"'); + $unignore_button = create_button('ignore.gif', 'mc_reportedp_unignore', 'mc_reportedp_unignore', 'align="middle"'); + + foreach ($context['reports'] as $report) + { + echo ' +
    + +
    +
    +
    + ', $report['subject'], ' ', $txt['mc_reportedp_by'], ' ', $report['author']['link'], ' +
    + +

    +
    + « ', $txt['mc_reportedp_last_reported'], ': ', $report['last_updated'], ' »
    '; + + // Prepare the comments... + $comments = array(); + foreach ($report['comments'] as $comment) + $comments[$comment['member']['id']] = $comment['member']['link']; + + echo ' + « ', $txt['mc_reportedp_reported_by'], ': ', implode(', ', $comments), ' » +
    +
    + ', $report['body'], ' +
    + +
    '; + } + + // Were none found? + if (empty($context['reports'])) + echo ' +
    + +
    +

    ', $txt['mc_reportedp_none_found'], '

    +
    + +
    '; + + echo ' +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    +
    + ', !$context['view_closed'] ? '' : '', ' +
    +
    + +
    +
    '; +} + +// Show a list of all the unapproved posts +function template_unapproved_posts() +{ + global $settings, $options, $context, $txt, $scripturl; + + // Just a big table of it all really... + echo ' +
    +
    +
    +

    ', $txt['mc_unapproved_posts'], '

    +
    '; + + // Make up some buttons + $approve_button = create_button('approve.gif', 'approve', 'approve', 'align="middle"'); + $remove_button = create_button('delete.gif', 'remove_message', 'remove', 'align="middle"'); + + // No posts? + if (empty($context['unapproved_items'])) + echo ' +
    + +
    +

    ', $txt['mc_unapproved_' . $context['current_view'] . '_none_found'], '

    +
    + +
    '; + else + echo ' +
    + +
    '; + + foreach ($context['unapproved_items'] as $item) + { + echo ' +
    +

    + ', $item['counter'], '  + ', $item['category']['name'], ' / ', $item['board']['name'], ' / ', $item['subject'], ' + ', $txt['mc_unapproved_by'], ' ', $item['poster']['link'], ' ', $txt['on'], ': ', $item['time'], ' +

    +
    +
    + +
    +
    ', $item['body'], '
    + + ', $approve_button, ''; + + if ($item['can_delete']) + echo ' + ', $context['menu_separator'], ' + ', $remove_button, ''; + + echo ' + '; + + echo ' + +
    +
    + +
    '; + } + + echo ' +
    +
    + + +
    '; + + if (!empty($context['unapproved_items'])) + echo ' +
    + +
    '; + + echo ' +
    + +
    +
    +
    '; +} + +// List all attachments awaiting approval. +function template_unapproved_attachments() +{ + global $settings, $options, $context, $txt, $scripturl; + + // Show all the attachments still oustanding. + echo ' +
    +
    +
    +

    ', $txt['mc_unapproved_attachments'], '

    +
    '; + + // The ever popular approve button, with the massively unpopular delete. + $approve_button = create_button('approve.gif', 'approve', 'approve', 'align="middle"'); + $remove_button = create_button('delete.gif', 'remove_message', 'remove', 'align="middle"'); + + // None awaiting? + if (empty($context['unapproved_items'])) + echo ' +
    + +
    +

    ', $txt['mc_unapproved_attachments_none_found'], '

    +
    + +
    '; + else + echo ' +
    + +
    + + + + + + + + + + + '; + + foreach ($context['unapproved_items'] as $item) + { + echo ' + + + + + + + '; + } + + if (!empty($context['unapproved_items'])) + echo ' + +
    ', $txt['mc_unapproved_attach_name'], '', $txt['mc_unapproved_attach_size'], '', $txt['mc_unapproved_attach_poster'], '', $txt['date'], '
    + ', $item['filename'], ' + + ', $item['size'], $txt['kilobyte'], ' + + ', $item['poster']['link'], ' + + ', $item['time'], '
    ', $txt['in'], ' ', $item['message']['subject'], ' +
    + +
    '; + + echo ' +
    +
    + + +
    '; + + if (!empty($context['unapproved_items'])) + echo ' +
    + +
    '; + + echo ' +
    + +
    +
    +
    '; +} + +function template_viewmodreport() +{ + global $context, $scripturl, $txt; + + echo ' +
    +
    +
    +

    + ', sprintf($txt['mc_viewmodreport'], $context['report']['message_link'], $context['report']['author']['link']), ' +

    +
    +
    +

    + + ', sprintf($txt['mc_modreport_summary'], $context['report']['num_reports'], $context['report']['last_updated']), ' + + '; + + // Make the buttons. + $close_button = create_button('close.gif', $context['report']['closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', $context['report']['closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', 'align="middle"'); + $ignore_button = create_button('ignore.gif', 'mc_reportedp_ignore', 'mc_reportedp_ignore', 'align="middle"'); + $unignore_button = create_button('ignore.gif', 'mc_reportedp_unignore', 'mc_reportedp_unignore', 'align="middle"'); + + echo ' + ', $context['report']['ignore'] ? $unignore_button : $ignore_button, ' + ', $close_button, ' + +

    +
    +
    + +
    + ', $context['report']['body'], ' +
    + +
    +
    +
    +

    ', $txt['mc_modreport_whoreported_title'], '

    +
    '; + + foreach ($context['report']['comments'] as $comment) + echo ' +
    + +
    +

    ', sprintf($txt['mc_modreport_whoreported_data'], $comment['member']['link'] . (empty($comment['member']['id']) && !empty($comment['member']['ip']) ? ' (' . $comment['member']['ip'] . ')' : ''), $comment['time']), '

    +

    ', $comment['message'], '

    +
    + +
    '; + + echo ' +
    +
    +

    ', $txt['mc_modreport_mod_comments'], '

    +
    +
    + +
    '; + + if (empty($context['report']['mod_comments'])) + echo ' +

    ', $txt['mc_modreport_no_mod_comment'], '

    '; + + foreach ($context['report']['mod_comments'] as $comment) + echo + '

    ', $comment['member']['link'], ': ', $comment['message'], ' (', $comment['time'], ')

    '; + + echo ' + +
    + +
    +
    + +
    +
    '; + + $alt = false; + + template_show_list('moderation_actions_list'); + + if (!empty($context['entries'])) + { + echo ' +
    +

    ', $txt['mc_modreport_modactions'], '

    +
    + + + + + + + + + + + '; + + foreach ($context['entries'] as $entry) + { + echo ' + + + + + + + + + + '; + } + echo ' + +
    ', $txt['modlog_action'], '', $txt['modlog_date'], '', $txt['modlog_member'], '', $txt['modlog_position'], '', $txt['modlog_ip'], '
    ', $entry['action'], '', $entry['time'], '', $entry['moderator']['link'], '', $entry['position'], '', $entry['ip'], '
    '; + + foreach ($entry['extra'] as $key => $value) + echo ' + ', $key, ': ', $value; + echo ' +
    '; + } + + echo ' + +
    +
    +
    '; +} + +// Callback function for showing a watched users post in the table. +function template_user_watch_post_callback($post) +{ + global $scripturl, $context, $txt, $delete_button; + + // We'll have a delete please bob. + if (empty($delete_button)) + $delete_button = create_button('delete.gif', 'remove_message', 'remove', 'align="middle"'); + + $output_html = ' +
    +
    + ' . $post['subject'] . ' ' . $txt['mc_reportedp_by'] . ' ' . $post['author_link'] . ' +
    +
    '; + + if ($post['can_delete']) + $output_html .= ' + ' . $delete_button . ' + '; + + $output_html .= ' +
    +

    +
    + « ' . $txt['mc_watched_users_posted'] . ': ' . $post['poster_time'] . ' » +
    +
    + ' . $post['body']; + + return $output_html; +} + +// Moderation settings +function template_moderation_settings() +{ + global $settings, $options, $context, $txt, $scripturl; + + echo ' +
    +
    +
    +

    ', $txt['mc_prefs_title'], '

    +
    +
    + ', $txt['mc_prefs_desc'], ' +
    +
    + +
    +
    +
    + ', $txt['mc_prefs_homepage'], ': +
    +
    '; + + foreach ($context['homepage_blocks'] as $k => $v) + echo ' +
    '; + + echo ' +
    '; + + // If they can moderate boards they have more options! + if ($context['can_moderate_boards']) + { + echo ' +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    '; + + } + + if ($context['can_moderate_approvals']) + { + echo ' + +
    + : +
    +
    + +
    '; + } + + echo ' +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +// Show a notice sent to a user. +function template_show_notice() +{ + global $txt, $settings, $options, $context; + + // We do all the HTML for this one! + echo ' + + + + ', $context['page_title'], ' + + + +
    +

    ', $txt['show_notice'], '

    +
    +
    +

    ', $txt['show_notice_subject'], ': ', $context['notice_subject'], '

    +
    +
    + +
    +
    +
    + ', $txt['show_notice_text'], ': +
    +
    + ', $context['notice_body'], ' +
    +
    +
    + +
    + +'; + +} + +// Add or edit a warning template. +function template_warn_template() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    +
    + ', $txt['mc_warning_template_desc'], ' +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    + :
    + ', $txt['mc_warning_template_body_desc'], ' +
    +
    + +
    +
    '; + + if ($context['template_data']['can_edit_personal']) + echo ' + + +
    + ', $txt['mc_warning_template_personal_desc'], ' +
    '; + + echo ' + +
    + +
    + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Modlog.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Modlog.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,115 @@ + + + + +
    + + + + + + + '; + + // Only display page numbers if not a result of a search. + if (!empty($context['page_index'])) + echo ' + + + '; + echo ' +
    +
    ' . $txt[119] . ' ', $txt['modlog_moderation_log'], '
    +
    ', empty($context['search_params']) ? $txt['modlog_total_entries'] : $txt['modlog_search_result'], ': ', $context['entry_count'], '
    +
    ', $txt['modlog_moderation_log_desc'], '
    ', $txt[139], ': ', $context['page_index'], '
    + + + '; + + foreach ($context['columns'] as $column) + { + if (!empty($column['not_sortable'])) + echo ' + '; + else + { + echo ' + '; + } + } + + echo ' + '; + + foreach ($context['entries'] as $entry) + { + echo ' + + + + + + + + + + + '; + } + + if (empty($context['entries'])) + echo ' + + + '; + + echo ' +
    ', $column['label'], ''; + if ($column['selected']) + echo '', $column['label'], ' '; + else + echo $column['label']; + echo '
    ', $entry['action'], '', $entry['time'], '', $entry['moderator']['link'], '', $entry['position'], '', $entry['ip'], '
    '; + + foreach ($entry['extra'] as $key => $value) + echo ' + ', $key, ': ', $value; + echo ' +
    + ', $txt['modlog_no_entries_found'], ' +
    + + + + '; + + if (!empty($context['page_index'])) + echo ' + + + '; + + echo ' +
    +
    + ', $txt['modlog_search'], ' (', $txt['modlog_by'], ': ', $context['search']['label'], '): + +
    + + + +
    ', $txt[139], ': ', $context['page_index'], '
    +
    + + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/MoveTopic.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/MoveTopic.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,92 @@ + +
    +
    +

    ', $txt['move_topic'], '

    +
    +
    + +
    +
    +
    +
    + ', $txt['move_to'], ': +
    +
    + +
    '; + + // Disable the reason textarea when the postRedirect checkbox is unchecked... + echo ' +
    +
    + + +
    +
    +
    + ', $txt['moved_why'], ' +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + +
    '; + + if ($context['back_to_topic']) + echo ' + '; + + echo ' + + +
    + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Notify.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Notify.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,53 @@ + +

    + ', $txt['notify'], ' +

    + + +
    +

    ', $context['notification_set'] ? $txt['notify_deactivate'] : $txt['notify_request'], '

    +

    + ', $txt['yes'], ' - ', $txt['no'], ' +

    +
    + '; +} + +function template_notify_board() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +

    + ', $txt['notify'], ' +

    +
    + +
    +

    ', $context['notification_set'] ? $txt['notifyboard_turnoff'] : $txt['notifyboard_turnon'], '

    +

    + ', $txt['yes'], ' - ', $txt['no'], ' +

    +
    + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Packages.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Packages.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2142 @@ + +
    +

    ', $txt[($context['uninstalling'] ? 'un' : '') . 'install_mod'], '

    +
    +
    '; + + if ($context['is_installed']) + echo ' + ', $txt['package_installed_warning1'], '
    +
    + ', $txt['package_installed_warning2'], '
    +
    '; + + echo $txt['package_installed_warning3'], ' +
    '; + + // Do errors exist in the install? If so light them up like a christmas tree. + if ($context['has_failure']) + { + echo ' +
    + ', $txt['package_will_fail_title'], '
    + ', $txt['package_will_fail_warning'], ' +
    '; + } + + if (isset($context['package_readme'])) + { + echo ' +
    +

    ', $txt['package_' . ($context['uninstalling'] ? 'un' : '') . 'install_readme'], '

    +
    +
    + +
    + ', $context['package_readme'], ' + ', $txt['package_available_readme_language'], ' + + +
    + +
    +
    '; + } + + echo ' +
    +
    +

    + ', $context['uninstalling'] ? $txt['package_uninstall_actions'] : $txt['package_install_actions'], ' "', $context['package_name'], '" +

    +
    '; + + // Are there data changes to be removed? + if ($context['uninstalling'] && !empty($context['database_changes'])) + { + echo ' +
    + +
    + [', $txt['package_db_uninstall_details'], '] +
    + ', $txt['package_db_uninstall_actions'], ': +
      '; + + foreach ($context['database_changes'] as $change) + echo ' +
    • ', $change, '
    • '; + echo ' +
    +
    +
    + +
    '; + } + + echo ' +
    '; + + if (empty($context['actions']) && empty($context['database_changes'])) + echo ' + ', $txt['corrupt_compatible'], ' +
    '; + else + { + echo ' + ', $txt['perform_actions'], ' + + + + + + + + + + + + '; + + $alternate = true; + $i = 1; + $action_num = 1; + $js_operations = array(); + foreach ($context['actions'] as $packageaction) + { + // Did we pass or fail? Need to now for later on. + $js_operations[$action_num] = isset($packageaction['failed']) ? $packageaction['failed'] : 0; + + echo ' + + + + + + + '; + + // Is there water on the knee? Operation! + if (isset($packageaction['operations'])) + { + echo ' + + + '; + + // Increase it. + $action_num++; + } + $alternate = !$alternate; + } + echo ' + +
    ', $txt['package_install_type'], '', $txt['package_install_action'], '', $txt['package_install_desc'], '
    ', isset($packageaction['operations']) ? '' : '', '', $i++, '.', $packageaction['type'], '', $packageaction['action'], '', $packageaction['description'], '
    + '; + + // Show the operations. + $alternate2 = true; + $operation_num = 1; + foreach ($packageaction['operations'] as $operation) + { + // Determine the position text. + $operation_text = $operation['position'] == 'replace' ? 'operation_replace' : ($operation['position'] == 'before' ? 'operation_after' : 'operation_before'); + + echo ' + + + + + + + + '; + + $operation_num++; + $alternate2 = !$alternate2; + } + + echo ' +
    ', $operation_num, '.', $txt[$operation_text], '', $operation['action'], '', $operation['description'], !empty($operation['ignore_failure']) ? ' (' . $txt['operation_ignore'] . ')' : '', '
    +
    + '; + + // What if we have custom themes we can install into? List them too! + if (!empty($context['theme_actions'])) + { + echo ' +
    +
    +

    + ', $context['uninstalling'] ? $txt['package_other_themes_uninstall'] : $txt['package_other_themes'], ' +

    +
    +
    +
    + ', $txt['package_other_themes_desc'], ' +
    + '; + + // Loop through each theme and display it's name, and then it's details. + foreach ($context['theme_actions'] as $id => $theme) + { + // Pass? + $js_operations[$action_num] = !empty($theme['has_failure']); + + echo ' + + + + + '; + + foreach ($theme['actions'] as $action) + { + echo ' + + + + + + + '; + + // Is there water on the knee? Operation! + if (isset($action['operations'])) + { + echo ' + + + '; + + // Increase it. + $action_num++; + } + } + + $alternate = !$alternate; + } + + echo ' +
    '; + if (!empty($context['themes_locked'])) + echo ' + '; + echo ' + + + ', $theme['name'], ' +
    ', isset($packageaction['operations']) ? '' : '', ' + + ', $action['type'], '', $action['action'], '', $action['description'], '
    + '; + + $alternate2 = true; + $operation_num = 1; + foreach ($action['operations'] as $operation) + { + // Determine the possition text. + $operation_text = $operation['position'] == 'replace' ? 'operation_replace' : ($operation['position'] == 'before' ? 'operation_after' : 'operation_before'); + + echo ' + + + + + + + + '; + $operation_num++; + $alternate2 = !$alternate2; + } + + echo ' +
    ', $operation_num, '.', $txt[$operation_text], '', $operation['action'], '', $operation['description'], !empty($operation['ignore_failure']) ? ' (' . $txt['operation_ignore'] . ')' : '', '
    +
    +
    '; + } + } + + // Are we effectively ready to install? + if (!$context['ftp_needed'] && (!empty($context['actions']) || !empty($context['database_changes']))) + { + echo ' +
    + +
    '; + } + // If we need ftp information then demand it! + elseif ($context['ftp_needed']) + { + echo ' +
    +

    ', $txt['package_ftp_necessary'], '

    +
    +
    + ', template_control_chmod(), ' +
    '; + } + echo ' + + ', (isset($context['form_sequence_number']) && !$context['ftp_needed']) ? ' + ' : '', ' +
    + +
    '; + + // Toggle options. + echo ' + '; + + // And a bit more for database changes. + if (!empty($context['database_changes'])) + echo ' + '; +} +function template_extract_package() +{ + global $context, $settings, $options, $txt, $scripturl; + + if (!empty($context['redirect_url'])) + { + echo ' + '; + } + + echo ' +
    '; + + if (empty($context['redirect_url'])) + { + echo ' +
    +

    ', $context['uninstalling'] ? $txt['uninstall'] : $txt['extracting'], '

    +
    +
    ', $txt['package_installed_extract'], '
    '; + } + else + echo ' +
    +

    ', $txt['package_installed_redirecting'], '

    +
    '; + + echo ' +
    + +
    '; + + // If we are going to redirect we have a slightly different agenda. + if (!empty($context['redirect_url'])) + { + echo ' + ', $context['redirect_text'], '

    + ', $txt['package_installed_redirect_go_now'], ' | ', $txt['package_installed_redirect_cancel'], ''; + } + elseif ($context['uninstalling']) + echo ' + ', $txt['package_uninstall_done']; + elseif ($context['install_finished']) + { + if ($context['extract_type'] == 'avatar') + echo ' + ', $txt['avatars_extracted']; + elseif ($context['extract_type'] == 'language') + echo ' + ', $txt['language_extracted']; + else + echo ' + ', $txt['package_installed_done']; + } + else + echo ' + ', $txt['corrupt_compatible']; + + echo ' +
    + +
    '; + + // Show the "restore permissions" screen? + if (function_exists('template_show_list') && !empty($context['restore_file_permissions']['rows'])) + { + echo '
    '; + template_show_list('restore_file_permissions'); + } + + echo ' +
    +
    '; +} + +function template_list() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['list_file'], '

    +
    +
    +

    ', $txt['files_archive'], ' ', $context['filename'], ':

    +
    +
    + +
    +
      '; + + foreach ($context['files'] as $fileinfo) + echo ' +
    1. ', $fileinfo['filename'], ' (', $fileinfo['size'], ' ', $txt['package_bytes'], ')
    2. '; + + echo ' +
    +
    + [ ', $txt['back'], ' ] +
    + +
    +
    +
    '; +} + +function template_examine() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['package_examine_file'], '

    +
    +
    +

    ', $txt['package_file_contents'], ' ', $context['filename'], ':

    +
    +
    + +
    +
    ', $context['filedata'], '
    + [ ', $txt['list_files'], ' ] +
    + +
    +
    +
    '; +} + +function template_view_installed() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ' . $txt['view_and_remove'] . '

    +
    '; + + if (empty($context['installed_mods'])) + { + echo ' +
    + ', $txt['no_mods_installed'], ' +
    '; + } + else + { + echo ' + + + + + + + + + + '; + + $alt = false; + foreach ($context['installed_mods'] as $i => $file) + { + echo ' + + + + + + '; + $alt = !$alt; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ', ++$i, '.', $file['name'], '', $file['version'], '[ ', $txt['uninstall'], ' ]
    +
    + [ ', $txt['delete_list'], ' ]'; + } + + echo ' +
    +
    '; +} + +function template_browse() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings, $forum_version; + + echo ' +
    +
    +

    + ', $txt['help'], ' ', $txt['packages_latest'], ' +

    +
    +
    + +
    +
    ', $txt['packages_latest_fetch'], '
    +
    + +
    + + '; + + if (empty($modSettings['disable_smf_js'])) + echo ' + '; + + echo ' + '; + + echo ' + '; + + echo ' +
    +
    +

    ', $txt['browse_packages'], '

    +
    '; + + if (!empty($context['available_mods'])) + { + echo ' +
    +
    +

    ', $txt['modification_package'], '

    +
    + + + + + + + + + + + '; + + $alt = false; + foreach ($context['available_mods'] as $i => $package) + { + echo ' + + + + + + '; + $alt = !$alt; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ', ++$i, '.', $package['name'], ' + ', $package['version']; + + if ($package['is_installed'] && !$package['is_newer']) + echo ' + '; + + echo ' + '; + + if ($package['can_uninstall']) + echo ' + [ ', $txt['uninstall'], ' ]'; + elseif ($package['can_upgrade']) + echo ' + [ ', $txt['package_upgrade'], ' ]'; + elseif ($package['can_install']) + echo ' + [ ', $txt['install_mod'], ' ]'; + + echo ' + [ ', $txt['list_files'], ' ] + [ ', $txt['package_delete'], ' ] +
    '; + } + + if (!empty($context['available_avatars'])) + { + echo ' +
    +
    +

    ', $txt['avatar_package'], '

    +
    + + + + + + + + + + '; + + foreach ($context['available_avatars'] as $i => $package) + { + echo ' + + + + + + '; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ', ++$i, '.', $package['name'], '', $package['version']; + + if ($package['is_installed'] && !$package['is_newer']) + echo ' + '; + + echo ' + '; + + if ($package['can_uninstall']) + echo ' + [ ', $txt['uninstall'], ' ]'; + elseif ($package['can_upgrade']) + echo ' + [ ', $txt['package_upgrade'], ' ]'; + elseif ($package['can_install']) + echo ' + [ ', $txt['install_mod'], ' ]'; + + echo ' + [ ', $txt['list_files'], ' ] + [ ', $txt['package_delete'], ' ] +
    '; + } + + if (!empty($context['available_languages'])) + { + echo ' +
    +
    +

    ' . $txt['language_package'] . '

    +
    + + + + + + + + + + '; + + foreach ($context['available_languages'] as $i => $package) + { + echo ' + + + + + + '; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ' . ++$i . '.' . $package['name'] . '' . $package['version']; + + if ($package['is_installed'] && !$package['is_newer']) + echo ' + '; + + echo ' + '; + + if ($package['can_uninstall']) + echo ' + [ ', $txt['uninstall'], ' ]'; + elseif ($package['can_upgrade']) + echo ' + [ ', $txt['package_upgrade'], ' ]'; + elseif ($package['can_install']) + echo ' + [ ', $txt['install_mod'], ' ]'; + + echo ' + [ ', $txt['list_files'], ' ] + [ ', $txt['package_delete'], ' ] +
    '; + } + + if (!empty($context['available_other'])) + { + echo ' +
    +
    +

    ' . $txt['unknown_package'] . '

    +
    + + + + + + + + + + '; + + foreach ($context['available_other'] as $i => $package) + { + echo ' + + + + + + '; + } + + echo ' + +
    ', $txt['mod_name'], '', $txt['mod_version'], '
    ' . ++$i . '.' . $package['name'] . '' . $package['version']; + + if ($package['is_installed'] && !$package['is_newer']) + echo ' + '; + + echo ' + '; + + if ($package['can_uninstall']) + echo ' + [ ', $txt['uninstall'], ' ]'; + elseif ($package['can_upgrade']) + echo ' + [ ', $txt['package_upgrade'], ' ]'; + elseif ($package['can_install']) + echo ' + [ ', $txt['install_mod'], ' ]'; + + echo ' + [ ', $txt['list_files'], ' ] + [ ', $txt['package_delete'], ' ] +
    '; + } + + if (empty($context['available_mods']) && empty($context['available_avatars']) && empty($context['available_languages']) && empty($context['available_other'])) + echo ' +
    ', $txt['no_packages'], '
    '; + + echo ' +
    +
    + ', $txt['package_installed_key'], ' + ', $txt['package_installed_current'], ' + ', $txt['package_installed_old'], ' +
    + +
    +
    + + + + +
    +
    +
    '; +} + +function template_servers() +{ + global $context, $settings, $options, $txt, $scripturl; + + if (!empty($context['package_ftp']['error'])) + echo ' +
    + ', $context['package_ftp']['error'], ' +
    '; + + echo ' +
    +
    +

    ', $txt['download_new_package'], '

    +
    '; + + if ($context['package_download_broken']) + { + echo ' +
    +

    ', $txt['package_ftp_necessary'], '

    +
    +
    + +
    +

    + ', $txt['package_ftp_why_download'], ' +

    +
    +
    +
    + +
    +
    + + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    '; + } + + echo ' +
    + +
    +
    + ' . $txt['package_servers'] . ' + +
    +
    + ' . $txt['add_server'] . ' +
    +
    +
    + ' . $txt['server_name'] . ': +
    +
    + +
    +
    + ' . $txt['serverurl'] . ': +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    + ', $txt['package_download_by_url'], ' +
    +
    +
    + ' . $txt['serverurl'] . ': +
    +
    + +
    +
    + ', $txt['package_download_filename'], ': +
    +
    +
    + ', $txt['package_download_filename_info'], ' +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +

    ' . $txt['package_upload_title'] . '

    +
    +
    + +
    +
    +
    +
    + ' . $txt['package_upload_select'] . ': +
    +
    + +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    '; +} + +function template_package_confirm() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    + +
    +
    '; +} + +function template_package_list() +{ + global $context, $settings, $options, $txt, $scripturl, $smcFunc; + + echo ' +
    +
    +

    ' . $context['page_title'] . '

    +
    +
    + +
    '; + + // No packages, as yet. + if (empty($context['package_list'])) + echo ' +
      +
    • ', $txt['no_packages'], '
    • +
    '; + // List out the packages... + else + { + echo ' +
      '; + foreach ($context['package_list'] as $i => $packageSection) + { + echo ' +
    • + ', $packageSection['title'], ''; + + if (!empty($packageSection['text'])) + echo ' +
      ', $packageSection['text'], '
      '; + + echo ' + <', $context['list_type'], ' id="package_section_', $i, '" class="packages">'; + + $alt = false; + + foreach ($packageSection['items'] as $id => $package) + { + echo ' +
    • '; + // Textual message. Could be empty just for a blank line... + if ($package['is_text']) + echo ' + ', empty($package['name']) ? ' ' : $package['name']; + // This is supposed to be a rule.. + elseif ($package['is_line']) + echo ' +
      '; + // A remote link. + elseif ($package['is_remote']) + { + echo ' + ', $package['link'], ''; + } + // A title? + elseif ($package['is_heading'] || $package['is_title']) + { + echo ' + ', $package['name'], ''; + } + // Otherwise, it's a package. + else + { + // 1. Some mod [ Download ]. + echo ' + ', $package['can_install'] ? '' . $package['name'] . ' [ ' . $txt['download'] . ' ]': $package['name']; + + // Mark as installed and current? + if ($package['is_installed'] && !$package['is_newer']) + echo '', $package['is_current'] ? $txt['package_installed_current'] : $txt['package_installed_old'], ''; + + echo ' + +
        '; + + // Show the mod type? + if ($package['type'] != '') + echo ' +
      • ', $txt['package_type'], ':  ', $smcFunc['ucwords']($smcFunc['strtolower']($package['type'])), '
      • '; + // Show the version number? + if ($package['version'] != '') + echo ' +
      • ', $txt['mod_version'], ':  ', $package['version'], '
      • '; + // How 'bout the author? + if (!empty($package['author']) && $package['author']['name'] != '' && isset($package['author']['link'])) + echo ' +
      • ', $txt['mod_author'], ':  ', $package['author']['link'], '
      • '; + // The homepage.... + if ($package['author']['website']['link'] != '') + echo ' +
      • ', $txt['author_website'], ':  ', $package['author']['website']['link'], '
      • '; + + // Desciption: bleh bleh! + // Location of file: http://someplace/. + echo ' +
      • ', $txt['file_location'], ':  ', $package['href'], '
      • +
      • ', $txt['package_description'], ':  ', $package['description'], '
      • +
      '; + } + $alt = !$alt; + echo ' +
    • '; + } + echo ' + + '; + } + echo ' +
    '; + + } + + echo ' +
    + +
    +
    + ', $txt['package_installed_key'], ' + ', $txt['package_installed_current'], ' + ', $txt['package_installed_old'], ' +
    +
    +
    + + '; + // Now go through and turn off all the sections. + if (!empty($context['package_list'])) + { + $section_count = count($context['package_list']); + echo ' + '; + } +} + +function template_downloaded() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +

    ', (empty($context['package_server']) ? $txt['package_uploaded_successfully'] : $txt['package_downloaded_successfully']), '

    +
      +
    • ', $context['package']['name'], ' + ', $context['package']['list_files']['link'], ' + ', $context['package']['install']['link'], ' +
    • +
    +

    +

    [ ', $txt['back'], ' ]

    +
    + +
    +
    +
    '; +} + +function template_install_options() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['package_install_options'], '

    +
    +
    + ', $txt['package_install_options_ftp_why'], ' +
    + +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +

    +
    + + +
    +
    +
    + +
    +
    +
    '; +} + +function template_control_chmod() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Nothing to do? Brilliant! + if (empty($context['package_ftp'])) + return false; + + if (empty($context['package_ftp']['form_elements_only'])) + { + echo ' + ', sprintf($txt['package_ftp_why'], 'document.getElementById(\'need_writable_list\').style.display = \'\'; return false;'), '
    +
    + ', $txt['package_ftp_why_file_list'], ' +
      '; + if (!empty($context['notwritable_files'])) + foreach ($context['notwritable_files'] as $file) + echo ' +
    • ', $file, '
    • '; + + echo ' +
    +
    '; + } + + echo ' +
    + ', !empty($context['package_ftp']['error']) ? $context['package_ftp']['error'] : '', ' +
    '; + + if (!empty($context['package_ftp']['destination'])) + echo ' +
    '; + + echo ' +
    +
    +
    + +
    +
    + + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    '; + + if (empty($context['package_ftp']['form_elements_only'])) + echo ' + +
    + + +
    '; + + if (!empty($context['package_ftp']['destination'])) + echo ' + +
    '; + + // Hide the details of the list. + if (empty($context['package_ftp']['form_elements_only'])) + echo ' + '; + + // Quick generate the test button. + echo ' + '; + + // Make sure the button gets generated last. + $context['insert_after_template'] .= ' + '; +} + +function template_ftp_required() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    + + ', $txt['package_ftp_necessary'], ' + +
    + ', template_control_chmod(), ' +
    +
    '; +} + +function template_view_operations() +{ + global $context, $txt, $settings; + + echo ' + + + ', $txt['operation_title'], ' + + + + + + + +
    +
    + ', $context['operations']['search'], ' +
    +
    + ', $context['operations']['replace'], ' +
    +
    + +'; +} + +function template_file_permissions() +{ + global $txt, $scripturl, $context, $settings; + + // This will handle expanding the selection. + echo ' + '; + + echo ' +
    +
    + ', $txt['package_file_perms_warning'], ': +
    +
      + ', $txt['package_file_perms_warning_desc'], ' +
    +
    +
    +
    + +
    +
    +

    + ', $txt['package_file_perms'], '', $txt['package_file_perms_new_status'], ' +

    +
    + + + + + + + + + + + + '; + + foreach ($context['file_tree'] as $name => $dir) + { + echo ' + + + + + + + + + + + '; + + if (!empty($dir['contents'])) + template_permission_show_contents($name, $dir['contents'], 1); + } + + echo ' + +
     ', $txt['package_file_perms_name'], ' ', $txt['package_file_perms_status'], '', $txt['package_file_perms_status_read'], '', $txt['package_file_perms_status_write'], '', $txt['package_file_perms_status_execute'], '', $txt['package_file_perms_status_custom'], '', $txt['package_file_perms_status_no_change'], '
    '; + + if (!empty($dir['type']) && ($dir['type'] == 'dir' || $dir['type'] == 'dir_recursive')) + echo ' + *'; + + echo ' + ', $name, ' + + ', ($dir['perms']['chmod'] ? $txt['package_file_perms_writable'] : $txt['package_file_perms_not_writable']), ' + ', ($dir['perms']['perms'] ? ' (' . $txt['package_file_perms_chmod'] . ': ' . substr(sprintf('%o', $dir['perms']['perms']), -4) . ')' : ''), ' +
    +
    +
    +

    ', $txt['package_file_perms_change'], '

    +
    +
    + +
    +
    +
    +
    + + +
    +
    + ', $txt['package_file_perms_custom'], ':  (?) +
    +
    + + + +
    +
    + ', $txt['package_file_perms_predefined_note'], ' +
    +
    +
    '; + + // Likely to need FTP? + if (empty($context['ftp_connected'])) + echo ' +

    + ', $txt['package_file_perms_ftp_details'], ': +

    + ', template_control_chmod(), ' +
    ', $txt['package_file_perms_ftp_retain'], '
    '; + + echo ' + +
    + + +
    +
    + +
    '; + + // Any looks fors we've already done? + foreach ($context['look_for'] as $path) + echo ' + '; + echo ' +

    '; +} + +function template_permission_show_contents($ident, $contents, $level, $has_more = false) +{ + global $settings, $txt, $scripturl, $context; + $js_ident = preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $ident); + // Have we actually done something? + $drawn_div = false; + + foreach ($contents as $name => $dir) + { + if (isset($dir['perms'])) + { + if (!$drawn_div) + { + $drawn_div = true; + echo ' + + '; + } + + $cur_ident = preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $ident . '/' . $name); + echo ' + + + + + + + + + + '; + + if (!empty($dir['contents'])) + { + template_permission_show_contents($ident . '/' . $name, $dir['contents'], $level + 1, !empty($dir['more_files'])); + + } + } + } + + // We have more files to show? + if ($has_more) + echo ' + + + + '; + + if ($drawn_div) + { + // Hide anything too far down the tree. + $isFound = false; + foreach ($context['look_for'] as $tree) + { + if (substr($tree, 0, strlen($ident)) == $ident) + $isFound = true; + } + + if ($level > 1 && !$isFound) + echo ' +
    ' . str_repeat(' ', $level * 5), ' + ', (!empty($dir['type']) && $dir['type'] == 'dir_recursive') || !empty($dir['list_contents']) ? '' : ''; + + if (!empty($dir['type']) && ($dir['type'] == 'dir' || $dir['type'] == 'dir_recursive')) + echo ' + *'; + + echo ' + ', $name, ' + ', (!empty($dir['type']) && $dir['type'] == 'dir_recursive') || !empty($dir['list_contents']) ? '' : '', ' + + ', ($dir['perms']['chmod'] ? $txt['package_file_perms_writable'] : $txt['package_file_perms_not_writable']), ' + ', ($dir['perms']['perms'] ? ' (' . $txt['package_file_perms_chmod'] . ': ' . substr(sprintf('%o', $dir['perms']['perms']), -4) . ')' : ''), ' +
    ' . str_repeat(' ', $level * 5), ' + « ', $txt['package_file_perms_more_files'], ' » +
    + + '; + } +} + +function template_action_permissions() +{ + global $txt, $scripturl, $context, $settings; + + $countDown = 3; + + echo ' +
    +
    +
    +

    ', $txt['package_file_perms_applying'], '

    +
    '; + + if (!empty($context['skip_ftp'])) + echo ' +
    + ', $txt['package_file_perms_skipping_ftp'], ' +
    '; + + // How many have we done? + $remaining_items = count($context['method'] == 'individual' ? $context['to_process'] : $context['directory_list']); + $progress_message = sprintf($context['method'] == 'individual' ? $txt['package_file_perms_items_done'] : $txt['package_file_perms_dirs_done'], $context['total_items'] - $remaining_items, $context['total_items']); + $progress_percent = round(($context['total_items'] - $remaining_items) / $context['total_items'] * 100, 1); + + echo ' +
    + +
    +
    + ', $progress_message, ' +
    +
    ', $progress_percent, '%
    +
     
    +
    +
    '; + + // Second progress bar for a specific directory? + if ($context['method'] != 'individual' && !empty($context['total_files'])) + { + $file_progress_message = sprintf($txt['package_file_perms_files_done'], $context['file_offset'], $context['total_files']); + $file_progress_percent = round($context['file_offset'] / $context['total_files'] * 100, 1); + + echo ' +
    +
    + ', $file_progress_message, ' +
    +
    ', $file_progress_percent, '%
    +
     
    +
    +
    '; + } + + echo ' +
    '; + + // Put out the right hidden data. + if ($context['method'] == 'individual') + echo ' + + + '; + else + echo ' + + + + + '; + + // Are we not using FTP for whatever reason. + if (!empty($context['skip_ftp'])) + echo ' + '; + + // Retain state. + foreach ($context['back_look_data'] as $path) + echo ' + '; + + echo ' + + +
    + +
    +
    + +
    + +
    +
    '; + + // Just the countdown stuff + echo ' + '; + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/PersonalMessage.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/PersonalMessage.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1747 @@ +'; + + // Show the capacity bar, if available. + if (!empty($context['limit_bar'])) + echo ' +
    +

    + ', $txt['pm_capacity'], ': + + + + ', $context['limit_bar']['text'], ' +

    +
    '; + + // Message sent? Show a small indication. + if (isset($context['pm_sent'])) + echo ' +
    + ', $txt['pm_sent'], ' +
    '; +} + +// Just the end of the index bar, nothing special. +function template_pm_below() +{ + global $context, $settings, $options; + + echo ' + '; +} + +function template_folder() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // The every helpful javascript! + echo ' + '; + + echo ' +'; + + // If we are not in single display mode show the subjects on the top! + if ($context['display_mode'] != 1) + { + template_subject_list(); + echo '

    '; + } + + // Got some messages to display? + if ($context['get_pmessage']('message', true)) + { + // Show the helpful titlebar - generally. + if ($context['display_mode'] != 1) + echo ' +
    +

    + ', $txt['author'], ' + ', $txt[$context['display_mode'] == 0 ? 'messages' : 'conversation'], ' +

    +
    '; + + // Show a few buttons if we are in conversation mode and outputting the first message. + if ($context['display_mode'] == 2) + { + // Build the normal button array. + $conversation_buttons = array( + 'reply' => array('text' => 'reply_to_all', 'image' => 'reply.gif', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=send;f=' . $context['folder'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';pmsg=' . $context['current_pm'] . ';u=all', 'active' => true), + 'delete' => array('text' => 'delete_conversation', 'image' => 'delete.gif', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=pmactions;pm_actions[' . $context['current_pm'] . ']=delete;conversation;f=' . $context['folder'] . ';start=' . $context['start'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'], 'custom' => 'onclick="return confirm(\'' . addslashes($txt['remove_message']) . '?\');"'), + ); + + // Show the conversation buttons. + echo ' +
    '; + + template_button_strip($conversation_buttons, 'right'); + + echo ' +
    '; + } + + while ($message = $context['get_pmessage']('message')) + { + $window_class = $message['alternate'] == 0 ? 'windowbg' : 'windowbg2'; + + echo ' +
    + +
    + +

    '; + + // Show online and offline buttons? + if (!empty($modSettings['onlineEnable']) && !$message['member']['is_guest']) + echo ' + ', $message['member']['online']['text'], ''; + + echo ' + ', $message['member']['link'], ' +

    +
      '; + + // Show the member's custom title, if they have one. + if (isset($message['member']['title']) && $message['member']['title'] != '') + echo ' +
    • ', $message['member']['title'], '
    • '; + + // Show the member's primary group (like 'Administrator') if they have one. + if (isset($message['member']['group']) && $message['member']['group'] != '') + echo ' +
    • ', $message['member']['group'], '
    • '; + + // Don't show these things for guests. + if (!$message['member']['is_guest']) + { + // Show the post group if and only if they have no other group or the option is on, and they are in a post group. + if ((empty($settings['hide_post_group']) || $message['member']['group'] == '') && $message['member']['post_group'] != '') + echo ' +
    • ', $message['member']['post_group'], '
    • '; + echo ' +
    • ', $message['member']['group_stars'], '
    • '; + + // Show avatars, images, etc.? + if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image'])) + echo ' +
    • + + ', $message['member']['avatar']['image'], ' + +
    • '; + + // Show how many posts they have made. + if (!isset($context['disabled_fields']['posts'])) + echo ' +
    • ', $txt['member_postcount'], ': ', $message['member']['posts'], '
    • '; + + // Is karma display enabled? Total or +/-? + if ($modSettings['karmaMode'] == '1') + echo ' +
    • ', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '
    • '; + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    • ', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '
    • '; + + // Is this user allowed to modify this member's karma? + if ($message['member']['karma']['allow']) + echo ' +
    • + ', $modSettings['karmaApplaudLabel'], ' ', $modSettings['karmaSmiteLabel'], ' +
    • '; + + // Show the member's gender icon? + if (!empty($settings['show_gender']) && $message['member']['gender']['image'] != '' && !isset($context['disabled_fields']['gender'])) + echo ' +
    • ', $txt['gender'], ': ', $message['member']['gender']['image'], '
    • '; + + // Show their personal text? + if (!empty($settings['show_blurb']) && $message['member']['blurb'] != '') + echo ' +
    • ', $message['member']['blurb'], '
    • '; + + // Any custom fields to show as icons? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 1 || empty($custom['value'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    • +
        '; + } + echo ' +
      • ', $custom['value'], '
      • '; + } + if ($shown) + echo ' +
      +
    • '; + } + + // This shows the popular messaging icons. + if ($message['member']['has_messenger'] && $message['member']['can_view_profile']) + echo ' +
    • +
        ', !isset($context['disabled_fields']['icq']) && !empty($message['member']['icq']['link']) ? ' +
      • ' . $message['member']['icq']['link'] . '
      • ' : '', !isset($context['disabled_fields']['msn']) && !empty($message['member']['msn']['link']) ? ' +
      • ' . $message['member']['msn']['link'] . '
      • ' : '', !isset($context['disabled_fields']['aim']) && !empty($message['member']['aim']['link']) ? ' +
      • ' . $message['member']['aim']['link'] . '
      • ' : '', !isset($context['disabled_fields']['yim']) && !empty($message['member']['yim']['link']) ? ' +
      • ' . $message['member']['yim']['link'] . '
      • ' : '', ' +
      +
    • '; + + // Show the profile, website, email address, and personal message buttons. + if ($settings['show_profile_buttons']) + { + echo ' +
    • + +
    • '; + } + + // Any custom fields for standard placement? + if (!empty($message['member']['custom_fields'])) + { + foreach ($message['member']['custom_fields'] as $custom) + if (empty($custom['placement']) || empty($custom['value'])) + echo ' +
    • ', $custom['title'], ': ', $custom['value'], '
    • '; + } + + // Are we showing the warning status? + if ($message['member']['can_see_warning']) + echo ' +
    • ', $context['can_issue_warning'] ? '' : '', '', $txt['user_warn_' . $message['member']['warning_status']], '', $context['can_issue_warning'] ? '' : '', '', $txt['warn_' . $message['member']['warning_status']], '
    • '; + } + + // Done with the information about the poster... on to the post itself. + echo ' +
    +
    +
    +
    +
    +
    + ', $message['subject'], ' +
    '; + + // Show who the message was sent to. + echo ' + « ', $txt['sent_to'], ': '; + + // People it was sent directly to.... + if (!empty($message['recipients']['to'])) + echo implode(', ', $message['recipients']['to']); + // Otherwise, we're just going to say "some people"... + elseif ($context['folder'] != 'sent') + echo '(', $txt['pm_undisclosed_recipients'], ')'; + + echo ' + ', $txt['on'], ': ', $message['time'], ' » + '; + + // If we're in the sent items, show who it was sent to besides the "To:" people. + if (!empty($message['recipients']['bcc'])) + echo ' +
    « ', $txt['pm_bcc'], ': ', implode(', ', $message['recipients']['bcc']), ' »'; + + if (!empty($message['is_replied_to'])) + echo ' +
    « ', $txt['pm_is_replied_to'], ' »'; + + echo ' +
    +
      '; + + // Show reply buttons if you have the permission to send PMs. + if ($context['can_send_pm']) + { + // You can't really reply if the member is gone. + if (!$message['member']['is_guest']) + { + // Is there than more than one recipient you can reply to? + if ($message['number_recipients'] > 1 && $context['display_mode'] != 2) + echo ' +
    • ', $txt['reply_to_all'], '
    • '; + + echo ' +
    • ', $txt['reply'], '
    • +
    • ', $txt['quote'], '
    • '; + } + // This is for "forwarding" - even if the member is gone. + else + echo ' +
    • ', $txt['reply_quote'], '
    • '; + } + echo ' +
    • ', $txt['delete'], '
    • '; + + if (empty($context['display_mode'])) + echo ' +
    • '; + + echo ' +
    +
    +
    +
    ', $message['body'], '
    + '; + + // Are there any custom profile fields for above the signature? + if (!empty($message['member']['custom_fields'])) + { + $shown = false; + foreach ($message['member']['custom_fields'] as $custom) + { + if ($custom['placement'] != 2 || empty($custom['value'])) + continue; + if (!$shown) + { + $shown = true; + echo ' +
    +
      '; + } + echo ' +
    • ', $custom['value'], '
    • '; + } + if ($shown) + echo ' +
    +
    '; + } + + // Show the member's signature? + if (!empty($message['member']['signature']) && empty($options['show_no_signatures']) && $context['signature_enabled']) + echo ' +
    ', $message['member']['signature'], '
    '; + + // Add an extra line at the bottom if we have labels enabled. + if ($context['folder'] != 'sent' && !empty($context['currently_using_labels']) && $context['display_mode'] != 2) + { + echo ' +
    '; + // Add the label drop down box. + if (!empty($context['currently_using_labels'])) + { + echo ' + + '; + } + echo ' +
    '; + } + + echo ' +
    +
    +
    +
    +
    + +
    '; + } + + if (empty($context['display_mode'])) + echo ' + +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
    +
    '; + + // Show a few buttons if we are in conversation mode and outputting the first message. + elseif ($context['display_mode'] == 2 && isset($conversation_buttons)) + { + echo ' + +
    '; + + template_button_strip($conversation_buttons, 'right'); + + echo ' +
    '; + } + + echo ' +
    '; + } + + // Individual messages = buttom list! + if ($context['display_mode'] == 1) + { + template_subject_list(); + echo '
    '; + } + + echo ' + +'; +} + +// Just list all the personal message subjects - to make templates easier. +function template_subject_list() +{ + global $context, $options, $settings, $modSettings, $txt, $scripturl; + + echo ' +
    + + + + + + + + + + '; + if (!$context['show_delete']) + echo ' + + + '; + $next_alternate = false; + + while ($message = $context['get_pmessage']('subject')) + { + echo ' + + + + + + + '; + $next_alternate = !$next_alternate; + } + + echo ' + +
    + ', $txt['pm_change_view'], ' + + ', $txt['date'], $context['sort_by'] == 'date' ? ' ' : '', ' + + ', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', ' + + ', ($context['from_or_to'] == 'from' ? $txt['from'] : $txt['to']), $context['sort_by'] == 'name' ? ' ' : '', ' + + +
    ', $txt['msg_alert_none'], '
    + + ', $message['is_replied_to'] ? '' . $txt['pm_replied'] . '' : '' . $txt['pm_read'] . '', '', $message['time'], '', ($context['display_mode'] != 0 && $context['current_pm'] == $message['id'] ? '*' : ''), '', $message['subject'], '', $message['is_unread'] ? ' ' . $txt['new'] . '' : '', '', ($context['from_or_to'] == 'from' ? $message['member']['link'] : (empty($message['recipients']['to']) ? '' : implode(', ', $message['recipients']['to']))), '
    +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    +
     '; + + if ($context['show_delete']) + { + if (!empty($context['currently_using_labels']) && $context['folder'] != 'sent') + { + echo ' + + '; + } + + echo ' + '; + } + + echo ' +
    +
    '; +} + +function template_search() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' + +
    +
    +

    ', $txt['pm_search_title'], '

    +
    '; + + if (!empty($context['search_errors'])) + { + echo ' +
    + ', implode('
    ', $context['search_errors']['messages']), ' +
    '; + } + + if ($context['simple_search']) + { + echo ' + '; + } + + // Advanced search! + else + { + echo ' + '; + + // Do we have some labels setup? If so offer to search by them! + if ($context['currently_using_labels']) + { + echo ' +
    + +
    + +
      '; + + foreach ($context['search_labels'] as $label) + echo ' +
    • + +
    • '; + + echo ' +
    +

    + + +


    +
    + +
    '; + } + } + + echo ' +
    '; +} + +function template_search_results() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    ', $txt['pm_search_results'], '

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + // complete results ? + if (empty($context['search_params']['show_complete']) && !empty($context['personal_messages'])) + echo ' + + + + + + + + + '; + + $alternate = true; + // Print each message out... + foreach ($context['personal_messages'] as $message) + { + // We showing it all? + if (!empty($context['search_params']['show_complete'])) + { + echo ' +
    +

    + ', $txt['search_on'], ': ', $message['time'], ' + ', $message['counter'], '  ', $message['subject'], ' +

    +
    +
    +

    ', $txt['from'], ': ', $message['member']['link'], ', ', $txt['to'], ': '; + + // Show the recipients. + // !!! This doesn't deal with the sent item searching quite right for bcc. + if (!empty($message['recipients']['to'])) + echo implode(', ', $message['recipients']['to']); + // Otherwise, we're just going to say "some people"... + elseif ($context['folder'] != 'sent') + echo '(', $txt['pm_undisclosed_recipients'], ')'; + + echo ' +

    +
    +
    + +
    + ', $message['body'], ' +

    '; + + if ($context['can_send_pm']) + { + $quote_button = create_button('quote.gif', 'reply_quote', 'reply_quote', 'align="middle"'); + $reply_button = create_button('im_reply.gif', 'reply', 'reply', 'align="middle"'); + // You can only reply if they are not a guest... + if (!$message['member']['is_guest']) + echo ' + ', $quote_button , '', $context['menu_separator'], ' + ', $reply_button , ' ', $context['menu_separator']; + // This is for "forwarding" - even if the member is gone. + else + echo ' + ', $quote_button , '', $context['menu_separator']; + } + + echo ' +

    +
    + +
    '; + } + // Otherwise just a simple list! + else + { + // !!! No context at all of the search? + echo ' + + + + + '; + } + + $alternate = !$alternate; + } + + // Finish off the page... + if (empty($context['search_params']['show_complete']) && !empty($context['personal_messages'])) + echo ' + +
    ', $txt['date'], '', $txt['subject'], '', $txt['from'], '
    ', $message['time'], '', $message['link'], '', $message['member']['link'], '
    '; + + // No results? + if (empty($context['personal_messages'])) + echo ' +
    + +
    +

    ', $txt['pm_search_none_found'], '

    +
    + +
    '; + + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + +} + +function template_send() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // Show which messages were sent successfully and which failed. + if (!empty($context['send_log'])) + { + echo ' +
    +

    ', $txt['pm_send_report'], '

    +
    +
    + +
    '; + if (!empty($context['send_log']['sent'])) + foreach ($context['send_log']['sent'] as $log_entry) + echo '', $log_entry, '
    '; + if (!empty($context['send_log']['failed'])) + foreach ($context['send_log']['failed'] as $log_entry) + echo '', $log_entry, '
    '; + echo ' +
    + +
    +
    '; + } + + // Show the preview of the personal message. + if (isset($context['preview_message'])) + echo ' +
    +

    ', $context['preview_subject'], '

    +
    +
    + +
    + ', $context['preview_message'], ' +
    + +
    +
    '; + + // Main message editing box. + echo ' +
    +

    + ', $txt['new_message'], ' ', $txt['new_message'], ' +

    +
    '; + + echo ' +
    +
    + +

    '; + + // If there were errors for sending the PM, show them. + if (!empty($context['post_error']['messages'])) + { + echo ' +
    + ', $txt['error_while_submitting'], ' +
      '; + + foreach ($context['post_error']['messages'] as $error) + echo ' +
    • ', $error, '
    • '; + + echo ' +
    +
    '; + } + + echo ' +
    '; + + // To and bcc. Include a button to search for members. + echo ' +
    + ', $txt['pm_to'], ': +
    '; + + // Autosuggest will be added by the JavaScript later on. + echo ' +
    + '; + + // A link to add BCC, only visible with JavaScript enabled. + echo ' + '; + + // A div that'll contain the items found by the autosuggest. + echo ' +
    '; + + echo ' +
    '; + + // This BCC row will be hidden by default if JavaScript is enabled. + echo ' +
    + ', $txt['pm_bcc'], ': +
    +
    + +
    +
    '; + + // The subject of the PM. + echo ' +
    + ', $txt['subject'], ': +
    +
    + +
    +

    '; + + // Showing BBC? + if ($context['show_bbc']) + { + echo ' +
    '; + } + + // What about smileys? + if (!empty($context['smileys']['postform']) || !empty($context['smileys']['popup'])) + echo ' +
    '; + + // Show BBC buttons, smileys and textbox. + echo ' + ', template_control_richedit($context['post_box_name'], 'smileyBox_message', 'bbcBox_message'); + + // Require an image to be typed to save spamming? + if ($context['require_verification']) + { + echo ' +
    + ', $txt['pm_visual_verification_label'], ': + ', template_control_verification($context['visual_verification_id'], 'all'), ' +
    '; + } + + // Send, Preview, spellcheck buttons. + echo ' +

    +

    + ', $context['browser']['is_firefox'] ? $txt['shortcuts_firefox'] : $txt['shortcuts'], ' +

    +

    + ', template_control_richedit_buttons($context['post_box_name']), ' +

    + + + + + + +
    +
    + +
    +
    '; + + // Show the message you're replying to. + if ($context['reply']) + echo ' +
    +
    +
    +

    ', $txt['subject'], ': ', $context['quoted_message']['subject'], '

    +
    +
    + +
    +
    + ', $txt['on'], ': ', $context['quoted_message']['time'], ' + ', $txt['from'], ': ', $context['quoted_message']['member']['name'], ' +

    + ', $context['quoted_message']['body'], ' +
    + +

    '; + + echo ' + + + '; +} + +// This template asks the user whether they wish to empty out their folder/messages. +function template_ask_delete() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    ', ($context['delete_all'] ? $txt['delete_message'] : $txt['delete_all']), '

    +
    +
    + +
    +

    ', $txt['delete_all_confirm'], '


    + ', $txt['yes'], ' - ', $txt['no'], ' +
    + +
    '; +} + +// This template asks the user what messages they want to prune. +function template_prune() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['pm_prune'], '

    +
    +
    + +
    +

    ', $txt['pm_prune_desc1'], ' ', $txt['pm_prune_desc2'], '

    +
    + +
    +
    + +
    + +
    '; +} + +// Here we allow the user to setup labels, remove labels and change rules for labels (i.e, do quite a bit) +function template_labels() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['pm_manage_labels'], '

    +
    +
    + ', $txt['pm_labels_desc'], ' +
    + + + + + + + + '; + if (count($context['labels']) < 2) + echo ' + + + '; + else + { + $alternate = true; + foreach ($context['labels'] as $label) + { + if ($label['id'] == -1) + continue; + + echo ' + + + + '; + + $alternate = !$alternate; + } + } + echo ' + +
    + ', $txt['pm_label_name'], ' + '; + + if (count($context['labels']) > 2) + echo ' + '; + + echo ' +
    ', $txt['pm_labels_no_exist'], '
    + +
    '; + + if (!count($context['labels']) < 2) + echo ' +
    + + +
    '; + + echo ' + +
    +
    +
    +

    ', $txt['pm_label_add_new'], '

    +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    +
    + +
    +
    + +
    + +

    '; +} + +// Template for reporting a personal message. +function template_report_message() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    + +
    +

    ', $txt['pm_report_title'], '

    +
    +
    + ', $txt['pm_report_desc'], ' +
    +
    + +
    +
    '; + + // If there is more than one admin on the forum, allow the user to choose the one they want to direct to. + // !!! Why? + if ($context['admin_count'] > 1) + { + echo ' +
    + ', $txt['pm_report_admins'], ': +
    +
    + +
    '; + } + + echo ' +
    + ', $txt['pm_report_reason'], ': +
    +
    + +
    +
    +
    + +
    +
    + +
    + +
    '; +} + +// Little template just to say "Yep, it's been submitted" +function template_report_message_complete() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +

    ', $txt['pm_report_title'], '

    +
    +
    + +
    +

    ', $txt['pm_report_done'], '

    + ', $txt['pm_report_return'], ' +
    + +
    '; +} + +// Manage rules. +function template_rules() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['pm_manage_rules'], '

    +
    +
    + ', $txt['pm_manage_rules_desc'], ' +
    + + + + + + + + '; + + if (empty($context['rules'])) + echo ' + + + '; + + $alternate = false; + foreach ($context['rules'] as $rule) + { + echo ' + + + + '; + $alternate = !$alternate; + } + + echo ' + +
    + ', $txt['pm_rule_title'], ' + '; + + if (!empty($context['rules'])) + echo ' + '; + + echo ' +
    + ', $txt['pm_rules_none'], ' +
    + ', $rule['name'], ' + + +
    +
    + [', $txt['pm_add_rule'], ']'; + + if (!empty($context['rules'])) + echo ' + [', $txt['pm_apply_rules'], ']'; + + if (!empty($context['rules'])) + echo ' + + '; + + echo ' +
    +
    '; + +} + +// Template for adding/editing a rule. +function template_add_rule() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' + '; + + echo ' +
    +
    +

    ', $context['rid'] == 0 ? $txt['pm_add_rule'] : $txt['pm_edit_rule'], '

    +
    +
    + +
    +
    +
    + ', $txt['pm_rule_name'], ':
    + ', $txt['pm_rule_name_desc'], ' +
    +
    + +
    +
    +
    + ', $txt['pm_rule_criteria'], ''; + + // Add a dummy criteria to allow expansion for none js users. + $context['rule']['criteria'][] = array('t' => '', 'v' => ''); + + // For each criteria print it out. + $isFirst = true; + foreach ($context['rule']['criteria'] as $k => $criteria) + { + if (!$isFirst && $criteria['t'] == '') + echo '
    '; + elseif (!$isFirst) + echo '
    '; + + echo ' + + + + + + + '; + + // If this is the dummy we add a means to hide for non js users. + if ($isFirst) + $isFirst = false; + elseif ($criteria['t'] == '') + echo '
    '; + } + + echo ' +
    + +

    + ', $txt['pm_rule_logic'], ': + +
    +
    + ', $txt['pm_rule_actions'], ''; + + // As with criteria - add a dummy action for "expansion". + $context['rule']['actions'][] = array('t' => '', 'v' => ''); + + // Print each action. + $isFirst = true; + foreach ($context['rule']['actions'] as $k => $action) + { + if (!$isFirst && $action['t'] == '') + echo '
    '; + elseif (!$isFirst) + echo '
    '; + + echo ' + + + + '; + + if ($isFirst) + $isFirst = false; + elseif ($action['t'] == '') + echo ' +
    '; + } + + echo ' +
    + +
    +
    + +

    +
    +

    ', $txt['pm_rule_description'], '

    +
    +
    +
    ', $txt['pm_rule_js_disabled'], '
    +
    +
    + + +
    +
    '; + + // Now setup all the bits! + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Poll.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Poll.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,156 @@ +:
  30. '; + + // Start the main poll form. + echo ' +
    +
    +
    +

    ', $context['page_title'], '

    +
    '; + + if (!empty($context['poll_error']['messages'])) + echo ' +
    +
    +
    + ', $context['is_edit'] ? $txt['error_while_editing_poll'] : $txt['error_while_adding_poll'], ': +
    +
    + ', empty($context['poll_error']['messages']) ? '' : implode('
    ', $context['poll_error']['messages']), ' +
    +
    +
    '; + + echo ' +
    + +
    + +
    + ', $txt['poll_question'], ': + +
      '; + + foreach ($context['choices'] as $choice) + { + echo ' +
    • + : + '; + + // Does this option have a vote count yet, or is it new? + if ($choice['votes'] != -1) + echo ' (', $choice['votes'], ' ', $txt['votes'], ')'; + + echo ' +
    • '; + } + + echo ' +
    • +
    + (', $txt['poll_add_option'], ') +
    +
    + ', $txt['poll_options'], ': +
    '; + + if ($context['can_moderate_poll']) + { + echo ' +
    + +
    +
    + +
    +
    +
    + ', $txt['poll_run_limit'], ' +
    +
    + ', $txt['days_word'], ' +
    +
    + +
    +
    + +
    '; + + if ($context['poll']['guest_vote_allowed']) + echo ' +
    + +
    +
    + +
    '; + } + + echo ' +
    + ', $txt['poll_results_visibility'], ': +
    +
    +
    +
    + +
    +
    +
    '; + // If this is an edit, we can allow them to reset the vote counts. + if ($context['is_edit']) + echo ' +
    + ', $txt['reset_votes'], ' + ' . $txt['reset_votes_check'] . ' +
    '; + echo ' +
    + +
    +
    + +
    + + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Post.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Post.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1096 @@ +:
  31. '), '); + }'; + + // If we are making a calendar event we want to ensure we show the current days in a month etc... this is done here. + if ($context['make_event']) + echo ' + var monthLength = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + function generateDays() + { + var dayElement = document.getElementById(\'day\'), yearElement = document.getElementById(\'year\'), monthElement = document.getElementById(\'month\'); + var days, selected = dayElement.selectedIndex; + + monthLength[1] = yearElement.options[yearElement.selectedIndex].value % 4 == 0 ? 29 : 28; + days = monthLength[monthElement.value - 1]; + + while (dayElement.options.length) + dayElement.options[0] = null; + + for (i = 1; i <= days; i++) + dayElement.options[dayElement.length] = new Option(i, i); + + if (selected < days) + dayElement.selectedIndex = selected; + }'; + + // End of the javascript, start the form and display the link tree. + echo ' + // ]]> +
    '; + + // If the user wants to see how their message looks - the preview section is where it's at! + echo ' +
    '; + + if ($context['make_event'] && (!$context['event']['new'] || !empty($context['current_board']))) + echo ' + '; + + // Start the main table. + echo ' +
    +

    ', $context['page_title'], '

    +
    +
    + +
    ', isset($context['current_topic']) ? ' + ' : ''; + + // If an error occurred, explain what happened. + echo ' + '; + + // If this won't be approved let them know! + if (!$context['becomes_approved']) + { + echo ' +

    + ', $txt['wait_for_approval'], ' + +

    '; + } + + // If it's locked, show a message to warn the replyer. + echo ' + '; + + // The post header... important stuff + echo ' +
    '; + + // Guests have to put in their name and email... + if (isset($context['name']) && isset($context['email'])) + { + echo ' +
    + ', $txt['name'], ': +
    +
    + +
    '; + + if (empty($modSettings['guest_post_no_email'])) + echo ' +
    + ', $txt['email'], ': +
    +
    + +
    '; + } + + // Now show the subject box for this post. + echo ' +
    + ', $txt['subject'], ': +
    +
    + +
    +
    + ', $txt['message_icon'], ': +
    +
    + + +
    +

    '; + + // Are you posting a calendar event? + if ($context['make_event']) + { + echo ' +
    +
    + ', $txt['calendar_event_title'], ' + +
    + ', $txt['calendar_year'], ' + + ', $txt['calendar_month'], ' + + ', $txt['calendar_day'], ' + +
    +
    '; + + if (!empty($modSettings['cal_allowspan']) || ($context['event']['new'] && $context['is_new_post'])) + { + echo ' +
    + ', $txt['calendar_event_options'], ' +
    +
      '; + + // If events can span more than one day then allow the user to select how long it should last. + if (!empty($modSettings['cal_allowspan'])) + { + echo ' +
    • + ', $txt['calendar_numb_days'], ' + +
    • '; + } + + // If this is a new event let the user specify which board they want the linked post to be put into. + if ($context['event']['new'] && $context['is_new_post']) + { + echo ' +
    • + ', $txt['calendar_post_in'], ' + +
    • '; + } + + echo ' +
    +
    +
    '; + } + + echo ' +
    '; + } + + // If this is a poll then display all the poll options! + if ($context['make_poll']) + { + echo ' +
    +
    + ', $txt['poll_question'], ' + +
      '; + + // Loop through all the choices and print them out. + foreach ($context['choices'] as $choice) + { + echo ' +
    • + : + +
    • '; + } + + echo ' +
    • +
    + (', $txt['poll_add_option'], ') +
    +
    + ', $txt['poll_options'], ' +
    +
    + +
    +
    + +
    +
    +
    + ', $txt['poll_run_limit'], ' +
    +
    + ', $txt['days_word'], ' +
    +
    + +
    +
    + +
    '; + + if ($context['poll_options']['guest_vote_enabled']) + echo ' +
    + +
    +
    + +
    '; + + echo ' +
    + ', $txt['poll_results_visibility'], ': +
    +
    +
    +
    + +
    +
    +
    +
    '; + } + + // Show the actual posting area... + if ($context['show_bbc']) + { + echo ' +
    '; + } + + // What about smileys? + if (!empty($context['smileys']['postform']) || !empty($context['smileys']['popup'])) + echo ' +
    '; + + echo ' + ', template_control_richedit($context['post_box_name'], 'smileyBox_message', 'bbcBox_message'); + + // If this message has been edited in the past - display when it was. + if (isset($context['last_modified'])) + echo ' +
    + ', $txt['last_edit'], ': + ', $context['last_modified'], ' +
    '; + + // If the admin has enabled the hiding of the additional options - show a link and image for it. + if (!empty($settings['additional_options_collapsable'])) + echo ' + '; + + // Display the check boxes for all the standard options - if they are available to the user! + echo ' +
    +
      + ', $context['can_notify'] ? '
    • ' : '', ' + ', $context['can_lock'] ? '
    • ' : '', ' +
    • + ', $context['can_sticky'] ? '
    • ' : '', ' +
    • ', ' + ', $context['can_move'] ? '
    • ' : '', ' + ', $context['can_announce'] && $context['is_first_post'] ? '
    • ' : '', ' + ', $context['show_approval'] ? '
    • ' : '', ' +
    +
    '; + + // If this post already has attachments on it - give information about them. + if (!empty($context['current_attachments'])) + { + echo ' +
    +
    + ', $txt['attached'], ': +
    +
    + + ', $txt['uncheck_unwatchd_attach'], ': +
    '; + foreach ($context['current_attachments'] as $attachment) + echo ' +
    + +
    '; + echo ' +
    '; + } + + // Is the user allowed to post any additional ones? If so give them the boxes to do it! + if ($context['can_post_attachment']) + { + echo ' +
    +
    + ', $txt['attach'], ': +
    +
    + (', $txt['clean_attach'], ')'; + + // Show more boxes only if they aren't approaching their limit. + if ($context['num_allowed_attachments'] > 1) + echo ' + +
    +
    (', $txt['more_attachments'], ')
    '; + + echo ' +
    '; + + // Show some useful information such as allowed extensions, maximum size and amount of attachments allowed. + if (!empty($modSettings['attachmentCheckExtensions'])) + echo ' + ', $txt['allowed_types'], ': ', $context['allowed_extensions'], '
    '; + + if (!empty($context['attachment_restrictions'])) + echo ' + ', $txt['attach_restrictions'], ' ', implode(', ', $context['attachment_restrictions']), '
    '; + + if (!$context['can_post_attachment_unapproved']) + echo ' + ', $txt['attachment_requires_approval'], '', '
    '; + + echo ' +
    +
    '; + } + + // Is visual verification enabled? + if ($context['require_verification']) + { + echo ' +
    + + ', $txt['verification'], ': + + ', template_control_verification($context['visual_verification_id'], 'all'), ' +
    '; + } + + // Finally, the submit buttons. + echo ' +

    + ', $context['browser']['is_firefox'] ? $txt['shortcuts_firefox'] : $txt['shortcuts'], ' +

    +

    + ', template_control_richedit_buttons($context['post_box_name']); + + // Option to delete an event if user is editing one. + if ($context['make_event'] && !$context['event']['new']) + echo ' + '; + + echo ' +

    +
    + +
    +
    '; + + // Assuming this isn't a new topic pass across the last message id. + if (isset($context['topic_last_message'])) + echo ' + '; + + echo ' + + + +
    '; + + echo ' + '; + + // If the user is replying to a topic show the previous posts. + if (isset($context['previous_posts']) && count($context['previous_posts']) > 0) + { + echo ' +
    +
    +

    ', $txt['topic_summary'], '

    +
    + '; + + $ignored_posts = array(); + foreach ($context['previous_posts'] as $post) + { + $ignoring = false; + if (!empty($post['is_ignored'])) + $ignored_posts[] = $ignoring = $post['id']; + + echo ' +
    + +
    +
    +
    ', $txt['posted_by'], ': ', $post['poster'], '
    + « ', $txt['on'], ': ', $post['time'], ' » +
    '; + + if ($context['can_quote']) + { + echo ' + '; + } + + echo ' +
    '; + + if ($ignoring) + { + echo ' +
    + ', $txt['ignoring_user'], ' + +
    '; + } + + echo ' +
    ', $post['message'], '
    +
    + +
    '; + } + + echo ' +
    + '; + } +} + +// The template for the spellchecker. +function template_spellcheck() +{ + global $context, $settings, $options, $txt; + + // The style information that makes the spellchecker look... like the forum hopefully! + echo ' + + + ', $txt['spell_check'], ' + + + + + + + + + +
    +
     
    + + + +
    + ', $txt['spellcheck_change_to'], '
    + +
    + ', $txt['spellcheck_suggest'], '
    + +
    +
    + + + + +
    +
    + +'; +} + +function template_quotefast() +{ + global $context, $settings, $options, $txt; + + echo ' + + + + ', $txt['retrieving_quote'], ' + + + + ', $txt['retrieving_quote'], ' + + + +'; +} + +function template_announce() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    ', $txt['announce_title'], '

    +
    +
    + ', $txt['announce_desc'], ' +
    +
    + +
    +

    + ', $txt['announce_this_topic'], ' ', $context['topic_subject'], ' +

    +
      '; + + foreach ($context['groups'] as $group) + echo ' +
    • + (', $group['member_count'], ') +
    • '; + + echo ' +
    • + +
    • +
    +
    + + + + + +
    +
    + +
    +
    +
    +
    '; +} + +function template_announcement_send() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    + +
    +

    ', $txt['announce_sending'], ' ', $context['topic_subject'], '

    +

    ', $context['percentage_done'], '% ', $txt['announce_done'], '

    +
    + + + + + + + +
    +
    + +
    +
    +
    +
    + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Printpage.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Printpage.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,137 @@ + + + + + + + ', $txt['print_page'], ' - ', $context['topic_subject'], ' + + + +

    ', $context['forum_name_html_safe'], '

    +

    ', $context['category_name'], ' => ', (!empty($context['parent_boards']) ? implode(' => ', $context['parent_boards']) . ' => ' : ''), $context['board_name'], ' => ', $txt['topic_started'], ': ', $context['poster_name'], ' ', $txt['search_on'], ' ', $context['post_time'], '

    +
    '; +} + +function template_main() +{ + global $context, $settings, $options, $txt; + + foreach ($context['posts'] as $post) + echo ' +
    + ', $txt['title'], ': ', $post['subject'], '
    + ', $txt['post_by'], ': ', $post['member'], ' ', $txt['search_on'], ' ', $post['time'], ' +
    +
    + ', $post['body'], ' +
    '; +} + +function template_print_below() +{ + global $context, $settings, $options; + + echo ' +
    + + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Profile.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Profile.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2982 @@ +'; + + // Prevent Chrome from auto completing fields when viewing/editing other members profiles + if ($context['browser']['is_chrome'] && !$context['user']['is_owner']) + echo ' + '; + + // If an error occurred while trying to save previously, give the user a clue! + if (!empty($context['post_errors'])) + echo ' + ', template_error_message(); + + // If the profile was update successfully, let the user know this. + if (!empty($context['profile_updated'])) + echo ' +
    + ', $context['profile_updated'], ' +
    '; +} + +// Template for closing off table started in profile_above. +function template_profile_below() +{ +} + +// This template displays users details without any option to edit them. +function template_summary() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // Display the basic information about the user + echo ' +
    +
    +

    + ', $txt['summary'], ' +

    +
    +
    +
    + +
    +

    ', $context['member']['name'], ' ', (!empty($context['member']['group']) ? $context['member']['group'] : $context['member']['post_group']), '

    + ', $context['member']['avatar']['image'], ' +
      '; + + // What about if we allow email only via the forum?? + if ($context['member']['show_email'] === 'yes' || $context['member']['show_email'] === 'no_through_forum' || $context['member']['show_email'] === 'yes_permission_override') + echo ' +
    • ', $txt['email'], '
    • '; + + // Don't show an icon if they haven't specified a website. + if ($context['member']['website']['url'] !== '' && !isset($context['disabled_fields']['website'])) + echo ' +
    • ', ($settings['use_image_buttons'] ? '' . $context['member']['website']['title'] . '' : $txt['www']), '
    • '; + + // Are there any custom profile fields for the summary? + if (!empty($context['custom_fields'])) + { + foreach ($context['custom_fields'] as $field) + if (($field['placement'] == 1 || empty($field['output_html'])) && !empty($field['value'])) + echo ' +
    • ', $field['output_html'], '
    • '; + } + + echo ' + ', !isset($context['disabled_fields']['icq']) && !empty($context['member']['icq']['link']) ? '
    • ' . $context['member']['icq']['link'] . '
    • ' : '', ' + ', !isset($context['disabled_fields']['msn']) && !empty($context['member']['msn']['link']) ? '
    • ' . $context['member']['msn']['link'] . '
    • ' : '', ' + ', !isset($context['disabled_fields']['aim']) && !empty($context['member']['aim']['link']) ? '
    • ' . $context['member']['aim']['link'] . '
    • ' : '', ' + ', !isset($context['disabled_fields']['yim']) && !empty($context['member']['yim']['link']) ? '
    • ' . $context['member']['yim']['link'] . '
    • ' : '', ' +
    + ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $context['member']['online']['text'] . '' : $context['member']['online']['text'], $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? ' ' . $context['member']['online']['text'] . '' : ''; + + // Can they add this member as a buddy? + if (!empty($context['can_have_buddy']) && !$context['user']['is_owner']) + echo ' +
    [', $txt['buddy_' . ($context['member']['is_buddy'] ? 'remove' : 'add')], ']'; + + echo ' +
    '; + + echo ' + '; + + echo ' +
    + +
    +
    +
    +
    + +
    +
    '; + + if ($context['user']['is_owner'] || $context['user']['is_admin']) + echo ' +
    ', $txt['username'], ':
    +
    ', $context['member']['username'], '
    '; + + if (!isset($context['disabled_fields']['posts'])) + echo ' +
    ', $txt['profile_posts'], ':
    +
    ', $context['member']['posts'], ' (', $context['member']['posts_per_day'], ' ', $txt['posts_per_day'], ')
    '; + + // Only show the email address fully if it's not hidden - and we reveal the email. + if ($context['member']['show_email'] == 'yes') + echo ' +
    ', $txt['email'], ':
    +
    ', $context['member']['email'], '
    '; + + // ... Or if the one looking at the profile is an admin they can see it anyway. + elseif ($context['member']['show_email'] == 'yes_permission_override') + echo ' +
    ', $txt['email'], ':
    +
    ', $context['member']['email'], '
    '; + + if (!empty($modSettings['titlesEnable']) && !empty($context['member']['title'])) + echo ' +
    ', $txt['custom_title'], ':
    +
    ', $context['member']['title'], '
    '; + + if (!empty($context['member']['blurb'])) + echo ' +
    ', $txt['personal_text'], ':
    +
    ', $context['member']['blurb'], '
    '; + + // If karma enabled show the members karma. + if ($modSettings['karmaMode'] == '1') + echo ' +
    ', $modSettings['karmaLabel'], '
    +
    ', ($context['member']['karma']['good'] - $context['member']['karma']['bad']), '
    '; + + elseif ($modSettings['karmaMode'] == '2') + echo ' +
    ', $modSettings['karmaLabel'], '
    +
    +', $context['member']['karma']['good'], '/-', $context['member']['karma']['bad'], '
    '; + + if (!isset($context['disabled_fields']['gender']) && !empty($context['member']['gender']['name'])) + echo ' +
    ', $txt['gender'], ':
    +
    ', $context['member']['gender']['name'], '
    '; + + echo ' +
    ', $txt['age'], ':
    +
    ', $context['member']['age'] . ($context['member']['today_is_birthday'] ? '   ' : ''), '
    '; + + if (!isset($context['disabled_fields']['location']) && !empty($context['member']['location'])) + echo ' +
    ', $txt['location'], ':
    +
    ', $context['member']['location'], '
    '; + + echo ' +
    '; + + // Any custom fields for standard placement? + if (!empty($context['custom_fields'])) + { + $shown = false; + foreach ($context['custom_fields'] as $field) + { + if ($field['placement'] != 0 || empty($field['output_html'])) + continue; + + if (empty($shown)) + { + echo ' +
    '; + $shown = true; + } + + echo ' +
    ', $field['name'], ':
    +
    ', $field['output_html'], '
    '; + } + + if (!empty($shown)) + echo ' +
    '; + } + + echo ' +
    '; + + // Can they view/issue a warning? + if ($context['can_view_warning'] && $context['member']['warning']) + { + echo ' +
    ', $txt['profile_warning_level'], ':
    +
    + ', $context['member']['warning'], '%'; + + // Can we provide information on what this means? + if (!empty($context['warning_status'])) + echo ' + (', $context['warning_status'], ')'; + + echo ' +
    '; + } + + // Is this member requiring activation and/or banned? + if (!empty($context['activate_message']) || !empty($context['member']['bans'])) + { + + // If the person looking at the summary has permission, and the account isn't activated, give the viewer the ability to do it themselves. + if (!empty($context['activate_message'])) + echo ' +
    ', $context['activate_message'], ' (', $context['activate_link_text'], ')
    '; + + // If the current member is banned, show a message and possibly a link to the ban. + if (!empty($context['member']['bans'])) + { + echo ' +
    ', $txt['user_is_banned'], ' [' . $txt['view_ban'] . ']
    + '; + } + } + + echo ' +
    ', $txt['date_registered'], ':
    +
    ', $context['member']['registered'], '
    '; + + // If the person looking is allowed, they can check the members IP address and hostname. + if ($context['can_see_ip']) + { + if (!empty($context['member']['ip'])) + echo ' +
    ', $txt['ip'], ':
    +
    ', $context['member']['ip'], '
    '; + + if (empty($modSettings['disableHostnameLookup']) && !empty($context['member']['ip'])) + echo ' +
    ', $txt['hostname'], ':
    +
    ', $context['member']['hostname'], '
    '; + } + + echo ' +
    ', $txt['local_time'], ':
    +
    ', $context['member']['local_time'], '
    '; + + if (!empty($modSettings['userLanguage']) && !empty($context['member']['language'])) + echo ' +
    ', $txt['language'], ':
    +
    ', $context['member']['language'], '
    '; + + echo ' +
    ', $txt['lastLoggedIn'], ':
    +
    ', $context['member']['last_login'], '
    +
    '; + + // Are there any custom profile fields for the summary? + if (!empty($context['custom_fields'])) + { + $shown = false; + foreach ($context['custom_fields'] as $field) + { + if ($field['placement'] != 2 || empty($field['output_html'])) + continue; + if (empty($shown)) + { + $shown = true; + echo ' +
    +
      '; + } + echo ' +
    • ', $field['output_html'], '
    • '; + } + if ($shown) + echo ' +
    +
    '; + } + + // Show the users signature. + if ($context['signature_enabled'] && !empty($context['member']['signature'])) + echo ' +
    +
    ', $txt['signature'], ':
    + ', $context['member']['signature'], ' +
    '; + + echo ' +
    + +
    +
    +
    +
    '; +} + +// Template for showing all the posts of the user, in chronological order. +function template_showPosts() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    + ', (!isset($context['attachments']) && empty($context['is_topics']) ? $txt['showMessages'] : (!empty($context['is_topics']) ? $txt['showTopics'] : $txt['showAttachments'])), ' - ', $context['member']['name'], ' +

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + // Button shortcuts + $quote_button = create_button('quote.gif', 'reply_quote', 'quote', 'align="middle"'); + $reply_button = create_button('reply_sm.gif', 'reply', 'reply', 'align="middle"'); + $remove_button = create_button('delete.gif', 'remove_message', 'remove', 'align="middle"'); + $notify_button = create_button('notify_sm.gif', 'notify_replies', 'notify', 'align="middle"'); + + // Are we displaying posts or attachments? + if (!isset($context['attachments'])) + { + // For every post to be displayed, give it its own div, and show the important details of the post. + foreach ($context['posts'] as $post) + { + echo ' +
    +
    + +
    +
    ', $post['counter'], '
    +
    +
    ', $post['board']['name'], ' / ', $post['subject'], '
    + « ', $txt['on'], ': ', $post['time'], ' » +
    +
    '; + + if (!$post['approved']) + echo ' +
    + ', $txt['post_awaiting_approval'], ' +
    '; + + echo ' + ', $post['body'], ' +
    +
    '; + + if ($post['can_reply'] || $post['can_mark_notify'] || $post['can_delete']) + echo ' +
    +
      '; + + // If they *can* reply? + if ($post['can_reply']) + echo ' +
    • ', $txt['reply'], '
    • '; + + // If they *can* quote? + if ($post['can_quote']) + echo ' +
    • ', $txt['quote'], '
    • '; + + // Can we request notification of topics? + if ($post['can_mark_notify']) + echo ' +
    • ', $txt['notify'], '
    • '; + + // How about... even... remove it entirely?! + if ($post['can_delete']) + echo ' +
    • ', $txt['remove'], '
    • '; + + if ($post['can_reply'] || $post['can_mark_notify'] || $post['can_delete']) + echo ' +
    +
    '; + + echo ' +
    + +
    +
    '; + } + } + else + { + echo ' + + + + + + + + + + '; + + // Looks like we need to do all the attachments instead! + $alternate = false; + foreach ($context['attachments'] as $attachment) + { + echo ' + + + + + + '; + $alternate = !$alternate; + } + + // No posts? Just end the table with a informative message. + if ((isset($context['attachments']) && empty($context['attachments'])) || (!isset($context['attachments']) && empty($context['posts']))) + echo ' + + + '; + + echo ' + +
    + + ', $txt['show_attach_filename'], ' + ', ($context['sort_order'] == 'filename' ? '' : ''), ' + + + + ', $txt['show_attach_downloads'], ' + ', ($context['sort_order'] == 'downloads' ? '' : ''), ' + + + + ', $txt['message'], ' + ', ($context['sort_order'] == 'subject' ? '' : ''), ' + + + + ', $txt['show_attach_posted'], ' + ', ($context['sort_order'] == 'posted' ? '' : ''), ' + +
    ', $attachment['filename'], '', !$attachment['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : '', '', $attachment['downloads'], '', $attachment['subject'], '', $attachment['posted'], '
    + ', isset($context['attachments']) ? $txt['show_attachments_none'] : ($context['is_topics'] ? $txt['show_topics_none'] : $txt['show_posts_none']), ' +
    '; + } + // Show more page numbers. + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; +} + +// Template for showing all the buddies of the current user. +function template_editBuddies() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    + ', $txt['editBuddies'], ' +

    +
    + + + + + + + + + + + '; + + // If they don't have any buddies don't list them! + if (empty($context['buddies'])) + echo ' + + + '; + + // Now loop through each buddy showing info on each. + $alternate = false; + foreach ($context['buddies'] as $buddy) + { + echo ' + + + + + + + + + + '; + + $alternate = !$alternate; + } + + echo ' +
    ', $txt['name'], '', $txt['status'], '', $txt['email'], '', $txt['icq'], '', $txt['aim'], '', $txt['yim'], '', $txt['msn'], '
    ', $txt['no_buddies'], '
    ', $buddy['link'], '', $buddy['online']['label'], '', ($buddy['show_email'] == 'no' ? '' : '' . $txt['email'] . ''), '', $buddy['icq']['link'], '', $buddy['aim']['link'], '', $buddy['yim']['link'], '', $buddy['msn']['link'], '', $txt['buddy_remove'], '
    '; + + // Add a new buddy? + echo ' +
    +
    +
    +
    +

    ', $txt['buddy_add'], '

    +
    + +
    + + + +
    + +
    +
    + + '; +} + +// Template for showing the ignore list of the current user. +function template_editIgnoreList() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +

    + ', $txt['editIgnoreList'], ' +

    +
    + + + + + + + + + + + '; + + // If they don't have anyone on their ignore list, don't list it! + if (empty($context['ignore_list'])) + echo ' + + + '; + + // Now loop through each buddy showing info on each. + $alternate = false; + foreach ($context['ignore_list'] as $member) + { + echo ' + + + + + + + + + + '; + + $alternate = !$alternate; + } + + echo ' +
    ', $txt['name'], '', $txt['status'], '', $txt['email'], '', $txt['icq'], '', $txt['aim'], '', $txt['yim'], '', $txt['msn'], '
    ', $txt['no_ignore'], '
    ', $member['link'], '', $member['online']['label'], '', ($member['show_email'] == 'no' ? '' : '' . $txt['email'] . ''), '', $member['icq']['link'], '', $member['aim']['link'], '', $member['yim']['link'], '', $member['msn']['link'], '', $txt['ignore_remove'], '
    '; + + // Add a new buddy? + echo ' +
    +
    +
    +
    +

    ', $txt['ignore_add'], '

    +
    + +
    + + + +
    + +
    +
    + + '; +} + +// This template shows an admin information on a users IP addresses used and errors attributed to them. +function template_trackActivity() +{ + global $context, $settings, $options, $scripturl, $txt; + + // The first table shows IP information about the user. + echo ' +
    +

    ', $txt['view_ips_by'], ' ', $context['member']['name'], '

    +
    '; + + // The last IP the user used. + echo ' +
    + +
    +
    +
    ', $txt['most_recent_ip'], ': + ', (empty($context['last_ip2']) ? '' : '
    + (' . $txt['why_two_ip_address'] . ')'), ' +
    +
    + ', $context['last_ip'], ''; + + // Second address detected? + if (!empty($context['last_ip2'])) + echo ' + , ', $context['last_ip2'], ''; + + echo ' +
    '; + + // Lists of IP addresses used in messages / error messages. + echo ' +
    ', $txt['ips_in_messages'], ':
    +
    + ', (count($context['ips']) > 0 ? implode(', ', $context['ips']) : '(' . $txt['none'] . ')'), ' +
    +
    ', $txt['ips_in_errors'], ':
    +
    + ', (count($context['ips']) > 0 ? implode(', ', $context['error_ips']) : '(' . $txt['none'] . ')'), ' +
    '; + + // List any members that have used the same IP addresses as the current member. + echo ' +
    ', $txt['members_in_range'], ':
    +
    + ', (count($context['members_in_range']) > 0 ? implode(', ', $context['members_in_range']) : '(' . $txt['none'] . ')'), ' +
    +
    +
    + +
    +
    '; + + // Show the track user list. + template_show_list('track_user_list'); +} + +// The template for trackIP, allowing the admin to see where/who a certain IP has been used. +function template_trackIP() +{ + global $context, $settings, $options, $scripturl, $txt; + + // This function always defaults to the last IP used by a member but can be set to track any IP. + // The first table in the template gives an input box to allow the admin to enter another IP to track. + echo ' +
    +

    ', $txt['trackIP'], '

    +
    +
    + +
    +
    ', $txt['enter_ip'], ':    
    +
    + +
    +
    '; + + // The table inbetween the first and second table shows links to the whois server for every region. + if ($context['single_ip']) + { + echo ' +
    +

    ', $txt['whois_title'], ' ', $context['ip'], '

    +
    +
    + +
    '; + foreach ($context['whois_servers'] as $server) + echo ' + ', $server['name'], '
    '; + echo ' +
    + +
    +
    '; + } + + // The second table lists all the members who have been logged as using this IP address. + echo ' +
    +

    ', $txt['members_from_ip'], ' ', $context['ip'], '

    +
    '; + if (empty($context['ips'])) + echo ' +

    ', $txt['no_members_from_ip'], '

    '; + else + { + echo ' + + + + + + + + '; + + // Loop through each of the members and display them. + foreach ($context['ips'] as $ip => $memberlist) + echo ' + + + + '; + + echo ' + +
    ', $txt['ip_address'], '', $txt['display_name'], '
    ', $ip, '', implode(', ', $memberlist), '
    +
    '; + } + + template_show_list('track_message_list'); + + echo '
    '; + + template_show_list('track_user_list'); +} + +function template_showPermissions() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +

    + ', $txt['showPermissions'], ' +

    +
    '; + + if ($context['member']['has_all_permissions']) + { + echo ' +

    ', $txt['showPermissions_all'], '

    '; + } + else + { + echo ' +

    ',$txt['showPermissions_help'],'

    +
    '; + + if (!empty($context['no_access_boards'])) + { + echo ' +
    +

    ', $txt['showPermissions_restricted_boards'], '

    +
    +
    + +
    ', $txt['showPermissions_restricted_boards_desc'], ':
    '; + foreach ($context['no_access_boards'] as $no_access_board) + echo ' + ', $no_access_board['name'], '', $no_access_board['is_last'] ? '' : ', '; + echo ' +
    + +
    '; + } + + // General Permissions section. + echo ' +
    +
    +

    ', $txt['showPermissions_general'], '

    +
    '; + if (!empty($context['member']['permissions']['general'])) + { + echo ' + + + + + + + + '; + + foreach ($context['member']['permissions']['general'] as $permission) + { + echo ' + + + + '; + } + echo ' + +
    ', $txt['showPermissions_permission'], '', $txt['showPermissions_status'], '
    + ', $permission['is_denied'] ? '' . $permission['name'] . '' : $permission['name'], ' + '; + + if ($permission['is_denied']) + echo ' + ', $txt['showPermissions_denied'], ': ', implode(', ', $permission['groups']['denied']),''; + else + echo ' + ', $txt['showPermissions_given'], ': ', implode(', ', $permission['groups']['allowed']); + + echo ' +
    +

    '; + } + else + echo ' +

    ', $txt['showPermissions_none_general'], '

    '; + + // Board permission section. + echo ' +
    +
    +
    +

    + ', $txt['showPermissions_select'], ': + +

    +
    +
    '; + if (!empty($context['member']['permissions']['board'])) + { + echo ' + + + + + + + + '; + foreach ($context['member']['permissions']['board'] as $permission) + { + echo ' + + + + '; + } + echo ' + +
    ', $txt['showPermissions_permission'], '', $txt['showPermissions_status'], '
    + ', $permission['is_denied'] ? '' . $permission['name'] . '' : $permission['name'], ' + '; + + if ($permission['is_denied']) + { + echo ' + ', $txt['showPermissions_denied'], ': ', implode(', ', $permission['groups']['denied']), ''; + } + else + { + echo ' + ', $txt['showPermissions_given'], ':  ', implode(', ', $permission['groups']['allowed']); + } + echo ' +
    '; + } + else + echo ' +

    ', $txt['showPermissions_none_board'], '

    '; + echo ' +
    +
    '; + } +} + +// Template for user statistics, showing graphs and the like. +function template_statPanel() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // First, show a few text statistics such as post/topic count. + echo ' +
    +
    +
    +

    + + ', $txt['statPanel_generalStats'], ' - ', $context['member']['name'], ' + +

    +
    +
    + +
    +
    +
    ', $txt['statPanel_total_time_online'], ':
    +
    ', $context['time_logged_in'], '
    +
    ', $txt['statPanel_total_posts'], ':
    +
    ', $context['num_posts'], ' ', $txt['statPanel_posts'], '
    +
    ', $txt['statPanel_total_topics'], ':
    +
    ', $context['num_topics'], ' ', $txt['statPanel_topics'], '
    +
    ', $txt['statPanel_users_polls'], ':
    +
    ', $context['num_polls'], ' ', $txt['statPanel_polls'], '
    +
    ', $txt['statPanel_users_votes'], ':
    +
    ', $context['num_votes'], ' ', $txt['statPanel_votes'], '
    +
    +
    + +
    +
    '; + + // This next section draws a graph showing what times of day they post the most. + echo ' +
    +
    +

    + ', $txt['statPanel_activityTime'], ' +

    +
    +
    + +
    '; + + // If they haven't post at all, don't draw the graph. + if (empty($context['posts_by_time'])) + echo ' + ', $txt['statPanel_noPosts'], ''; + // Otherwise do! + else + { + echo ' +
      '; + + // The labels. + foreach ($context['posts_by_time'] as $time_of_day) + { + echo ' + +
      +
      + ', sprintf($txt['statPanel_activityTime_posts'], $time_of_day['posts'], $time_of_day['posts_percent']), ' +
      +
      + ', $time_of_day['hour_format'], ' + '; + } + + echo ' + +
    '; + } + + echo ' + +
    + +
    +
    '; + + // Two columns with the most popular boards by posts and activity (activity = users posts / total posts). + echo ' +
    +
    +
    +

    + ', $txt['statPanel_topBoards'], ' +

    +
    +
    + +
    '; + + if (empty($context['popular_boards'])) + echo ' + ', $txt['statPanel_noPosts'], ''; + + else + { + echo ' +
    '; + + // Draw a bar for every board. + foreach ($context['popular_boards'] as $board) + { + echo ' +
    ', $board['link'], '
    +
    +
    + ', sprintf($txt['statPanel_topBoards_memberposts'], $board['posts'], $board['total_posts_member'], $board['posts_percent']), ' +
    + ', empty($context['hide_num_posts']) ? $board['posts'] : '', ' +
    '; + } + + echo ' +
    '; + } + echo ' +
    + +
    +
    '; + echo ' +
    +
    +

    + ', $txt['statPanel_topBoardsActivity'], ' +

    +
    +
    + +
    '; + + if (empty($context['board_activity'])) + echo ' + ', $txt['statPanel_noPosts'], ''; + else + { + echo ' +
    '; + + // Draw a bar for every board. + foreach ($context['board_activity'] as $activity) + { + echo ' +
    ', $activity['link'], '
    +
    +
    + ', sprintf($txt['statPanel_topBoards_posts'], $activity['posts'], $activity['total_posts'], $activity['posts_percent']), ' +
    + ', $activity['percent'], '% +
    '; + } + + echo ' +
    '; + } + echo ' +
    + +
    +
    +
    '; + + echo ' +
    +
    '; +} + +// Template for editing profile options. +function template_edit_options() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // The main header! + echo ' +
    +
    +

    + '; + + // Don't say "Profile" if this isn't the profile... + if (!empty($context['profile_header_text'])) + echo ' + ', $context['profile_header_text']; + else + echo ' + ', $txt['profile']; + + echo ' + +

    +
    '; + + // Have we some description? + if ($context['page_desc']) + echo ' +

    ', $context['page_desc'], '

    '; + + echo ' +
    + +
    '; + + // Any bits at the start? + if (!empty($context['profile_prehtml'])) + echo ' +
    ', $context['profile_prehtml'], '
    '; + + if (!empty($context['profile_fields'])) + echo ' +
    '; + + // Start the big old loop 'of love. + $lastItem = 'hr'; + foreach ($context['profile_fields'] as $key => $field) + { + // We add a little hack to be sure we never get more than one hr in a row! + if ($lastItem == 'hr' && $field['type'] == 'hr') + continue; + + $lastItem = $field['type']; + if ($field['type'] == 'hr') + { + echo ' +
    +
    +
    '; + } + elseif ($field['type'] == 'callback') + { + if (isset($field['callback_func']) && function_exists('template_profile_' . $field['callback_func'])) + { + $callback_func = 'template_profile_' . $field['callback_func']; + $callback_func(); + } + } + else + { + echo ' +
    + ', $field['label'], ''; + + // Does it have any subtext to show? + if (!empty($field['subtext'])) + echo ' +
    + ', $field['subtext'], ''; + + echo ' +
    +
    '; + + // Want to put something infront of the box? + if (!empty($field['preinput'])) + echo ' + ', $field['preinput']; + + // What type of data are we showing? + if ($field['type'] == 'label') + echo ' + ', $field['value']; + + // Maybe it's a text box - very likely! + elseif (in_array($field['type'], array('int', 'float', 'text', 'password'))) + echo ' + '; + + // You "checking" me out? ;) + elseif ($field['type'] == 'check') + echo ' + '; + + // Always fun - select boxes! + elseif ($field['type'] == 'select') + { + echo ' + '; + } + + // Something to end with? + if (!empty($field['postinput'])) + echo ' + ', $field['postinput']; + + echo ' +
    '; + } + } + + if (!empty($context['profile_fields'])) + echo ' +
    '; + + // Are there any custom profile fields - if so print them! + if (!empty($context['custom_fields'])) + { + if ($lastItem != 'hr') + echo ' +
    '; + + echo ' +
    '; + + foreach ($context['custom_fields'] as $field) + { + echo ' +
    + ', $field['name'], ':
    + ', $field['desc'], ' +
    +
    + ', $field['input_html'], ' +
    '; + } + + echo ' +
    '; + + } + + // Any closing HTML? + if (!empty($context['profile_posthtml'])) + echo ' +
    ', $context['profile_posthtml'], '
    '; + elseif ($lastItem != 'hr') + echo ' +
    '; + + // Only show the password box if it's actually needed. + if ($context['require_password']) + echo ' +
    +
    + ', $txt['current_password'], ':
    + ', $txt['required_security_reasons'], ' +
    +
    + +
    +
    '; + + echo ' +
    '; + + // The button shouldn't say "Change profile" unless we're changing the profile... + if (!empty($context['submit_button_text'])) + echo ' + '; + else + echo ' + '; + + echo ' + + + +
    +
    + +
    +
    +
    '; + + // Some javascript! + echo ' + '; + + // Any final spellchecking stuff? + if (!empty($context['show_spellchecking'])) + echo ' +
    '; +} + +// Personal Message settings. +function template_profile_pm_settings() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    + +
    +
    + +
    +
    + +
    +
    + + +
    + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    '; + +} + +// Template for showing theme settings. Note: template_options() actually adds the theme specific options. +function template_profile_theme_settings() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + echo ' +
    +
    +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • '; + + if ($settings['allow_no_censored']) + echo ' +
    • + + +
    • '; + + echo ' +
    • + + +
    • +
    • + + +
    • '; + + if (!empty($modSettings['enable_buddylist'])) + echo ' +
    • + + +
    • '; + + echo ' +
    • + + +
    • '; + + // Choose WYSIWYG settings? + if (empty($modSettings['disable_wysiwyg'])) + echo ' +
    • + + +
    • '; + + if (empty($modSettings['disableCustomPerPage'])) + { + echo ' +
    • + + +
    • +
    • + + +
    • '; + } + + if (!empty($modSettings['cal_enabled'])) + echo ' +
    • + + +
    • '; + + echo ' +
    • + + +
    • +
    • + + +
    • +
    +
    +
    '; +} + +function template_notification() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + // The main containing header. + echo ' +
    +

    + ', $txt['profile'], ' +

    +
    +

    ', $txt['notification_info'], '

    +
    + +
    +
    '; + + // Allow notification on announcements to be disabled? + if (!empty($modSettings['allow_disableAnnounce'])) + echo ' + +
    '; + + // More notification options. + echo ' + +
    '; + + if (empty($modSettings['disallow_sendBody'])) + echo ' + +
    '; + + echo ' +
    + + +

    + +
    + +
    + + + + +

    +
    +
    + +
    +
    '; + + template_show_list('topic_notification_list'); + + echo ' +
    '; + + template_show_list('board_notification_list'); +} + +// Template for choosing group membership. +function template_groupMembership() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // The main containing header. + echo ' +
    +
    +

    + ', $txt['profile'], ' +

    +
    +

    ', $txt['groupMembership_info'], '

    '; + + // Do we have an update message? + if (!empty($context['update_message'])) + echo ' +
    + ', $context['update_message'], '. +
    '; + + // Requesting membership to a group? + if (!empty($context['group_request'])) + { + echo ' +
    +
    +

    ', $txt['request_group_membership'], '

    +
    + +
    + ', $txt['request_group_membership_desc'], ': + +
    + + +
    +
    + +
    '; + } + else + { + echo ' + + + + + + + + '; + + $alternate = true; + foreach ($context['groups']['member'] as $group) + { + echo ' + '; + + if ($context['can_edit_primary']) + echo ' + '; + + echo ' + + + '; + $alternate = !$alternate; + } + + echo ' + +
    ', $txt['current_membergroups'], '
    + + + + '; + + // Can they leave their group? + if ($group['can_leave']) + echo ' + ' . $txt['leave_group'] . ''; + echo ' +
    '; + + if ($context['can_edit_primary']) + echo ' +
    + +
    '; + + // Any groups they can join? + if (!empty($context['groups']['available'])) + { + echo ' +
    + + + + + + + + '; + + $alternate = true; + foreach ($context['groups']['available'] as $group) + { + echo ' + + + + '; + $alternate = !$alternate; + } + echo ' + +
    + ', $txt['available_groups'], ' +
    + ', (empty($group['color']) ? $group['name'] : '' . $group['name'] . ''), '', (!empty($group['desc']) ? '
    ' . $group['desc'] . '' : ''), ' +
    '; + + if ($group['type'] == 3) + echo ' + ', $txt['join_group'], ''; + elseif ($group['type'] == 2 && $group['pending']) + echo ' + ', $txt['approval_pending']; + elseif ($group['type'] == 2) + echo ' + ', $txt['request_group'], ''; + + echo ' +
    '; + } + + // Javascript for the selector stuff. + echo ' + '; + } + + echo ' + + +
    '; +} + +function template_ignoreboards() +{ + global $context, $txt, $settings, $scripturl; + // The main containing header. + echo ' + + +
    +
    +

    + ', $txt['profile'], ' +

    +
    +

    ', $txt['ignoreboards_info'], '

    +
    + +
    +
      '; + + $i = 0; + $limit = ceil($context['num_boards'] / 2); + foreach ($context['categories'] as $category) + { + if ($i == $limit) + { + echo ' +
    +
      '; + + $i++; + } + + echo ' +
    • + ', $category['name'], ' +
        '; + + foreach ($category['boards'] as $board) + { + if ($i == $limit) + echo ' +
      +
    • +
    +
      +
    • +
        '; + + echo ' +
      • + +
      • '; + + $i++; + } + + echo ' +
      +
    • '; + } + + echo ' +
    +
    '; + + // Show the standard "Save Settings" profile button. + template_profile_save(); + + echo ' +
    + +
    +
    +
    '; +} + +// Simple load some theme variables common to several warning templates. +function template_load_warning_variables() +{ + global $modSettings, $context; + + $context['warningBarWidth'] = 200; + // Setup the colors - this is a little messy for theming. + $context['colors'] = array( + 0 => 'green', + $modSettings['warning_watch'] => 'darkgreen', + $modSettings['warning_moderate'] => 'orange', + $modSettings['warning_mute'] => 'red', + ); + + // Work out the starting color. + $context['current_color'] = $context['colors'][0]; + foreach ($context['colors'] as $limit => $color) + if ($context['member']['warning'] >= $limit) + $context['current_color'] = $color; +} + +// Show all warnings of a user? +function template_viewWarning() +{ + global $context, $txt, $scripturl, $settings; + + template_load_warning_variables(); + + echo ' +
    +

    + + ', sprintf($txt['profile_viewwarning_for_user'], $context['member']['name']), ' + +

    +
    +
    + +
    +
    +
    + ', $txt['profile_warning_name'], ': +
    +
    + ', $context['member']['name'], ' +
    +
    + ', $txt['profile_warning_level'], ': +
    +
    +
    +
    +
    +
    ', $context['member']['warning'], '%
    +
     
    +
    +
    +
    +
    '; + + // There's some impact of this? + if (!empty($context['level_effects'][$context['current_level']])) + echo ' +
    + ', $txt['profile_viewwarning_impact'], ': +
    +
    + ', $context['level_effects'][$context['current_level']], ' +
    '; + + echo ' +
    +
    + +
    '; + + template_show_list('view_warnings'); +} + +// Show a lovely interface for issuing warnings. +function template_issueWarning() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + template_load_warning_variables(); + + echo ' + '; + + echo ' +
    +
    +

    + + ', $context['user']['is_owner'] ? $txt['profile_warning_level'] : $txt['profile_issue_warning'], ' + +

    +
    '; + + if (!$context['user']['is_owner']) + echo ' +

    ', $txt['profile_warning_desc'], '

    '; + + echo ' +
    + +
    +
    '; + + if (!$context['user']['is_owner']) + echo ' +
    + ', $txt['profile_warning_name'], ': +
    +
    + ', $context['member']['name'], ' +
    '; + + echo ' +
    + ', $txt['profile_warning_level'], ':'; + + // Is there only so much they can apply? + if ($context['warning_limit']) + echo ' +
    ', sprintf($txt['profile_warning_limit_attribute'], $context['warning_limit']), ''; + + echo ' +
    +
    + +
    +  ', $txt['profile_warning_max'], ' +
    ', $txt['profile_warning_impact'], ':
    '; + // For non-javascript give a better list. + foreach ($context['level_effects'] as $limit => $effect) + echo ' + ', sprintf($txt['profile_warning_effect_text'], $limit, $effect), '
    '; + + echo ' +
    +
    +
    '; + + if (!$context['user']['is_owner']) + { + echo ' +
    + ', $txt['profile_warning_reason'], ':
    + ', $txt['profile_warning_reason_desc'], ' +
    +
    + +
    +
    +
    +
    +
    + ', $txt['profile_warning_notify'], ': +
    +
    + +
    +
    + ', $txt['profile_warning_notify_subject'], ': +
    +
    + +
    +
    + ', $txt['profile_warning_notify_body'], ': +
    +
    + +
    + +
    '; + } + echo ' +
    +
    + + +
    +
    + +
    +
    '; + + // Previous warnings? + echo '
    +
    +

    + ', $txt['profile_warning_previous'], ' +

    +
    + + + + + + + + + + '; + + // Print the warnings. + $alternate = 0; + foreach ($context['previous_warnings'] as $warning) + { + $alternate = !$alternate; + echo ' + + + + + + '; + } + + if (empty($context['previous_warnings'])) + echo ' + + + '; + + echo ' + +
    ', $txt['profile_warning_previous_issued'], '', $txt['profile_warning_previous_time'], '', $txt['profile_warning_previous_reason'], '', $txt['profile_warning_previous_level'], '
    ', $warning['issuer']['link'], '', $warning['time'], ' +
    + ', $warning['reason'], ' +
    '; + + if (!empty($warning['id_notice'])) + echo ' +
    + +
    '; + echo ' +
    ', $warning['counter'], '
    + ', $txt['profile_warning_previous_none'], ' +
    +
    ', $txt['pages'], ': ', $context['page_index'], '
    '; + + // Do our best to get pretty javascript enabled. + echo ' + '; +} + +// Template to show for deleting a users account - now with added delete post capability! +function template_deleteAccount() +{ + global $context, $settings, $options, $scripturl, $txt, $scripturl; + + // The main containing header. + echo ' +
    +
    +

    + ', $txt['deleteAccount'], ' +

    +
    '; + // If deleting another account give them a lovely info box. + if (!$context['user']['is_owner']) + echo ' +

    ', $txt['deleteAccount_desc'], '

    '; + echo ' +
    + +
    '; + + // If they are deleting their account AND the admin needs to approve it - give them another piece of info ;) + if ($context['needs_approval']) + echo ' +
    ', $txt['deleteAccount_approval'], '
    '; + + // If the user is deleting their own account warn them first - and require a password! + if ($context['user']['is_owner']) + { + echo ' +
    ', $txt['own_profile_confirm'], '
    +
    + ', $txt['current_password'], ': +      + + + + +
    '; + } + // Otherwise an admin doesn't need to enter a password - but they still get a warning - plus the option to delete lovely posts! + else + { + echo ' +
    ', $txt['deleteAccount_warning'], '
    '; + + // Only actually give these options if they are kind of important. + if ($context['can_delete_posts']) + echo ' +
    + ', $txt['deleteAccount_posts'], ': + +
    '; + + echo ' +
    + +
    +
    + + + + +
    '; + } + echo ' +
    + +
    +
    +
    '; +} + +// Template for the password box/save button stuck at the bottom of every profile page. +function template_profile_save() +{ + global $context, $settings, $options, $txt; + + echo ' + +
    '; + + // Only show the password box if it's actually needed. + if ($context['require_password']) + echo ' +
    +
    + ', $txt['current_password'], ':
    + ', $txt['required_security_reasons'], ' +
    +
    + +
    +
    '; + + echo ' +
    + + + + +
    '; +} + +// Small template for showing an error message upon a save problem in the profile. +function template_error_message() +{ + global $context, $txt; + + echo ' +
    + ', !empty($context['custom_error_title']) ? $context['custom_error_title'] : $txt['profile_errors_occurred'], ': +
      '; + + // Cycle through each error and display an error message. + foreach ($context['post_errors'] as $error) + echo ' +
    • ', isset($txt['profile_error_' . $error]) ? $txt['profile_error_' . $error] : $error, '.
    • '; + + echo ' +
    +
    '; +} + +// Display a load of drop down selectors for allowing the user to change group. +function template_profile_group_manage() +{ + global $context, $txt, $scripturl; + + echo ' +
    + ', $txt['primary_membergroup'], ':
    + (', $txt['moderator_why_missing'], ') +
    +
    + +
    +
    + ', $txt['additional_membergroups'], ': +
    +
    + + '; + // For each membergroup show a checkbox so members can be assigned to more than one group. + foreach ($context['member_groups'] as $member_group) + if ($member_group['can_be_additional']) + echo ' +
    '; + echo ' +
    + + +
    '; + +} + +// Callback function for entering a birthdate! +function template_profile_birthdate() +{ + global $txt, $context; + + // Just show the pretty box! + echo ' +
    + ', $txt['dob'], ':
    + ', $txt['dob_year'], ' - ', $txt['dob_month'], ' - ', $txt['dob_day'], ' +
    +
    + - + - + +
    '; +} + +// Show the signature editing box? +function template_profile_signature_modify() +{ + global $txt, $context, $settings; + + echo ' +
    + ', $txt['signature'], ':
    + ', $txt['sig_info'], '
    +
    '; + + if ($context['show_spellchecking']) + echo ' + '; + + echo ' +
    +
    +
    '; + + // If there is a limit at all! + if (!empty($context['signature_limits']['max_length'])) + echo ' + ', sprintf($txt['max_sig_characters'], $context['signature_limits']['max_length']), ' ', $context['signature_limits']['max_length'], '
    '; + + if ($context['signature_warning']) + echo ' + ', $context['signature_warning'], ''; + + // Load the spell checker? + if ($context['show_spellchecking']) + echo ' + '; + + // Some javascript used to count how many characters have been used so far in the signature. + echo ' + +
    '; +} + +function template_profile_avatar_select() +{ + global $context, $txt, $modSettings; + + // Start with the upper menu + echo ' +
    + ', $txt['personal_picture'], ' +
    + ', !empty($context['member']['avatar']['allow_server_stored']) ? '
    ' : '', ' + ', !empty($context['member']['avatar']['allow_external']) ? '
    ' : '', ' + ', !empty($context['member']['avatar']['allow_upload']) ? '' : '', ' +
    +
    '; + + // If users are allowed to choose avatars stored on the server show selection boxes to choice them from. + if (!empty($context['member']['avatar']['allow_server_stored'])) + { + echo ' +
    +
    + +
    +
    + +
    +
    Do Nothing
    + +
    '; + } + + // If the user can link to an off server avatar, show them a box to input the address. + if (!empty($context['member']['avatar']['allow_external'])) + { + echo ' +
    +
    ', $txt['avatar_by_url'], '
    + +
    '; + } + + // If the user is able to upload avatars to the server show them an upload box. + if (!empty($context['member']['avatar']['allow_upload'])) + { + echo ' +
    + + ', ($context['member']['avatar']['id_attach'] > 0 ? '

    ' : ''), ' +
    '; + } + + echo ' + +
    '; +} + +// Callback for modifying karam. +function template_profile_karma_modify() +{ + global $context, $modSettings, $txt; + + echo ' +
    + ', $modSettings['karmaLabel'], ' +
    +
    + ', $modSettings['karmaApplaudLabel'], ' ', $modSettings['karmaSmiteLabel'], '
    + (', $txt['total'], ': ', ($context['member']['karma']['good'] - $context['member']['karma']['bad']), ') +
    '; +} + +// Select the time format! +function template_profile_timeformat_modify() +{ + global $context, $modSettings, $txt, $scripturl, $settings; + + echo ' +
    + ', $txt['time_format'], ':
    + ', $txt['help'], ' +  ', $txt['date_format'], ' +
    +
    +
    + +
    '; +} + +// Time offset? +function template_profile_timeoffset_modify() +{ + global $txt, $context; + + echo ' +
    + ', $txt['time_offset'], ':
    + ', $txt['personal_time_offset'], ' +
    +
    + ', $txt['timeoffset_autodetect'], '
    ', $txt['current_time'], ': ', $context['current_forum_time'], ' +
    '; +} + +// Theme? +function template_profile_theme_pick() +{ + global $txt, $context, $scripturl; + + echo ' +
    + ', $txt['current_theme'], ': +
    +
    + ', $context['member']['theme']['name'], ' ', $txt['change'], ' +
    '; +} + +// Smiley set picker. +function template_profile_smiley_pick() +{ + global $txt, $context, $modSettings, $settings; + + echo ' +
    + ', $txt['smileys_current'], ': +
    +
    + :) +
    '; +} + +// Change the way you login to the forum. +function template_authentication_method() +{ + global $context, $settings, $options, $scripturl, $modSettings, $txt; + + // The main header! + echo ' + +
    +
    +

    + ', $txt['authentication'], ' +

    +
    +

    ', $txt['change_authentication'], '

    +
    + +
    +
    +
    +  (?)
    + +
    +
    +
    +
    + ', $txt['authenticate_openid_url'], ': +
    +
    + +
    +
    +
    +
    + ', $txt['choose_pass'], ': +
    +
    + + +
    +
    + ', $txt['verify_pass'], ': +
    +
    + + +
    +
    +
    +
    '; + + if ($context['require_password']) + echo ' +
    +
    +
    + ', $txt['current_password'], ':
    + ', $txt['required_security_reasons'], ' +
    +
    + +
    +
    '; + +echo ' +
    + + + + +
    +
    + +
    +
    '; + + // The password stuff. + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Recent.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Recent.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,446 @@ + +
    +

    + ',$txt['recent_posts'],' +

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + foreach ($context['posts'] as $post) + { + echo ' +
    + +
    +
    ', $post['counter'], '
    +
    +
    ', $post['board']['link'], ' / ', $post['link'], '
    + « ', $txt['last_post'], ' ', $txt['by'], ' ', $post['poster']['link'], ' ', $txt['on'], ' ', $post['time'], ' » +
    +
    ', $post['message'], '
    +
    '; + + if ($post['can_reply'] || $post['can_mark_notify'] || $post['can_delete']) + echo ' +
    +
      '; + + // If they *can* reply? + if ($post['can_reply']) + echo ' +
    • ', $txt['reply'], '
    • '; + + // If they *can* quote? + if ($post['can_quote']) + echo ' +
    • ', $txt['quote'], '
    • '; + + // Can we request notification of topics? + if ($post['can_mark_notify']) + echo ' +
    • ', $txt['notify'], '
    • '; + + // How about... even... remove it entirely?! + if ($post['can_delete']) + echo ' +
    • ', $txt['remove'], '
    • '; + + if ($post['can_reply'] || $post['can_mark_notify'] || $post['can_delete']) + echo ' +
    +
    '; + + echo ' + +
    '; + + } + + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    + '; +} + +function template_unread() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' +
    '; + + $showCheckboxes = !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $settings['show_mark_read']; + + if ($showCheckboxes) + echo ' +
    + + + '; + + if ($settings['show_mark_read']) + { + // Generate the button strip. + $mark_read = array( + 'markread' => array('text' => !empty($context['no_board_limits']) ? 'mark_as_read' : 'mark_read_short', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=' . (!empty($context['no_board_limits']) ? 'all' : 'board' . $context['querystring_board_limits']) . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + if ($showCheckboxes) + $mark_read['markselectread'] = array( + 'text' => 'quick_mod_markread', + 'image' => 'markselectedread.gif', + 'lang' => true, + 'url' => 'javascript:document.quickModForm.submit();', + ); + } + + if (!empty($context['topics'])) + { + echo ' +
    '; + + if (!empty($mark_read) && !empty($settings['use_tabs'])) + template_button_strip($mark_read, 'right'); + + echo ' + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + echo ' +
    + + + + + + '; + + // Show a "select all" box for quick moderation? + if ($showCheckboxes) + echo ' + + '; + else + echo ' + '; + echo ' + + + '; + + foreach ($context['topics'] as $topic) + { + // Calculate the color class of the topic. + $color_class = ''; + if (strpos($topic['class'], 'sticky') !== false) + $color_class = 'stickybg'; + if (strpos($topic['class'], 'locked') !== false) + $color_class .= 'lockedbg'; + + $color_class2 = !empty($color_class) ? $color_class . '2' : ''; + + echo ' + + + + + + '; + + if ($showCheckboxes) + echo ' + '; + echo ' + '; + } + + if (!empty($context['topics']) && !$context['showing_all_topics']) + $mark_read['readall'] = array('text' => 'unread_topics_all', 'image' => 'markreadall.gif', 'lang' => true, 'url' => $scripturl . '?action=unread;all' . $context['querystring_board_limits'], 'active' => true); + + if (empty($settings['use_tabs']) && !empty($mark_read)) + echo ' + + + '; + + if (empty($context['topics'])) + echo ' + '; + + echo ' + +
      + ', $txt['subject'], $context['sort_by'] == 'subject' ? ' ' : '', ' + + ', $txt['replies'], $context['sort_by'] == 'replies' ? ' ' : '', ' + + ', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', ' + + + + ', $txt['last_post'], $context['sort_by'] == 'last_post' ? ' ' : '', ' +
    + + + + +
    + ', $topic['is_sticky'] ? '' : '', '', $topic['first_post']['link'], '', $topic['is_sticky'] ? '' : '', ' + ', $txt['new'], ' +

    + ', $txt['started_by'], ' ', $topic['first_post']['member']['link'], ' + ', $txt['in'], ' ', $topic['board']['link'], ' + ', $topic['pages'], ' +

    +
    +
    + ', $topic['replies'], ' ', $txt['replies'], ' +
    + ', $topic['views'], ' ', $txt['views'], ' +
    + ', $txt['last_post'], ' + ', $topic['last_post']['time'], '
    + ', $txt['by'], ' ', $topic['last_post']['member']['link'], ' +
    + +
    + ', template_button_strip($mark_read, 'top'), ' +
    +
    +
    '; + + if (!empty($settings['use_tabs']) && !empty($mark_read)) + template_button_strip($mark_read, 'right'); + + echo ' + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + else + echo ' +
    +

    + ', $context['showing_all_topics'] ? $txt['msg_alert_none'] : $txt['unread_topics_visit_none'], ' +

    +
    '; + + if ($showCheckboxes) + echo ' +
    '; + + echo ' +
    +

    + ', !empty($modSettings['enableParticipation']) ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ', $txt['normal_topic'], '
    + ', sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']), '
    + ', sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']), ' +

    +

    + ', $txt['locked_topic'], '
    ', ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['sticky_topic'] . '
    ' : ''), ($modSettings['pollMode'] == '1' ? ' + ' . $txt['poll'] : ''), ' +

    +
    +
    '; +} + +function template_replies() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' +
    '; + + $showCheckboxes = !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $settings['show_mark_read']; + + if ($showCheckboxes) + echo ' +
    + + + '; + + if (isset($context['topics_to_mark']) && !empty($settings['show_mark_read'])) + { + // Generate the button strip. + $mark_read = array( + 'markread' => array('text' => 'mark_as_read', 'image' => 'markread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=unreadreplies;topics=' . $context['topics_to_mark'] . ';' . $context['session_var'] . '=' . $context['session_id']), + ); + + if ($showCheckboxes) + $mark_read['markselectread'] = array( + 'text' => 'quick_mod_markread', + 'image' => 'markselectedread.gif', + 'lang' => true, + 'url' => 'javascript:document.quickModForm.submit();', + ); + } + + if (!empty($context['topics'])) + { + echo ' +
    '; + + if (!empty($mark_read) && !empty($settings['use_tabs'])) + template_button_strip($mark_read, 'right'); + + echo ' + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + echo ' +
    + + + + + + '; + + // Show a "select all" box for quick moderation? + if ($showCheckboxes) + echo ' + + '; + else + echo ' + '; + echo ' + + + '; + + foreach ($context['topics'] as $topic) + { + // Calculate the color class of the topic. + $color_class = ''; + if (strpos($topic['class'], 'sticky') !== false) + $color_class = 'stickybg'; + if (strpos($topic['class'], 'locked') !== false) + $color_class .= 'lockedbg'; + + $color_class2 = !empty($color_class) ? $color_class . '2' : ''; + + echo ' + + + + + + '; + + if ($showCheckboxes) + echo ' + '; + echo ' + '; + } + + if (empty($settings['use_tabs']) && !empty($mark_read)) + echo ' + + + '; + + echo ' + +
      + ', $txt['subject'], $context['sort_by'] === 'subject' ? ' ' : '', ' + + ', $txt['replies'], $context['sort_by'] === 'replies' ? ' ' : '', ' + + ', $txt['last_post'], $context['sort_by'] === 'last_post' ? ' ' : '', ' + + + + ', $txt['last_post'], $context['sort_by'] === 'last_post' ? ' ' : '', ' +
    + + + + +
    + ', $topic['is_sticky'] ? '' : '', '', $topic['first_post']['link'], '', $topic['is_sticky'] ? '' : '', ' + ', $txt['new'], ' +

    + ', $txt['started_by'], ' ', $topic['first_post']['member']['link'], ' + ', $txt['in'], ' ', $topic['board']['link'], ' + ', $topic['pages'], ' +

    +
    +
    + ', $topic['replies'], ' ', $txt['replies'], ' +
    + ', $topic['views'], ' ', $txt['views'], ' +
    + ', $txt['last_post'], ' + ', $topic['last_post']['time'], '
    + ', $txt['by'], ' ', $topic['last_post']['member']['link'], ' +
    + +
    + ', template_button_strip($mark_read, 'top'), ' +
    +
    +
    '; + + if (!empty($settings['use_tabs']) && !empty($mark_read)) + template_button_strip($mark_read, 'right'); + + echo ' + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + else + echo ' +
    +

    + ', $context['showing_all_topics'] ? $txt['msg_alert_none'] : $txt['unread_topics_visit_none'], ' +

    +
    '; + + if ($showCheckboxes) + echo ' +
    '; + + echo ' +
    +

    + ', !empty($modSettings['enableParticipation']) ? ' + ' . $txt['participation_caption'] . '
    ' : '', ' + ', $txt['normal_topic'], '
    + ', sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']), '
    + ', sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']), ' +

    +

    + ', $txt['locked_topic'], '
    ', ($modSettings['enableStickyTopics'] == '1' ? ' + ' . $txt['sticky_topic'] . '
    ' : '') . ($modSettings['pollMode'] == '1' ? ' + ' . $txt['poll'] : '') . ' +

    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Register.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Register.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,715 @@ + +
    +

    ', $txt['registration_agreement'], '

    +
    + +
    +

    ', $context['agreement'], '

    +
    + +
    '; + + // Age restriction in effect? + if ($context['show_coppa']) + echo ' +

    + '; + else + echo ' + '; + + echo ' +
    + + '; + +} + +// Before registering - get their information. +function template_registration_form() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' + + '; + + // Any errors? + if (!empty($context['registration_errors'])) + { + echo ' +
    + ', $txt['registration_errors_occurred'], ' +
      '; + + // Cycle through each error and display an error message. + foreach ($context['registration_errors'] as $error) + echo ' +
    • ', $error, '
    • '; + + echo ' +
    +
    '; + } + + echo ' +
    +
    +

    ', $txt['registration_form'], '

    +
    +
    +

    ', $txt['required_info'], '

    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    + +
    +
    '; + + // If OpenID is enabled, give the user a choice between password and OpenID. + if (!empty($modSettings['enableOpenID'])) + { + echo ' +
    +
    + ', $txt['authenticate_label'], ': + (?) +
    +
    + + +
    +
    '; + } + + echo ' +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    '; + + // If OpenID is enabled, give the user a choice between password and OpenID. + if (!empty($modSettings['enableOpenID'])) + { + echo ' + +
    +
    ', $txt['authenticate_openid_url'], ':
    +
    + +
    +
    '; + + } + + echo ' +
    + +
    '; + + // If we have either of these, show the extra group. + if (!empty($context['profile_fields']) || !empty($context['custom_fields'])) + { + echo ' +
    +

    ', $txt['additional_information'], '

    +
    +
    + +
    +
    '; + } + + if (!empty($context['profile_fields'])) + { + // Any fields we particularly want? + foreach ($context['profile_fields'] as $key => $field) + { + if ($field['type'] == 'callback') + { + if (isset($field['callback_func']) && function_exists('template_profile_' . $field['callback_func'])) + { + $callback_func = 'template_profile_' . $field['callback_func']; + $callback_func(); + } + } + else + { + echo ' +
    + ', $field['label'], ':'; + + // Does it have any subtext to show? + if (!empty($field['subtext'])) + echo ' + ', $field['subtext'], ''; + + echo ' +
    +
    '; + + // Want to put something infront of the box? + if (!empty($field['preinput'])) + echo ' + ', $field['preinput']; + + // What type of data are we showing? + if ($field['type'] == 'label') + echo ' + ', $field['value']; + + // Maybe it's a text box - very likely! + elseif (in_array($field['type'], array('int', 'float', 'text', 'password'))) + echo ' + '; + + // You "checking" me out? ;) + elseif ($field['type'] == 'check') + echo ' + '; + + // Always fun - select boxes! + elseif ($field['type'] == 'select') + { + echo ' + '; + } + + // Something to end with? + if (!empty($field['postinput'])) + echo ' + ', $field['postinput']; + + echo ' +
    '; + } + } + } + + // Are there any custom fields? + if (!empty($context['custom_fields'])) + { + foreach ($context['custom_fields'] as $field) + echo ' +
    + ', $field['name'], ': + ', $field['desc'], ' +
    +
    ', $field['input_html'], '
    '; + } + + // If we have either of these, close the list like a proper gent. + if (!empty($context['profile_fields']) || !empty($context['custom_fields'])) + { + echo ' +
    +
    + +
    '; + } + + if ($context['visual_verification']) + { + echo ' +
    +

    ', $txt['verification'], '

    +
    +
    + +
    + ', template_control_verification($context['visual_verification_id'], 'all'), ' +
    + +
    '; + } + + echo ' +
    + +
    + +
    + '; +} + +// After registration... all done ;). +function template_after() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Not much to see here, just a quick... "you're now registered!" or what have you. + echo ' +
    +
    +

    ', $context['title'], '

    +
    +
    + +

    ', $context['description'], '

    + +
    +
    '; +} + +// Template for giving instructions about COPPA activation. +function template_coppa() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Formulate a nice complicated message! + echo ' +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +

    ', $context['coppa']['body'], '

    +

    + ', $txt['coppa_form_link_popup'], ' | ', $txt['coppa_form_link_download'], ' +

    +

    ', $context['coppa']['many_options'] ? $txt['coppa_send_to_two_options'] : $txt['coppa_send_to_one_option'], '

    '; + + // Can they send by post? + if (!empty($context['coppa']['post'])) + { + echo ' +

    1) ', $txt['coppa_send_by_post'], '

    +
    + ', $context['coppa']['post'], ' +
    '; + } + + // Can they send by fax?? + if (!empty($context['coppa']['fax'])) + { + echo ' +

    ', !empty($context['coppa']['post']) ? '2' : '1', ') ', $txt['coppa_send_by_fax'], '

    +
    + ', $context['coppa']['fax'], ' +
    '; + } + + // Offer an alternative Phone Number? + if ($context['coppa']['phone']) + { + echo ' +

    ', $context['coppa']['phone'], '

    '; + } + echo ' +
    + +
    '; +} + +// An easily printable form for giving permission to access the forum for a minor. +function template_coppa_form() +{ + global $context, $settings, $options, $txt, $scripturl; + + // Show the form (As best we can) + echo ' + + + + + + + + + + +
    ', $context['forum_contacts'], '
    + ', $txt['coppa_form_address'], ': ', $context['ul'], '
    + ', $context['ul'], '
    + ', $context['ul'], '
    + ', $context['ul'], ' +
    + ', $txt['coppa_form_date'], ': ', $context['ul'], ' +

    +
    + ', $context['coppa_body'], ' +
    +
    '; +} + +// Show a window containing the spoken verification code. +function template_verification_sound() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' + + + + ', $context['page_title'], ' + + + + + + + +'; +} + +function template_admin_register() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    +

    ', $txt['admin_browse_register_new'], '

    +
    +
    + + +
    '; + + if (!empty($context['registration_done'])) + echo ' +
    + ', $context['registration_done'], ' +
    '; + + echo ' +
    +
    + + ', $txt['admin_register_username_desc'], ' +
    +
    + +
    +
    + + ', $txt['admin_register_email_desc'], ' +
    +
    + +
    +
    + + ', $txt['admin_register_password_desc'], ' +
    +
    + +
    '; + + if (!empty($context['member_groups'])) + { + echo ' +
    + + ', $txt['admin_register_group_desc'], ' +
    +
    + +
    '; + } + + echo ' +
    + + ', $txt['admin_register_email_detail_desc'], ' +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + + +
    +
    + + +
    +
    +
    '; +} + +// Form for editing the agreement shown for people registering to the forum. +function template_edit_agreement() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Just a big box to edit the text file ;). + echo ' +
    +

    ', $txt['registration_agreement'], '

    +
    '; + + // Warning for if the file isn't writable. + if (!empty($context['warning'])) + echo ' +

    ', $context['warning'], '

    '; + + echo ' +
    + +
    '; + + // Is there more than one language to choose from? + if (count($context['editable_agreements']) > 1) + { + echo ' +
    +
    + ', $txt['admin_agreement_select_language'], ':  + +
    + + + +
    +
    +
    '; + } + + echo ' +
    '; + + // Show the actual agreement in an oversized text box. + echo ' +

    + +

    +

    + +

    +
    + + + + +
    +
    +
    + +
    +
    '; +} + +function template_edit_reserved_words() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +

    ', $txt['admin_reserved_set'], '

    +
    +
    + +
    +

    ', $txt['admin_reserved_line'], '

    +

    + +

    +
      +
    • +
    • +
    • +
    • +
    +
    + +
    +
    + + + +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Reminder.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Reminder.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,197 @@ + +
    + + +
    '; +} + +function template_reminder_pick() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    + + + +
    '; +} + +function template_sent() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    + '; +} + +function template_set_password() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' + +
    +
    + + + + +
    + '; +} + +function template_ask() +{ + global $context, $settings, $options, $txt, $scripturl, $modSettings; + + echo ' + +
    +
    + + + +
    '; + + if ($context['account_type'] == 'password') + echo ' +'; + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Reports.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Reports.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,255 @@ + +
    +
    +

    ', $txt['generate_reports'], '

    +
    +
    + ', $txt['generate_reports_desc'], ' +
    +
    +

    ', $txt['generate_reports_type'], '

    +
    +
    + +
    +
    '; + + // Go through each type of report they can run. + foreach ($context['report_types'] as $type) + { + echo ' +
    + + +
    '; + if (isset($type['description'])) + echo ' +
    ', $type['description'], '
    '; + } + echo ' +
    +
    + + +
    +
    + +
    +
    + +
    '; +} + +// This is the standard template for showing reports in. +function template_main() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Build the reports button array. + $report_buttons = array( + 'generate_reports' => array('text' => 'generate_reports', 'image' => 'print.gif', 'lang' => true, 'url' => $scripturl . '?action=admin;area=reports', 'active' => true), + 'print' => array('text' => 'print', 'image' => 'print.gif', 'lang' => true, 'url' => $scripturl . '?action=admin;area=reports;rt=' . $context['report_type']. ';st=print', 'custom' => 'target="_blank"'), + ); + + echo ' +
    +
    +

    ', $txt['results'], '

    +
    +
    '; + + if (!empty($report_buttons) && !empty($settings['use_tabs'])) + template_button_strip($report_buttons, 'right'); + + echo ' +
    '; + + // Go through each table! + foreach ($context['tables'] as $table) + { + echo ' + '; + + if (!empty($table['title'])) + echo ' + + + + + + '; + + // Now do each row! + $row_number = 0; + $alternate = false; + foreach ($table['data'] as $row) + { + if ($row_number == 0 && !empty($table['shading']['top'])) + echo ' + '; + else + echo ' + '; + + // Now do each column. + $column_number = 0; + + foreach ($row as $key => $data) + { + // If this is a special separator, skip over! + if (!empty($data['separator']) && $column_number == 0) + { + echo ' + '; + break; + } + + // Shaded? + if ($column_number == 0 && !empty($table['shading']['left'])) + echo ' + '; + else + echo ' + '; + + $column_number++; + } + + echo ' + '; + + $row_number++; + $alternate = !$alternate; + } + echo ' + +
    ', $table['title'], '
    + ', $data['v'], ': + + ', $data['v'] == $table['default_value'] ? '' : ($data['v'] . (empty($data['v']) ? '' : ':')), ' + + ', $data['v'], ' +
    '; + } + echo ' +
    +
    '; +} + +// Header of the print page! +function template_print_above() +{ + global $context, $settings, $options, $txt; + + echo ' + + + + ', $context['page_title'], ' + + + '; +} + +function template_print() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // Go through each table! + foreach ($context['tables'] as $table) + { + echo ' +
    + '; + + if (!empty($table['title'])) + echo ' + + + '; + + // Now do each row! + $alternate = false; + $row_number = 0; + foreach ($table['data'] as $row) + { + if ($row_number == 0 && !empty($table['shading']['top'])) + echo ' + '; + else + echo ' + '; + + // Now do each column!! + $column_number = 0; + foreach ($row as $key => $data) + { + // If this is a special separator, skip over! + if (!empty($data['separator']) && $column_number == 0) + { + echo ' + '; + break; + } + + // Shaded? + if ($column_number == 0 && !empty($table['shading']['left'])) + echo ' + '; + else + echo ' + '; + + $column_number++; + } + + echo ' + '; + + $row_number++; + $alternate = !$alternate; + } + echo ' +
    + ', $table['title'], ' +
    + ', $data['v'], ': + + ', $data['v'] == $table['default_value'] ? '' : ($data['v'] . (empty($data['v']) ? '' : ':')), ' + + ', $data['v'], ' +
    +

    '; + } +} + +// Footer of the print page. +function template_print_below() +{ + global $context, $settings, $options; + + echo ' + + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Search.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Search.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,519 @@ + +
    +

    + ', !empty($settings['use_buttons']) ? '' : ' ', $txt['set_parameters'], ' +

    +
    '; + + if (!empty($context['search_errors'])) + echo ' +

    ', implode('
    ', $context['search_errors']['messages']), '

    '; + + // Simple Search? + if ($context['simple_search']) + { + echo ' + '; + } + + // Advanced search! + else + { + echo ' + '; + + if (empty($context['search_params']['topic'])) + { + echo ' +
    + +
    + + +
    '; + + echo ' +
    + + + +
    +
    +
    + +
    '; + } + + } + + echo ' + + + '; +} + +function template_results() +{ + global $context, $settings, $options, $txt, $scripturl, $message; + + if (isset($context['did_you_mean']) || empty($context['topics'])) + { + echo ' +
    +
    +

    + ', $txt['search_adjust_query'], ' +

    +
    + +
    '; + + // Did they make any typos or mistakes, perhaps? + if (isset($context['did_you_mean'])) + echo ' +

    ', $txt['search_did_you_mean'], ' ', $context['did_you_mean'], '.

    '; + + echo ' +
    + ', $txt['search_for'], ': + + + + + + + + + '; + + if (!empty($context['search_params']['brd'])) + foreach ($context['search_params']['brd'] as $board_id) + echo ' + '; + + echo ' +
    +
    + +

    '; + } + + if ($context['compact']) + { + // Quick moderation set to checkboxes? Oh, how fun :/. + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1) + echo ' +
    '; + + echo ' +
    +

    + '; + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1) + echo ' + '; + echo ' + +  ', $txt['mlist_search_results'],': ',$context['search_params']['search'],' +

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + while ($topic = $context['get_topics']()) + { + $color_class = ''; + if ($topic['is_sticky']) + $color_class = 'stickybg'; + if ($topic['is_locked']) + $color_class .= 'lockedbg'; + + echo ' +
    +
    + +
    '; + + foreach ($topic['matches'] as $message) + { + echo ' +
    +
    ', $message['counter'], '
    +
    ', $topic['board']['link'], ' / ', $message['subject_highlighted'], '
    + « ',$txt['by'],' ', $message['member']['link'], ' ',$txt['on'],' ', $message['time'], ' » +
    '; + + if (!empty($options['display_quick_mod'])) + { + echo ' +
    '; + + if ($options['display_quick_mod'] == 1) + { + echo ' + '; + } + else + { + if ($topic['quick_mod']['remove']) + echo ' + ', $txt['remove_topic'], ''; + + if ($topic['quick_mod']['lock']) + echo ' + ', $txt['set_lock'], ''; + + if ($topic['quick_mod']['lock'] || $topic['quick_mod']['remove']) + echo ' +
    '; + + if ($topic['quick_mod']['sticky']) + echo ' + ', $txt['set_sticky'], ''; + + if ($topic['quick_mod']['move']) + echo ' + ', $txt['move_topic'], ''; + } + + echo ' +
    '; + } + + if ($message['body_highlighted'] != '') + echo ' +
    +
    ', $message['body_highlighted'], '
    '; + } + + echo ' +
    + +
    +
    '; + + } + if (!empty($context['topics'])) + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + { + echo ' +
    +
    + '; + + if ($context['can_move']) + { + echo ' + '; + } + + echo ' + + +
    +
    +
    '; + } + + + if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && !empty($context['topics'])) + echo ' + +
    '; + + } + else + { + echo ' +
    +

    +  ', $txt['mlist_search_results'],': ',$context['search_params']['search'],' +

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + + if (empty($context['topics'])) + echo ' +
    (', $txt['search_no_results'], ')
    '; + + while ($topic = $context['get_topics']()) + { + foreach ($topic['matches'] as $message) + { + echo ' +
    +
    + +
    +
    ', $message['counter'], '
    +
    +
    ', $topic['board']['link'], ' / ', $message['subject_highlighted'], '
    + « ', $txt['message'], ' ', $txt['by'], ' ', $message['member']['link'], ' ', $txt['on'], ' ', $message['time'], ' » +
    +
    ', $message['body_highlighted'], '
    '; + + if ($topic['can_reply'] || $topic['can_mark_notify']) + echo ' +
    +
      '; + + // If they *can* reply? + if ($topic['can_reply']) + echo ' +
    • ', $txt['reply'], '
    • '; + + // If they *can* quote? + if ($topic['can_quote']) + echo ' +
    • ', $txt['quote'], '
    • '; + + // Can we request notification of topics? + if ($topic['can_mark_notify']) + echo ' +
    • ', $txt['notify'], '
    • '; + + if ($topic['can_reply'] || $topic['can_mark_notify']) + echo ' +
    +
    '; + echo ' +
    +
    + +
    +
    '; + } + } + + echo ' +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    '; + } + + // Show a jump to box for easy navigation. + echo ' +
    +
     
    + '; + +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/SendTopic.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/SendTopic.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,280 @@ + +
    +
    +

    + ', $context['page_title'], ' +

    +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    + +
    + +
    '; +} + +// Send an email to a user! +function template_custom_email() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    + ', $context['page_title'], ' +

    +
    +
    + +
    +
    +
    + ', $txt['sendtopic_receiver_name'], ': +
    +
    + ', $context['recipient']['link'], ' +
    '; + + // Can the user see the persons email? + if ($context['can_view_receipient_email']) + echo ' +
    + ', $txt['sendtopic_receiver_email'], ': +
    +
    + ', $context['recipient']['email_link'], ' +
    +
    +
    +
    '; + + // If it's a guest we need their details. + if ($context['user']['is_guest']) + echo ' +
    + +
    +
    + +
    +
    +
    + ', $txt['send_email_disclosed'], ' +
    +
    + + '; + // Otherwise show the user that we know their email. + else + echo ' +
    + ', $txt['sendtopic_sender_email'], ':
    + ', $txt['send_email_disclosed'], ' +
    +
    + ', $context['user']['email'], ' +
    '; + + echo ' +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    + +
    '; + + foreach ($context['form_hidden_vars'] as $key => $value) + echo ' + '; + + echo ' + +
    +
    +
    '; +} + +function template_report() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    + +
    +

    ', $txt['report_to_mod'], '

    +
    +
    + +
    '; + + if (!empty($context['post_errors'])) + { + echo ' +
    +
      '; + + foreach ($context['post_errors'] as $error) + echo ' +
    • ', $error, '
    • '; + + echo ' +
    +
    '; + } + + echo ' +

    ', $txt['report_to_mod_func'], '

    +
    +
    '; + + if ($context['user']['is_guest']) + { + echo ' +
    + : +
    +
    + +
    '; + } + + echo ' +
    + : +
    +
    + +
    '; + + if ($context['require_verification']) + { + echo ' +
    + ', $txt['verification'], ': +
    +
    + ', template_control_verification($context['visual_verification_id'], 'all'), ' +
    '; + } + + echo ' +
    +
    + +
    +
    + +
    + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Settings.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Settings.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,278 @@ + 'show_board_desc', + 'label' => $txt['board_desc_inside'], + 'default' => true, + ), + array( + 'id' => 'show_children', + 'label' => $txt['show_children'], + 'default' => true, + ), + array( + 'id' => 'use_sidebar_menu', + 'label' => $txt['use_sidebar_menu'], + 'default' => true, + ), + array( + 'id' => 'show_no_avatars', + 'label' => $txt['show_no_avatars'], + 'default' => true, + ), + array( + 'id' => 'show_no_signatures', + 'label' => $txt['show_no_signatures'], + 'default' => true, + ), + array( + 'id' => 'show_no_censored', + 'label' => $txt['show_no_censored'], + 'default' => true, + ), + array( + 'id' => 'return_to_post', + 'label' => $txt['return_to_post'], + 'default' => true, + ), + array( + 'id' => 'no_new_reply_warning', + 'label' => $txt['no_new_reply_warning'], + 'default' => true, + ), + array( + 'id' => 'view_newest_first', + 'label' => $txt['recent_posts_at_top'], + 'default' => true, + ), + array( + 'id' => 'view_newest_pm_first', + 'label' => $txt['recent_pms_at_top'], + 'default' => true, + ), + array( + 'id' => 'posts_apply_ignore_list', + 'label' => $txt['posts_apply_ignore_list'], + 'default' => false, + ), + array( + 'id' => 'wysiwyg_default', + 'label' => $txt['wysiwyg_default'], + 'default' => false, + ), + array( + 'id' => 'popup_messages', + 'label' => $txt['popup_messages'], + 'default' => true, + ), + array( + 'id' => 'copy_to_outbox', + 'label' => $txt['copy_to_outbox'], + 'default' => true, + ), + array( + 'id' => 'pm_remove_inbox_label', + 'label' => $txt['pm_remove_inbox_label'], + 'default' => true, + ), + array( + 'id' => 'auto_notify', + 'label' => $txt['auto_notify'], + 'default' => true, + ), + array( + 'id' => 'topics_per_page', + 'label' => $txt['topics_per_page'], + 'options' => array( + 0 => $txt['per_page_default'], + 5 => 5, + 10 => 10, + 25 => 25, + 50 => 50, + ), + 'default' => true, + ), + array( + 'id' => 'messages_per_page', + 'label' => $txt['messages_per_page'], + 'options' => array( + 0 => $txt['per_page_default'], + 5 => 5, + 10 => 10, + 25 => 25, + 50 => 50, + ), + 'default' => true, + ), + array( + 'id' => 'calendar_start_day', + 'label' => $txt['calendar_start_day'], + 'options' => array( + 0 => $txt['days'][0], + 1 => $txt['days'][1], + 6 => $txt['days'][6], + ), + 'default' => true, + ), + array( + 'id' => 'display_quick_reply', + 'label' => $txt['display_quick_reply'], + 'options' => array( + 0 => $txt['display_quick_reply1'], + 1 => $txt['display_quick_reply2'], + 2 => $txt['display_quick_reply3'] + ), + 'default' => true, + ), + array( + 'id' => 'display_quick_mod', + 'label' => $txt['display_quick_mod'], + 'options' => array( + 0 => $txt['display_quick_mod_none'], + 1 => $txt['display_quick_mod_check'], + 2 => $txt['display_quick_mod_image'], + ), + 'default' => true, + ), + ); +} + +function template_settings() +{ + global $context, $settings, $options, $scripturl, $txt; + + $context['theme_settings'] = array( + array( + 'id' => 'header_logo_url', + 'label' => $txt['header_logo_url'], + 'description' => $txt['header_logo_url_desc'], + 'type' => 'text', + ), + array( + 'id' => 'site_slogan', + 'label' => $txt['site_slogan'], + 'description' => $txt['site_slogan_desc'], + 'type' => 'text', + ), + array( + 'id' => 'smiley_sets_default', + 'label' => $txt['smileys_default_set_for_theme'], + 'options' => $context['smiley_sets'], + 'type' => 'text', + ), + array( + 'id' => 'forum_width', + 'label' => $txt['forum_width'], + 'description' => $txt['forum_width_desc'], + 'type' => 'text', + 'size' => 8, + ), + '', + array( + 'id' => 'linktree_link', + 'label' => $txt['current_pos_text_img'], + ), + array( + 'id' => 'show_mark_read', + 'label' => $txt['enable_mark_as_read'], + ), + array( + 'id' => 'allow_no_censored', + 'label' => $txt['allow_no_censored'], + ), + array( + 'id' => 'enable_news', + 'label' => $txt['enable_random_news'], + ), + '', + array( + 'id' => 'show_newsfader', + 'label' => $txt['news_fader'], + ), + array( + 'id' => 'newsfader_time', + 'label' => $txt['admin_fader_delay'], + 'type' => 'number', + ), + array( + 'id' => 'number_recent_posts', + 'label' => $txt['number_recent_posts'], + 'description' => $txt['number_recent_posts_desc'], + 'type' => 'number', + ), + array( + 'id' => 'show_stats_index', + 'label' => $txt['show_stats_index'], + ), + array( + 'id' => 'show_latest_member', + 'label' => $txt['latest_members'], + ), + array( + 'id' => 'show_group_key', + 'label' => $txt['show_group_key'], + ), + array( + 'id' => 'display_who_viewing', + 'label' => $txt['who_display_viewing'], + 'options' => array( + 0 => $txt['who_display_viewing_off'], + 1 => $txt['who_display_viewing_numbers'], + 2 => $txt['who_display_viewing_names'], + ), + 'type' => 'number', + ), + '', + array( + 'id' => 'show_modify', + 'label' => $txt['last_modification'], + ), + array( + 'id' => 'show_profile_buttons', + 'label' => $txt['show_view_profile_button'], + ), + array( + 'id' => 'show_user_images', + 'label' => $txt['user_avatars'], + ), + array( + 'id' => 'show_blurb', + 'label' => $txt['user_text'], + ), + array( + 'id' => 'show_gender', + 'label' => $txt['gender_images'], + ), + array( + 'id' => 'hide_post_group', + 'label' => $txt['hide_post_group'], + 'description' => $txt['hide_post_group_desc'], + ), + '', + array( + 'id' => 'show_bbc', + 'label' => $txt['admin_bbc'], + ), + array( + 'id' => 'additional_options_collapsable', + 'label' => $txt['additional_options_collapsable'], + ), + ); +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/SplitTopics.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/SplitTopics.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,486 @@ + +
    + +
    +

    ', $txt['split'], '

    +
    +
    + +
    +

    + : + +

    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    + +
    + +
    + '; +} + +function template_main() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['split'], '

    +
    +
    + +
    +

    ', $txt['split_successful'], '

    + +
    + +
    +
    '; +} + +function template_select() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +
    +

    ', $txt['split'], ' - ', $txt['select_split_posts'], '

    +
    +
    + ', $txt['please_select_split'], ' +
    +
    + ', $txt['pages'], ': ', $context['not_selected']['page_index'], ' +
    +
      '; + + foreach ($context['not_selected']['messages'] as $message) + echo ' +
    • + +
      +
      + -> + ', $message['subject'], ' ', $txt['by'], ' ', $message['poster'], '
      + ', $message['time'], ' +
      +
      ', $message['body'], '
      +
      + +
    • '; + + echo ' +
    • +
    +
    +
    +
    +

    + ', $txt['split_selected_posts'], ' (', $txt['split_reset_selection'], ') +

    +
    +
    + ', $txt['split_selected_posts_desc'], ' +
    +
    + ', $txt['pages'], ': ', $context['selected']['page_index'], ' +
    +
      '; + + if (!empty($context['selected']['messages'])) + foreach ($context['selected']['messages'] as $message) + echo ' +
    • + +
      +
      + <- + ', $message['subject'], ' ', $txt['by'], ' ', $message['poster'], '
      + ', $message['time'], ' +
      +
      ', $message['body'], '
      +
      + +
    • '; + + echo ' +
    • +
    +
    +
    +

    + + + + +

    +
    +
    +
    + '; +} + +function template_merge_done() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['merge'], '

    +
    +
    + +
    +

    ', $txt['merge_successful'], '

    +
    + +
    + +
    +
    +
    '; +} + +function template_merge() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +

    ', $txt['merge'], '

    +
    +
    + ', $txt['merge_desc'], ' +
    +
    + +
    +
    +
    + ', $txt['topic_to_merge'], ': +
    +
    + ', $context['origin_subject'], ' +
    '; + + if (!empty($context['boards']) && count($context['boards']) > 1) + { + echo ' +
    + ', $txt['target_board'], ': +
    +
    +
    + + + +
    +
    '; + } + + echo ' +
    +
    +
    +
    + ', $txt['merge_to_topic_id'], ': +
    +
    +
    + + + + +
    +
    '; + + echo ' +
    +
    + +

    +
    +

    ', $txt['target_topic'], '

    +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    +
    + +
    +
      '; + + $merge_button = create_button('merge.gif', 'merge', ''); + + foreach ($context['topics'] as $topic) + echo ' +
    • + ', $merge_button, '  + ', $topic['subject'], ' ', $txt['started_by'], ' ', $topic['poster']['link'], ' +
    • '; + + echo ' +
    +
    + +
    +
    + ', $txt['pages'], ': ', $context['page_index'], ' +
    +
    +
    '; +} + +function template_merge_extra_options() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +
    +
    +
    +

    ', $txt['merge_topic_list'], '

    +
    + + + + + + + + + + + '; + foreach ($context['topics'] as $topic) + echo ' + + + + + + + '; + echo ' + +
    ', $txt['merge_check'], '', $txt['subject'], '', $txt['started_by'], '', $txt['last_post'], '' . $txt['merge_include_notifications'] . '
    + + + ' . $topic['subject'] . ' + + ', $topic['started']['link'], '
    + ', $topic['started']['time'], ' +
    + ' . $topic['updated']['link'] . '
    + ', $topic['updated']['time'], ' +
    + +
    +
    +
    + +
    '; + + echo ' +
    + ', $txt['merge_select_subject'], ' + +
    +
    + +
    '; + + if (!empty($context['boards']) && count($context['boards']) > 1) + { + echo ' +
    + ', $txt['merge_select_target_board'], ' +
      '; + foreach ($context['boards'] as $board) + echo ' +
    • + ' . $board['name'] . ' +
    • '; + echo ' +
    +
    '; + } + if (!empty($context['polls'])) + { + echo ' +
    + ' . $txt['merge_select_poll'] . ' +
      '; + foreach ($context['polls'] as $poll) + echo ' +
    • + ' . $poll['question'] . ' (' . $txt['topic'] . ': ' . $poll['topic']['subject'] . ') +
    • '; + echo ' +
    • + (' . $txt['merge_no_poll'] . ') +
    • +
    +
    '; + } + echo ' + + +
    +
    + +
    +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Stats.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Stats.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,473 @@ + +
    +

    ', $context['page_title'], '

    +
    +
    +

    + + ', $txt['general_stats'], ' + +

    +
    +
    +
    +
    + +
    +
    +
    ', $txt['total_members'], ':
    +
    ', $context['show_member_list'] ? '' . $context['num_members'] . '' : $context['num_members'], '
    +
    ', $txt['total_posts'], ':
    +
    ', $context['num_posts'], '
    +
    ', $txt['total_topics'], ':
    +
    ', $context['num_topics'], '
    +
    ', $txt['total_cats'], ':
    +
    ', $context['num_categories'], '
    +
    ', $txt['users_online'], ':
    +
    ', $context['users_online'], '
    +
    ', $txt['most_online'], ':
    +
    ', $context['most_members_online']['number'], ' - ', $context['most_members_online']['date'], '
    +
    ', $txt['users_online_today'], ':
    +
    ', $context['online_today'], '
    '; + + if (!empty($modSettings['hitStats'])) + echo ' +
    ', $txt['num_hits'], ':
    +
    ', $context['num_hits'], '
    '; + + echo ' +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    ', $txt['average_members'], ':
    +
    ', $context['average_members'], '
    +
    ', $txt['average_posts'], ':
    +
    ', $context['average_posts'], '
    +
    ', $txt['average_topics'], ':
    +
    ', $context['average_topics'], '
    +
    ', $txt['total_boards'], ':
    +
    ', $context['num_boards'], '
    +
    ', $txt['latest_member'], ':
    +
    ', $context['common_stats']['latest_member']['link'], '
    +
    ', $txt['average_online'], ':
    +
    ', $context['average_online'], '
    +
    ', $txt['gender_ratio'], ':
    +
    ', $context['gender']['ratio'], '
    '; + + if (!empty($modSettings['hitStats'])) + echo ' +
    ', $txt['average_hits'], ':
    +
    ', $context['average_hits'], '
    '; + + echo ' +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + + ', $txt['top_posters'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_posters'] as $poster) + { + echo ' +
    + ', $poster['link'], ' +
    +
    '; + + if (!empty($poster['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ', $poster['num_posts'], ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +

    + + ', $txt['top_boards'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_boards'] as $board) + { + echo ' +
    + ', $board['link'], ' +
    +
    '; + + if (!empty($board['post_percent'])) + echo ' +
    +
    +
    '; + echo ' + ', $board['num_posts'], ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + + ', $txt['top_topics_replies'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_topics_replies'] as $topic) + { + echo ' +
    + ', $topic['link'], ' +
    +
    '; + if (!empty($topic['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ' . $topic['num_replies'] . ' +
    '; + } + echo ' +
    +
    +
    + +
    +
    + +
    +
    +

    + + ', $txt['top_topics_views'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_topics_views'] as $topic) + { + echo ' +
    ', $topic['link'], '
    +
    '; + + if (!empty($topic['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ' . $topic['num_views'] . ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + + ', $txt['top_starters'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_starters'] as $poster) + { + echo ' +
    + ', $poster['link'], ' +
    +
    '; + + if (!empty($poster['post_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ', $poster['num_topics'], ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +

    + + ', $txt['most_time_online'], ' + +

    +
    +
    + +
    +
    '; + + foreach ($context['top_time_online'] as $poster) + { + echo ' +
    + ', $poster['link'], ' +
    +
    '; + + if (!empty($poster['time_percent'])) + echo ' +
    +
    +
    '; + + echo ' + ', $poster['time_online'], ' +
    '; + } + + echo ' +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + + ', $txt['forum_history'], ' + +

    +
    '; + + if (!empty($context['yearly'])) + { + echo ' + + + + + + + + ', $txt['smf_stats_14'], ''; + + if (!empty($modSettings['hitStats'])) + echo ' + '; + + echo ' + + + '; + + foreach ($context['yearly'] as $id => $year) + { + echo ' + + + + + + '; + + if (!empty($modSettings['hitStats'])) + echo ' + '; + + echo ' + '; + + foreach ($year['months'] as $month) + { + echo ' + + + + + + '; + + if (!empty($modSettings['hitStats'])) + echo ' + '; + + echo ' + '; + + if ($month['expanded']) + { + foreach ($month['days'] as $day) + { + echo ' + + + + + + '; + + if (!empty($modSettings['hitStats'])) + echo ' + '; + + echo ' + '; + } + } + } + } + + echo ' + +
    ', $txt['yearly_summary'], '', $txt['stats_new_topics'], '', $txt['stats_new_posts'], '', $txt['stats_new_members'], '', $txt['page_views'], '
    + * ', $year['year'], ' + ', $year['new_topics'], '', $year['new_posts'], '', $year['new_members'], '', $year['most_members_online'], '', $year['hits'], '
    + ', $month['month'], ' ', $month['year'], ' + ', $month['new_topics'], '', $month['new_posts'], '', $month['new_members'], '', $month['most_members_online'], '', $month['hits'], '
    ', $day['year'], '-', $day['month'], '-', $day['day'], '', $day['new_topics'], '', $day['new_posts'], '', $day['new_members'], '', $day['most_members_online'], '', $day['hits'], '
    +
    + + + '; + } +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Themes.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Themes.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1180 @@ + +
    + +
    +

    + ', $txt['help'], ' + ', $txt['themeadmin_title'], ' + +

    +
    +
    + ', $txt['themeadmin_explain'], ' +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + : +
    +
    +
    '; + foreach ($context['themes'] as $theme) + echo ' +
    '; + + echo ' +
    + + +
    +
    + +
    +
    + + ', $txt['theme_select'], ' +
    +
    + : +
    +
    + + ', $txt['theme_select'], ' +
    +
    +
    + +
    +
    + +
    + +
    '; + + // Link to simplemachines.org for latest themes and info! + echo ' +
    +
    +

    + ', $txt['help'], ' ', $txt['theme_latest'], ' +

    +
    +
    + +
    +
    + ', $txt['theme_latest_fetch'], ' +
    +
    + +
    +
    '; + + // Warn them if theme creation isn't possible! + if (!$context['can_create_new']) + echo ' +
    ', $txt['theme_install_writable'], '
    '; + + echo ' +
    +
    +

    + ', $txt['help'], ' ', $txt['theme_install'], ' +

    +
    +
    + +
    +
    '; + + // Here's a little box for installing a new theme. + // !!! Should the value="theme_gz" be there?! + if ($context['can_create_new']) + echo ' +
    + : +
    +
    + +
    '; + + echo ' +
    + : +
    +
    + +
    '; + + if ($context['can_create_new']) + echo ' +
    + +
    +
    + +
    '; + + echo ' +
    +
    + +
    +
    + +
    + +
    + +
    + + '; + + if (empty($modSettings['disable_smf_js'])) + echo ' + '; + + echo ' + '; + + // Gotta love IE4, and its hatefulness... + if ($context['browser']['is_ie4']) + echo ' + '; + else + echo ' + '; +} + +function template_list_themes() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['themeadmin_list_heading'], '

    +
    +
    + ', $txt['themeadmin_list_tip'], ' +
    '; + + // Show each theme.... with X for delete and a link to settings. + foreach ($context['themes'] as $theme) + { + echo ' +
    +

    + ', $theme['name'], '', !empty($theme['version']) ? ' (' . $theme['version'] . ')' : '', ''; + + // You *cannot* delete the default theme. It's important! + if ($theme['id'] != 1) + echo ' + ', $txt['theme_remove'], ''; + + echo ' +

    +
    +
    + +
    +
    +
    ', $txt['themeadmin_list_theme_dir'], ':
    + ', $theme['theme_dir'], $theme['valid_path'] ? '' : ' ' . $txt['themeadmin_list_invalid'], ' +
    ', $txt['themeadmin_list_theme_url'], ':
    +
    ', $theme['theme_url'], '
    +
    ', $txt['themeadmin_list_images_url'], ':
    +
    ', $theme['images_url'], '
    +
    +
    + +
    '; + } + + echo ' + +
    +
    +

    ', $txt['themeadmin_list_reset'], '

    +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + +
    +
    + + +
    + +
    + +
    +
    +
    '; +} + +function template_reset_list() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['themeadmin_reset_title'], '

    +
    +
    + ', $txt['themeadmin_reset_tip'], ' +
    '; + + // Show each theme.... with X for delete and a link to settings. + $alternate = false; + + foreach ($context['themes'] as $theme) + { + $alternate = !$alternate; + + echo ' +
    +

    ', $theme['name'], '

    +
    +
    + +
    + +
    + +
    '; + } + + echo ' +
    +
    '; +} + +function template_set_options() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    + +
    +

    ', $txt['theme_options_title'], ' - ', $context['theme_settings']['name'], '

    +
    +
    + ', $context['theme_options_reset'] ? $txt['themeadmin_reset_options_info'] : $txt['theme_options_defaults'], ' +
    +
    + +
    +
      '; + + foreach ($context['options'] as $setting) + { + echo ' +
    • '; + + if ($context['theme_options_reset']) + echo ' + '; + + if ($setting['type'] == 'checkbox') + { + echo ' + + '; + } + elseif ($setting['type'] == 'list') + { + echo ' +   + '; + } + else + echo ' +   + '; + + if (isset($setting['description'])) + echo ' +
      ', $setting['description'], ''; + + echo ' +
    • '; + } + + echo ' +
    +
    + + +
    +
    + +
    +
    +
    +
    '; +} + +function template_set_settings() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +
    +

    + ', $txt['help'], ' ', $txt['theme_settings'], ' - ', $context['theme_settings']['name'], ' +

    +
    '; + + // !!! Why can't I edit the default theme popup. + if ($context['theme_settings']['theme_id'] != 1) + echo ' +
    +

    + ', $txt['theme_edit'], ' +

    +
    + '; + + echo ' +
    +

    + ', $txt['theme_url_config'], ' +

    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    '; + + // Do we allow theme variants? + if (!empty($context['theme_variants'])) + { + echo ' +
    +

    + ', $txt['theme_variants'], ' +

    +
    +
    + +
    +
    +
    + : +
    +
    + +
    +
    + : +
    +
    + + +
    +
    + +
    + +
    '; + } + + echo ' +
    +

    + ', $txt['theme_options'], ' +

    +
    +
    + +
    +
    '; + + foreach ($context['settings'] as $setting) + { + // Is this a separator? + if (empty($setting)) + { + echo ' +
    +
    +
    '; + } + // A checkbox? + elseif ($setting['type'] == 'checkbox') + { + echo ' +
    + :'; + + if (isset($setting['description'])) + echo '
    + ', $setting['description'], ''; + + echo ' +
    +
    + + +
    '; + } + // A list with options? + elseif ($setting['type'] == 'list') + { + echo ' +
    + :'; + + if (isset($setting['description'])) + echo '
    + ', $setting['description'], ''; + + echo ' +
    +
    + +
    '; + } + // A regular input box, then? + else + { + echo ' +
    + :'; + + if (isset($setting['description'])) + echo '
    + ', $setting['description'], ''; + + echo ' +
    +
    + +
    '; + } + } + + echo ' +
    +
    + +
    +
    + +
    + +
    +
    +
    '; + + if (!empty($context['theme_variants'])) + { + echo ' + '; + } +} + +// This template allows for the selection of different themes ;). +function template_pick() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    '; + + // Just go through each theme and show its information - thumbnail, etc. + foreach ($context['available_themes'] as $theme) + { + echo ' + +
    + +
    +
    +

    ', $theme['description'], '

    '; + + if (!empty($theme['variants'])) + { + echo ' + : + + '; + } + + echo ' +
    +

    + ', $theme['num_users'], ' ', ($theme['num_users'] == 1 ? $txt['theme_user'] : $txt['theme_users']), ' +

    +
    + +
    + +
    '; + + if (!empty($theme['variants'])) + { + echo ' + '; + } + } + + echo ' +
    +
    +
    '; +} + +// Okay, that theme was installed successfully! +function template_installed() +{ + global $context, $settings, $options, $scripturl, $txt; + + // Not much to show except a link back... + echo ' +
    +
    +

    ', $context['page_title'], '

    +
    +
    + +
    +

    + ', $context['installed_theme']['name'], ' ', $txt['theme_installed_message'], ' +

    +

    + ', $txt['back'], ' +

    +
    + +
    +
    +
    '; +} + +function template_edit_list() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['themeadmin_edit_title'], '

    +
    '; + + $alternate = false; + + foreach ($context['themes'] as $theme) + { + $alternate = !$alternate; + + echo ' +
    +

    + ', $theme['name'], '', !empty($theme['version']) ? ' + (' . $theme['version'] . ')' : '', ' +

    +
    + '; + } + + echo ' +
    +
    '; +} + +function template_copy_template() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    +
    +

    ', $txt['themeadmin_edit_filename'], '

    +
    +
    + ', $txt['themeadmin_edit_copy_warning'], ' +
    +
    + +
    +
      '; + + $alternate = false; + foreach ($context['available_templates'] as $template) + { + $alternate = !$alternate; + + echo ' +
    • + ', $template['filename'], $template['already_exists'] ? ' (' . $txt['themeadmin_edit_exists'] . ')' : '', ' + '; + + if ($template['can_copy']) + echo '', $txt['themeadmin_edit_do_copy'], ''; + else + echo $txt['themeadmin_edit_no_copy']; + + echo ' + +
    • '; + } + + echo ' +
    +
    + +
    +
    +
    '; +} + +function template_edit_browse() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +
    + + + + + + + + + '; + + $alternate = false; + + foreach ($context['theme_files'] as $file) + { + $alternate = !$alternate; + + echo ' + + + + + '; + } + + echo ' + +
    ', $txt['themeadmin_edit_filename'], '', $txt['themeadmin_edit_modified'], '', $txt['themeadmin_edit_size'], '
    '; + + if ($file['is_editable']) + echo '', $file['filename'], ''; + + elseif ($file['is_directory']) + echo '', $file['filename'], ''; + + else + echo $file['filename']; + + echo ' + ', !empty($file['last_modified']) ? $file['last_modified'] : '', '', $file['size'], '
    +
    +
    '; +} + +// Wanna edit the stylesheet? +function template_edit_style() +{ + global $context, $settings, $options, $scripturl, $txt; + + if ($context['session_error']) + echo ' +
    + ', $txt['error_session_timeout'], ' +
    '; + + // From now on no one can complain that editing css is difficult. If you disagree, go to www.w3schools.com. + echo ' +
    + + '; + + // Just show a big box.... gray out the Save button if it's not saveable... (ie. not 777.) + echo ' +
    +
    +

    ', $txt['theme_edit'], ' - ', $context['edit_filename'], '

    +
    +
    + +
    '; + + if (!$context['allow_save']) + echo ' + ', $txt['theme_edit_no_save'], ': ', $context['allow_save_filename'], '
    '; + + echo ' +
    +
    + + +
    +
    + +
    + + +
    +
    +
    '; +} + +// This edits the template... +function template_edit_template() +{ + global $context, $settings, $options, $scripturl, $txt; + + if ($context['session_error']) + echo ' +
    + ', $txt['error_session_timeout'], ' +
    '; + + if (isset($context['parse_error'])) + echo ' +
    + ', $txt['themeadmin_edit_error'], ' +
    ', $context['parse_error'], '
    +
    '; + + // Just show a big box.... gray out the Save button if it's not saveable... (ie. not 777.) + echo ' +
    +
    +
    +

    ', $txt['theme_edit'], ' - ', $context['edit_filename'], '

    +
    +
    + +
    '; + + if (!$context['allow_save']) + echo ' + ', $txt['theme_edit_no_save'], ': ', $context['allow_save_filename'], '
    '; + + foreach ($context['file_parts'] as $part) + echo ' + :
    +
    + +
    '; + + echo ' +
    + + + +
    +
    + +
    +
    +
    '; +} + +function template_edit_file() +{ + global $context, $settings, $options, $scripturl, $txt; + + if ($context['session_error']) + echo ' +
    + ', $txt['error_session_timeout'], ' +
    '; + + //Is this file writeable? + if (!$context['allow_save']) + echo ' +
    + ', $txt['theme_edit_no_save'], ': ', $context['allow_save_filename'], ' +
    '; + + // Just show a big box.... gray out the Save button if it's not saveable... (ie. not 777.) + echo ' +
    +
    +
    +

    ', $txt['theme_edit'], ' - ', $context['edit_filename'], '

    +
    +
    + +
    +
    + + + +
    + +
    + +
    +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Who.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Who.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,227 @@ + +
    +
    +

    ', $txt['who_title'], '

    +
    +
    +
    + '; + echo ' +
    ', $txt['who_show1'], ' + + +
    +
    + + + + + + + + + '; + + // For every member display their name, time and action (and more for admin). + $alternate = 0; + + foreach ($context['members'] as $member) + { + // $alternate will either be true or false. If it's true, use "windowbg2" and otherwise use "windowbg". + echo ' + + + + + '; + + // Switch alternate to whatever it wasn't this time. (true -> false -> true -> false, etc.) + $alternate = !$alternate; + } + + // No members? + if (empty($context['members'])) + { + echo ' + + + '; + } + + echo ' + +
    ', $txt['who_user'], ' ', $context['sort_by'] == 'user' ? '' : '', '', $txt['who_time'], ' ', $context['sort_by'] == 'time' ? '' : '', '', $txt['who_action'], '
    '; + + // Guests don't have information like icq, msn, y!, and aim... and they can't be messaged. + if (!$member['is_guest']) + { + echo ' + + ', $context['can_send_pm'] ? '' : '', $settings['use_image_buttons'] ? '' . $member['online']['text'] . '' : $member['online']['text'], $context['can_send_pm'] ? '' : '', ' + ', isset($context['disabled_fields']['icq']) ? '' : $member['icq']['link'] , ' ', isset($context['disabled_fields']['msn']) ? '' : $member['msn']['link'], ' ', isset($context['disabled_fields']['yim']) ? '' : $member['yim']['link'], ' ', isset($context['disabled_fields']['aim']) ? '' : $member['aim']['link'], ' + '; + } + + echo ' + + ', $member['is_guest'] ? $member['name'] : '' . $member['name'] . '', ' + '; + + if (!empty($member['ip'])) + echo ' + (' . $member['ip'] . ')'; + + echo ' + ', $member['time'], '', $member['action'], '
    + ', $txt['who_no_online_' . ($context['show_by'] == 'guests' || $context['show_by'] == 'spiders' ? $context['show_by'] : 'members')], ' +
    +
    +
    + '; + + echo ' +
    ', $txt['who_show1'], ' + + +
    +
    +
    + '; +} + +function template_credits() +{ + global $context, $txt; + + // The most important part - the credits :P. + echo ' +
    +
    +

    ', $txt['credits'], '

    +
    '; + + foreach ($context['credits'] as $section) + { + if (isset($section['pretext'])) + echo ' +
    + +
    +

    ', $section['pretext'], '

    +
    + +
    '; + + if (isset($section['title'])) + echo ' +
    +

    ', $section['title'], '

    +
    '; + + echo ' +
    + +
    +
    '; + + foreach ($section['groups'] as $group) + { + if (isset($group['title'])) + echo ' +
    + ', $group['title'], ' +
    +
    '; + + // Try to make this read nicely. + if (count($group['members']) <= 2) + echo implode(' ' . $txt['credits_and'] . ' ', $group['members']); + else + { + $last_peep = array_pop($group['members']); + echo implode(', ', $group['members']), ' ', $txt['credits_and'], ' ', $last_peep; + } + + echo ' +
    '; + } + + echo ' +
    '; + + if (isset($section['posttext'])) + echo ' +

    ', $section['posttext'], '

    '; + + echo ' +
    + +
    '; + } + + echo ' +
    +

    ', $txt['credits_copyright'], '

    +
    +
    + +
    +
    +
    ', $txt['credits_forum'], '
    ', ' +
    ', $context['copyrights']['smf']; + + echo ' +
    +
    '; + + if (!empty($context['copyrights']['mods'])) + { + echo ' +
    +
    ', $txt['credits_modifications'], '
    +
    ', implode('
    ', $context['copyrights']['mods']), '
    +
    '; + } + + echo ' +
    + +
    +
    '; +} +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Wireless.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Wireless.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1521 @@ + + + + +'; +} + +// This is the board index (main page) in WAP 1.1. +function template_wap_boardindex() +{ + global $context, $settings, $options, $scripturl; + + // This is the "main" card... + echo ' + +

    ', $context['forum_name_html_safe'], '

    '; + + // Show an anchor for each category. + foreach ($context['categories'] as $category) + { + // Skip it if it's empty. + if (!empty($category['boards'])) + echo ' +

    ', $category['name'], '

    '; + } + + // Okay, that's it for the main card. + echo ' +
    '; + + // Now fill out the deck of cards with the boards in each category. + foreach ($context['categories'] as $category) + { + // Begin the card, and make the name available. + echo ' + +

    ', strip_tags($category['name']), '

    '; + + // Now show a link for each board. + foreach ($category['boards'] as $board) + echo ' +

    ', $board['name'], '

    '; + + echo ' +
    '; + } +} + +// This is the message index (list of topics in a board) for WAP 1.1. +function template_wap_messageindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + +

    ', $context['name'], '

    '; + + if (isset($context['boards']) && count($context['boards']) > 0) + { + foreach ($context['boards'] as $board) + echo ' +

    - ', $board['name'], '

    '; + echo ' +


    '; + } + + if (!empty($context['topics'])) + { + echo ' +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + + foreach ($context['topics'] as $topic) + echo ' +

    ', $topic['first_post']['subject'], '', (!$topic['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), ' - ', $topic['first_post']['member']['name'], '

    '; + + echo ' +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + } + + echo ' +
    '; +} + +function template_wap_display() +{ + global $context, $settings, $options, $txt; + + echo ' + +

    ' . $context['linktree'][1]['name'] . ' > ' . $context['linktree'][count($context['linktree']) - 2]['name'] . '

    +

    ', $context['subject'], '

    +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + + while ($message = $context['get_message']()) + { + // This is a special modification to the post so it will work on phones: + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  32. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo ' +

    ', $message['member']['name'], ':', (!$message['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), '

    +

    ', $message['body'], '

    '; + } + + echo ' +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    + '; +} + +function template_wap_login() +{ + global $context, $modSettings, $scripturl, $txt; + + echo ' + '; + + if (isset($context['login_errors'])) + foreach ($context['login_errors'] as $error) + echo ' +

    ', $error, '

    '; + + echo ' +

    ', $txt['username'], ':
    +

    + +

    ', $txt['password'], ':
    +

    '; + + // Open ID? + if (!empty($modSettings['enableOpenID'])) + echo ' +

    —', $txt['or'], '—

    + +

    ', $txt['openid'], ':
    +

    '; + + echo ' +

    + + + + + +

    +
    '; +} + +function template_wap_recent() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + +

    ', $_REQUEST['action'] == 'unread' ? $txt['wireless_recent_unread_posts'] : $txt['wireless_recent_unread_replies'], '

    '; + + if (empty($context['topics'])) + echo ' +

    ', $txt['old_posts'], '

    '; + else + { + echo ' +

    ', $txt['pages'], ': ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + foreach ($context['topics'] as $topic) + { + echo ' +

    ', $topic['first_post']['subject'], '

    '; + } + } + + echo ' +
    '; +} + +function template_wap_error() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' + +

    ', $context['error_title'], '

    +

    ', $context['error_message'], '

    +

    ', $txt['wireless_error_home'], '

    +
    '; +} + +function template_wap_below() +{ + global $context, $settings, $options, $txt; + + echo ' + +

    + ', $txt['wireless_go_to_full_version'], ' +

    +
    +'; +} + +// The cHTML protocol used for i-mode starts here. +function template_imode_above() +{ + global $context, $settings, $options, $user_info; + + echo ' + + + '; + + // Present a canonical url for search engines to prevent duplicate content in their indices. + if ($user_info['is_guest'] && !empty($context['canonical_url'])) + echo ' + '; + + echo ' + ', $context['page_title'], ' + + '; +} + +function template_imode_boardindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + + '; + $count = 0; + foreach ($context['categories'] as $category) + { + if (!empty($category['boards']) || $category['is_collapsed']) + echo ' + '; + + foreach ($category['boards'] as $board) + { + $count++; + echo ' + '; + } + } + echo ' + '; + if ($context['user']['is_guest']) + echo ' + '; + else + { + if ($context['allow_pm']) + echo ' + '; + echo ' + + + '; + } + echo ' +
    ', $context['forum_name_html_safe'], '
    ', $category['can_collapse'] ? '' : '', $category['name'], $category['can_collapse'] ? '' : '', '
    ', $board['new'] ? '' : '', $count < 10 ? '&#' . (59105 + $count) . ';' : '-', $board['new'] ? '' : ($board['children_new'] ? '.' : ''), ' ', $board['name'], '
    ', $txt['wireless_options'], '
    ', $txt['wireless_options_login'], '
    ', empty($context['user']['unread_messages']) ? $txt['wireless_pm_inbox'] : sprintf($txt['wireless_pm_inbox_new'], $context['user']['unread_messages']), '
    ', $txt['wireless_recent_unread_posts'], '
    ', $txt['wireless_recent_unread_replies'], '
    ', $txt['wireless_options_logout'], '
    '; +} + +function template_imode_messageindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + + '; + + if (!empty($context['boards'])) + { + echo ' + '; + foreach ($context['boards'] as $board) + echo ' + '; + } + + $count = 0; + if (!empty($context['topics'])) + { + echo ' + + '; + foreach ($context['topics'] as $topic) + { + $count++; + echo ' + '; + } + } + echo ' + + ', !empty($context['links']['next']) ? ' + ' : '', !empty($context['links']['prev']) ? ' + ' : '', $context['can_post_new'] ? ' + ' : '', ' +
    ', $context['name'], '
    ', $txt['parent_boards'], '
    ', $board['new'] ? '- ' : ($board['children_new'] ? '-.' : '- '), '', $board['name'], '
    ', $txt['topics'], '
    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '
    ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $topic['first_post']['subject'], '', (!$topic['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), $topic['new'] && $context['user']['is_logged'] ? ' [' . $txt['new'] . ']' : '', '
    ', $txt['wireless_navigation'], '
    ', $txt['wireless_navigation_up'], '
    ' . $txt['wireless_navigation_next'] . '
    [*] ' . $txt['wireless_navigation_prev'] . '
    ' . $txt['start_new_topic'] . '
    '; +} + +function template_imode_display() +{ + global $context, $settings, $options, $scripturl, $board, $txt; + + echo ' + + + + '; + while ($message = $context['get_message']()) + { + // This is a special modification to the post so it will work on phones: + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  33. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo ' +
  34. '; + } + echo ' + + ', $context['user']['is_logged'] ? ' + ' : '', !empty($context['links']['next']) ? ' + ' : '', !empty($context['links']['prev']) ? ' + ' : '', $context['can_reply'] ? ' + ' : ''; + + if (!empty($context['wireless_more']) && empty($context['wireless_moderate'])) + echo ' + '; + elseif (!empty($context['wireless_moderate'])) + { + if ($context['can_sticky']) + echo ' + '; + if ($context['can_lock']) + echo ' + '; + } + + echo ' +
    ' . $context['linktree'][1]['name'] . ' > ' . $context['linktree'][count($context['linktree']) - 2]['name'] . '
    ', $context['subject'], '
    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '
    ', $message['first_new'] ? ' + ' : '', + $context['wireless_moderate'] && $message['member']['id'] ? '' . $message['member']['name'] . '' : '' . $message['member']['name'] . '', ': + ', ((empty($context['wireless_more']) && $message['can_modify']) || !empty($context['wireless_moderate']) ? '[' . $txt['wireless_display_edit'] . ']' : ''), (!$message['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), '
    + ', $message['body'], ' +
    ', $txt['wireless_navigation'], '
    ', $txt['wireless_navigation_index'], '
    ' . $txt['mark_unread'] . '
    ' . $txt['wireless_navigation_next'] . '
    ' . $txt['wireless_navigation_prev'] . '
    ' . $txt['reply'] . '
    ', $txt['wireless_display_moderate'], '
    ', $txt['wireless_display_' . ($context['is_sticky'] ? 'unsticky' : 'sticky')], '
    ', $txt['wireless_display_' . ($context['is_locked'] ? 'unlock' : 'lock')], '
    '; +} + +function template_imode_post() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + // !!! $modSettings['guest_post_no_email'] + echo ' +
    + '; + + if (!$context['becomes_approved']) + echo ' + '; + + if ($context['locked']) + echo ' + '; + + if (isset($context['name']) && isset($context['email'])) + { + echo ' + + '; + + if (empty($modSettings['guest_post_no_email'])) + echo ' + + '; + } + + // !!! Needs a more specific imode template. + if ($context['require_verification']) + echo ' + + '; + + echo ' + + + + + + +
    ' . $txt['wait_for_approval'] . '
    ' . $txt['topic_locked_no_reply'] . '
    ', isset($context['post_error']['long_name']) || isset($context['post_error']['no_name']) ? '' . $txt['username'] . '' : $txt['username'], ':
    ', isset($context['post_error']['no_email']) || isset($context['post_error']['bad_email']) ? '' . $txt['email'] . '' : $txt['email'], ':
    ', !empty($context['post_error']['need_qr_verification']) ? '' . $txt['verification'] . '' : $txt['verification'], ':
    ', template_control_verification($context['visual_verification_id'], 'all'), '
    ', isset($context['post_error']['no_subject']) ? '' . $txt['subject'] . '' : $txt['subject'], ':
    ', isset($context['post_error']['no_message']) || isset($context['post_error']['long_message']) ? '' . $txt['message'] . '' : $txt['message'], ':
    + + + + + ', isset($context['current_topic']) ? ' + ' : '', ' + +
    +  ', !empty($context['current_topic']) ? '' . $txt['wireless_navigation_topic'] . '' : '' . $txt['wireless_navigation_index'] . '', ' +
    +
    '; +} + +function template_imode_login() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    + + '; + if (isset($context['login_errors'])) + foreach ($context['login_errors'] as $error) + echo ' + '; + echo ' + + + + '; + + // Open ID? + if (!empty($modSettings['enableOpenID'])) + echo ' + + + '; + + echo ' + + + +
    ', $txt['login'], '
    ', $error, '
    ', $txt['username'], ':
    ', $txt['password'], ':
    —', $txt['or'], '—
    ', $txt['openid'], ':
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    +
    '; +} + +function template_imode_pm() +{ + global $context, $settings, $options, $scripturl, $txt, $user_info; + + if ($_REQUEST['action'] == 'findmember') + { + echo ' +
    + + + + + '; + if (!empty($context['last_search'])) + { + echo ' + '; + if (empty($context['results'])) + echo ' + '; + else + { + echo ' + '; + $count = 0; + foreach ($context['results'] as $result) + { + $count++; + echo ' + '; + } + } + } + echo ' + + '; + if (!empty($context['results'])) + echo empty($context['links']['next']) ? '' : ' + ', empty($context['links']['prev']) ? '' : ' + '; + echo ' +
    ', $txt['wireless_pm_search_member'], '
    ', $txt['find_members'], '
    + ', $txt['wireless_pm_search_name'], ': + ', empty($_REQUEST['u']) ? '' : ' + ', ' +
    ', $txt['find_results'], '
    [-] ', $txt['find_no_results'], '
    ', empty($context['links']['prev']) ? '' : '<< < ', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', empty($context['links']['next']) ? '' : ' > >> ', '
    + ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $result['name'], ' +
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    [#] ' . $txt['wireless_navigation_next'] . '
    [*] ' . $txt['wireless_navigation_prev'] . '
    +
    '; + } + elseif (!empty($_GET['sa'])) + { + echo ' + '; + if ($_GET['sa'] == 'addbuddy') + { + echo ' + + '; + $count = 0; + foreach ($context['buddies'] as $buddy) + { + $count++; + if ($buddy['selected']) + echo ' + '; + else + echo ' + '; + } + echo ' + + +
    ', $txt['wireless_pm_add_buddy'], '
    ', $txt['wireless_pm_select_buddy'], '
    [-] ', $buddy['name'], '
    + ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $buddy['name'], ' +
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    '; + } + if ($_GET['sa'] == 'send' || $_GET['sa'] == 'send2') + { + echo ' +
    + + ', empty($context['post_error']['messages']) ? '' : ' + ', ' + + + + '; + if ($context['reply']) + echo ' + + + '; + echo ' + + +
    ', $txt['new_message'], '
    ' . implode('
    ', $context['post_error']['messages']) . '
    + ', $txt['pm_to'], ': '; + if (empty($context['recipients']['to'])) + echo $txt['wireless_pm_no_recipients']; + else + { + $to_names = array(); + $ids = array(); + foreach ($context['recipients']['to'] as $to) + { + $ids[] = $to['id']; + $to_names[] = $to['name']; + } + echo implode(', ', $to_names); + $ids = implode(',', $ids); + } + echo ' + ', empty($ids) ? '' : '', '
    + ', $txt['wireless_pm_search_member'], '', empty($user_info['buddies']) ? '' : '
    + ' . $txt['wireless_pm_add_buddy'] . '', ' +
    + ', $txt['subject'], ': +
    + ', $txt['message'], ':
    + +
    + + + + + + + + +
    ', $txt['wireless_pm_reply_to'], '
    ', $context['quoted_message']['subject'], '
    ', $context['quoted_message']['body'], '
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    +
    '; + } + } + elseif (empty($_GET['pmsg'])) + { + echo ' + + + '; + $count = 0; + while ($message = $context['get_pmessage']()) + { + $count++; + echo ' + '; + } + + if ($context['currently_using_labels']) + { + $labels = array(); + ksort($context['labels']); + foreach ($context['labels'] as $label) + $labels[] = '' . $label['name'] . '' . (!empty($label['unread_messages']) ? ' (' . $label['unread_messages'] . ')' : ''); + echo ' + + '; + } + echo ' + + ', empty($context['links']['next']) ? '' : ' + ', empty($context['links']['prev']) ? '' : ' + ', $context['can_send_pm'] ? ' + ' : '', ' +
    ', $context['current_label_id'] == -1 ? $txt['wireless_pm_inbox'] : $txt['pm_current_label'] . ': ' . $context['current_label'], '
    ', empty($context['links']['prev']) ? '' : '<< < ', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', empty($context['links']['next']) ? '' : ' > >> ', '
    + ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $message['subject'], ' ', $txt['wireless_pm_by'], ' ', $message['member']['name'], '', $message['is_unread'] ? ' [' . $txt['new'] . ']' : '', ' +
    ', $txt['pm_labels'], '
    + ', implode(', ', $labels), ' +
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    [#] ' . $txt['wireless_navigation_next'] . '
    [*] ' . $txt['wireless_navigation_prev'] . '
    ' . $txt['new_message'] . '
    '; + } + else + { + $message = $context['get_pmessage'](); + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  35. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo ' + + + + + + '; + if ($context['can_send_pm']) + echo ' + '; + + if ($context['can_send_pm'] && $message['number_recipients'] > 1) + echo ' + '; + + echo ' +
    ', $message['subject'], '
    + ', $txt['wireless_pm_by'], ': ', $message['member']['name'], '
    + ', $txt['on'], ': ', $message['time'], ' +
    + ', $message['body'], ' +
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    ', $txt['wireless_pm_reply'], '
    ', $txt['wireless_pm_reply_all'], '
    '; + } +} + +function template_imode_recent() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + + '; + + $count = 0; + if (empty($context['topics'])) + echo ' + '; + else + { + echo ' + '; + foreach ($context['topics'] as $topic) + { + $count++; + echo ' + '; + } + } + echo ' + + ', !empty($context['links']['next']) ? ' + ' : '', !empty($context['links']['prev']) ? ' + ' : '', ' +
    ', $_REQUEST['action'] == 'unread' ? $txt['wireless_recent_unread_posts'] : $txt['wireless_recent_unread_replies'], '
    ', $txt['old_posts'], '
    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '
    ', $count < 10 ? '&#' . (59105 + $count) . '; ' : '', '', $topic['first_post']['subject'], '
    ', $txt['wireless_navigation'], '
    [0] ', $txt['wireless_navigation_up'], '
    [#] ' . $txt['wireless_navigation_next'] . '
    [*] ' . $txt['wireless_navigation_prev'] . '
    '; +} + +function template_imode_error() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' + + + + +
    ', $context['error_title'], '
    ', $context['error_message'], '
    [0] ', $txt['wireless_error_home'], '
    '; +} + +function template_imode_profile() +{ + global $context, $settings, $options, $scripturl, $board, $txt; + + echo ' + + + + + '; + + if (!empty($context['member']['bans'])) + { + echo ' + '; + } + + echo ' + + '; + + if (!$context['user']['is_owner'] && $context['can_send_pm']) + echo ' + '; + + if (!$context['user']['is_owner'] && !empty($context['can_edit_ban'])) + echo ' + '; + + echo ' + '; + + echo ' +
    ', $txt['summary'], ' - ', $context['member']['name'], '
    + ', $txt['name'], ': ', $context['member']['name'], ' +
    + ', $txt['position'], ': ', (!empty($context['member']['group']) ? $context['member']['group'] : $context['member']['post_group']), ' +
    + ', $txt['lastLoggedIn'], ': ', $context['member']['last_login'], ' +
    + ', $txt['user_banned_by_following'], ':'; + + foreach ($context['member']['bans'] as $ban) + echo ' +
    ', $ban['explanation'], ''; + + echo ' +
    ', $txt['additional_info'], '
    ', $txt['wireless_profile_pm'], '.
    ', $txt['profileBanUser'], '.
    ', $txt['wireless_error_home'], '.
    '; +} + +function template_imode_ban_edit() +{ + global $context, $settings, $options, $scripturl, $board, $txt, $modSettings; + + echo ' +
    + + + + + + + '; + + if (!empty($context['ban_suggestions'])) + { + echo ' + + '; + + if (empty($modSettings['disableHostnameLookup'])) + echo ' + '; + + echo ' + + '; + } + + echo ' + + + '; + + echo ' +
    ', $context['ban']['is_new'] ? $txt['ban_add_new'] : $txt['ban_edit'] . ' \'' . $context['ban']['name'] . '\'', '
    + ', $txt['ban_name'], ': + +
    + ', $txt['ban_expiration'], ':
    + ', $txt['never'], '
    + ', $txt['ban_will_expire_within'], ' ', $txt['ban_days'], '
    + ', $txt['ban_expired'], '
    +
    + ', $txt['ban_reason'], ': + +
    + ', $txt['ban_notes'], ':
    + +
    + ', $txt['ban_restriction'], ':
    + ', $txt['ban_full_ban'], '
    + ', $txt['ban_cannot_post'], '
    + ', $txt['ban_cannot_register'], '
    + ', $txt['ban_cannot_login'], ' +
    ', $txt['ban_triggers'], '
    + ', $txt['wireless_ban_ip'], ':
    +      +
    + ', $txt['wireless_ban_hostname'], ':
    +      +
    + ', $txt['wireless_ban_email'], ':
    +      +
    + ', $txt['ban_on_username'], ':
    '; + + if (empty($context['ban_suggestions']['member']['id'])) + echo ' +     '; + else + echo ' +     ', $context['ban_suggestions']['member']['name'], ' + '; + + echo ' +
    ', $txt['wireless_additional_info'], '
    ', $txt['wireless_error_home'], '.
    + + + +
    '; +} + +function template_imode_below() +{ + global $context, $settings, $options, $txt; + + echo ' +
    ', $txt['wireless_go_to_full_version'], ' + +'; +} + +// XHTMLMP (XHTML Mobile Profile) templates used for WAP 2.0 start here +function template_wap2_above() +{ + global $context, $settings, $options, $user_info; + + echo ' + + + + ', $context['page_title'], ''; + + // Present a canonical url for search engines to prevent duplicate content in their indices. + if ($user_info['is_guest'] && !empty($context['canonical_url'])) + echo ' + '; + + echo ' + + + '; +} + +function template_wap2_boardindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +

    ', $context['forum_name_html_safe'], '

    '; + + $count = 0; + foreach ($context['categories'] as $category) + { + if (!empty($category['boards']) || $category['is_collapsed']) + echo ' +

    ', $category['can_collapse'] ? '' : '', $category['name'], $category['can_collapse'] ? '' : '', '

    '; + + foreach ($category['boards'] as $board) + { + $count++; + echo ' +

    ', $board['new'] ? '' : '', $count < 10 ? '[' . $count . '' : '[-', $board['children_new'] && !$board['new'] ? '' : '', '] ', $board['new'] || $board['children_new'] ? '' : '', '', $board['name'], '

    '; + } + } + + echo ' +

    ', $txt['wireless_options'], '

    '; + if ($context['user']['is_guest']) + echo ' +

    ', $txt['wireless_options_login'], '

    '; + else + { + if ($context['allow_pm']) + echo ' +

    ', empty($context['user']['unread_messages']) ? $txt['wireless_pm_inbox'] : sprintf($txt['wireless_pm_inbox_new'], $context['user']['unread_messages']), '

    '; + echo ' +

    ', $txt['wireless_recent_unread_posts'], '

    +

    ', $txt['wireless_recent_unread_replies'], '

    +

    ', $txt['wireless_options_logout'], '

    '; + } +} + +function template_wap2_messageindex() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +

    ', $context['name'], '

    '; + + if (!empty($context['boards'])) + { + echo ' +

    ', $txt['parent_boards'], '

    '; + foreach ($context['boards'] as $board) + echo ' +

    ', $board['new'] ? '[-] ' : ($board['children_new'] ? '[-] ' : '[-] '), '', $board['name'], '

    '; + } + + $count = 0; + if (!empty($context['topics'])) + { + echo ' +

    ', $txt['topics'], '

    +

    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + foreach ($context['topics'] as $topic) + { + $count++; + echo ' +

    ', $count < 10 ? '[' . $count . '] ' : '', '', $topic['first_post']['subject'], '', (!$topic['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), $topic['new'] && $context['user']['is_logged'] ? ' [' . $txt['new'] . ']' : '', '

    '; + } + } + + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    ', !empty($context['links']['next']) ? ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ' : '', !empty($context['links']['prev']) ? ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    ' : '', $context['can_post_new'] ? ' +

    ' . $txt['start_new_topic'] . '

    ' : ''; +} + +function template_wap2_display() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +

    ' . $context['linktree'][1]['name'] . ' > ' . $context['linktree'][count($context['linktree']) - 2]['name'] . '

    +

    ', $context['subject'], '

    +

    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + $alternate = true; + while ($message = $context['get_message']()) + { + // This is a special modification to the post so it will work on phones: + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  36. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo $message['first_new'] ? ' + ' : '', ' +

    + ', $context['wireless_moderate'] && $message['member']['id'] ? '' . $message['member']['name'] . '' : '' . $message['member']['name'] . '', ': + ', ((empty($context['wireless_more']) && $message['can_modify']) || !empty($context['wireless_moderate']) ? '[' . $txt['wireless_display_edit'] . ']' : ''), (!$message['approved'] ? ' (' . $txt['awaiting_approval'] . ')' : ''), '
    + ', $message['body'], ' +

    '; + $alternate = !$alternate; + } + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_index'], '

    ', $context['user']['is_logged'] ? ' +

    [1] ' . $txt['mark_unread'] . '

    ' : '', !empty($context['links']['next']) ? ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ' : '', !empty($context['links']['prev']) ? ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    ' : '', $context['can_reply'] ? ' +

    ' . $txt['reply'] . '

    ' : ''; + + if (!empty($context['wireless_more']) && empty($context['wireless_moderate'])) + echo ' +

    ', $txt['wireless_display_moderate'], '

    '; + elseif (!empty($context['wireless_moderate'])) + { + if ($context['can_sticky']) + echo ' +

    ', $txt['wireless_display_' . ($context['is_sticky'] ? 'unsticky' : 'sticky')], '

    '; + if ($context['can_lock']) + echo ' +

    ', $txt['wireless_display_' . ($context['is_locked'] ? 'unlock' : 'lock')], '

    '; + } +} + +function template_wap2_login() +{ + global $context, $modSettings, $scripturl, $txt; + + echo ' +
    +

    ', $txt['login'], '

    '; + + if (isset($context['login_errors'])) + foreach ($context['login_errors'] as $error) + echo ' +

    ', $error, '

    '; + + echo ' +

    ', $txt['username'], ':

    +

    +

    ', $txt['password'], ':

    +

    '; + + // Open ID? + if (!empty($modSettings['enableOpenID'])) + echo ' +

    —', $txt['or'], '—

    +

    ', $txt['openid'], ':

    +

    '; + + echo ' +

    +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    +
    '; +} + +function template_wap2_post() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +

    ', $context['page_title'], '

    '; + + if (!$context['becomes_approved']) + echo ' +

    + ' . $txt['wait_for_approval'] . ' + +

    '; + + if ($context['locked']) + echo ' +

    + ' . $txt['topic_locked_no_reply'] . ' +

    '; + + if (isset($context['name']) && isset($context['email'])) + { + echo ' +

    + ' . $txt['username'] . ': +

    '; + + if (empty($modSettings['guest_post_no_email'])) + echo ' +

    + ' . $txt['email'] . ': +

    '; + } + + if ($context['require_verification']) + echo ' +

    + ' . $txt['verification'] . ': ', template_control_verification($context['visual_verification_id'], 'all'), ' +

    '; + + echo ' +

    + ', $txt['subject'], ': +

    +

    + ', $txt['message'], ':
    + +

    +

    + + + + + ', isset($context['current_topic']) ? ' + ' : '', ' + +

    +

    [0] ', !empty($context['current_topic']) ? '' . $txt['wireless_navigation_topic'] . '' : '' . $txt['wireless_navigation_index'] . '', '

    +
    '; +} + +function template_wap2_pm() +{ + global $context, $settings, $options, $scripturl, $txt, $user_info; + + if ($_REQUEST['action'] == 'findmember') + { + echo ' +
    +

    ', $txt['wireless_pm_search_member'], '

    +

    ', $txt['find_members'], '

    +

    + ', $txt['wireless_pm_search_name'], ': + ', empty($_REQUEST['u']) ? '' : ' + ', ' +

    +

    +
    '; + if (!empty($context['last_search'])) + { + echo ' +

    ', $txt['find_results'], '

    '; + if (empty($context['results'])) + echo ' +

    [-] ', $txt['find_no_results'], '

    '; + else + { + echo ' +

    ', empty($context['links']['prev']) ? '' : '<< < ', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', empty($context['links']['next']) ? '' : ' > >> ', '

    '; + $count = 0; + foreach ($context['results'] as $result) + { + $count++; + echo ' +

    + [', $count < 10 ? $count : '-', '] ', $result['name'], ' +

    '; + } + } + } + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    '; + if (!empty($context['results'])) + echo empty($context['links']['next']) ? '' : ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ', empty($context['links']['prev']) ? '' : ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    '; + } + elseif (!empty($_GET['sa'])) + { + if ($_GET['sa'] == 'addbuddy') + { + echo ' +

    ', $txt['wireless_pm_add_buddy'], '

    +

    ', $txt['wireless_pm_select_buddy'], '

    '; + $count = 0; + foreach ($context['buddies'] as $buddy) + { + $count++; + if ($buddy['selected']) + echo ' +

    [-] ', $buddy['name'], '

    '; + else + echo ' +

    + [', $count < 10 ? $count : '-', '] ', $buddy['name'], ' +

    '; + } + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    '; + } + if ($_GET['sa'] == 'send' || $_GET['sa'] == 'send2') + { + echo ' +
    +

    ', $txt['new_message'], '

    ', empty($context['post_error']['messages']) ? '' : ' +

    ' . implode('
    ', $context['post_error']['messages']) . '

    ', ' +

    + ', $txt['pm_to'], ': '; + if (empty($context['recipients']['to'])) + echo $txt['wireless_pm_no_recipients']; + else + { + $to_names = array(); + $ids = array(); + foreach ($context['recipients']['to'] as $to) + { + $ids[] = $to['id']; + $to_names[] = $to['name']; + } + echo implode(', ', $to_names); + $ids = implode(',', $ids); + } + echo ' + ', empty($ids) ? '' : '', '
    + ', $txt['wireless_pm_search_member'], '', empty($user_info['buddies']) ? '' : '
    + ' . $txt['wireless_pm_add_buddy'] . '', ' +

    +

    + ', $txt['subject'], ': +

    +

    + ', $txt['message'], ':
    + +

    +

    + + + + + + + + +

    '; + if ($context['reply']) + echo ' +

    ', $txt['wireless_pm_reply_to'], '

    +

    ', $context['quoted_message']['subject'], '

    +

    ', $context['quoted_message']['body'], '

    '; + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    +
    '; + } + } + elseif (empty($_GET['pmsg'])) + { + echo ' +

    ', $context['current_label_id'] == -1 ? $txt['wireless_pm_inbox'] : $txt['pm_current_label'] . ': ' . $context['current_label'], '

    +

    ', empty($context['links']['prev']) ? '' : '<< < ', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', empty($context['links']['next']) ? '' : ' > >> ', '

    '; + $count = 0; + while ($message = $context['get_pmessage']()) + { + $count++; + echo ' +

    + [', $count < 10 ? $count : '-', '] ', $message['subject'], ' ', $txt['wireless_pm_by'], ' ', $message['member']['name'], '', $message['is_unread'] ? ' [' . $txt['new'] . ']' : '', ' +

    '; + } + + if ($context['currently_using_labels']) + { + $labels = array(); + ksort($context['labels']); + foreach ($context['labels'] as $label) + $labels[] = '' . $label['name'] . '' . (!empty($label['unread_messages']) ? ' (' . $label['unread_messages'] . ')' : ''); + echo ' +

    + ', $txt['pm_labels'], ' +

    +

    + ', implode(', ', $labels), ' +

    '; + } + + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    ', empty($context['links']['next']) ? '' : ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ', empty($context['links']['prev']) ? '' : ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    ', $context['can_send_pm'] ? ' +

    ' . $txt['new_message'] . '

    ' : ''; + } + else + { + $message = $context['get_pmessage'](); + $message['body'] = preg_replace('~
    (.+?)
    ~', '
    --- $1 ---', $message['body']); + $message['body'] = strip_tags(str_replace( + array( + '
    ', + '
    ', + '', + '', + '
  37. ', + $txt['code_select'], + ), + array( + '
    ', + '
    --- ' . $txt['wireless_end_quote'] . ' ---
    ', + '
    ', + '
    --- ' . $txt['wireless_end_code'] . ' ---
    ', + '
    * ', + '', + ), $message['body']), '
    '); + + echo ' +

    ', $message['subject'], '

    +

    + ', $txt['wireless_pm_by'], ': ', $message['member']['name'], '
    + ', $txt['on'], ': ', $message['time'], ' +

    +

    + ', $message['body'], ' +

    +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    '; + if ($context['can_send_pm']) + echo ' +

    ', $txt['wireless_pm_reply'], '

    '; + + if ($context['can_send_pm'] && $message['number_recipients'] > 1) + echo ' +

    ', $txt['wireless_pm_reply_all'], '

    '; + + } +} + +function template_wap2_recent() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' +

    ', $_REQUEST['action'] == 'unread' ? $txt['wireless_recent_unread_posts'] : $txt['wireless_recent_unread_replies'], '

    '; + + $count = 0; + if (empty($context['topics'])) + echo ' +

    ', $txt['old_posts'], '

    '; + else + { + echo ' +

    ', !empty($context['links']['prev']) ? '<< < ' : '', '(', $context['page_info']['current_page'], '/', $context['page_info']['num_pages'], ')', !empty($context['links']['next']) ? ' > >> ' : '', '

    '; + foreach ($context['topics'] as $topic) + { + $count++; + echo ' +

    ', ($count < 10 ? '[' . $count . '] ' : ''), '', $topic['first_post']['subject'], '

    '; + } + } + echo ' +

    ', $txt['wireless_navigation'], '

    +

    [0] ', $txt['wireless_navigation_up'], '

    ', !empty($context['links']['next']) ? ' +

    [#] ' . $txt['wireless_navigation_next'] . '

    ' : '', !empty($context['links']['prev']) ? ' +

    [*] ' . $txt['wireless_navigation_prev'] . '

    ' : ''; +} + +function template_wap2_error() +{ + global $context, $settings, $options, $txt, $scripturl; + + echo ' +

    ', $context['error_title'], '

    +

    ', $context['error_message'], '

    +

    [0] ', $txt['wireless_error_home'], '

    '; +} + +function template_wap2_profile() +{ + global $context, $settings, $options, $scripturl, $board, $txt; + + echo ' +

    ', $txt['summary'], ' - ', $context['member']['name'], '

    +

    ', $txt['name'], ': ', $context['member']['name'], '

    +

    ', $txt['position'], ': ', (!empty($context['member']['group']) ? $context['member']['group'] : $context['member']['post_group']), '

    +

    ', $txt['lastLoggedIn'], ': ', $context['member']['last_login'], '

    '; + + if (!empty($context['member']['bans'])) + { + echo ' +

    ', $txt['user_banned_by_following'], ':

    '; + + foreach ($context['member']['bans'] as $ban) + echo ' +

    ', $ban['explanation'], '

    '; + + } + + echo ' + +

    ', $txt['additional_info'], '

    '; + + if (!$context['user']['is_owner'] && $context['can_send_pm']) + echo ' +

    ', $txt['wireless_profile_pm'], '.

    '; + + if (!$context['user']['is_owner'] && !empty($context['can_edit_ban'])) + echo ' +

    ', $txt['profileBanUser'], '.

    '; + + echo ' +

    ', $txt['wireless_error_home'], '.

    '; + +} + +function template_wap2_ban_edit() +{ + global $context, $settings, $options, $scripturl, $board, $txt, $modSettings; + + echo ' +
    +

    ', $context['ban']['is_new'] ? $txt['ban_add_new'] : $txt['ban_edit'] . ' \'' . $context['ban']['name'] . '\'', '

    +

    + ', $txt['ban_name'], ': + +

    +

    + ', $txt['ban_expiration'], ':
    + ', $txt['never'], '
    + ', $txt['ban_will_expire_within'], ' ', $txt['ban_days'], '
    + ', $txt['ban_expired'], '
    +

    +

    + ', $txt['ban_reason'], ': + +

    +

    + ', $txt['ban_notes'], ':
    + +

    +

    + ', $txt['ban_restriction'], ':
    + ', $txt['ban_full_ban'], '
    + ', $txt['ban_cannot_post'], '
    + ', $txt['ban_cannot_register'], '
    + ', $txt['ban_cannot_login'], ' +

    '; + + if (!empty($context['ban_suggestions'])) + { + echo ' +

    ', $txt['ban_triggers'], '

    +

    + ', $txt['wireless_ban_ip'], ':
    +      +

    '; + + if (empty($modSettings['disableHostnameLookup'])) + echo ' +

    + ', $txt['wireless_ban_hostname'], ':
    +      +

    '; + + echo ' +

    + ', $txt['wireless_ban_email'], ':
    +      +

    +

    + ', $txt['ban_on_username'], ':
    '; + + if (empty($context['ban_suggestions']['member']['id'])) + echo ' +     '; + else + echo ' +     ', $context['ban_suggestions']['member']['name'], ' + '; + + echo ' +

    '; + } + + echo ' + +

    +

    ', $txt['wireless_additional_info'], '

    +

    ', $txt['wireless_error_home'], '.

    '; + + echo ' + + + +
    '; +} + +function template_wap2_below() +{ + global $context, $settings, $options, $txt; + + echo ' + ', $txt['wireless_go_to_full_version'], ' + +'; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/Xml.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/Xml.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,389 @@ + + + ', cleanXml($context['message']), ' +'; +} + +function template_quotefast() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + ', cleanXml($context['quote']['xml']), ' +'; +} + +function template_modifyfast() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + + +'; + +} + +function template_modifydone() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + '; + if (empty($context['message']['errors'])) + { + echo ' + ' . $txt['last_edit'] . ': ' . $context['message']['modified']['time'] . ' ' . $txt['by'] . ' ' . $context['message']['modified']['name'] . ' »'), ']]> + + '; + } + else + echo ' + ', $context['message']['errors']), ']]>'; + echo ' + +'; +} + +function template_modifytopicdone() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + '; + if (empty($context['message']['errors'])) + { + echo ' + ' . $txt['last_edit'] . ': ' . $context['message']['modified']['time'] . ' ' . $txt['by'] . ' ' . $context['message']['modified']['name'] . ' »'), ']]>'; + if (!empty($context['message']['subject'])) + echo ' + '; + } + else + echo ' + ', $context['message']['errors'])), ']]>'; + echo ' + +'; +} + +function template_post() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + + + + + '; + if (!empty($context['post_error']['messages'])) + foreach ($context['post_error']['messages'] as $message) + echo ' + '; + echo ' + + + + + ', isset($context['post_error']['no_message']) || isset($context['post_error']['long_message']) ? ' + ' : '', ' + + ', isset($context['topic_last_message']) ? $context['topic_last_message'] : '0', ''; + + if (!empty($context['previous_posts'])) + { + echo ' + '; + foreach ($context['previous_posts'] as $post) + echo ' + + + + + ', $post['is_ignored'] ? '1' : '0', ' + '; + echo ' + '; + } + + echo ' +'; +} + +function template_stats() +{ + global $context, $settings, $options, $txt, $modSettings; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> +'; + foreach ($context['yearly'] as $year) + foreach ($year['months'] as $month); + { + echo ' + '; + foreach ($month['days'] as $day) + echo ' + '; + echo ' + '; + } + echo ' +'; +} + +function template_split() +{ + global $context, $settings, $options; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + + '; + foreach ($context['changes'] as $change) + { + if ($change['type'] == 'remove') + echo ' + '; + else + echo ' + + + + + + '; + } + echo ' +'; +} + +// This is just to hold off some errors if people are stupid. +if (!function_exists('template_button_strip')) +{ + function template_button_strip($button_strip, $direction = 'top', $strip_options = array()) + { + } + function template_menu() + { + } + function theme_linktree() + { + } +} + +function template_results() +{ + global $context, $settings, $options, $txt; + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> +'; + + if (empty($context['topics'])) + echo ' + ', $txt['search_no_results'], ''; + else + { + echo ' + '; + + while ($topic = $context['get_topics']()) + { + echo ' + + ', $topic['id'], ' + ', $topic['relevance'], ' + + ', $topic['board']['id'], ' + ', cleanXml($topic['board']['name']), ' + ', $topic['board']['href'], ' + + + ', $topic['category']['id'], ' + ', cleanXml($topic['category']['name']), ' + ', $topic['category']['href'], ' + + '; + foreach ($topic['matches'] as $message) + { + echo ' + + ', $message['id'], ' + + + + ', $message['timestamp'], ' + ', $message['start'], ' + + + ', $message['member']['id'], ' + ', cleanXml($message['member']['name']), ' + ', $message['member']['href'], ' + + '; + } + echo ' + + '; + } + + echo ' + '; + } + + echo ' +'; +} + +function template_jump_to() +{ + global $context, $settings, $options; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> +'; + foreach ($context['jump_to'] as $category) + { + echo ' + '; + foreach ($category['boards'] as $board) + echo ' + '; + } + echo ' +'; +} + +function template_message_icons() +{ + global $context, $settings, $options; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> +'; + foreach ($context['icons'] as $icon) + echo ' + '; + echo ' +'; +} + +function template_check_username() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '> + + ', cleanXml($context['checked_username']), ' +'; +} + +// This prints XML in it's most generic form. +function template_generic_xml() +{ + global $context, $settings, $options, $txt; + + echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '>'; + + // Show the data. + template_generic_xml_recursive($context['xml_data'], 'smf', '', -1); +} + +// Recursive function for displaying generic XML data. +function template_generic_xml_recursive($xml_data, $parent_ident, $child_ident, $level) +{ + // This is simply for neat indentation. + $level++; + + echo "\n" . str_repeat("\t", $level), '<', $parent_ident, '>'; + + foreach ($xml_data as $key => $data) + { + // A group? + if (is_array($data) && isset($data['identifier'])) + template_generic_xml_recursive($data['children'], $key, $data['identifier'], $level); + // An item... + elseif (is_array($data) && isset($data['value'])) + { + echo "\n", str_repeat("\t", $level), '<', $child_ident; + + if (!empty($data['attributes'])) + foreach ($data['attributes'] as $k => $v) + echo ' ' . $k . '="' . $v . '"'; + echo '>'; + } + + } + + echo "\n", str_repeat("\t", $level), ''; +} + +function template_webslice_header_above() +{ + global $settings; + + echo ' + '; +} + +function template_webslice_header_below() +{ +} + +// This shows a webslice of the recent posts. +function template_webslice_recent_posts() +{ + global $context, $scripturl, $txt; + + echo ' +
    +
    + ', cleanXml($txt['recent_posts']), ' +
    '; + + $alternate = 0; + foreach ($context['recent_posts_data'] as $item) + { + echo ' +
    + ', cleanXml($item['subject']), ' ', cleanXml($txt['by']), ' ', cleanXml(!empty($item['poster']['link']) ? '' . $item['poster']['name'] . '' : $item['poster']['name']), ' +
    '; + $alternate = !$alternate; + } + + echo ' +
    +
    +
    '; + + if ($context['user']['is_guest']) + echo ' + ', $txt['login'], ''; + else + echo ' + ', cleanXml($context['user']['name']), ', ', cleanXml($txt['msg_alert_you_have']), ' ', cleanXml($context['user']['messages']), ' ', cleanXml($context['user']['messages'] != 1 ? $txt['msg_alert_messages'] : $txt['message_lowercase']), '', cleanXml($txt['newmessages4'] . ' ' . $context['user']['unread_messages']), ' ', cleanXml($context['user']['unread_messages'] == 1 ? $txt['newmessages0'] : $txt['newmessages1']); + + echo ' +
    +
    '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/admin.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/admin.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,627 @@ +/* Styles for the admin quick search. +------------------------------------------------------- */ + +#quick_search form, h3.catbg #quick_search form +{ + padding: 7px; + line-height: 0.9em; + font-size: 0.8em !important; +} + +ol.search_results +{ + margin-top: 0; + padding-top: 0; +} +ol.search_results li +{ + padding-top: 1em; + border-bottom: 1px solid #ccc; +} + +/* Styles for the core features screen. +------------------------------------------------------- */ +.features +{ + padding: 0 1em !important; + overflow: auto; +} +.features_image +{ + float: left; + margin: 0 2em 0.5em 1em; +} +.features_switch +{ + margin: 0.2em 1em 1em 1em; + float: right; +} +.features h4 +{ + padding: 1em 0 0.5em 0.5em; + margin: 0; + font-size: 1.1em; +} +.features p +{ + padding: 0 1em; + margin: 0; +} + +/* Styles for the admin home screen bar. +------------------------------------------------------- */ +#admin_main_section +{ + overflow: hidden; + margin: 1em 0; +} +#admincenter .content +{ + padding: 1em; +} + +#live_news +{ + width: 64%; + font-size: 0.85em; +} +#live_news div.content +{ + padding: 0; +} +#live_news div.content dl +{ + padding: 0.5em 0 0 0.5em; +} + +#supportVersionsTable +{ + width: 34%; +} +#version_details +{ + overflow: auto; + height: 9.5em; +} +#smfAnnouncements +{ + height: 13.5em; + padding: 0 0.5em; + overflow: auto; +} +#smfAnnouncements dt +{ + border-bottom: 1px dashed #000; +} +#smfAnnouncements dd +{ + padding: 0; + margin: 0 0 1em 1.5em; +} +#update_section +{ + margin: 0.5em 0 0; +} + +#quick_tasks, #quick_tasks ul +{ + margin: 0; + padding: 0; +} +#quick_tasks li +{ + float: left; + list-style-type: none; + margin: 0; + padding: 0.5em 0; + width: 49.5%; + height: 4.5em; +} +.quick_task +{ + display: block; + width: 100%; + margin: 0 1em; + padding: 0; +} +.home_image +{ + float: left; + margin: 0 1em 1em 1em; +} + +/* Common admin center classes. +------------------------------------------------------- */ +hr.hrcolor +{ + margin: 10px 0; +} +h3.titlebg form +{ + font-size: 80%; +} +.windowbg.nopadding +{ + margin: 0.3em 0 0 0; + padding: 0; +} +.windowbg ol +{ + margin-top: 0; + margin-bottom: 0; +} + +.table_caption, tr.table_caption td +{ + color: #000; + font-size: 10px; + font-weight: bold; +} +.additional_row div.floatleft +{ + padding: 0 0.8em; +} +fieldset +{ + margin-bottom: 0.5em; + border: 1px solid #cacdd3; + padding: 0.5em; +} +fieldset dl +{ + margin: 0; +} +legend +{ + font-weight: bold; + color: #000; +} +.information a +{ + font-weight: bold; +} + +/* Styles for the package manager. +------------------------------------------------- */ +#package_list .tborder +{ + margin: .25em 0 .25em 26px; +} +#package_list ol, #package_list ol li +{ + list-style: decimal; + margin-left: 50px; + border: none; +} +#package_list ol ul, #package_list ol ul li +{ + margin-left: 0; + list-style: none; +} +#package_list +{ + list-style-type: none; +} +#package_list li +{ + border: 1px solid #cacdd3; + padding: 0.2em; + margin: 1px; +} +.description +{ + max-height: 15em; + overflow: auto; + padding-bottom: .5em; +} +.information +{ + max-height: 15em; + overflow: auto; + padding-bottom: .5em; +} +.package_section +{ + border: 1px solid #cacdd3; +} +ul.packages li +{ + border: none !important; + list-style-type: none; +} +code#find_code, code#replace_code +{ + display: block; + font-family: "dejavu sans mono", "monaco", "lucida console", "courier new", monospace; + font-size: x-small; + background: #eef; + line-height: 1.5em; + padding: 3px 1em; + overflow: auto; + white-space: pre; + /* Show a scrollbar after about 24 lines. */ + max-height: 24em; +} +span.package_server +{ + padding: 0 3em; +} +ul.package_servers +{ + margin: 0; + padding: 0; +} +ul.package_servers li +{ + list-style-type: none; +} +pre.file_content +{ + overflow: auto; + width: 100%; + padding-bottom: 1em; +} +.operation +{ + padding: 0 1em; +} + +/* Styles for the file permissions section. +------------------------------------------------- */ +.filepermissions +{ + font-size: 0.8em; + white-space: nowrap; +} +.fperm +{ + display: block; + width: 35%; + text-align: center; +} +.perm_read +{ + background-color: #d1f7bf; +} +.perm_write +{ + background-color: #ffbbbb; +} +.perm_execute +{ + background-color: #fdd7af; +} +.perm_custom +{ + background-color: #c2c6c0; +} +.perm_nochange +{ + background-color: #eee; +} + +/* Styles for the BBC permissions +------------------------------------------------- */ +.list_bbc +{ + width: 33%; +} + +/* Styles for the manage boards section. +------------------------------------------------- */ +#manage_boards ul +{ + padding: 0; + margin: 0 0 0.6em 0; + max-height: 30em; + overflow: auto; +} +#manage_boards li +{ + list-style-type: none; + border: 1px solid #cacdd3; + padding: 0.2em; + margin: 1px; + clear: right; +} +#manage_boards li img +{ + vertical-align: middle; + padding-bottom: 3px; +} +#manage_boards li#recycle_board +{ + background-color: #dee; +} +.move_links +{ + padding: 0 13px 0 0; +} +.modify_boards +{ + padding: 0 0.5em; +} +#manage_boards span.post_group, #manage_boards span.regular_members +{ + border-bottom: 1px dotted #000; + cursor: help; +} + +/* Styles for the manage members section. +------------------------------------------------- */ +.msearch_details +{ + display: block; + width: 49%; +} +dl.right dt +{ + padding-right: 10px; +} + +/* Styles for the manage maintenance section. +------------------------------------------------- */ +.maintenance_finished, #task_completed +{ + margin: 1ex; + padding: 1ex 2ex; + border: 1px dashed green; + color: green; + background: #efe; +} +/* Styles for the manage calendar section. +------------------------------------------------- */ +dl.settings dt.small_caption +{ + width: 20%; +} +dl.settings dd.small_caption +{ + width: 79%; +} +/* Styles for the manage permissions section. +------------------------------------------------- */ +dl.admin_permissions dt +{ + width: 35%; +} +dl.admin_permissions dd +{ + width: 64%; +} + +/* Styles for the manage search section. +------------------------------------------------- */ +dl.settings dt.large_caption +{ + width: 70%; +} +dl.settings dd.large_caption +{ + width: 29%; +} +span.search_weight +{ + width: 40px; + padding: 0 0.5em; + text-align: right; + display: inline-block; +} +.search_settings +{ + width: 47%; +} + +/* Styles for the manage bans section. +------------------------------------------------- */ +.ban_restriction +{ + margin: 0.2em 0 0.2em 2.2em; +} +.ban_settings +{ + width: 46%; +} +#manage_bans dl +{ + margin-bottom: 1em; +} +#manage_bans fieldset dl.settings +{ + margin-bottom: 0; +} + +/* Styles for the manage subscriptions section. +------------------------------------------------- */ +#fixed_area +{ + width: 97%; +} +ul.pending_payments +{ + margin: 0; + padding: 0; +} +ul.pending_payments li +{ + list-style-type: none; +} + +/* Styles for the manage permissions section. +------------------------------------------------- */ +.perm_name, .perm_profile, .perm_board +{ + display: block; + width: 40%; +} +.perm_boards +{ + padding: 0; + margin: 0 0 0.6em 0; +} +.perm_boards li +{ + list-style-type: none; + border: 1px solid #cacdd3; + padding: 0.2em; + margin: 1px; +} +.perm_groups +{ + background-color: #fff; +} +.perm_classic +{ + margin: 0.2em; +} +.permission_groups +{ + padding: 0; + margin: 0; +} +.permission_groups li +{ + list-style-type: none; + padding: 0.2em; + margin: 1px; +} +.perms +{ + width: 20px; + display: inline-block; + text-align: center; +} + +/* Styles for the themes section. +------------------------------------------------- */ +ul.theme_options +{ + padding: 0; + margin: 0; +} +ul.theme_options li +{ + list-style: none; + padding: 0.4em; +} +.is_directory +{ + padding-left: 18px; + background: url(../images/admin/boards.gif) no-repeat; +} +.edit_file +{ + width: 96%; + font-family: monospace; + margin-top: 1ex; + white-space: pre; +} + +dl.themes_list +{ + margin: 0; +} +dl.themes_list dt +{ + margin-bottom: 3px; +} +dl.themes_list dd +{ + font-style: italic; + white-space: nowrap; +} + +/* Styles for the registration center. +------------------------------------------------- */ +.agreement, .reserved_names +{ + padding: 0; +} +#agreement, #reserved +{ + width: 99%; +} + +/* Styles for the moderation center. +------------------------------------------------- */ +#modcenter +{ + display: block; + width: 100%; +} +.modblock_left +{ + width: 49%; + float: left; + clear: right; + margin: 0 0 1em 0; +} +.modblock_right +{ + width: 49%; + float: right; + margin: 0 0 1em 0; +} + +.modbox +{ + height: 150px; + overflow: auto; +} +/* Moderation Notes */ +ul.moderation_notes +{ + margin: 0; + padding: 0; + list-style: none; + overflow: auto; + height: 8.5em; +} +ul.moderation_notes li +{ + padding: 4px 0 4px 4px; + border-bottom: 1px solid #cccccc; +} +.notes +{ + margin: 0.5em 0; +} +.post_note +{ + width: 85%; +} + +/* Styles for the error log. +------------------------------------------------- */ + +h3.grid_header +{ + height: 25px; +} +#error_log +{ + width: 100%; +} +#error_log tr.windowbg td, #error_log tr.windowbg2 td +{ + padding: 8px; + line-height: 160%; +} +#error_log td.half_width +{ + width: 50%; +} +#error_log td.checkbox_column +{ + width: 15px; + vertical-align: top; + text-align: center; +} +#error_log td div.marginleft +{ + margin: 0 0 0 1ex; +} +#manage_boards span.botslice, #manage_maintenance span.botslice, #manage_mail span.botslice +{ + margin-bottom: 4px; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/compat.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/compat.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,2416 @@ +/************************************************************************************************** + This file will *attempt* to make themes designed for older versions of SMF usable with SMF 2.0. + Unfortunately, the end result will be far from perfect, in most cases. Therefore, we encourage + theme designers to rebase their themes on either the default or core theme. +**************************************************************************************************/ + +/* Styles for the general looks of things +------------------------------------------ */ + +/* Help popups require a different styling of the body element. */ +body#help_popup +{ + width: auto; + padding: 1em; + min-width: 0; +} + +/* The main content area. +------------------------------------------------------- */ +.content, .roundframe +{ + padding: 0.5em 1.2em; + margin: 0; + border: 1px solid #adadad; + color: #000; + background-color: #ecedf3; +} +.content p, .roundframe p +{ + margin: 0 0 0.5em 0; +} +.content fieldset +{ + border: 2px groove #fff; + padding: 1em; + margin: 0 0 0.3em 0; +} + +/* Reset header margins. */ +h1, h2, h3, h4, h5, h6 +{ + font-size: 1em; + margin: 0; + padding: 0; +} + +/* Alternative for u tag */ +.underline +{ + text-decoration: underline; +} + +/* Common classes for easy styling. +------------------------------------------------------- */ + +.floatright +{ + float: right; +} +.floatleft +{ + float: left; +} + +.flow_auto +{ + overflow: auto; +} +.flow_hidden +{ + overflow: hidden; +} +.clear +{ + clear: both; +} +.clear_left +{ + clear: left; +} +.clear_right +{ + clear: right; +} + +/* Default font sizes: small (8pt), normal (10pt), and large (14pt). */ +.smalltext, tr.smalltext th +{ + font-size: 0.85em; + font-family: verdana, sans-serif; +} +.middletext +{ + font-size: 0.9em; + font-family: verdana, sans-serif; +} +.normaltext +{ + font-size: 1em; + line-height: 1.2em; +} +.largetext +{ + font-size: 1.4em; +} +.centertext +{ + margin: 0 auto; + text-align: center; +} +.righttext +{ + margin-left: auto; + margin-right: 0; + text-align: right; +} +.lefttext +{ + margin-left: 0; + margin-right: auto; + text-align: left; +} +/* some common padding styles */ +.padding +{ + padding: 0.7em; +} +.main_section, .lower_padding +{ + padding-bottom: 0.5em; +} +/* a quick reset list class. */ +ul.reset, ul.reset li +{ + padding: 0; + margin: 0; + list-style: none; +} + +/* the page navigation area */ +.pagesection +{ + font-size: 0.9em; + padding: 0.5em; + overflow: hidden; +} +.pagesection .pagelinks +{ + padding: 0.5em 0; +} + +/* GenericList */ +table.table_grid thead tr.catbg th.smalltext +{ + white-space: nowrap; +} + +.custom_fields_above_signature +{ + clear: right; + padding: 1em 0 3px 0; + width: 98%; + border-top: 1px solid #666; + line-height: 1.4em; + font-size: 0.85em; +} + +/* Semantic classes introduced per RC2, used as alternatives for .windowbg and .windowbg2 +------------------------------------------------------------------------------------------ */ +.description +{ + padding: 1em; + font-size: 0.9em; + line-height: 1.5em; + border: 1px solid #bbb; + background: #f5f5f0; + margin: 0 0 1em 0; +} +.information +{ + padding: 0.5em 1em; + font-size: 0.9em; + line-height: 1.5em; + border: 1px solid #bbb; + background: #f0f6f0; + margin: 0 0 1em 0; +} +.information p +{ + padding: 1em; + margin: 0; +} + +/* Lists with settings use these a lot. +------------------------------------------------------- */ +dl.settings +{ + clear: right; + overflow: auto; + margin: 0 0 10px 0; + padding: 0; +} +dl.settings dt +{ + width: 48%; + float: left; + margin: 0 0 10px 0; + padding: 0; + clear: both; +} +dl.settings dt.settings_title +{ + width: 100%; + float: none; + margin: 0 0 10px 0; + padding: 5px 0 0 0; + font-weight: bold; + clear: both; +} +dl.settings dt.windowbg +{ + width: 98%; + float: left; + margin: 0 0 3px 0; + padding: 0 0 5px 0; + clear: both; +} +dl.settings dd +{ + width: 48%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} +dl.settings img +{ + margin: 0 10px 0 0; +} + +/* Styles for a very basic dropdown menu implementation. +------------------------------------------------------- */ +div#admin_menu +{ + margin: 1em 0 0 0; +} + +ul.dropmenu, ul.dropmenu li ul +{ + margin: 0; + padding: 0; + list-style: none; +} +ul.dropmenu +{ + margin: 0 0 0 15px; +} +ul.dropmenu li +{ + position: relative; + float: left; + padding-right: 4px; + text-transform: uppercase; +} +ul.dropmenu li a.firstlevel +{ + margin: 0; + padding: 5px; + cursor: default; + font-size: x-small; + color: #000; + background: #f0f0f0; + border: 1px solid #818181; + text-decoration: none; +} +ul.dropmenu li a.active +{ + padding-left: 3px; +} +ul.dropmenu li a.active.firstlevel +{ + background: #819db5; + color: #fff; +} +ul.dropmenu li ul li +{ + background: none; + width: 14em; + float: none; + margin: 0; + padding: 0; +} +ul.dropmenu li ul +{ + margin: 5px 0 0 0; + z-index: 90; + display: none; + position: absolute; + top: 100%; + border: 1px solid #808080; + background: #f8f8fb; +} +ul.dropmenu li ul li ul, ul.dropmenu li ul li.over ul +{ + display: none; + position: absolute; + left: -999em; + top: 0; + border: 1px solid #a0a0a0; + background: #fff; +} +ul.dropmenu li ul li a +{ + display: block; + padding: 5px; + font-size: x-small; + text-decoration: none; + background: none; + text-transform: none; + color: #000; +} +ul.dropmenu li ul li a.active +{ + font-weight: bold; +} +ul.dropmenu li ul li a:hover, #dropmenu ul li ul li:hover +{ + background: #c8e2fb; +} +ul.dropmenu li:hover ul, ul.dropmenu li.over ul +{ + display: block; +} +ul.dropmenu li ul li:hover ul, ul.dropmenu li ul li.over ul +{ + display: block; + left: 13em; +} + +/* The dropdown menu toggle image */ +#menu_toggle +{ + float: right; + margin-right: 10px; + padding-top: 3px; +} +#menu_toggle span +{ + position: relative; + right: 5000px; +} + +.generic_tab_strip +{ + margin: 0 1em 2em; +} +.generic_tab_strip .buttonlist +{ + float: left !important; +} + + +/* The linktree. +----------------- */ +ul.linktree +{ + clear: both; + list-style: none; + margin: 1.5em 0.5em 0.5em 0.5em; + padding: 0; +} +ul.linktree li +{ + margin: 0; + padding: 0; + display: inline; + font-size: 0.8em; +} +ul.linktree li a +{ + color: #000; +} +ul.linktree li a:hover +{ + color: #cc3333; +} +ul.linktree li span +{ + font-weight: bold; +} + +/* Styles for a typical table. +------------------------------------------------------- */ +table.table_list +{ + width: 100%; +} +table.table_list p +{ + padding: 0; + margin: 0; +} +table.table_list td,table.table_list th +{ + padding: 5px; +} +table.table_list tbody.header td +{ + padding: 0; +} +table.table_list tbody.content td.stats +{ + font-size: 90%; + width: 15%; + text-align: center; +} +table.table_list tbody.content td.lastpost +{ + line-height: 1.2em; + font-size: 85%; + width: 24%; +} +table.table_list tbody.content td.icon +{ + text-align: center; + width: 6%; +} + +/* Styles for headers used in Curve templates. +------------------------------------------------------- */ +h3.catbg, h3.catbg2, h3.titlebg, h4.titlebg, h4.catbg, div.titlebg, .table_list tbody.header td +{ + overflow: hidden; + line-height: 2em; + font-weight: bold; +} +h3.titlebg, h4.titlebg, h3.catbg, h4.catbg +{ + border-left: 1px solid #adadad; + border-right: 1px solid #adadad; +} +h3.titlebg, h4.catbg +{ + padding: 0 0 0 0.5em; +} +h3.catbg img.icon, div.titlebg img.icon, h3.catbg img +{ + float: left; + margin: 5px 8px 0 0; +} +h4.catbg a.toggle img +{ + vertical-align: middle; + margin: -2px 5px 0 5px; +} + +/* Styles for the board index. +------------------------------------------------- */ + +p#stats +{ + text-align: right; +} +h3#newsfader +{ + font-size: 1em; +} +#smfNewsFader +{ + font-weight: bold; + line-height: 1.4em; + padding: 1em; + font-size: 1em; + text-align: center; +} +#upshrink_ic +{ + margin-right: 2ex; + text-align: right; +} +.categoryframe +{ + margin-top: 0.4em; +} +.categoryframe h3 +{ + margin: 0; +} +table.boardsframe +{ + width: 100%; +} +table.boardsframe td.icon +{ + text-align: center; + padding: 0.5em; + width: 6%; +} +table.boardsframe td.info +{ + width: 60%; + padding: 0; +} +table.boardsframe td.info h4 +{ + padding: 0.4em 0.4em 0 0.4em; + margin: 0; +} +table.boardsframe td.info p +{ + padding: 0 0.4em 0.5em 0.4em; + margin: 0; +} +table.boardsframe td.info p.moderators +{ + font-size: 0.8em; + font-family: verdana, sans-serif; +} +table.boardsframe td.stats +{ + width: 8%; + vertical-align: middle; + text-align: center; +} +table.boardsframe td.lastpost +{ + width: 20%; + vertical-align: top; + padding: 0.5em; +} +#posticons +{ + clear: both; + width: 100%; +} +#posticons .buttonlist +{ + margin-right: 1em; + float: right; +} + +/* the newsfader */ +#smfFadeScroller +{ + text-align: center; + overflow: auto; + color: #000000; /* shouldn't be shorthand style due to JS bug in IE! */ +} + +/* Styles for the info center on the board index. +---------------------------------------------------- */ + +#infocenterframe +{ + margin-top: 2em; + clear: both; +} +/* each section in infocenter has this class */ +.infocenter_section +{ + clear: both; +} +.infocenter_section p.section +{ + display: block; + margin: 0; + width: 30px; + text-align: center; + float: left; + padding: 0.5em 0 0 0; +} +.infocenter_section div.sectionbody +{ + margin-left: 30px; + padding: 0.3em; + border-left: 1px solid #a0a0a0; + min-height: 25px; + height: auto !important; +} +/* recent posts - or just one recent post */ +dl#infocenter_recentposts +{ + float: left; + width: 100%; + padding: 0; + margin: 0; +} +dl#infocenter_recentposts dt +{ + clear: left; + float: left; + padding: 0.1em; + width: 68%; + white-space: nowrap; + overflow: hidden; +} +dl#infocenter_recentposts dd +{ + clear: right; + float: right; + padding: 0.1em; + width: 25%; + text-align: right; + white-space: nowrap; + overflow: hidden; +} +/* login form */ +form#infocenter_login ul.horizlist label +{ + white-space: nowrap; + font-size: 90%; + font-weight: bold; +} + +/* Styles for the message (topic) index. +---------------------------------------------------- */ + +#childboards table +{ + width: 100%; +} +.modbuttons +{ + clear: both; + width: 100%; +} +.buttonlist, .buttonlist_bottom +{ + margin-right: 1em; + float: right; +} +#messageindex td.icon1, #messageindex td.icon2 +{ + text-align: center; + padding: 0.5em; + width: 5%; +} +#messageindex td.subject +{ + padding: 0.5em; +} +#messageindex td.starter +{ + text-align: center; + padding: 0.5em; + width: 14%; +} +#messageindex td.replies +{ + text-align: center; + padding: 0.5em; + width: 4%; +} +#messageindex td.views +{ + text-align: center; + padding: 0.5em; + width: 4%; +} +#messageindex td.lastpost +{ + padding: 0.5em; + width: 22%; +} +#messageindex td.moderation +{ + text-align: center; + padding: 0.5em; + width: 4%; +} +#topic_icons p +{ + display: block; + padding: 0.5em 0.5em 0.1em 0.5em; + margin: 0; + border-bottom: none; + font-weight: normal !important; +} +#topic_icons ul +{ + display: block; + padding: 0.5em 1em 0.1em 1em; + margin: 0; + border-bottom: none; + font-weight: normal !important; +} +#message_index_jump_to +{ + margin: 2em 4em 0 2em; +} +.lastpost img +{ + float: right; +} + +/* Styles for the display template (topic view). +---------------------------------------------------- */ +.linked_events +{ + clear: both; + margin: 1em 0; +} +.linked_events .edit_event +{ + color: #f00; +} +#moderationbuttons +{ + margin-left: 0.5em; +} +#postbuttons .nav, #postbuttons_lower .nav +{ + margin: 0.5em 0.5em 0 0; + text-align: right; +} +#postbuttons_lower .nav +{ + margin: 0 0.5em 0.5em 0; +} +#postbuttons, #postbuttons_lower +{ + text-align: right; +} + +/* Poll question */ +h4#pollquestion +{ + padding: 1em 0 1em 2em; +} + +/* Poll vote options */ +#poll_options ul.options +{ + border-top: 1px solid #696969; + padding: 1em 2.5em 0 2em; + margin: 0 0 1em 0; +} +#poll_options div.submitbutton +{ + clear: both; + padding: 0 0 1em 2em; +} + +#poll_options div.submitbutton.border +{ + border-bottom: 1px solid #696969; + margin: 0 0 1em 0; +} + +/* Poll results */ +#poll_options dl.options +{ + border: solid #696969; + border-width: 1px 0; + padding: 1em 2.5em 0 2em; + margin: 0 0 1em 0; +} +#poll_options dl.options dt.voted +{ + font-weight: bold; +} +#poll_options dl.options dd +{ + margin: 0.5em 0 1em 0; +} + +/* Poll notices */ +#poll_options p +{ + margin: 0 1.5em 0.2em 1.5em; + padding: 0 0.5em 0.5em 0.5em; +} + +div#pollmoderation +{ + margin: -1em 0 0 2em; + padding: 0; +} + +.approve_post +{ + margin: 2ex; + padding: 1ex; + border: 2px dashed #cc3344; + color: #000; + font-weight: bold; +} +#forumposts h3.catbg3 +{ + font-weight: normal; + padding: 0.4em; + overflow: hidden; +} +#forumposts h3.catbg3 img +{ + float: left; + vertical-align: middle; +} +#forumposts h3.catbg3 span +{ + float: left; + padding-left: 2%; +} +#forumposts h3.catbg3 span#top_subject +{ + padding-left: 9.5em; +} +.poster +{ + width: 15em; + float: left; +} +.post +{ + clear: right; +} +.postarea +{ + margin-left: 16em; +} +.messageicon +{ + float: left; + margin: 0 0.5em 0.5em 0; +} +.messageicon img +{ + padding: 6px 3px; +} +.keyinfo +{ + float: left; + clear: none; + width: 50%; + min-height: 3em; +} +ul.postingbuttons +{ + float: right; + padding: 0 0.5em 0 0; +} +ul.postingbuttons li +{ + float: left; + margin: 0 0.5em 0 0; +} +.modifybutton +{ + float: right; + margin: 0 0.5em 0.5em 0; +} +.attachments hr +{ + clear: both; + margin: 1em 0 1em 0; +} +.attachments +{ + padding-top: 1em; +} +.postfooter +{ + margin-left: 16em; +} +.topborder +{ + border-top: 1px solid #bbb; +} +.moderatorbar +{ + clear: right; + margin: 1em 0 0 16em; +} +#pollmoderation, #moderationbuttons_strip +{ + float: left; +} + +/* Styles for the quick reply area. +---------------------------------------------------- */ + +#quickReplyOptions #quickReplyWarning +{ + border: none; + text-align: left; + margin: 0; + width: 25%; + float: left; +} +#quickReplyOptions #quickReplyContent +{ + text-align: right; + float: left; + width: 67.5%; + padding: 1em; + border-left: 1px solid #aaa; +} + +#quickReplyOptions #quickReplyContent textarea, #quickReplyOptions #quickReplyContent input +{ + margin-bottom: .5em; +} + +#quickReplyWarning +{ + width: 20%; + float: left; + padding: 0.5em 1em; +} +#quickReplyContent +{ + width: 75%; + float: right; + padding: 0.5em 0; +} +#quickReplyOptions .roundframe +{ + overflow: hidden; +} + +/* The jump to box */ +#display_jump_to +{ + clear: both; + padding: 5px; +} + +/* Separator of posts. More useful in the print stylesheet. */ +#forumposts .post_separator +{ + display: none; +} + +/* Styles for edit post section +---------------------------------------------------- */ +form#postmodify .roundframe +{ + padding: 0 12%; +} +#post_header +{ + margin-bottom: 0.5em; + border-bottom: 1px solid #666; + padding: 0.5em; + overflow: hidden; +} +#post_header dt +{ + float: left; + margin: 0; + padding: 0; + width: 15%; + margin: .3em 0; + font-weight: bold; +} +#post_header dd +{ + float: left; + margin: 0; + padding: 0; + width: 83%; + margin: .3em 0; +} +#post_header img +{ + vertical-align: middle; +} +ul.post_options +{ + margin: 0 0 0 1em; + padding: 0; + list-style: none; + overflow: hidden; +} +ul.post_options li +{ + margin: 0.2em 0; + width: 49%; + float: left; +} +#postAdditionalOptionsHeader +{ + margin-top: 1em; +} +#postMoreOptions +{ + border-bottom: 1px solid #666; + padding: 0.5em; +} +#postAttachment, #postAttachment2 +{ + overflow: hidden; + margin: .5em 0; + padding: 0; + border-bottom: 1px solid #666; + padding: 0.5em; +} +#postAttachment dd, #postAttachment2 dd +{ + margin: .3em 0 .3em 1em; +} +#postAttachment dt, #postAttachment2 dt +{ + font-weight: bold; +} +#postAttachment3 +{ + margin-left: 1em; +} +#post_confirm_strip, #shortcuts +{ + padding: 1em 0 0 0; +} +.post_verification +{ + margin-top: .5em; +} +.post_verification #verification_control +{ + margin: .3em 0 .3em 1em; +} +/* The BBC buttons */ +#bbcBox_message +{ + margin: 1em 0 0.5em 0; +} +#bbcBox_message div +{ + margin: 0.2em 0; + vertical-align: top; +} +#bbcBox_message div img +{ + margin: 0 1px 0 0; + vertical-align: top; +} +#bbcBox_message select +{ + margin: 0 2px; +} +/* The smiley strip */ +#smileyBox_message +{ + margin: 0.75em 0 0.5em 0; +} + +/* Styles for edit event section +---------------------------------------------------- */ +#post_event .roundframe +{ + padding: 1% 12%; +} +#post_event fieldset +{ + margin-bottom: 0.5em; + border: none; + border-bottom: 1px solid #666; + padding: 0.5em; + clear: both; +} +#post_event legend +{ + font-weight: bold; + color: #000; +} +#post_event div.event_options +{ + width: 49%; + float: left; +} +#post_event ul.event_main, ul.event_options +{ + padding: 0; + overflow: hidden; +} +#post_event ul.event_main li +{ + list-style-type: none; + margin: 0.2em 0; + width: 49%; + float: left; +} +#post_event ul.event_options +{ + margin: 0; + padding: 0 0 .7em .7em; +} +#post_event ul.event_options li +{ + list-style-type: none; + margin: 0.3em 0 0 0; +} + +/* Styles for edit poll section. +---------------------------------------------------- */ + +#edit_poll fieldset +{ + margin-bottom: 0.5em; + border: none; + border-bottom: 1px solid #666; + padding: 0.5em; + clear: both; +} +#edit_poll legend +{ + font-weight: bold; + color: #000; +} +#edit_poll ul.poll_main, dl.poll_options +{ + overflow: hidden; + padding: 0 0 0 .7em; + list-style: none; +} +#edit_poll ul.poll_main li +{ + margin: 0.2em 0; +} +#edit_poll dl.poll_options dt +{ + width: 35%; +} +#edit_poll dl.poll_options dd +{ + width: 63%; +} + +/* Styles for the recent messages section. +---------------------------------------------------- */ + +.readbuttons +{ + clear: both; + width: 100%; +} +.buttonlist, .buttonlist_bottom +{ + margin-right: 1em; + float: right; +} + +/* Styles for the move topic section. +---------------------------------------------------- */ + +#move_topic dl +{ + margin-bottom: 0; +} +.move_topic +{ + width: 710px; + margin: auto; + text-align: left; +} +div.move_topic fieldset +{ + margin: 0.5em 0; + border: 1px solid #cacdd3; + padding: 0.5em; +} + +/* Styles for the send topic section. +---------------------------------------------------- */ + +fieldset.send_topic +{ + margin-bottom: 0.5em; + border: none; + padding: 0.5em; +} +dl.send_topic +{ + margin-bottom: 0; +} +dl.send_mail dt +{ + width: 35%; +} +dl.send_mail dd +{ + width: 64%; +} + +/* Styles for the split topic section. +---------------------------------------------------- */ + +div#selected, div#not_selected +{ + width: 49%; +} +ul.split_messages li.windowbg, ul.split_messages li.windowbg2 +{ + border: 1px solid #adadad; + padding: 1em; + margin: 1px; +} +ul.split_messages li a.split_icon +{ + padding: 0 0.5em; +} +ul.split_messages div.post +{ + padding: 1em 0 0 0; + border-top: 1px solid #fff; +} + +/* Styles for the merge topic section. +---------------------------------------------------- */ + +ul.merge_topics li +{ + list-style-type: none; +} +dl.merge_topic dt +{ + width: 25%; +} +dl.merge_topic dd +{ + width: 74%; +} +fieldset.merge_options +{ + margin-bottom: 0.5em; +} +fieldset.merge_options legend +{ + font-weight: bold; +} +.custom_subject +{ + margin: 0.5em 0; +} + +/* Styles for the login areas. +------------------------------------------------------- */ +.login +{ + width: 540px; + margin: 0 auto; +} +.login dl +{ + overflow: auto; + clear: right; +} +.login dt, .login dd +{ + margin: 0 0 0.4em 0; + width: 44%; + padding: 0.1em; +} +.login dt +{ + float: left; + clear: both; + text-align: right; + font-weight: bold; +} +.login dd +{ + width: 54%; + float: right; + text-align: left; +} +.login p +{ + text-align: center; +} +.login h3 img +{ + float: left; + margin: 4px 0.5em 0 0; +} + +/* Styles for the registration section. +------------------------------------------------------- */ +.register_error +{ + border: 1px dashed red; + padding: 5px; + margin: 0 1ex 1ex 1ex; +} +.register_error span +{ + text-decoration: underline; +} + +/* Additional profile fields */ +dl.register_form +{ + margin: 0; + clear: right; + overflow: auto; +} + +dl.register_form dt +{ + font-weight: normal; + float: left; + clear: both; + width: 50%; + margin: 0.5em 0 0 0; +} + +dl.register_form dt strong +{ + font-weight: bold; +} + +dl.register_form dt span +{ + display: block; +} + +dl.register_form dd +{ + float: left; + width: 49%; + margin: 0.5em 0 0 0; +} + +#confirm_buttons +{ + text-align: center; + padding: 1em 0; +} + +.coppa_contact +{ + padding: 4px; + width: 32ex; + background-color: #fff; + color: #000; + margin-left: 5ex; + border: 1px solid #000; +} + +/* Styles for maintenance mode. +------------------------------------------------------- */ +#maintenance_mode +{ + width: 75%; + min-width: 520px; + text-align: left; +} +#maintenance_mode img.floatleft +{ + margin-right: 1em; +} + +/* common for all admin sections */ +h3.titlebg img +{ + vertical-align: middle; + margin-right: 0.5em; +} +tr.titlebg td +{ + padding-left: 0.7em; +} +#admin_menu +{ + min-height: 2em; + padding-left: 0; +} +#admin_content +{ + clear: left; +} +#admin_login .centertext +{ + padding: 1em; +} +#admin_login .centertext .error +{ + padding: 0 0 1em 0; +} + +/* Styles for sidebar menus. +------------------------------------------------------- */ +.left_admmenu, .left_admmenu ul, .left_admmenu li +{ + padding: 0; + margin: 0; + list-style: none; +} +#left_admsection +{ + background-color: #ecedf3; + padding: 1px; + border: 1px solid #ADADAD; + width: 160px; + float: left; + margin-right: 10px; +} +.adm_section h4.titlebg +{ + font-size: 95%; + margin-bottom: 5px; +} +.left_admmenu li +{ + padding: 0 0 0 0.5em; +} +.left_admmenu +{ + margin-bottom: 1.1em; +} +#main_admsection +{ + margin-left: 174px; +} + +tr.windowbg td, tr.windowbg2 td +{ + padding: 0.3em 0.7em; +} +#credits p +{ + padding: 0; + font-style: italic; + margin: 0; +} + +/* Styles for generic tables. +------------------------------------------------------- */ +.topic_table table +{ + width: 100%; +} +.topic_table .icon1, .topic_table .icon2, .topic_table .stats +{ + text-align: center; +} +#topic_icons +{ + margin-top: 1em; +} +#topic_icons .description +{ + margin: 0; +} +.topic_table table thead +{ + border-bottom: 1px solid #fff; +} +/* the subject column */ +.topic_table td +{ + font-size: 1em; +} +.topic_table td.subject +{ + padding: 4px; +} +.topic_table td.subject p, .topic_table td.stats, .topic_table td.lastpost +{ + font-size: 0.85em; + padding: 0; + margin: 0; +} +.topic_table td.lastpost, .topic_table td.lastpost +{ + font-size: 0.9em; + line-height: 100%; + padding: 4px; +} +.topic_table td.stickybg2 +{ + background-image: url(../images/icons/quick_sticky.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lockedbg2 +{ + background-image: url(../images/icons/quick_lock.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lastpost +{ + background-image: none; +} + +/* Styles for (fatal) errors. +------------------------------------------------- */ + +#fatal_error +{ + border: 1px solid #aaa; +} + +.errorbox +{ + padding: 1em; + border: 1px solid #cc3344; + color: #000; + background-color: #ffe4e9; + margin: 1em 0; +} +.errorbox h3 +{ + padding: 0; + margin: 0; + font-size: 1.1em; + text-decoration: underline; +} +.errorbox p +{ + margin: 1em 0 0 0; +} +.errorbox p.alert +{ + padding: 0; + margin: 0; + float: left; + width: 1em; + font-size: 1.5em; +} + +/* Styles for the profile section. +------------------------------------------------- */ + +dl +{ + overflow: auto; + margin: 0; + padding: 0; +} + +/* Fixes for the core theme */ +#profileview +{ + padding: 1px; + border: 1px solid #696969; + background-color: #ecedf3; +} +#profileview .content +{ + border: none; +} +#basicinfo .content +{ + padding: 1em; +} +#detailedinfo .content +{ + padding: 0.7em 1.2em; + border-left: 1px solid #aaa; +} + +/* The basic user info on the left */ +#basicinfo +{ + width: 20%; + float: left; +} +#detailedinfo +{ + width: 78%; + float: right; +} +#basicinfo h4 +{ + font-size: 135%; + font-weight: 100; + line-height: 105%; + white-space: pre-wrap; /* css-2.1 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + overflow: hidden; +} +#basicinfo h4 span.position +{ + font-size: 80%; + font-weight: 100; + display: block; +} +#basicinfo img.avatar +{ + display: block; + margin: 10px 0 0 0; +} +#basicinfo ul +{ + list-style-type: none; + margin: 10px 0 0 0; +} +#basicinfo ul li +{ + display: block; + float: left; + margin-right: 5px; + height: 20px; +} +#basicinfo span#userstatus +{ + display: block; + clear: both; +} +#basicinfo span#userstatus img +{ + vertical-align: middle; +} +#detailedinfo div.content dl, #tracking div.content dl +{ + clear: right; + overflow: auto; + margin: 0 0 18px 0; + padding: 0 0 15px 0; + border-bottom: 1px solid #ccc; +} +#detailedinfo div.content dt, #tracking div.content dt +{ + width: 30%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#detailedinfo div.content dd, #tracking div.content dd +{ + width: 70%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} +#detailedinfo div.content dl.noborder +{ + border-bottom: 0; +} +#detailedinfo div.content dt.clear +{ + width: 100%; +} +.signature, .custom_fields_above_signature, .attachments +{ + width: 98%; + overflow: auto; + clear: right; + border-top: 1px solid #666; +} +.signature h5 +{ + font-size: 100%; + margin-bottom: 10px; +} +#personal_picture +{ + display: block; + margin-bottom: 0.3em; +} +#avatar_server_stored div +{ + float: left; +} + +#main_admsection #basicinfo, #main_admsection #detailedinfo +{ + width: 100%; +} +#main_admsection #detailedinfo .content +{ + border: none !important; +} +#main_admsection #basicinfo +{ + border-bottom: 1px solid #ccc; +} +#main_admsection #basicinfo h4 +{ + float: left; +} +#main_admsection #basicinfo img.avatar +{ + float: right; + vertical-align: top; +} +#main_admsection #basicinfo ul +{ + clear: left; + padding-top: 10px; +} +#main_admsection #basicinfo span#userstatus +{ + clear: left; +} +#main_admsection #basicinfo p#infolinks +{ + display: none; + clear: both; +} +#main_admsection #basicinfo .botslice +{ + clear: both; +} + +/* Simple feedback messages */ +div#profile_error, div#profile_success +{ + margin: 0 0 1em 0; + padding: 1em 2em; + border: 1px solid; +} +div#profile_error +{ + border-color: red; + color: red; + background: #fee; +} + +div#profile_error span +{ + text-decoration: underline; +} + +div#profile_success +{ + border-color: green; + color: green; + background: #efe; +} + +/* Profile statistics */ +#generalstats div.content dt +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#generalstats div.content dd +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +/* Activity by time */ +.activity_stats +{ + margin: 0; + padding: 0; + list-style: none; +} +.activity_stats li +{ + width: 4.16%; + float: left; +} +.activity_stats li span +{ + display: block; + border: solid #000; + border-width: 1px 1px 0 0; + text-align: center; +} +.activity_stats li.last span +{ + border-right: none; +} +.activity_stats li div.bar +{ + margin: 0 auto; + width: 15px; +} +.activity_stats li div.bar div +{ + background: #6294CE; +} +.activity_stats li div.bar span +{ + position: absolute; + top: -1000em; + left: -1000em; +} + +/* Most popular boards by posts and activity */ +#popularposts +{ + width: 50%; + float: left; +} +#popularactivity +{ + width: 50%; + float: right; +} + +#popularposts div.content dt, #popularactivity div.content dt +{ + width: 65%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#popularposts div.content dd, #popularactivity div.content dd +{ + width: 35%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +.profile_pie +{ + background-image: url(../images/stats_pie.png); + float: left; + height: 20px; + width: 20px; + margin: 0 1em 0 0; + padding: 0; + text-indent: -1000em; +} + +/* View posts */ +.time +{ + float: right; +} +.counter +{ + margin: 0 0 0 0; + padding: 0.2em 0.5em 0.1em 0.2em; + font-size: 2.2em; + font-weight: bold; + color: #354c5f; + float: left; +} +.list_posts +{ + border-top: 1px solid #adadad; + padding-top: 1em; + margin-top: 0.5em; +} +div.core_posts +{ + border: 1px solid #adadad; + margin-bottom: 3px; +} +div.core_posts div.content +{ + background: none; + border: none; +} +.topic h4 +{ + margin: 3px 0; +} + +.mod_icons +{ + text-align: right; + margin-right: 1em; +} + +#permissions dt +{ + width: 48%; + float: left; + line-height: 1.2em; + margin: 0; + padding: 1%; + clear: both; + border-top: 1px solid #fff; +} + +#permissions dd +{ + width: 48%; + float: left; + margin: 0; + padding: 1%; + border-top: 1px solid #fff; +} + +#tracking div.content dl +{ + border-bottom: 0; + margin: 0; + padding: 0; +} + +#creator dl +{ + margin: 0; +} +#creator dt +{ + width: 40%; + float: left; + clear: both; + margin: 0 0 10px 0; +} +#creator dd +{ + float: left; + width: 60%; + margin: 0 0 10px 0; +} + +.ignoreboards +{ + margin: 0; + padding: 0; + width: 49%; + overflow: auto; +} +.ignoreboards a +{ + text-decoration: underline; +} +.ignoreboards ul +{ + overflow: auto; + margin: 0 0 0 1em; + padding: 0; +} +.ignoreboards li +{ + list-style: none; + float: left; + clear: both; +} + +#theme_settings +{ + overflow: auto; + margin: 0; + padding: 0; +} + +#theme_settings li +{ + list-style: none; + margin: 10px 0; + padding: 0; +} +/*Paid Subscriptions*/ +#paid_subscription +{ + width: 100%; +} +#paid_subscription dl.settings +{ + margin-bottom: 0; +} +#paid_subscription dl.settings dd, #paid_subscription dl.settings dt +{ + margin-bottom: 4px; +} +/*pick theme*/ +#pick_theme +{ + width: 100%; + float: left; +} + +/* Styles for the statistics center. +------------------------------------------------- */ +#statistics +{ + padding-bottom: 0.5em; +} +#statistics h4.titlebg +{ + text-align: center; + margin-bottom: 5px; +} +#stats_left, #top_posters, #top_topics_replies, #top_topics_starter +{ + float: left; + width: 49.5%; +} +#stats_right, #top_boards, #top_topics_views, #most_online +{ + float: right; + width: 49.5%; +} +dl.stats +{ + clear: both; + overflow: hidden; + margin: 0; + padding: 0; +} +dl.stats dt +{ + width: 49%; + float: left; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; + clear: both; + font-size: 1em; +} +dl.stats dd +{ + text-align: right; + width: 50%; + font-size: 1em; + float: right; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; +} +.stats_bar +{ + float: left; + background-image: url(../images/bar_stats.png); + height: 16px; + font-size: 0.9em; + display: block; + text-align: left; + color: #fff; + font-weight: bold; + background-position: top center; +} +.stats_bar span +{ + padding-left: 2px; +} + +/* Styles for the personal messages section. +------------------------------------------------- */ + +#personal_messages +{ + padding: 1px; +} +#personal_messages #top_subject +{ + padding-left: 11.75em !important; +} +#personal_messages div.labels +{ + padding: 0 1em 0 0; +} +#personal_messages .capacity_bar +{ + background: #fff; + border: 1px solid #000; + height: 7px; + width: 75%; + margin: 0 auto; +} +#personal_messages .capacity_bar div +{ + border: none; + height: 7px; +} +#personal_messages .capacity_bar div.empty +{ + background: #468008; +} +#personal_messages .capacity_bar div.filled +{ + background: #EEA800; +} +#personal_messages .capacity_bar div.full +{ + background: #A53D05; +} +#personal_messages .reportlinks +{ + padding: 0.5em 1.3em; +} + +/* Styles for the calendar section. +------------------------------------------------- */ +.calendar_table +{ + margin-bottom: 0.7em; +} + +/* Used to indicate the current day in the grid. */ +.calendar_today +{ + background-color: #fff; +} + +#month_grid +{ + width: 200px; + text-align: center; + float: left; +} + +#month_grid table +{ + width: 200px; + border-collapse: collapse; + border: 1px solid #adadad; +} + +#month_grid table td, #month_grid table th +{ + border: 1px solid #adadad; +} + +#main_grid table +{ + width: 100%; + padding-bottom: 4px; + border-collapse: collapse; + border: 1px solid #adadad; +} + +#main_grid table td, #main_grid table th +{ + border: 1px solid #adadad; +} + +#main_grid table h3.catbg +{ + text-align: center; + + border-top: 1px solid #adadad; + border-bottom: none; +} + +#main_grid table h4 +{ + border: none; +} + +#main_grid table.weeklist td.windowbg +{ + text-align: center; + height: 49px; + width: 25px; + font-size: large; + padding: 0 7px; + border-bottom: 1px solid #adadad; +} + +#main_grid table.weeklist td.weekdays +{ + height: 49px; + width: 100%; + padding: 4px; + text-align: left; + vertical-align: middle; + border-right: 1px solid #adadad; + border-bottom: 1px solid #adadad; +} + +#main_grid h3.weekly +{ + text-align: center; + padding-left: 0; + font-size: large; + height: 29px; +} + +#main_grid h3 span.floatleft, #main_grid h3 span.floatright +{ + display: block; + +} + +#main_grid table th.days +{ + width: 14%; +} + +#main_grid table td.weeks +{ + vertical-align: middle; + text-align: center; +} + +#main_grid table td.days +{ + vertical-align: top; + +} + +a.modify_event +{ + color: red; +} + +span.hidelink +{ + font-style: italic; +} + +#calendar_navigation +{ + text-align: center; +} + +#calendar .buttonlist_bottom +{ + border-bottom: 1px solid #adadad; + padding: 0 0 0 1ex; + margin: 0 0 1ex 0; +} + +/* Styles for the memberlist section. +------------------------------------------------- */ +#mlist_search +{ + margin: auto; + width: 500px; +} +span.memberstatsbar, span.memberstatsbar span +{ + height: 1.1em; + display: block; +} +span.memberstatsbar +{ + background: #fff; + border: 1px solid #888; +} +span.memberstatsbar span +{ + background: #fe9540; +} + +/* Styles for the basic search section. +------------------------------------------------- */ +#simple_search p +{ + padding: 0.5em; +} +#simple_search, #simple_search p, #advanced_search +{ + text-align: center !important; + margin: 0; +} +#search_error +{ + font-style: italic; + padding: 0.3em 1em; +} +#search_term_input +{ + font-size: 115%; + margin: 0 0 1em; +} + +/* Styles for the advanced search section. +------------------------------------------------- */ +#searchform fieldset +{ + text-align: left; + padding: 0; + margin: 0.5em 0; + border: none; +} +#advanced_search dl#search_options +{ + margin: 0 auto; + width: 600px; + padding-top: 1em; + overflow: hidden; +} +#advanced_search dt +{ + clear: both; + float: left; + padding: 0.2em; + text-align: right; + width: 20%; +} +#advanced_search dd +{ + width: 75%; + float: left; + padding: 0.2em; + margin: 0 0 0 0.5em; + text-align: left; +} +#searchform p.clear +{ + clear: both; +} + +/* Boards picker */ +#searchform fieldset div#searchBoardsExpand ul +{ + overflow: auto; + margin: 0 0 0 1em; + padding: 0; + width: 48%; +} +#searchform fieldset div#searchBoardsExpand ul ul +{ + width: auto; +} +#searchform fieldset div#searchBoardsExpand a +{ + text-decoration: underline; +} +#searchform fieldset div#searchBoardsExpand li +{ + list-style: none; + float: left; + clear: both; +} +#searchform fieldset p +{ + padding: 4px; + text-align: left; + margin-top: 5px; +} + +/* Styles for the search results page. +------------------------------------------------- */ +.pagelinks +{ + padding: 0.5em; +} +.topic_table td blockquote, .topic_table td .quoteheader +{ + margin: 0.5em; +} +.search_results_posts +{ + overflow: hidden; +} +.search_results_posts .inner +{ + padding: 0.5em 1em; + overflow: hidden; +} +.search_results_posts .windowbg2 +{ + margin-top: 4px; +} +.search_results_posts .buttons +{ + padding: 5px 1em 0 0; +} + +/* Styles for the help section. +------------------------------------------------- */ + +#helpmain +{ + padding: 1em; + border: 1px solid #696969; +} + +/* Samples should be easily distinguishable. */ +#helpmain .help_sample +{ + border: 1px solid #99a; + background: #fff; + padding: 1em; + overflow: auto; + margin-bottom: 1em; +} +#helpmain .help_sample .linktree +{ + font-weight: bold; +} + +/* We need some air between the lines */ +#helpmain p +{ + margin: 0 0 1.5em 0; + line-height: 1.5em; +} + +#helpmain ol +{ + font-weight: bold; + list-style-type: disc; + margin-bottom: 1em; + margin-top: 1em; + line-height: 1.5em; +} +#helpmain ol.la +{ + font-weight: normal; + list-style-type: circle; + margin: 0.5em 0 1em 0; + padding-left: 1.5em; +} + +ul.basic_helplist +{ + padding: 0.8em 1.5em; + line-height: 1.5em; +} +#helpmain .boardsframe p +{ + margin: 0; +} +#helpmain #messageindex +{ + clear: right; +} + +/* ...but leave the tab strips alone! */ +#helpmain .buttonlist_bottom ul, #helpmain .buttonlist ul +{ + margin: 0 !important; + padding: 0 0 0 1em !important; +} + +#helpmain .buttonlist_bottom ul li, #helpmain .buttonlist ul li +{ + margin: 0 0.2em 0 0 !important; + padding: 0 !important; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/editor.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/editor.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,32 @@ +/* This is the editor's playground (textarea for non-wysiwyg, iframe for wysiwyg). */ +.editor +{ + width: 100%; + max-width: 100%; + min-width: 100%; +} + +.editor, .rich_editor_frame +{ + border: 1px solid #808080; + padding: 2px !important; + margin: 0; +} + +.rich_editor_frame +{ + background: #fff; +} + +/* The resize handle. */ +.richedit_resize +{ + height: 5px; + font-size: 0; + background: #eee url(../images/bbc/resize-handle.gif) no-repeat 50% 1px; + border: 1px solid #ddd; + border-top-width: 0; + cursor: s-resize; + width: 100%; + padding: 0 2px; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/editor_ie.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/editor_ie.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,45 @@ +/* This is the editor's playground (textarea for non-wysiwyg, iframe for wysiwyg). */ +.editor +{ + width: 635px; + max-width: 100%; + min-width: 100%; +} + +/* This is the IFRAME that holds the editor. */ +.rich_editor_frame +{ + border: 1px solid #808080; +} + +/* This is the WYSIWYG editor */ +.rich_editor +{ + background-color: #fff; + color: #000; + font-family: verdana; + font-size: x-small; + border: none; +} + +.rich_editor p +{ + margin: 0; +} + +.rich_editor a img +{ + border: 0; +} + +/* The resize handle. */ +.richedit_resize +{ + height: 5px; + font-size: 0; + background: #eee url(../images/bbc/resize-handle.gif) no-repeat 50% 1px; + border: 1px solid #ddd; + border-top-width: 0; + cursor: s-resize; + width: 100%; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/ie6.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/ie6.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,208 @@ +.codeheader, code.bbc_code +{ + width: 96%; + margin: 0 auto; +} +code.bbc_code +{ + white-space: normal; +} +h3.catbg input.input_check +{ + margin: 0 4px; +} +h3.catbg img.icon, h4.titlebg img.icon +{ + margin: 1px 3px 0 0; +} +h3.catbg span.ie6_header, h4.catbg span.ie6_header, h3.titlebg span.ie6_header, h4.titlebg span.ie6_header +{ + padding: 6px 0; +} +#statistics h4.titlebg span.ie6_header +{ + padding: 0; +} +#statistics h4.titlebg span.ie6_header img.icon +{ + padding: 5px 0; +} +/* The dropdown menus +------------------------------------------------------- */ + +.dropmenu li +{ + width: 1px; +} +.dropmenu li a span +{ + white-space: nowrap; +} +.dropmenu li a:hover +{ + text-decoration: none; +} +.dropmenu li.iehover +{ + z-index: 120; +} + +/* the page section */ +.pagesection +{ + overflow: auto; +} +/* the user section needs some attention */ +#main_menu +{ + width: 98%; +} +#top_section +{ + height: 65px; +} + +/* the tabled definition lists */ +/* I commented the following out. Not sure why it was there. +/* Changing float: left; to float: right; sorts the settings dd class in index.css*/ +/* All the others seem fine too.*/ +/*dl.settings dd, #creator dd, dl.stats dd, dl.register_form dd, #poll_options dl.options dd, .login dd +{ + float: none !important; + width: auto; +}*/ +/* generic lists header */ +/* Side paddings must NOT be defined here.*/ +.table_grid thead th +{ + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +/* overflow: hidden doesn't help in IE6. */ +h3.titlebg a, h3.titlebg, h4.titlebg, h4.titlebg a +{ + display: inline-block; +} + +#upper_section +{ + display: inline-block; +} + +/* Overrides for the message index template +------------------------------------------------------- */ +#messageindex table +{ + margin-top: 5px; +} +#messageindex table th +{ + border-bottom: 1px solid #fff; +} +#topic_icons .description +{ + padding: 2em 1em 1em 1em; + overflow: auto; +} + +/* Overrides for the display template +------------------------------------------------------- */ +#forumposts .postarea +{ + margin-left: 0; + margin-right: 0; + float: right; +} +.keyinfo +{ + padding-bottom: 6px; +} +.inner +{ + clear: both; +} +.post +{ + word-wrap: break-word; +} +.buttonlist ul li +{ + width: 1%; + white-space: nowrap; +} +#forumposts h3.catbg +{ + clear: both; +} +#quickReplyOptions form textarea +{ + width: 98%; +} + +/* Styles for the statistics center. +------------------------------------------------- */ +#statistics div.content +{ + height: 210px; + overflow: hidden; +} +#statistics div.top_row +{ + height: 150px; +} + +/* Overrides for the admin template +------------------------------------------------------- */ +#main_admsection +{ + height: 100%; +} +#main_admsection table +{ + width: 99%; +} + +/* Overrides for the profile template +------------------------------------------------------- */ +#basicinfo h4 +{ + word-wrap: break-word; +} +.ignoreboards +{ + margin: 0 1%; + padding: 0; + width: 45%; +} + +/* Overrides for the personal messages template +------------------------------------------------------- */ +#personal_messages .postarea +{ + margin-left: 0; + margin-right: 0; + float: right; +} + +/* Overrides for the admin section of the register template +------------------------------------------------------- */ +#registration_agreement +{ + width: 99.5%; + margin: 0 auto; +} + +#edit_poll ul.poll_main li +{ + padding-left: 0; + margin: 0 -2em; +} +#postmodify div.roundframe { margin-right: 0;} + +/* Overrides for the recent posts template +------------------------------------------------------- */ +.list_posts +{ + word-wrap: break-word; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/ie7.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/ie7.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,103 @@ +code.bbc_code +{ + white-space: normal; +} +h3.catbg input.input_check +{ + margin: 0 4px; +} + +/* The dropdown menus +------------------------------------------------------- */ +/* the dropmenu - RTL tweak */ +.dropmenu li ul +{ + margin: 0 -50px 0 0; +} +/* the hover effects */ +.dropmenu li.iehover +{ + z-index: 120; +} +/* the tabled definition lists +/* I commented the following out. Not sure why it was there. +/* Changing float: left; to float: right; sorts the settings dd class in index.css*/ +/* All the others seem fine too.*/ +/*dl.settings dd, #creator dd, dl.stats dd, dl.register_form dd, #poll_options dl.options dd, .login dd +{ + float: none !important; + width: auto; +}*/ +/* generic lists header */ +/* Side paddings must NOT be defined here.*/ +.table_grid thead th +{ + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +/* Overrides for the messageindex template +------------------------------------------------------- */ +#messageindex table +{ + margin-top: 5px; +} +#messageindex table th +{ + border-bottom: 1px solid #fff; +} +#topic_icons .description +{ + padding: 2em 1em 1em 1em; + overflow: auto; +} + +/* Overrides for the display template +------------------------------------------------------- */ +.post +{ + padding-top: 1em; + float: none; + word-wrap: break-word; +} +#content_section #forumposts div.cat_bar +{ + margin-top: 8px; + clear: both; +} +#content_section .pagesection +{ + height: 1%; +} +#quickReplyOptions form textarea +{ + width: 98%; +} + +/* Overrides for the profile template +------------------------------------------------------- */ +#basicinfo h4 +{ + word-wrap: break-word; +} + +/* Overrides for the calendar template +------------------------------------------------- */ +#main_grid table.weeklist h4.titlebg +{ + margin: 2px 0 -4px 0; +} + +/* Overrides for the personal messages template +------------------------------------------------------- */ +#postmodify dl #pm_to, #postmodify dl #bcc_div2, #postmodify dl #pm_subject +{ + clear:both !important; +} + +/* Overrides for the recent posts template +------------------------------------------------------- */ +.list_posts +{ + word-wrap: break-word; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/index.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/index.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,3620 @@ +/* Styles for the general looks for the Curve theme. +------------------------------------------------------- */ + +/* Normal, standard links. */ +a:link, a:visited +{ + color: #346; + text-decoration: none; +} +a:hover +{ + text-decoration: underline; + cursor: pointer; +} + +/* Links that open in a new window. */ +a.new_win:link, a.new_win:visited +{ + color: #346; + text-decoration: none; +} +a.new_win:hover +{ + text-decoration: underline; +} + +/* Tables should show empty cells. */ +table +{ + empty-cells: show; +} + +/* Set a fontsize that will look the same in all browsers. */ +body +{ + background: #E9EEF2 url(../images/theme/backdrop.png) repeat-x; + font: 78%/130% "Verdana", "Arial", "Helvetica", sans-serif; + margin: 0 auto; + padding: 15px 0; +} + +/* Help popups require a different styling of the body element. */ +body#help_popup +{ + padding: 1em; +} + +/* use dark grey for the text, leaving #000 for headers etc */ +body, td, th, tr +{ + color: #444; +} + +/* This division wraps the entire forum when a forum width is set. */ +div#wrapper +{ + margin: 0 auto; + min-width: 764px; + max-width: 2300px; +} + +/* lets give all forms zero padding/margins */ +form +{ + padding: 0; + margin: 0; +} + +/* We can style the different types of input buttons to be uniform throughout different browsers and their color themes. + .button_submit - covers input[type=submit], input[type=button], button[type=submit] and button[type=button] in all browsers + .button_reset - covers input[type=reset] and button[type=reset] throughout all browsers + .input_check - covers input[type=checkbox] throughout all browsers + .input_radio - covers input[type=radio] throughout all browsers + .input_text - covers input[type=text] throughout all browsers + .input_file - covers input[type=file] throughout all browsers +*/ + +input, button, select, textarea +{ + font: 95%/115% verdana, Helvetica, sans-serif; + color: #000; + background: #fff; + border: 1px solid #7f9db9; + padding: 2px; +} + +/* Select elements look horrible with the extra padding, so leave them unpadded. */ +select +{ + padding: 0; +} + +/* Add some padding to the options instead. */ +select option +{ + padding: 1px; +} + +/* The font size of textareas should be just a little bit larger. */ +textarea +{ + font: 100%/130% verdana, Helvetica, sans-serif; +} + +/* Buttons should be styled a bit differently, in order to make them look more button'ish. */ +.button_submit, .button_reset +{ + background: #cde7ff url(../images/theme/submit_bg.png) no-repeat; + border: 1px solid #aaa; + cursor: pointer; + font-weight: normal; +} +input:hover, textarea:hover, button:hover, select:hover +{ + border: 1px solid #454545; +} +.button_submit:hover, .button_reset:hover +{ + border: 1px solid #aaa; + background: url(../images/theme/submit_bg.png) no-repeat 0 -140px #cde7ff; +} +input:focus, textarea:focus, button:focus, select:focus +{ + border: 1px solid #454545; +} + +/* All input elements that are checkboxes or radio buttons shouldn't have a border around them. */ +input.input_check, input.input_radio +{ + border: none; + background: none; +} +h3.catbg input.input_check +{ + margin: 9px 7px 0 7px; +} + +/* Give disabled text input elements a different background color. */ +input[disabled].input_text +{ + background-color: #eee; +} + +/* Standard horizontal rule.. ([hr], etc.) */ +hr, .hrcolor +{ + height: 1px; + border: 0; + color: #ccc; + background-color: #ccc; +} + +/* By default set the color on these tags as #000. */ +h1, h2, h3, h4, h5, h6 +{ + color: #000; + font-size: 1em; + margin: 0; + padding: 0; +} + +/* Fieldsets are used to group elements. */ +fieldset +{ + border: 1px solid #c4c4c4; + padding: 1em; + margin: 0 0 0.5em 0; +} +fieldset legend +{ + font-weight: bold; + color: #444; +} +/* No image should have a border when linked. */ +a img +{ + border: 0; +} + +/* Define strong as bold, and em as italics */ +strong +{ + font-weight: bold; +} + +em +{ + font-style: italic; +} +/* Alternative for u tag */ +.underline +{ + text-decoration: underline; +} + +/* Common classes to easy styling. +------------------------------------------------------- */ + +.floatright +{ + float: right; +} +.floatleft +{ + float: left; +} + +.flow_auto +{ + overflow: auto; +} +.flow_hidden +{ + overflow: hidden; +} +.flow_hidden .windowbg, .flow_hidden .windowbg2 +{ + margin-top: 2px; +} +.clear +{ + clear: both; +} +.clear_left +{ + clear: left; +} +.clear_right +{ + clear: right; +} + +/* Default font sizes: small (8pt), normal (10pt), and large (14pt). */ +.smalltext, tr.smalltext th +{ + font-size: 0.85em; + font-family: verdana, sans-serif; +} +.middletext +{ + font-size: 0.9em; + line-height: 1em; + font-family: verdana, sans-serif; +} +.normaltext +{ + font-size: 1em; + line-height: 1.2em; +} +.largetext +{ + font-size: 1.4em; +} +.centertext +{ + margin: 0 auto; + text-align: center; +} +.righttext +{ + margin-left: auto; + margin-right: 0; + text-align: right; +} +.lefttext +{ + margin-left: 0; + margin-right: auto; + text-align: left; +} +.double_height +{ + line-height: 2em; +} +/* some common padding styles */ +.padding +{ + padding: 0.7em; +} +.main_section, .lower_padding +{ + padding-bottom: 0.5em; +} +/* a quick reset list class. */ +ul.reset, ul.reset li +{ + padding: 0; + margin: 0; + list-style: none; +} + +/* Some BBC related styles. +------------------------------------------------------- */ + +/* A quote, perhaps from another post. */ +blockquote.bbc_standard_quote, blockquote.bbc_alternate_quote +{ + font-size: x-small; + color: #000; + line-height: 1.4em; + background: url(../images/theme/quote.png) 0.1em 0.1em no-repeat; + border-top: 2px solid #99A; + border-bottom: 2px solid #99A; + padding: 1.1em 1.4em; + margin: 0.1em 0 0.3em 0; + overflow: auto; +} + +/* Alterate blockquote stylings */ +blockquote.bbc_standard_quote +{ + background-color: #d7daec; +} +blockquote.bbc_alternate_quote +{ + background-color: #e7eafc; +} + +/* A code block - maybe PHP ;). */ +code.bbc_code +{ + display: block; + font-family: "dejavu sans mono", "monaco", "lucida console", "courier new", monospace; + font-size: x-small; + background: #eef; + border-top: 2px solid #999; + border-bottom: 2px solid #999; + line-height: 1.5em; + padding: 3px 1em; + overflow: auto; + white-space: nowrap; + /* Show a scrollbar after about 24 lines. */ + max-height: 24em; +} + +/* The "Quote:" and "Code:" header parts... */ +.codeheader, .quoteheader +{ + color: #666; + font-size: x-small; + font-weight: bold; + padding: 0 0.3em; +} + +/* For links to change the code stuff... */ +.codeoperation +{ + font-weight: normal; +} + +/* Styling for BBC tags */ +.bbc_link:link, .bbc_link:visited +{ + border-bottom: 1px solid #A8B6CF; +} +.bbc_link:hover +{ + text-decoration: none; + border-bottom: 1px solid #346; +} +.bbc_size +{ + line-height: 1.4em; +} +.bbc_color a +{ + color: inherit; +} +.bbc_img +{ + border: 0; +} +.bbc_table +{ + font: inherit; + color: inherit; +} +.bbc_table td +{ + font: inherit; + color: inherit; + vertical-align: top; +} +.bbc_u +{ + text-decoration: underline; +} +.bbc_list +{ + text-align: left; +} +.bbc_tt +{ + font-family: "dejavu sans mono", "monaco", "lucida console", "courier new", monospace; +} + +/* Generally, those [?] icons. This makes your cursor a help icon. */ +.help +{ + cursor: help; +} + +/* /me uses this a lot. (emote, try typing /me in a post.) */ +.meaction +{ + color: red; +} + +/* Highlighted text - such as search results. */ +.highlight +{ + font-weight: bold; + color: #ff7200 !important; + font-size: 1.1em; +} + +/* A more discreet highlight color, for selected membergroups etc. */ +.highlight2 +{ + background-color: #D1E1EF; + color: #000 !important; +} + +/* Generic, mostly color-related, classes. +------------------------------------------------------- */ + +.titlebg, .titlebg2, tr.titlebg th, tr.titlebg td, tr.titlebg2 td +{ + color: #222; + font-family: arial, helvetica, sans-serif; + font-size: 1.1em; + font-weight: bold; + background: #e3e9ef url(../images/theme/main_block.png) no-repeat -10px -380px; +} +.catbg, .catbg2, tr.catbg td, tr.catbg2 td, tr.catbg th, tr.catbg2 th +{ + color: #fff; + font-family: arial, helvetica, sans-serif; + font-size: 1.1em; + font-weight: bold; + background: #a7b9cd url(../images/theme/main_block.png) no-repeat -10px -280px; +} + +/* adjust the table versions of headers */ +tr.titlebg th, tr.titlebg2 th, td.titlebg, td.titlebg2, tr.catbg th, tr.catbg2 th, td.catbg, td.catbg2 +{ + padding: 0 6px; +} +tr.titlebg th a:link, tr.titlebg th a:visited, tr.titlebg2 td a:link, tr.titlebg2 td a:visited +{ + color: #222; +} +tr.catbg th a:link, tr.catbg th a:visited, tr.catbg2 td a:link, tr.catbg2 td a:visited +{ + color: #fff; +} +.catbg select +{ + height: 1.5em; + font-size: 0.85em; +} + +/* Alternating backgrounds for posts, and several other sections of the forum. */ +.windowbg, #preview_body +{ + color: #000; + background-color: #e7eaef; +} +.windowbg2 +{ + color: #000; + background-color: #f0f4f7; +} +.windowbg3 +{ + color: #000; + background-color: #cacdd3; +} + +/* the page navigation area */ +.pagesection +{ + font-size: 0.9em; + padding: 0.2em; + overflow: hidden; + margin-bottom: 1px; +} +div.pagesection div.floatright input +{ + margin-top: 3px; +} + +.pagelinks +{ + padding: 0.6em 0 0.4em 0; +} + +/* Colors for background of posts requiring approval */ +.approvebg +{ + color: #000; + background-color: #ffeaea; +} +.approvebg2 +{ + color: #000; + background-color: #fff2f2; +} + +/* Color for background of *topics* requiring approval */ +.approvetbg +{ + color: #000; + background-color: #e4a17c; +} +.approvetbg2 +{ + color: #000; + background-color: #f3bd9f; +} + +/* Sticky topics get a different background */ +.stickybg +{ + background: #e8d8cf; +} +.stickybg2 +{ + background: #f2e3d9; +} + +/* Locked posts get a different shade, too! */ +.lockedbg +{ + background: #d4dce2; + font-style: italic; +} +.lockedbg2 +{ + background: #d8e1e7; + font-style: italic; +} + +/* Posts and personal messages displayed throughout the forum. */ +.post, .personalmessage +{ + overflow: auto; + line-height: 1.4em; + padding: 0.1em 0; +} + +/* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ +.signature, .attachments +{ + width: 98%; + overflow: auto; + clear: right; + padding: 1em 0 3px 0; + border-top: 1px solid #aaa; + line-height: 1.4em; + font-size: 0.85em; +} +.custom_fields_above_signature +{ + width: 98%; + clear: right; + padding: 1em 0 3px 0; + border-top: 1px solid #aaa; + line-height: 1.4em; + font-size: 0.85em; +} + +/* Sometimes there will be an error when you post */ +.error +{ + color: red; +} + +/* Messages that somehow need to attract the attention. */ +.alert +{ + color: red; +} + +/* Calendar colors for birthdays, events and holidays */ +.birthday +{ + color: #920ac4; +} + +.event +{ + color: #078907; +} + +.holiday +{ + color: #000080; +} + +/* Colors for warnings */ +.warn_mute +{ + color: red; +} + +.warn_moderate +{ + color: #ffa500; +} + +.warn_watch, .success +{ + color: green; +} + +a.moderation_link, a.moderation_link:visited +{ + color: red; + font-weight: bold; +} + +.openid_login +{ + background: white url(../images/openid.gif) no-repeat; + padding-left: 18px; +} + +/* a descriptive style */ +.description, .description_board, .plainbox +{ + padding: 0.5em 1em; + font-size: 0.9em; + line-height: 1.4em; + border: 1px solid #bbb; + background: #f5f5f0; + margin: 0.2em 1px 1em 1px; +} +.description_board +{ + margin: 1em 1px 0 1px; +} + +/* an informative style */ +.information +{ + padding: 0.5em 1em; + font-size: 0.9em; + line-height: 1.3em; + border: 1px solid #bbb; + background: #f0f6f0; + margin: 0.2em 1px 1em 1px; +} +.information p +{ + padding: 1em; + margin: 0; +} +p.para2 +{ + padding: 1em 0 3.5em 0; + margin: 0; +} +/* AJAX notification bar +------------------------------------------------------- */ +#ajax_in_progress +{ + background: url(../images/theme/loadingbar.png) repeat-x; + color: #f96f00; + text-align: center; + font-size: 16pt; + padding: 8px; + width: 100%; + height: 66px; + line-height: 25px; + position: fixed; + top: 0; + left: 0; +} + +#ajax_in_progress a +{ + color: orange; + text-decoration: underline; + font-size: smaller; + float: right; + margin-right: 20px; +} + +/* Lists with settings use these a lot. +------------------------------------------------------- */ +dl.settings +{ + clear: right; + overflow: auto; + margin: 0 0 10px 0; + padding: 0; +} +dl.settings dt +{ + width: 40%; + float: left; + margin: 0 0 10px 0; + padding: 0; + clear: both; +} +dl.settings dt.settings_title +{ + width: 100%; + float: none; + margin: 0 0 10px 0; + padding: 5px 0 0 0; + font-weight: bold; + clear: both; +} +dl.settings dt.windowbg +{ + width: 98%; + float: left; + margin: 0 0 3px 0; + padding: 0 0 5px 0; + clear: both; +} +dl.settings dd +{ + width: 56%; + float: right; + overflow: auto; + margin: 0 0 3px 0; + padding: 0; +} +dl.settings img +{ + margin: 0 10px 0 0; +} +/* help icons */ +dl.settings dt a img +{ + position: relative; + top: 2px; +} + +/* Styles for rounded headers. +------------------------------------------------------- */ +h3.catbg, h3.catbg2, h3.titlebg, h4.titlebg, h4.catbg +{ + overflow: hidden; + height: 31px; + line-height: 31px; + font-size: 1.2em; + font-weight: bold; +} +h3.catbg a:link, h3.catbg a:visited, h4.catbg a:link, h4.catbg a:visited, h3.catbg, .table_list tbody.header td, .table_list tbody.header td a +{ + color: #fff; +} +h3.catbg2 a, h3.catbg2 +{ + color: #feb; +} +h3.catbg a:hover, h4.catbg a:hover, .table_list tbody.header td a:hover +{ + color: #fd9; + text-decoration: none; +} +h3.catbg2 a:hover +{ + color: #fff; + text-decoration: none; +} +h3.titlebg a, h3.titlebg, h4.titlebg, h4.titlebg a +{ + color: #222; +} +h3.titlebg a:hover, h4.titlebg a:hover +{ + color: #53616f; + text-decoration: none; +} +h3.catbg img.icon, h4.titlebg img.icon +{ + vertical-align: middle; + margin: -2px 5px 0 0; +} +h4.catbg a.toggle img +{ + vertical-align: middle; + margin: -2px 5px 0 5px; +} +h4.catbg, h4.catbg2 , h3.catbg , h3.catbg2 , .table_list tbody.header td.catbg +{ + background: url(../images/theme/main_block.png) no-repeat 100% -160px; + padding-right: 9px; +} +h4.titlebg, h3.titlebg +{ + background: url(../images/theme/main_block.png) no-repeat 100% -200px; + padding-right: 9px; +} +h4.titlebg img.icon +{ + float: left; + margin: 5px 8px 0 0; +} +div.cat_bar +{ + background: #99abbf url(../images/theme/main_block.png) no-repeat 0 -160px; + padding-left: 9px; + height: 31px; + overflow: hidden; + margin-bottom: 1px; +} +div.title_bar +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 0 -200px; + padding-left: 9px; + height: 31px; + overflow: hidden; + margin-bottom: 1px; +} + +/* rounded bars needs a different background here */ + +div.roundframe div.cat_bar +{ + background: #99abbf url(../images/theme/main_block.png) no-repeat 0 -240px; + margin-bottom: 0; +} +div.roundframe div.cat_bar h3.catbg +{ + background: url(../images/theme/main_block.png) no-repeat 100% -240px; +} +div.title_barIC +{ + background: #dadfe6 url(../images/theme/main_block.png) no-repeat 0 -120px; + padding-left: 9px; + height: 31px; + overflow: hidden; + margin-bottom: 1px; +} +div.title_barIC h4.titlebg +{ + background: url(../images/theme/main_block.png) no-repeat 100% -120px; +} +#upshrinkHeaderIC p.pminfo +{ + margin: 0; + padding: 0.5em; +} +img#upshrink_ic, img#newsupshrink +{ + float: right; + margin: 10px 5px 0 0; +} +table.table_list a.unreadlink, table.table_list a.collapse +{ + float: right; +} +table.table_list a.collapse +{ + margin: 10px 5px 0 1em; + height: 31px; + line-height: 31px; +} + +/* The half-round header bars for some tables. */ +.table_grid tr.catbg, .table_grid tr.titlebg +{ + font-size: 0.95em; + border-bottom: 1px solid #fff; +} +.table_grid tr.catbg th, .table_grid tr.titlebg th +{ + height: 28px; + line-height: 28px; +} +tr.catbg th.first_th +{ + background: #a7b9cd url(../images/theme/main_block.png) no-repeat 0 -280px; +} +tr.catbg th.last_th +{ + background: #a7b9cd url(../images/theme/main_block.png) no-repeat 100% -280px; +} +tr.titlebg th.first_th +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 0 -380px; +} +tr.titlebg th.last_th +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 100% -380px; +} +.table_grid th.last_th input +{ + margin: 0 2px; +} +.table_grid th.lefttext +{ + padding: 0 0.7em; +} + +/* a general table class */ +table.table_grid +{ + border-collapse: collapse; + margin-top: 0.1em; +} +table.table_grid td +{ + padding: 3px; + border-bottom: 1px solid #fff; + border-right: 1px solid #fff; +} + +/* GenericList */ +.additional_row +{ + padding: 0.5em 0 0.5em 0; +} +table.table_grid thead tr.catbg th +{ + white-space: nowrap; +} + +/* table_grid styles for Profile > Show Permissions. */ +#permissions table.table_grid td +{ + padding: 0.4em 0.8em; + cursor: default; +} + +/* Common styles used to add corners to divisions. +------------------------------------------------------- */ +.windowbg span.topslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -30px no-repeat; +} +.windowbg span.topslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -30px no-repeat; + height: 11px; +} +.windowbg span.botslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -40px no-repeat; + font-size: 5px; + line-height: 5px; + margin-bottom: 0.2em; +} +.windowbg span.botslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -40px no-repeat; + height: 11px; +} + +.windowbg2 span.topslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -60px no-repeat; +} +.windowbg2 span.topslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -60px no-repeat; + height: 11px; +} +.windowbg2 span.botslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -71px no-repeat; + font-size: 5px; + line-height: 5px; + margin-bottom: 0.2em; +} +.windowbg2 span.botslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -71px no-repeat; + height: 11px; +} +.approvebg span.topslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 0 no-repeat; +} +.approvebg span.topslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% 0 no-repeat; + height: 11px; +} +.approvebg span.botslice +{ + display: block; + padding-left: 20px; + background: url(../images/theme/main_block.png) 0 -11px no-repeat; + margin-bottom: 0.2em; +} +.approvebg span.botslice span +{ + display: block; + background: url(../images/theme/main_block.png) 100% -11px no-repeat; + height: 11px; +} +.postbg +{ + border-left: 1px solid #7f7f7f; + border-right: 1px solid #7f7f7f; +} + +/* Used for sections that need somewhat larger corners. +----------------------------------------------------------- */ +.roundframe +{ + padding: 0 10px; + background: #f5f5f5; + border-left: 1px solid #c5c5c5; + border-right: 1px solid #c5c5c5; +} +.roundframe dl, .roundframe dt, .roundframe p +{ + margin: 0; +} +.roundframe p +{ + padding: 0.5em; +} +span.upperframe +{ + padding: 0; + display: block; + background: url(../images/theme/main_block.png) 0 -90px no-repeat; + padding-left: 20px; +} +span.upperframe span +{ + padding: 0; + height: 12px; + display: block; + background: url(../images/theme/main_block.png) 100% -90px no-repeat; +} +span.lowerframe +{ + padding: 0; + display: block; + background: url(../images/theme/main_block.png) 0 -102px no-repeat; + padding-left: 20px; +} +span.lowerframe span +{ + padding: 0; + height: 12px; + display: block; + background: url(../images/theme/main_block.png) 100% -102px no-repeat; +} + +/* The main content area. +------------------------------------------------------- */ +.content +{ + padding: 0.5em 1.2em; + margin: 0; + border: none; +} +.content p +{ + margin: 0 0 0.5em 0; +} + +/* Styles used by the auto suggest control. +------------------------------------------------------- */ +.auto_suggest_div +{ + border: 1px solid #000; + position: absolute; + visibility: hidden; +} +.auto_suggest_item +{ + background-color: #ddd; +} +.auto_suggest_item_hover +{ + background-color: #888; + cursor: pointer; + color: #eee; +} + +/* Styles for the standard dropdown menus. +------------------------------------------------------- */ +#main_menu +{ + padding: 0 0.5em; + float: left; + margin: 0; + width: 98%; +} + +.dropmenu, .dropmenu ul +{ + list-style: none; + line-height: 1em; + padding: 0; + margin: 0; +} +.dropmenu +{ + padding: 0 0.5em; +} +.dropmenu a +{ + display: block; + color: #000; + text-decoration: none; +} +.dropmenu a span +{ + display: block; + padding: 0 0 0 5px; + font-size: 0.9em; +} +/* the background's first level only */ +.dropmenu li a.firstlevel +{ + margin-right: 8px; +} +.dropmenu li a.firstlevel span.firstlevel +{ + display: block; + position: relative; + left: -5px; + padding-left: 5px; + height: 22px; + line-height: 19px; + white-space: pre; +} +.dropmenu li +{ + float: left; + padding: 0; + margin: 0; + position: relative; +} +.dropmenu li ul +{ + z-index: 90; + display: none; + position: absolute; + width: 19.2em; + font-weight: normal; + border-bottom: 1px solid #999; + background: url(../images/theme/menu_gfx.png) 0 -130px no-repeat; + padding: 7px 0 0 0; +} +.dropmenu li li +{ + width: 19em; + margin: 0; + border-left: 1px solid #999; + border-right: 1px solid #999; +} +.dropmenu li li a span +{ + display: block; + padding: 8px; +} +.dropmenu li ul ul +{ + margin: -1.8em 0 0 13em; +} + +/* the active button */ +.dropmenu li a.active +{ + background: url(../images/theme/menu_gfx.png) no-repeat 100% 0; + color: #fff; + font-weight: bold; +} +.dropmenu li a.active span.firstlevel +{ + background: url(../images/theme/menu_gfx.png) no-repeat 0 0; +} +/* the hover effects */ +.dropmenu li a.firstlevel:hover, .dropmenu li:hover a.firstlevel +{ + background: url(../images/theme/menu_gfx.png) no-repeat 100% -30px; + color: #fff; + cursor: pointer; + text-decoration: none; +} +.dropmenu li a.firstlevel:hover span.firstlevel, .dropmenu li:hover a.firstlevel span.firstlevel +{ + background: url(../images/theme/menu_gfx.png) no-repeat 0 -30px; +} +/* the hover effects on level2 and 3 */ +.dropmenu li li a:hover, .dropmenu li li:hover>a +{ + background: #d4dbe4; + color: #000; + text-decoration: none; +} +.dropmenu li:hover ul ul, .dropmenu li:hover ul ul ul +{ + top: -999em; +} +.dropmenu li li:hover ul +{ + top: auto; +} +.dropmenu li:hover ul +{ + display: block; +} +.dropmenu li li.additional_items +{ + background-color: #fff; +} + +/* The dropdown menu toggle image */ +#menu_toggle +{ + float: right; + margin-right: 10px; + padding-top: 3px; +} +#menu_toggle span +{ + position: relative; + right: 5000px; +} + +/* Styles for the standard button lists. +------------------------------------------------------- */ + +.buttonlist ul +{ + z-index: 100; + padding: 5px; + margin: 0 0.2em 5px 0; +} +.buttonlist ul li +{ + margin: 0; + padding: 0; + list-style: none; + float: left; +} +.buttonlist ul li a +{ + display: block; + font-size: 0.8em; + color: #000; + background: #e8e8e8 url(../images/theme/menu_gfx.png) no-repeat 0 -60px; + padding: 0 0 0 8px; + margin-left: 12px; + text-transform: uppercase; + cursor: pointer; +} +.buttonlist ul li a:hover +{ + background: url(../images/theme/menu_gfx.png) no-repeat 0 0; + color: #fff; + text-decoration: none; +} +.buttonlist ul li a span +{ + background: url(../images/theme/menu_gfx.png) no-repeat 100% -60px; + display: block; + height: 19px; + line-height: 19px; + padding: 0 8px 0 0; +} +.buttonlist ul li a:hover span +{ + background: #fff url(../images/theme/menu_gfx.png) no-repeat 100% 0; +} +/* the active one */ +.buttonlist ul li a.active +{ + background: #5a6c85 url(../images/theme/menu_gfx.png) no-repeat 0 -90px; + color: #fff; + font-weight: bold; +} +.buttonlist ul li a.active span +{ + background: url(../images/theme/menu_gfx.png) no-repeat 100% -90px; +} +.buttonlist ul li a.active +{ + font-weight: bold; +} +.buttonlist ul li a.active:hover +{ + color: #ddf; +} +.align_top ul li a, .align_bottom ul li a +{ + margin: 0 12px 0 0; +} + +/* the navigation list */ +ul#navigation +{ + margin: 0; + font-size: 0.9em; + padding: 1em 0.4em; +} +ul#navigation li +{ + float: none; + font-size: 0.95em; + display: inline; +} + +#adm_submenus +{ + padding-left: 2em; + overflow: hidden; +} + +/* Styles for the general looks for the Curve theme. +------------------------------------------------------- */ + +/* the framing graphics */ +#header +{ + background: url(../images/theme/main_block.png) #fefefe no-repeat 0 -480px; + padding-left: 20px; +} +#header div.frame +{ + background: url(../images/theme/main_block.png) no-repeat 100% -480px; + display: block; + padding: 5px 20px 1em 0; +} +/* the content section */ +#content_section +{ + background: #FFFFFF url(../images/theme/frame_repeat.png) repeat-y top left; + padding-left: 20px; +} +#content_section div.frame +{ + background: url(../images/theme/frame_repeat.png) repeat-y top right; + display: block; + padding: 0 20px 0 0; +} +#main_content_section +{ + width: 100%; + min-height: 200px; +} + +/* the main title, always stay at 45 pixels in height! */ +h1.forumtitle +{ + line-height: 45px; + font-size: 1.8em; + font-family: Geneva, verdana, sans-serif; + margin: 0; + padding: 0; + float: left; +} +/* float these items to the right */ +#siteslogan, img#smflogo +{ + margin: 0; + padding: 0; + float: right; + line-height: 3em; +} +h3, h4 +{ + padding-bottom: 3px; +} +/* the upshrink image needs some tweaking */ +img#upshrink +{ + float: right; + margin: 1em; +} +/* ..so does the SMF logo */ +img#smflogo +{ + margin-left: 1em; +} +/* the upper_section, float the two each way */ +#upper_section +{ + padding: 5px; + margin-bottom: 1.5em; +} +#upper_section ul li.greeting +{ + font-size: 1.3em; + font-weight: bold; + line-height: 1.5em; +} +#upper_section div.news +{ + width: 50%; + float: right; + text-align: right; +} +#guest_form +{ + overflow: hidden; +} +#guest_form .info +{ + padding: 4px 0 ; + line-height: 1.3em; +} +div#upper_section div.user +{ + width: 50%; + float: left; + overflow: auto; +} +div#upper_section div.user p +{ + float: left; + margin: 0 1em 1em 0; + padding: 0; +} +div#upper_section div.user ul +{ + margin: 0; + padding-left: 10px; +} +div#upper_section div.user ul li +{ + margin-bottom: 2px; +} +div#upper_section div.news p +{ + display: inline; +} +div#upper_section div.news form +{ + padding-bottom: 10px; +} +/* clearing the floats */ +#top_section +{ + min-height: 65px; + overflow: hidden; + margin-bottom: 3px; +} +#upper_section +{ + overflow: hidden; +} + +/* The navigation list (i.e. linktree) */ +.navigate_section +{ + padding: 0.5em; + margin: 0 0 0 0; +} +.navigate_section ul +{ + display: block; + margin: 0; + font-size: 0.9em; + padding: 1em 0 0.5em 0; + border-top: 1px solid #ccc; + overflow: hidden; + list-style: none; + clear: both; + width: 100%; +} +.navigate_section ul li +{ + float: left; + padding: 0 0.5em 0 0; + font-size: 0.95em; +} +.navigate_section ul li a +{ + white-space: pre; +} + +/* The footer wih copyright links etc. */ +#footer_section +{ + text-align: center; + background: url(../images/theme/main_block.png) no-repeat 0 -820px; + padding-left: 20px; +} +#footer_section span.smalltext +{ + font-size: 100%; +} +#footer_section div.frame +{ + background: url(../images/theme/main_block.png) no-repeat 100% -820px; + display: block; + padding: 60px 0 0 0; +} +#footer_section ul li, #footer_section p +{ + font-size: 0.8em; +} +#footer_section ul li +{ + display: inline; + padding-right: 5px; +} +#footer_section ul li.copyright +{ + display: block; +} +select.qaction, input.qaction +{ + font-size: 0.85em; + padding: 0; +} +#mlist table tbody td.windowbg2 +{ + text-align: center; +} + +/* Styles for a typical table. +------------------------------------------------------- */ +table.table_list +{ + width: 100%; +} +table.table_list p +{ + padding: 0; + margin: 0; +} +table.table_list td, table.table_list th +{ + padding: 5px; +} +table.table_list tbody.header td +{ + padding: 0; +} +table.table_list tbody.content td.stats +{ + font-size: 90%; + width: 15%; + text-align: center; +} +table.table_list tbody.content td.lastpost +{ + line-height: 1.3em; + font-size: 85%; + width: 24%; +} +table.table_list tbody.content td.icon +{ + text-align: center; + width: 6%; +} + +/* Styles for the board index. +------------------------------------------------- */ + +/* the board title! */ +.table_list tbody.content td.info a.subject +{ + font-weight: bold; + font-size: 110%; + color: #d97b33; +} +.table_list tbody.content td.children +{ + color: #555; + font-size: 85%; +} +p.moderators +{ + font-size: 0.8em; + font-family: verdana, sans-serif; +} +/* hide the table header/footer parts - but its here for those needing to style it */ +#boardindex_table .table_list thead, #boardindex_table .table_list tfoot +{ + display: none; +} + +/* the posting icons */ +#posting_icons +{ + padding: 0 1em 0.5em 1em; + margin: 0 0 1em 0; + line-height: 1em; +} +#posting_icons ul +{ + font-size: 0.8em; +} +#posting_icons img +{ + vertical-align: middle; + margin: 0 0 0 4ex; +} +#postbuttons_upper ul li a span +{ + line-height: 19px; + padding: 0 0 0 6px; +} +.nextlinks +{ + text-align: right; + margin-top: -1px; +} +.nextlinks_bottom +{ + clear: right; + text-align: right; +} +.mark_read +{ + padding: 0 0.5em; +} + +/* the newsfader */ +#newsfader +{ + margin: 0 2px; +} +#smfFadeScroller +{ + text-align: center; + padding: 0 2em; + overflow: auto; + margin: 1em 0; + color: #575757; /* shouldn't be shorthand style due to a JS bug in IE! */ +} + +/* Styles for the info center on the board index. +---------------------------------------------------- */ + +#upshrinkHeaderIC +{ + margin-top: 4px; +} +dl#ic_recentposts +{ + margin: 0 0 0.5em 0; + padding: 0.5em; + line-height: 1.3em; +} +dl#ic_recentposts dt +{ + float: left; +} +dl#ic_recentposts dd +{ + text-align: right; +} +#upshrinkHeaderIC p +{ + margin: 0 0 0.5em 0; + padding: 0.5em; +} +#upshrinkHeaderIC p.last +{ + margin: 0; + padding: 0.5em; + border-top: 2px dotted #bbb; +} +#upshrinkHeaderIC p.inline +{ + border: none; + margin: 0; + padding: 0.2em 0.5em 0.2em 0.5em; +} +#upshrinkHeaderIC p.stats +{ + font-size: 1.1em; + padding-top: 8px; +} +form#ic_login +{ + padding: 0.5em; + height: 2em; +} +form#ic_login ul li +{ + margin: 0; + padding: 0; + float: left; + width: 20%; + text-align: center; +} +form#ic_login ul li label +{ + display: block; +} + +/* the small stats */ +#index_common_stats +{ + display: block; + margin: 0 0 0.5em 0; + text-align: right; + font-size: 0.9em; + position: relative; + top: -20px; + line-height: 1px; +} + +img.new_posts +{ + padding: 0 0.1em; +} +/* Styles for the message (topic) index. +---------------------------------------------------- */ +div.table_frame .table_list +{ + border-collapse: collapse; + margin: 2px 0; +} +.table_frame .table_list td.icon, .table_frame .table_list td.info, .table_frame .table_list td.stats +{ + border-right: 2px solid white; +} +#messageindex +{ + clear: both; +} +/* the page navigation area */ +.childboards +{ + margin-bottom: 0.2em; +} +#childboards h3 +{ + padding-bottom: 0; +} +#childboards .table_list thead +{ + display: none; +} +#childboards .table_list +{ + margin-bottom: 1em; +} +.lastpost img +{ + float: right; + padding: 4px; +} + +/* Styles for the display template (topic view). +---------------------------------------------------- */ + +#postbuttons div.buttons +{ + padding: 0.5em; + width: 40%; + float: right; +} +#postbuttons div.middletext +{ + width: 60%; +} +#postbuttons span +{ + display: block; + text-align: right; +} +#postbuttons span.lower +{ + clear: right; +} +#postbuttons .buttonlist +{ + float: right; +} +#postbuttons #pagelinks +{ + padding-top: 1em; +} +#moderationbuttons +{ + overflow: hidden; +} +/* Events */ +.linked_events +{ + padding: 1em 0; +} +.edit_event +{ + margin: 0 1em; + vertical-align: middle; +} +/* Poll question */ +#poll +{ + overflow: hidden; +} +#poll .content +{ + padding: 0 1em; +} +h4#pollquestion +{ + padding: 0 0 0.5em 2em; +} + +/* Poll vote options */ +#poll_options ul.options +{ + border-top: 1px solid #9999aa; + padding: 1em 2.5em 0 2em; + margin: 0 0 1em 0; +} +#poll_options div.submitbutton +{ + border-bottom: 1px solid #9999aa; + clear: both; + padding: 0 0 1em 2em; + margin: 0 0 1em 0; +} + +/* Poll results */ +#poll_options dl.options +{ + border: solid #9999aa; + border-width: 1px 0; + padding: 1em 2.5em 1em 2em; + margin: 0 1em 1em 0; + line-height: 1.1em !important; +} + +#poll_options dl.options dt +{ + padding: 0.3em 0; + width: 30%; + float: left; + margin: 0; + clear: left; +} + +#poll_options dl.options .voted +{ + font-weight: bold; +} + +#poll_options dl.options dd +{ + margin: 0 0 0 2em; + padding: 0.1em 0 0 0; + width: 60%; + max-width: 450px; + float: left; +} + +#poll_options dl.options .percentage +{ + display: block; + float: right; + padding: 0.2em 0 0.3em 0; +} + +/* Poll notices */ +#poll_options p +{ + margin: 0 1.5em 0.2em 1.5em; + padding: 0 0.5em 0.5em 0.5em; +} + +div#pollmoderation +{ + margin: 0; + padding: 0; + overflow: auto; +} + +/* onto the posts */ +#forumposts +{ + clear: both; +} +#forumposts .cat_bar +{ + margin: 0 0 2px 0; +} +/* author and topic information */ +#forumposts h3 span#author +{ + margin: 0 7.7em 0 0; +} +#forumposts h3 img +{ + float: left; + margin: 4px 0.5em 0 0; +} +#forumposts h3.catbg +{ + margin-bottom: 3px; +} +p#whoisviewing +{ + margin: 0; + padding: 0.5em; +} +/* poster and postarea + moderation area underneath */ +.post_wrapper +{ + float:left; + width:100%; +} +.poster +{ + float: left; + width: 15em; +} +.postarea, .moderatorbar +{ + margin: 0 0 0 16em; +} +.postarea div.flow_hidden +{ + width: 100%; +} + +.moderatorbar +{ + clear: right; +} +/* poster details and list of items */ +.poster h4, .poster ul +{ + padding: 0; + margin: 0 1em 0 1.5em; +} +.poster h4 +{ + margin: 0.2em 0 0.4em 1.1em; + font-size: 120%; +} +.poster h4, .poster h4 a +{ + color: #c06002; +} +.poster ul ul +{ + margin: 0.3em 1em 0 0; + padding: 0; +} +.poster ul ul li +{ + display: inline; +} +.poster li.stars, .poster li.avatar, .poster li.blurb, li.postcount, li.im_icons ul +{ + margin-top: 0.5em; +} +.poster li.avatar +{ + overflow: hidden; +} +.poster li.warning +{ + line-height: 1.2em; + padding-top: 1em; +} +.poster li.warning a img +{ + vertical-align: bottom; + padding: 0 0.2em; +} +.messageicon +{ + float: left; + margin: 0 0.5em 0 0; +} +.messageicon img +{ + padding: 6px 3px; +} +.keyinfo +{ + float: left; + width: 50%; +} +.modifybutton +{ + clear: right; + float: right; + margin: 6px 20px 10px 0; + text-align: right; + font: bold 0.85em arial, sans-serif; + color: #334466; +} + +/* The quick buttons */ +div.quickbuttons_wrap +{ + padding: 0.2em 0; + width: 100%; + float: left; +} + +ul.quickbuttons +{ + margin: 0.9em 11px 0 0; + clear: right; + float: right; + text-align: right; + font: bold 0.85em arial, sans-serif; +} +ul.quickbuttons li +{ + float: left; + display: inline; + margin: 0 0 0 11px; +} +ul.quickbuttons li a +{ + padding: 0 0 0 20px; + display: block; + height: 20px; + line-height: 18px; + float: left; +} +ul.quickbuttons a:hover +{ + color: #a70; +} +ul.quickbuttons li.quote_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 0; +} +ul.quickbuttons li.remove_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -30px; +} +ul.quickbuttons li.modify_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -60px; +} +ul.quickbuttons li.approve_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -90px; +} +ul.quickbuttons li.restore_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -120px; +} +ul.quickbuttons li.split_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -150px; +} +ul.quickbuttons li.reply_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -180px; +} +ul.quickbuttons li.reply_all_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -180px; +} +ul.quickbuttons li.notify_button +{ + background: url(../images/theme/quickbuttons.png) no-repeat 0 -210px; +} +ul.quickbuttons li.inline_mod_check +{ + margin: 0 0 0 5px; +} + +.post +{ + margin-top: 0.5em; + clear: right; +} +.inner +{ + padding: 1em 1em 2px 0; + margin: 0 1em 0 0; + border-top: 1px solid #99a; +} +img.smiley +{ + vertical-align: bottom; +} +#forumposts .modified +{ + float: left; +} +#forumposts .reportlinks +{ + margin-right: 1.5em; + text-align: right; + clear: right; +} +#forumposts .signature, .post .signature +{ + margin: 1em 0 0 0; +} +#forumposts span.botslice +{ + clear: both; +} +.attachments hr +{ + clear: both; + margin: 1em 0 1em 0; +} +.attachments +{ + padding: 1em 0 2em 0; +} +.attachments div +{ + padding: 0 0.5em; +} + +/* Styles for the quick reply area. +---------------------------------------------------- */ + +#quickreplybox +{ + padding-bottom: 1px; +} +#quickReplyOptions .roundframe +{ + padding: 0 10%; +} +#quickReplyOptions form textarea +{ + height: 100px; + width: 635px; + max-width: 100%; + min-width: 100%; + margin: 0.25em 0 1em 0; +} +/* The jump to box */ +#display_jump_to +{ + clear: both; + padding: 5px; + margin-top: 6px; + text-align: right; +} + +/* Separator of posts. More useful in the print stylesheet. */ +#forumposts .post_separator +{ + display: none; +} + +/* Styles for edit post section +---------------------------------------------------- */ +form#postmodify .roundframe +{ + padding: 0 12%; +} +#post_header, .postbox +{ + padding: 0.5em; + overflow: hidden; +} +#post_header dt, .postbox dt +{ + float: left; + padding: 0; + width: 15%; + margin: .5em 0 0 0; + font-weight: bold; +} +#post_header dd, .postbox dd +{ + float: left; + padding: 0; + width: 83%; + margin: .3em 0; +} +#post_header img +{ + vertical-align: middle; +} +ul.post_options +{ + margin: 0 0 0 1em; + padding: 0; + list-style: none; + overflow: hidden; +} +ul.post_options li +{ + margin: 0.2em 0; + width: 49%; + float: left; +} +#postAdditionalOptionsHeader +{ + margin-top: 1em; +} +#postMoreOptions +{ + border-bottom: 1px solid #cacdd3; + padding: 0.5em; +} +#postAttachment, #postAttachment2 +{ + overflow: hidden; + margin: .5em 0; + padding: 0; + border-bottom: 1px solid #cacdd3; + padding: 0.5em; +} +#postAttachment dd, #postAttachment2 dd +{ + margin: .3em 0 .3em 1em; +} +#postAttachment dt, #postAttachment2 dt +{ + font-weight: bold; +} +#postAttachment3 +{ + margin-left: 1em; +} +#post_confirm_strip, #shortcuts +{ + padding: 1em 0 0 0; +} +.post_verification +{ + margin-top: .5em; +} +.post_verification #verification_control +{ + margin: .3em 0 .3em 1em; +} +/* The BBC buttons */ +#bbcBox_message +{ + margin: 0.75em 0.5em; +} +#bbcBox_message div +{ + margin: 0.2em 0; + vertical-align: top; +} +#bbcBox_message div img +{ + margin: 0 1px 0 0; + vertical-align: top; +} +#bbcBox_message select +{ + margin: 0 2px; +} +/* The smiley strip */ +#smileyBox_message +{ + margin: 0.5em; +} + +/* Styles for edit event section +---------------------------------------------------- */ +#post_event .roundframe +{ + padding: 0 12%; +} +#post_event fieldset +{ + padding: 0.5em; + clear: both; +} +#post_event #event_main input +{ + margin: 0 0 1em 0; + float: left; +} +#post_event #event_main div.smalltext +{ + width: 33em; + float: right; +} +#post_event div.event_options +{ + float: right; +} +#post_event ul.event_main, ul.event_options +{ + padding: 0; + overflow: hidden; +} +#post_event ul.event_main li +{ + list-style-type: none; + margin: 0.2em 0; + width: 49%; + float: left; +} +#post_event ul.event_options +{ + margin: 0; + padding: 0 0 .7em .7em; +} +#post_event ul.event_options li +{ + list-style-type: none; + margin: 0; + float: left; +} +#post_event #event_main select, #post_event ul.event_options li select, #post_event ul.event_options li .input_check +{ + margin: 0 1em 0 0; +} + +/* Styles for edit poll section. +---------------------------------------------------- */ + +#edit_poll +{ + overflow: hidden; +} +#edit_poll fieldset +{ + padding: 0.5em; + clear: both; + overflow: hidden; +} +#edit_poll fieldset input +{ + margin-left: 8.1em; +} +#edit_poll ul.poll_main li +{ + padding-left: 1em; +} +#edit_poll ul.poll_main input +{ + margin-left: 1em; +} +#edit_poll ul.poll_main, dl.poll_options +{ + overflow: hidden; + padding: 0 0 .7em .7em; + list-style: none; +} +#edit_poll ul.poll_main li +{ + margin: 0.2em 0; +} +#edit_poll dl.poll_options dt +{ + width: 33%; + padding: 0 0 0 1em; +} +#edit_poll dl.poll_options dd +{ + width: 65%; +} +#edit_poll dl.poll_options dd input +{ + margin-left: 0; +} + +/* Styles for the recent messages section. +---------------------------------------------------- */ + +#readbuttons_top .pagelinks, #readbuttons .pagelinks +{ + padding-bottom: 1em; + width: 60%; +} +#readbuttons .pagelinks +{ + padding-top: 1em; +} +#recent +{ + clear: both; +} + +/* Styles for the move topic section. +---------------------------------------------------- */ + +#move_topic dl +{ + margin-bottom: 0; +} +#move_topic dl.settings dt +{ + width: 40%; +} +#move_topic dl.settings dd +{ + width: 59%; +} +.move_topic +{ + width: 710px; + margin: auto; + text-align: left; +} +div.move_topic fieldset +{ + padding: 0.5em; +} + +/* Styles for the send topic section. +---------------------------------------------------- */ + +fieldset.send_topic +{ + border: none; + padding: 0.5em; +} +dl.send_topic +{ + margin-bottom: 0; +} +dl.send_mail dt +{ + width: 35%; +} +dl.send_mail dd +{ + width: 64%; +} + +/* Styles for the report topic section. +---------------------------------------------------- */ + +#report_topic dl +{ + margin-bottom: 0; +} +#report_topic dl.settings dt +{ + width: 20%; +} +#report_topic dl.settings dd +{ + width: 79%; +} + +/* Styles for the split topic section. +---------------------------------------------------- */ + +div#selected, div#not_selected +{ + width: 49%; +} +ul.split_messages li.windowbg, ul.split_messages li.windowbg2 +{ + margin: 1px; +} +ul.split_messages li a.split_icon +{ + padding: 0 0.5em; +} +ul.split_messages div.post +{ + padding: 1em 0 0 0; + border-top: 1px solid #fff; +} + +/* Styles for the merge topic section. +---------------------------------------------------- */ +ul.merge_topics li +{ + list-style-type: none; +} +dl.merge_topic dt +{ + width: 25%; +} +dl.merge_topic dd +{ + width: 74%; +} +fieldset.merge_options +{ + clear: both; +} +.custom_subject +{ + margin: 0.5em 0; +} + +/* Styles for the login areas. +------------------------------------------------------- */ +.login +{ + width: 540px; + margin: 0 auto; +} +.login dl +{ + overflow: auto; + clear: right; +} +.login dt, .login dd +{ + margin: 0 0 0.4em 0; + width: 44%; + padding: 0.1em; +} +.login dt +{ + float: left; + clear: both; + text-align: right; + font-weight: bold; +} +.login dd +{ + width: 54%; + float: right; + text-align: left; +} +.login p +{ + text-align: center; +} + +/* Styles for the registration section. +------------------------------------------------------- */ +.register_error +{ + border: 1px dashed red; + padding: 5px; + margin: 0 1ex 1ex 1ex; +} +.register_error span +{ + text-decoration: underline; +} + +/* Additional profile fields */ +dl.register_form +{ + margin: 0; + clear: right; +} + +dl.register_form dt +{ + font-weight: normal; + float: left; + clear: both; + width: 50%; + margin: 0.5em 0 0 0; +} + +dl.register_form dt strong +{ + font-weight: bold; +} + +dl.register_form dt span +{ + display: block; +} + +dl.register_form dd +{ + float: left; + width: 49%; + margin: 0.5em 0 0 0; +} + +#confirm_buttons +{ + text-align: center; + padding: 1em 0; +} + +.coppa_contact +{ + padding: 4px; + width: 32ex; + background-color: #fff; + color: #000; + margin-left: 5ex; + border: 1px solid #000; +} + +.valid_input +{ + background-color: #f5fff0; +} +.invalid_input +{ + background-color: #fff0f0; +} + +/* Styles for maintenance mode. +------------------------------------------------------- */ +#maintenance_mode +{ + width: 75%; + min-width: 520px; + text-align: left; +} +#maintenance_mode img.floatleft +{ + margin-right: 1em; +} + +/* common for all admin sections */ +h3.titlebg img +{ + vertical-align: middle; + margin-right: 0.5em; + margin-top: -1px; +} +tr.titlebg td +{ + padding-left: 0.7em; +} +#admin_menu +{ + min-height: 2em; + padding-left: 0; +} +#admin_content +{ + clear: left; + padding-top: 0.5em; +} +/* Custom profile fields like to play with us some times. */ +#admin_content .custom_field +{ + margin-bottom: 15px; +} +#admin_login .centertext +{ + padding: 1em; +} +#admin_login .centertext .error +{ + padding: 0 0 1em 0; +} + +/* Styles for sidebar menus. +------------------------------------------------------- */ +.left_admmenu, .left_admmenu ul, .left_admmenu li +{ + padding: 0; + margin: 0; + list-style: none; +} +#left_admsection +{ + width: 160px; + float: left; + padding-right: 10px; +} +.adm_section h4.titlebg +{ + font-size: 95%; + margin-bottom: 5px; +} +#main_container +{ + position: relative; +} +.left_admmenu li +{ + padding: 0 0 0 0.5em; +} +.left_admmenu +{ + margin-bottom: 0.5em; +} +#main_admsection +{ + position: relative; + left: 0; + right: 0; + overflow: hidden; +} + +tr.windowbg td, tr.windowbg2 td, tr.approvebg td, tr.highlight2 td +{ + padding: 0.3em 0.7em; +} +#credits p +{ + padding: 0; + font-style: italic; + margin: 0; +} + +/* Styles for generic tables. +------------------------------------------------------- */ +.topic_table table +{ + width: 100%; +} +.topic_table .icon1, .topic_table .icon2, .topic_table .stats +{ + text-align: center; +} +#topic_icons +{ + margin: 1em 0 0 0; +} +#topic_icons .description +{ + margin: 0; +} +.topic_table table thead +{ + border-bottom: 1px solid #fff; +} +/* the subject column */ +.topic_table td +{ + font-size: 1em; +} +.topic_table td.subject p, .topic_table td.stats +{ + font-size: 0.85em; + padding: 0; + margin: 0; +} +.topic_table td.lastpost +{ + font-size: 0.85em; + line-height: 1.3em; + padding: 4px; +} +.topic_table td.stickybg2 +{ + background-image: url(../images/icons/quick_sticky.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lockedbg2 +{ + background-image: url(../images/icons/quick_lock.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.locked_sticky2 +{ + background-image: url(../images/icons/quick_sticky_lock.gif); + background-repeat: no-repeat; + background-position: 98% 4px; +} +.topic_table td.lastpost +{ + background-image: none; +} + +/* Styles for (fatal) errors. +------------------------------------------------- */ + +#fatal_error +{ + width: 80%; + margin: auto; +} + +.errorbox +{ + padding: 1em; + border: 1px solid #cc3344; + color: #000; + background-color: #ffe4e9; + margin-bottom: 1em; +} +.errorbox h3 +{ + padding: 0; + margin: 0; + font-size: 1.1em; + text-decoration: underline; +} +.errorbox p +{ + margin: 1em 0 0 0; +} +.errorbox p.alert +{ + padding: 0; + margin: 0; + float: left; + width: 1em; + font-size: 1.5em; +} + +/* Styles for the profile section. +------------------------------------------------- */ + +dl +{ + overflow: auto; + margin: 0; + padding: 0; +} + +/* The basic user info on the left */ +#basicinfo +{ + width: 20%; + float: left; +} +#basicinfo .windowbg .content +{ + padding-left: 20px; +} +#detailedinfo +{ + width: 79.5%; + float: right; +} +#basicinfo h4 +{ + font-size: 135%; + font-weight: 100; + line-height: 105%; + white-space: pre-wrap; + overflow: hidden; +} +#basicinfo h4 span.position +{ + font-size: 80%; + font-weight: 100; + display: block; +} +#basicinfo img.avatar +{ + display: block; + margin: 10px 0 0 0; +} +#basicinfo ul +{ + list-style-type: none; + margin: 10px 0 0 0; +} +#basicinfo ul li +{ + display: block; + float: left; + margin-right: 5px; + height: 20px; +} +#basicinfo span#userstatus +{ + display: block; + clear: both; +} +#basicinfo span#userstatus img +{ + vertical-align: middle; +} +#detailedinfo div.content dl, #tracking div.content dl +{ + clear: right; + overflow: auto; + margin: 0 0 18px 0; + padding: 0 0 15px 0; + border-bottom: 1px #ccc solid; +} +#detailedinfo div.content dt, #tracking div.content dt +{ + width: 35%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#detailedinfo div.content dd, #tracking div.content dd +{ + width: 65%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} +#detailedinfo div.content dl.noborder +{ + border-bottom: 0; +} +#detailedinfo div.content dt.clear +{ + width: 100%; +} +.signature, .custom_fields_above_signature +{ + border-top: 1px #ccc solid; +} +.signature h5 +{ + font-size: 0.85em; + margin-bottom: 10px; +} +#personal_picture +{ + display: block; + margin-bottom: 0.3em; +} +#avatar_server_stored div +{ + float: left; +} +#avatar_upload +{ + overflow: auto; +} +#main_admsection #basicinfo, #main_admsection #detailedinfo +{ + width: 100%; +} +#main_admsection #basicinfo h4 +{ + float: left; + width: 35%; +} +#main_admsection #basicinfo img.avatar +{ + float: right; + vertical-align: top; +} +#main_admsection #basicinfo ul +{ + clear: left; +} +#main_admsection #basicinfo span#userstatus +{ + clear: left; +} +#main_admsection #basicinfo p#infolinks +{ + display: none; + clear: both; +} +#main_admsection #basicinfo .botslice +{ + clear: both; +} + +/* Simple feedback messages */ +div#profile_error, div#profile_success +{ + margin: 0 0 1em 0; + padding: 1em 2em; + border: 1px solid; +} +div#profile_error +{ + border-color: red; + color: red; + background: #fee; +} + +div#profile_error span +{ + text-decoration: underline; +} + +div#profile_success +{ + border-color: green; + color: green; + background: #efe; +} + +/* Profile statistics */ +#generalstats div.content dt +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#generalstats div.content dd +{ + width: 50%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +/* Activity by time */ +#activitytime +{ + margin: 6px 0; +} +.activity_stats +{ + margin: 0; + padding: 0; + list-style: none; +} +.activity_stats li +{ + margin: 0; + padding: 0; + width: 4.16%; + float: left; +} +.activity_stats li span +{ + display: block; + border: solid #000; + border-width: 1px 1px 0 0; + text-align: center; +} +.activity_stats li.last span +{ + border-right: none; +} +.activity_stats li div.bar +{ + margin: 0 auto; + width: 15px; +} +.activity_stats li div.bar div +{ + background: #6294CE; +} +.activity_stats li div.bar span +{ + position: absolute; + top: -1000em; + left: -1000em; +} + +/* Most popular boards by posts and activity */ +#popularposts +{ + width: 49.5%; + float: left; +} +#popularactivity +{ + width: 49.5%; + float: right; +} + +#popularposts div.content dt, #popularactivity div.content dt +{ + width: 65%; + float: left; + margin: 0 0 3px 0; + padding: 0; + font-weight: bold; + clear: both; +} +#popularposts div.content dd, #popularactivity div.content dd +{ + width: 35%; + float: left; + margin: 0 0 3px 0; + padding: 0; +} + +.profile_pie +{ + background-image: url(../images/stats_pie.png); + float: left; + height: 20px; + width: 20px; + margin: 0 1em 0 0; + padding: 0; + text-indent: -1000em; +} + +/* View posts */ +.topic .time +{ + float: right; +} + +.counter +{ + margin: 0 0 0 0; + padding: 0.2em 0.5em 0.1em 0.2em; + font-size: 2.2em; + font-weight: bold; + color: #3f3f3f; + float: left; +} +.list_posts +{ + border-top: 2px solid #b3b3bf; + padding-top: 12px; + margin-top: 6px; + overflow: auto; +} + +.core_posts +{ + margin-bottom: 3px; +} + +.topic h4 +{ + margin: 3px 0; +} + +.topic .post +{ + margin: 0 1em; + min-height: 80px; + height: auto !important; + height: 80px; +} + +.topic .mod_icons +{ + text-align: right; + margin-right: 1em; +} + +#tracking div.content dl +{ + border-bottom: 0; + margin: 0; + padding: 0; +} + +#creator dl +{ + margin: 0; +} +#creator dt +{ + width: 40%; + float: left; + clear: both; + margin: 0 0 10px 0; +} +#creator dd +{ + float: right; + width: 55%; + margin: 0 0 10px 2px; + overflow: auto; +} + +.ignoreboards +{ + margin: 0 2%; + padding: 0; + width: 45%; +} +.ignoreboards a +{ + font-weight: bold; + border-bottom: 1px solid #c4c4c4; + padding: 0.1em 0; +} +.ignoreboards a:hover +{ + text-decoration: none; + border-bottom: 1px solid #334466; +} +.ignoreboards ul +{ + margin: 0; + padding: 0; +} +.ignoreboards li +{ + list-style: none; + float: left; + clear: both; +} +.ignoreboards li.category +{ + margin: 0.7em 0 0 0; + width: 100%; +} +.ignoreboards li ul +{ + margin: 0.2em 0 0 0; +} +.ignoreboards li.category ul li.board +{ + width: 93%; +} + +#theme_settings +{ + overflow: auto; + margin: 0; + padding: 0; +} + +#theme_settings li +{ + list-style: none; + margin: 10px 0; + padding: 0; +} +/* Paid Subscriptions */ +#paid_subscription +{ + width: 100%; +} +#paid_subscription dl.settings +{ + margin-bottom: 0; +} +#paid_subscription dl.settings dd, #paid_subscription dl.settings dt +{ + margin-bottom: 4px; +} +/* Pick theme */ +#pick_theme +{ + width: 100%; + float: left; +} +/*Issue a warning*/ +#warn_body{ + width: 80%; + font-size: 0.9em; +} + +/* Styles for the statistics center. +------------------------------------------------- */ +#statistics +{ + padding: 0.5em 0; +} +#statistics div.title_bar +{ + margin: 4px 0 -2px 0; +} +#statistics h3.catbg +{ + text-align: center; +} +#statistics div.content +{ + min-height: 210px; +} +#statistics div.top_row +{ + min-height: 150px; +} +#stats_left, #top_posters, #top_topics_replies, #top_topics_starter +{ + float: left; + width: 49.5%; +} +#stats_right, #top_boards, #top_topics_views, #most_online +{ + float: right; + width: 49.5%; +} +dl.stats +{ + clear: both; + overflow: hidden; + margin: 0; + padding: 0; +} +dl.stats dt +{ + width: 49%; + float: left; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; + clear: both; + font-size: 1em; +} +dl.stats dd +{ + text-align: right; + width: 50%; + font-size: 1em; + float: right; + margin: 0 0 4px 0; + line-height: 16px; + padding: 0; +} +.statsbar div.bar +{ + float: left; + background: url(../images/bar_stats.png) no-repeat; + display: block; + margin: 0 4px; + height: 16px; +} +.statsbar div.bar div +{ + position: relative; + right: -4px; + padding: 0 4px 0 0; + background: url(../images/bar_stats.png) no-repeat 100%; + height: 16px; +} +tr.windowbg2 th.stats_month +{ + width: 25%; + padding: 0 2em; + text-align: left; +} +tr.windowbg2 td.stats_day +{ + padding: 0 3.5em; + text-align: left; +} + +/* Styles for the personal messages section. +------------------------------------------------- */ + +#personal_messages h3 span#author, #personal_messages h3 span#topic_title +{ + float: left; +} +#personal_messages h3 span#author +{ + margin: 0 0 0 0.5em; +} +#personal_messages h3 span#topic_title +{ + margin: 0 0 0 9em; +} +#personal_messages div.labels +{ + padding: 0 1em 0 0; +} +#personal_messages .capacity_bar +{ + background: #f0f4f7; + display: block; + margin: 0.5em 0 0 1em; + height: 1em; + border: 1px solid #adadad; + width: 10em; +} +#personal_messages .capacity_bar span +{ + border-right: 1px solid #adadad; + display: block; + height: 1em; +} +#personal_messages .capacity_bar span.empty +{ + background: #a6d69d; +} +#personal_messages .capacity_bar span.filled +{ + background: #eea800; +} +#personal_messages .capacity_bar span.full +{ + background: #f10909; +} +#personal_messages .reportlinks +{ + padding: 0.5em 1.3em; +} +#searchLabelsExpand li +{ + padding: 0.3em 0.5em; +} +#manrules div.righttext +{ + padding: 0.3em 0.1em; +} +dl.addrules dt.floatleft +{ + width: 15em; + color: #333; + padding: 0 1.25em 0.5em 1.25em; +} +#addrule fieldset +{ + clear: both; +} + +/* Styles for the calendar section. +------------------------------------------------- */ +.calendar_table +{ + margin-bottom: 0.7em; +} + +/* Used to indicate the current day in the grid. */ +.calendar_today +{ + background-color: #fff; +} + +#month_grid +{ + width: 200px; + text-align: center; + float: left; +} +#month_grid div.cat_bar +{ + height: 25px; +} +#month_grid h3.catbg +{ + height: 25px; + line-height: 27px; +} +#month_grid table +{ + width: 200px; +} +#main_grid table +{ + width: 100%; + padding-bottom: 4px; +} +#main_grid table h3.catbg +{ + text-align: center; + height: 29px; + border-top: 2px solid #fff; + border-bottom: none; +} +#main_grid table.weeklist td.windowbg +{ + text-align: center; + height: 49px; + width: 25px; + font-size: large; + padding: 0 7px; + border-bottom: 2px solid #fff; +} +#main_grid table.weeklist td.weekdays +{ + height: 49px; + width: 100%; + padding: 4px; + text-align: left; + vertical-align: middle; + border-bottom: 2px solid #fff; +} +#main_grid h3.weekly +{ + text-align: center; + padding-left: 0; + font-size: large; + height: 29px; +} +#main_grid h3 span.floatleft, #main_grid h3 span.floatright +{ + display: block; + font-weight: bold; +} +#main_grid table th.days +{ + width: 14%; + padding: 4px 0; +} +#main_grid table.weeklist h4.titlebg +{ + margin: 0 0 0 0; + height: 23px; + line-height: 27px; +} +#main_grid table td.weeks +{ + vertical-align: middle; + text-align: center; + font-weight: bold; + font-size: large; +} +#main_grid table td.days +{ + vertical-align: top; + text-align: center; +} + +a.modify_event +{ + color: red; +} + +span.hidelink +{ + font-style: italic; +} + +#calendar_navigation +{ + text-align: center; +} + +/* Styles for the memberlist section. +------------------------------------------------- */ +#mlist_search +{ + margin: auto; + width: 500px; +} + +/* Styles for the basic search section. +------------------------------------------------- */ +#searchform, #simple_search p +{ + padding: 0.5em; + margin: 0; +} +#simple_search, #simple_search p, #advanced_search +{ + text-align: center !important; + margin: 0; +} +#search_error +{ + font-style: italic; + padding: 0.3em 1em; +} +#search_term_input +{ + font-size: 115%; + margin: 0 0 1em; +} + +/* Styles for the advanced search section. +------------------------------------------------- */ +#searchform fieldset +{ + text-align: left; + padding: 0; + border: none; +} +#advanced_search dl#search_options +{ + margin: 0 auto; + width: 600px; + padding-top: 1em; + overflow: hidden; +} +#advanced_search dt +{ + clear: both; + float: left; + padding: 0.2em; + text-align: right; + width: 20%; +} +#advanced_search dd +{ + width: 75%; + float: left; + padding: 0.2em; + margin: 0 0 0 0.5em; + text-align: left; +} +#searchform p.clear +{ + clear: both; +} + +/* Styles for the search results page. +------------------------------------------------- */ +.topic_table td blockquote, .topic_table td .quoteheader +{ + margin: 0.5em; +} +.search_results_posts +{ + overflow: hidden; +} +.search_results_posts .buttons +{ + padding: 5px 1em 0 0; +} + +/* Styles for the help section. +------------------------------------------------- */ + +#help_container +{ + margin: 4px 0 0 0; + padding: 0 0 8px 0; +} +#helpmain +{ + padding: 0 1em; +} +#helpmain p +{ + margin: 0 0 1.5em 0; + line-height: 1.5em; +} +#helpmain ul +{ + line-height: 1.5em; +} + +/* Styles for print media. +------------------------------------------------------- */ +@media print +{ + #headerarea + { + display: none; + } + + .tborder + { + border: none; + } +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/install.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/install.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,91 @@ +body +{ + width: 90%; +} +#top_section +{ + height: 70px; + min-height: 65px; +} +#upper_section +{ + margin-bottom: 0; + padding: 0; +} +#upper_section .user +{ + height: 4em; +} +#upper_section .news +{ + height: 80px; +} +#main_screen +{ + padding: 0 40px; +} +#main_screen h2 +{ + font-size: 1.5em; + border-bottom: 1px solid #d05800; + line-height: 1.5em; + margin: 0 0 0.5em 0; + color: #d05800; +} +#main-steps +{ + float: right; + width: 50%; + margin-top: -60px; +} +#main-steps h2 +{ + font-size: 1.1em; + border-bottom: 1px solid #d05800; + line-height: 1.1em; + margin: 0 0 0.5em 0; + color: #d05800; + margin-right: 40px; +} +#main-steps ul +{ + list-style: none; + padding-left: 0; + margin: 0; +} +#main-steps ul li +{ + padding: 1px 0; + font-size: 0.9em; +} +#main-steps ul li.stepdone +{ + color: #aaa; +} +#main-steps ul li.stepcurrent +{ + color: #000; + font-weight: bold; +} +#main-steps ul li.stepwaiting +{ + color: #666; +} +.panel +{ + font-weight: normal; +} +a:link, a:hover, a:visited +{ + text-decoration: underline; +} +.progress +{ + position: relative; + margin: -16px 3px 0 3px; +} +.overall_progress +{ + position: relative; + margin: -25px 3px 0 3px; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/report.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/report.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,59 @@ +body +{ + color: #000; + background-color: #fff; + zoom: 1; +} +body, td, .normaltext +{ + font-family: Verdana, arial, helvetica, serif; + font-size: small; +} +*, a:link, a:visited, a:hover, a:active +{ + color: #000 !important; +} +.smalltext, .quoteheader, .codeheader +{ + font-size: x-small; +} +.largetext +{ + font-size: large; +} +hr +{ + height: 1px; + border: 0; + color: #000; + background-color: #000; +} +.catbg +{ + background-color: #d6d6d6; + font-weight: bold; +} +.titlebg, tr.titlebg td, .titlebg a:link, .titlebg a:visited +{ + font-style: normal; + background-color: #f0f4f7; +} +.bordercolor +{ + background-color: #333; +} +.windowbg +{ + color: #000; + background-color: #fff; +} +.windowbg2 +{ + color: #000; + background-color: #f1f1f1; +} +.copyright +{ + font-size: x-small; + text-align: center; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/rtl.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/rtl.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1082 @@ +/* Common classes to ease styling. +------------------------------------------------------- */ + +.floatright +{ + float: left; +} +.floatleft +{ + float: right; +} +.clear_left +{ + clear: right; +} +.clear_right +{ + clear: left; +} +.righttext +{ + margin-left: auto; + margin-right: 0; + text-align: left; +} +.lefttext +{ + margin-left: 0; + margin-right: auto; + text-align: right; +} + +/* Styling for BBC tags */ +.bbc_list +{ + text-align: right; +} + +/* GenericList */ +.additional_row input +{ + margin-left: 0; + margin-right: 1em; +} +/* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ +.signature, .attachments +{ + clear: left; +} +.custom_fields_above_signature +{ + clear: left; +} +.openid_login +{ + padding-right: 18px; + padding-left: 0; +} + +/* Lists with settings use these a lot. +------------------------------------------------------- */ +dl.settings +{ + clear: left; +} +dl.settings dt +{ + float: right; + clear: both; +} +dl.settings dt.windowbg +{ + float: right; +} +dl.settings dd +{ + float: left; +} +dl.settings img +{ + margin: 0 0 0 10px; +} + +/* Styles for rounded headers. +------------------------------------------------------- */ + +h3.catbg img.icon, h4.titlebg img.icon +{ + vertical-align: middle; + margin: -2px 0 0 5px; +} +h4.titlebg, h3.titlebg +{ + padding-right: 9px; + padding-left: 0; +} +h4.titlebg img.icon +{ + float: right; + margin: 5px 0 0 8px; +} + +table.table_list a.unreadlink, table.table_list a.collapse +{ + float: left; +} +table.table_list a.collapse +{ + margin: 10px 1em 0 5px; +} +.table_grid th.first_th, tr.catbg th.first_th +{ + background: #a8bace url(../images/theme/main_block.png) no-repeat 100% -240px; +} +.table_grid th.last_th, tr.catbg th.last_th +{ + background: #a8bace url(../images/theme/main_block.png) no-repeat 0 -240px; +} +tr.titlebg th.first_th +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 100% -340px; +} +tr.titlebg th.last_th +{ + background: #e3e9ef url(../images/theme/main_block.png) no-repeat 0 -340px; +} + +/* Styles for the standard dropdown menus. +------------------------------------------------------- */ +#main_menu +{ + padding: 0 0.5em; + float: right; + text-align: right; +} +.dropmenu li +{ + float: right; + margin: 0 0 0 8px; +} +.dropmenu li ul ul +{ + right: 15em; +} +.dropmenu li ul +{ + background: url(../images/theme/menu_gfx.png) 100% -130px no-repeat; + right: 5px; +} + +/* The dropdown menu toggle image */ +#menu_toggle +{ + float: left; + margin-right: 0; + margin-left: 10px; + padding-top: 3px; +} +#menu_toggle span +{ + position: relative; + left: 0; +} + +/* Styles for the standard button lists. +------------------------------------------------------- */ +.buttonlist ul +{ + margin: 0 0 0 0.2em; +} +.buttonlist ul li a +{ + margin-left: 0; + margin-right: 12px; +} +.buttonlist ul li a span +{ + left: 8px; +} +.align_top ul li a, .align_bottom ul li a +{ + margin: 0 0 0 12px; +} +#adm_submenus +{ + padding-left: 0; + padding-right: 2em; +} +/* the main title, always stay at 45 pixels in height! */ +h1.forumtitle +{ + float: right; +} +/* float these items to the left */ +#siteslogan, img#smflogo +{ + float: left; +} +/* the upshrink image needs some tweaking */ +img#upshrink +{ + float: left; +} +/* ..so does the SMF logo */ +img#smflogo +{ + margin-right: 1em; +} +#upper_section div.news +{ + float: left; + text-align: left; +} +div#upper_section div.user +{ + float: right; +} +div#upper_section div.user p +{ + float: right; + margin: 0 0 1em 1em; +} +div#upper_section div.user ul +{ + padding-left: 0; + padding-right: 10px; +} + +/* The navigation list (i.e. linktree) */ +.navigate_section ul li +{ + float: right; + padding: 0 0 0 0.5em; +} + +/* Styles for the board index. +------------------------------------------------- */ + +/* the posting icons */ +#posting_icons +{ + padding: 0 1em 0.5em 1em; +} +#posting_icons img +{ + margin: 0 4ex 0 0; +} +#posting_icons .buttonlist +{ + float: left; +} +#postbuttons_upper ul li a span +{ + line-height: 19px; + padding: 0 6px 0 0; +} + +dl#ic_recentposts dt +{ + float: right; +} +dl#ic_recentposts dd +{ + text-align: left; +} +form#ic_login ul li +{ + float: right; + width: 20%; +} + +/* the small stats */ +#index_common_stats +{ + text-align: left; +} +img#upshrink_ic, img#newsupshrink +{ + float: right; + margin: 10px 0 0 5px; +} + +/* Styles for the message (topic) index. +---------------------------------------------------- */ +.table_frame .table_list td.icon, .table_frame .table_list td.info, .table_frame .table_list td.stats +{ + border-right: none; + border-left: 2px solid white; +} +.lastpost img +{ + float: left; +} + +/* Styles for the display template (topic view). +---------------------------------------------------- */ +#postbuttons div.buttons +{ + float: right; +} +#postbuttons span +{ + text-align: left; +} +#postbuttons span.lower +{ + clear: left; +} +#postbuttons .buttonlist +{ + float: left; +} + +h4#pollquestion +{ + padding: 0.5em 2em 0.5em 0; +} +/* Poll vote options */ +#poll_options ul.options +{ + padding: 1em 2em 0 2.5em; + margin: 0 0 1em 0; +} +#poll_options div.submitbutton +{ + clear: both; + padding: 0 2em 1em 0; + margin: 0 0 1em 0; +} + +/* Poll results */ +#poll_options dl.options +{ + padding: 1em 2em 1em 2.5em; + margin: 0 0 1em 1em; +} +#poll_options dl.options dt +{ + float: right; + clear: right; +} +#poll_options dl.options dd +{ + margin: 0 2em 0 0; + float: right; +} +span.percent +{ + float: left; +} + +/* author and topic information */ +#forumposts h3 span#author +{ + margin: 0 0 0 7.7em; +} +#forumposts h3 img +{ + float: right; + margin: 4px 0 0 0.5em; +} +/* poster and postarea + moderation area underneath */ +.poster +{ + float: right; + width: 15em; +} +.postarea, .moderatorbar +{ + margin: 0 16em 0 0; +} +.moderatorbar +{ + clear: left; +} +/* poster details and list of items */ +.poster h4, .poster ul +{ + padding: 0; + margin: 0 1.5em 0 1em; +} +.poster h4 +{ + margin: 0.2em 1.1em 0.4em 0; +} +.poster ul ul +{ + margin: 0.3em 0 0 1em; +} +.messageicon +{ + float: right; + margin: 0 0 0 0.5em; +} + +.keyinfo +{ + float: right; +} +.modifybutton +{ + clear: left; + float: left; + margin: 8px 0 10px 20px; + text-align: left; +} + +/* The quick buttons */ +ul.quickbuttons +{ + margin: 0.9em 0 0 11px; + clear: left; + float: left; + text-align: left; +} +ul.quickbuttons li +{ + float: left; + margin: 0 11px 0 0; +} +ul.quickbuttons li a +{ + padding: 0 20px 0 0; + float: left; +} +ul.quickbuttons li.quote_button +{ + background-position: 100% 0; +} +ul.quickbuttons li.remove_button +{ + background-position: 100% -30px; +} +ul.quickbuttons li.modify_button +{ + background-position: 100% -60px; +} +ul.quickbuttons li.approve_button +{ + background-position: 100% -90px; +} +ul.quickbuttons li.restore_button +{ + background-position: 100% -120px; +} +ul.quickbuttons li.split_button +{ + background-position: 100% -150px; +} +ul.quickbuttons li.reply_button +{ + background-position: 100% -180px; +} +ul.quickbuttons li.reply_all_button +{ + background-position: 100% -180px; +} +ul.quickbuttons li.notify_button +{ + background-position: 100% -210px; +} +ul.quickbuttons li.inline_mod_check +{ + margin: 0 5px 0 0; +} +.post +{ + clear: left; +} +.inner +{ + padding: 1em 0 0 1em; + margin: 0 0 0 1em; +} +#forumposts .modified +{ + float: right; +} +#forumposts .reportlinks +{ + margin-left: 1.5em; + text-align: left; + clear: left; +} + +#moderationbuttons_strip +{ + float: right; +} +#moderationbuttons_strip ul +{ + margin: 0 0.2em 0 0; + padding: 0 1em 0 0; +} +/* The jump to box */ +#display_jump_to +{ + text-align: left; +} + +/* Styles for edit post section +---------------------------------------------------- */ +#post_header dt +{ + float: right; +} +#post_header dd +{ + float: right; +} +ul.post_options +{ + margin: 0 1em 0 0; +} +ul.post_options li +{ + float: right; +} +#postAttachment dd, #postAttachment2 dd +{ + margin: .3em 1em .3em 0; +} +#postAttachment dt, #postAttachment2 dt +{ + font-weight: bold; +} +#postAttachment3 +{ + margin-left: 0; + margin-left: 1em; +} +.post_verification #verification_control +{ + margin: .3em 1em .3em 0; +} + +/* Styles for edit event section +---------------------------------------------------- */ +#post_event div.event_options +{ + float: left; +} +#post_event #event_main input +{ + margin: 0 0 1em 0; + float: right; +} +#post_event #event_main div.smalltext +{ + float: left; +} +#post_event ul.event_main li +{ + float: left; +} +#post_event ul.event_options +{ + padding: 0 .7em .7em 0; +} +#post_event #event_main select, #post_event ul.event_options li select, #post_event ul.event_options li .input_check +{ + margin: 0 0 0 1em; +} + +/* Styles for edit poll section. +---------------------------------------------------- */ + +#edit_poll fieldset input +{ + margin-right: 7em; +} +#edit_poll ul.poll_main li +{ + padding-right: 1em; +} +#edit_poll ul.poll_main input +{ + margin-right: 1em; +} +#edit_poll div.poll_options +{ + float: right; +} +#edit_poll ul.poll_main, dl.poll_options +{ + padding: 0 .7em 0 0; +} +#edit_poll dl.poll_options dt +{ + padding: 0 1em 0 0; +} +#edit_poll dl.poll_options dd input +{ + margin-right: 0; +} + +/* Styles for the personal messages section. +------------------------------------------------- */ + +#personal_messages h3 span#author, #personal_messages h3 span#topic_title +{ + float: right; +} +#personal_messages h3 span#author +{ + margin: 0 0.5em 0 0; +} +#personal_messages h3 span#topic_title +{ + margin: 0 9em 0 0; +} +#personal_messages .labels +{ + padding: 0 0 0 1em; +} + +/* Styles for the move topic section. +---------------------------------------------------- */ +.move_topic +{ + text-align: right; +} +/* Styles for the login areas. +------------------------------------------------------- */ +.login dt +{ + float: right; +} +.login dd +{ + float: right; + text-align: right; +} +.login h3 img +{ + margin: 0 0 0.5em; +} + +/* Additional profile fields */ +dl.register_form +{ + clear: left; +} + +dl.register_form dt +{ + float: right; +} +/* Styles for maintenance mode. +------------------------------------------------------- */ +#maintenance_mode +{ + text-align: right; +} +#maintenance_mode img.floatleft +{ + margin-left: 1em; +} +/* common for all admin sections */ +h3.titlebg img +{ + margin-left: 0.5em; +} +tr.titlebg td +{ + padding-right: 0.7em; +} +#admin_menu +{ + padding-right: 0; +} +#admin_content +{ + clear: right; +} +/* Styles for sidebar menus. +------------------------------------------------------- */ +#left_admsection +{ + float: right; + padding-right: 0; + padding-left: 10px; +} +.left_admmenu li +{ + padding: 0 0.5em 0 0; +} +/* Styles for generic tables. +------------------------------------------------------- */ +.topic_table td.stickybg2 +{ + background-image: url(../images/icons/quick_sticky.gif); + background-repeat: no-repeat; + background-position: 2% 4px; +} +.topic_table td.lockedbg2 +{ + background-image: url(../images/icons/quick_lock.gif); + background-repeat: no-repeat; + background-position: 2% 4px; +} +.topic_table td.locked_sticky2 +{ + background-image: url(../images/icons/quick_sticky_lock.gif); + background-repeat: no-repeat; + background-position: 2% 4px; +} +.topic_table td.lastpost +{ + background-image: none; +} +/* Styles for (fatal) errors. +------------------------------------------------- */ +.errorbox p.alert +{ + float: right; +} +/* Styles for the profile section. +------------------------------------------------- */ +#basicinfo +{ + float: right; +} +#detailedinfo +{ + float: left; +} +#basicinfo ul li +{ + float: right; + margin-right: 0; + margin-left: 5px; +} +#detailedinfo div.content dl, #tracking div.content dl +{ + clear: left; +} +#detailedinfo div.content dt, #tracking div.content dt +{ + float: right; +} +#detailedinfo div.content dd, #tracking div.content dd +{ + float: right; +} +#avatar_server_stored div +{ + float: right; +} + +#main_admsection #basicinfo h4 +{ + float: right; +} +#main_admsection #basicinfo img.avatar +{ + float: left; +} +#main_admsection #basicinfo ul +{ + clear: right; +} +#main_admsection #basicinfo span#userstatus +{ + clear: right; +} + +/* Profile statistics */ +#generalstats div.content dt +{ + float: right; +} +#generalstats div.content dd +{ + float: right; +} + +/* Activity by time */ +#activitytime +{ + clear: right; +} +.activity_stats li +{ + float: right; +} +.activity_stats li span +{ + border-width: 1px 0 0 1px; +} +.activity_stats li.last span +{ + border-left: none; +} + +/* Most popular boards by posts and activity */ +#popularposts +{ + float: right; +} +#popularactivity +{ + float: left; +} + +#popularposts div.content dt, #popularactivity div.content dt +{ + float: right; +} +#popularposts div.content dd, #popularactivity div.content dd +{ + float: right; +} + +.profile_pie +{ + background-image: url(../images/stats_pie_rtl.png); + float: right; + margin-right: 0; + margin-left: 1em; +} + +/* View posts */ +.topic .time +{ + float: left; +} +.counter +{ + padding: 0.2em 0.2em 0.1em 0.5em; + float: right; +} +.topic .mod_icons +{ + text-align: left; + margin-right: 0; + margin-left: 1em; +} +#permissions div.permission_name +{ + margin: 0 0 0 1%; +} + +#ip_list li.header, #ip_list li.ip +{ + float: right; +} +#creator dt +{ + float: right; +} +#creator dd +{ + float: right; +} + +.ignoreboards ul +{ + margin: 0 1em 0 0; +} +.ignoreboards li +{ + float: right; +} + +#pick_theme +{ + float: right; +} +/* Styles for the statistics center. +------------------------------------------------- */ +#stats_left, #top_posters, #top_topics_replies, #top_topics_starter +{ + float: right; +} +#stats_right, #top_boards, #top_topics_views, #most_online +{ + float: left; +} +dl.stats dt +{ + float: right; +} +dl.stats dd +{ + text-align: left; +} +.statsbar div.bar +{ + float: right; +} +.statsbar div.bar div +{ + right: -6px; + padding: 0 0 0 6px; +} +tr.windowbg2 th.stats_month, tr.windowbg2 td.stats_day +{ + text-align: right; +} + +/* Styles for the calendar section. +------------------------------------------------- */ +#month_grid +{ + float: right; +} + +#main_grid table.weeklist td.windowbg +{ + + border-left: 2px solid #fff; + border-bottom: 2px solid #fff; +} + +#main_grid table.weeklist td.weekdays +{ + text-align: left; + vertical-align: middle; + border-right: 2px solid #fff; + border-bottom: 2px solid #fff; +} + +/* Styles for the advanced search section. +------------------------------------------------- */ +#searchform fieldset +{ + text-align: right; +} +#advanced_search dt +{ + float: right; + text-align: left; +} +#advanced_search dd +{ + float: right; + margin: 0 0.5em 0 0; + text-align: right; +} +/* Boards picker */ +#searchform fieldset div#searchBoardsExpand ul +{ + margin: 0 1em 0 0; +} +#searchform fieldset div#searchBoardsExpand li +{ + float: right; +} +#searchform fieldset p +{ + text-align: right; +} + +.search_results_posts .buttons +{ + padding: 5px 0 0 1em; +} + +/* Styles for the help section. +------------------------------------------------- */ +#helpmain h3.section +{ + padding: 0 0.5em 0.5em 0; +} +/* put back the bullets please */ +#helpmain ul +{ + margin: 0 2em 1em 0; + padding-left: 0; + padding-right: 1em; +} +#helpmain #messageindex +{ + clear: left; +} + +/* Styles for the admincenter (reverse admin.css). +------------------------------------------------- */ +#quick_search +{ + margin-left: 5px; +} +.features_image +{ + float: right; + margin: 0 1em 0.5em 2em; +} +.features_switch +{ + float: left; +} +.features h4 +{ + padding: 1em 0.5em 0.5em 0; +} +/* admin home */ +#live_news div.content dl +{ + padding: 0.5em 0.5em 0 0; +} +#smfAnnouncements dd +{ + padding: 0; + margin: 0 1.5em 1em 0; +} +#quick_tasks li +{ + float: right; + list-style-type: none; +} +.home_image +{ + float: right; +} +/* common admin classes */ +.additional_row input +{ + margin-left: 0; + margin-right: 2em; +} +#error_log td div.marginleft +{ + margin: 0 1ex 0 0 !important; +} + +/* Styles for the package manager. +------------------------------------------------- */ +#package_list .tborder +{ + margin: .25em 26px .25em 0; +} +#package_list ol, #package_list ol li +{ + margin-left: 0; + margin-right: 50px; +} +/* ManageBoards */ +#manage_boards ul +{ + overflow: hidden; +} +#manage_boards li +{ + overflow: hidden; +} +.move_links +{ + padding: 0 0 0 13px; +} + +span.search_weight +{ + text-align: left; +} +/* Manage Bans */ +.ban_restriction +{ + margin: 0.2em 2.2em 0.2em 0; +} +/* Themes */ +.is_directory +{ + padding-right: 18px; + background: url(../images/admin/boards.gif) no-repeat; + background-position: 100% 0; +} +/* Styles for the moderation center. +------------------------------------------------- */ +.modblock_left +{ + float: right; + clear: left; +} +.modblock_right +{ + float: left; +} +ul.moderation_notes li +{ + padding: 4px 4px 4px 0; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/webkit.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/webkit.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,10 @@ +/* + Special styles for Safari (and other Webkit-based browsers like Chrome) + Webkit needs this otherwise the post goes off to the right. + Causes issues in IE browsers, and breaks cached search engines pages. +*/ + +table.table_list tbody.header td div.cat_bar +{ + margin-bottom: -1px; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/css/wireless.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/css/wireless.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,35 @@ +.catbg, tr.catbg td +{ + background-color: #6d92aa; + color: #fff; +} +.titlebg, .titlebg a, .titlebg a:link, .titlebg a:visited +{ + background-color: #b6dbff; + color: #000; + text-decoration: none; +} +.windowbg, tr.windowbg td +{ + background-color: #fff; + color: #000; +} +.windowbg2, tr.windowbg2 td +{ + background-color: #c0c0c0; + color: #000; +} +.new, a:link.new, a:visited.new +{ + background-color: #2f2fc0; + color: #fff; +} +.updated +{ + color: red; +} +/* Resize our post area as needed */ +#message +{ + width: 98%; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fader.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/fader.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,118 @@ +// smfFadeIndex: the current item in smfFadeContent. +var smfFadeIndex = -1; +// smfFadePercent: percent of fade. (-64 to 510.) +var smfFadePercent = 510 +// smfFadeSwitch: direction. (in or out) +var smfFadeSwitch = false; +// smfFadeScroller: the actual div to mess with. +var smfFadeScroller = document.getElementById('smfFadeScroller'); +// The ranges to fade from for R, G, and B. (how far apart they are.) +var smfFadeRange = { + 'r': smfFadeFrom.r - smfFadeTo.r, + 'g': smfFadeFrom.g - smfFadeTo.g, + 'b': smfFadeFrom.b - smfFadeTo.b +}; + +// Divide by 20 because we are doing it 20 times per one ms. +smfFadeDelay /= 20; + +// Start the fader! +window.setTimeout('smfFader();', 20); + +// Main fading function... called 50 times every second. +function smfFader() +{ + if (smfFadeContent.length <= 1) + return; + + // A fix for Internet Explorer 4: wait until the document is loaded so we can use setInnerHTML(). + if (typeof(window.document.readyState) != "undefined" && window.document.readyState != "complete") + { + window.setTimeout('smfFader();', 20); + return; + } + + // Starting out? Set up the first item. + if (smfFadeIndex == -1) + { + setInnerHTML(smfFadeScroller, smfFadeBefore + smfFadeContent[0] + smfFadeAfter); + smfFadeIndex = 1; + + // In Mozilla, text jumps around from this when 1 or 0.5, etc... + if (typeof(smfFadeScroller.style.MozOpacity) != "undefined") + smfFadeScroller.style.MozOpacity = "0.90"; + else if (typeof(smfFadeScroller.style.opacity) != "undefined") + smfFadeScroller.style.opacity = "0.90"; + // In Internet Explorer, we have to define this to use it. + else if (typeof(smfFadeScroller.style.filter) != "undefined") + smfFadeScroller.style.filter = "alpha(opacity=100)"; + } + + // Are we already done fading in? If so, fade out. + if (smfFadePercent >= 510) + smfFadeSwitch = !smfFadeSwitch; + // All the way faded out? + else if (smfFadePercent <= -64) + { + smfFadeSwitch = !smfFadeSwitch; + + // Go to the next item, or first if we're out of items. + setInnerHTML(smfFadeScroller, smfFadeBefore + smfFadeContent[smfFadeIndex++] + smfFadeAfter); + if (smfFadeIndex >= smfFadeContent.length) + smfFadeIndex = 0; + } + + // Increment or decrement the fade percentage. + if (smfFadeSwitch) + smfFadePercent -= 255 / smfFadeDelay * 2; + else + smfFadePercent += 255 / smfFadeDelay * 2; + + // If it's not outside 0 and 256... (otherwise it's just delay time.) + if (smfFadePercent < 256 && smfFadePercent > 0) + { + // Easier... also faster... + var tempPercent = smfFadePercent / 255, rounded; + + if (typeof(smfFadeScroller.style.MozOpacity) != "undefined") + { + rounded = Math.round(tempPercent * 100) / 100; + smfFadeScroller.style.MozOpacity = rounded == 1 ? "0.99" : rounded; + } + else if (typeof(smfFadeScroller.style.opacity) != "undefined") + { + rounded = Math.round(tempPercent * 100) / 100; + smfFadeScroller.style.opacity = rounded == 1 ? "0.99" : rounded; + } + else + { + var done = false; + if (typeof(smfFadeScroller.filters.alpha) != "undefined") + { + // Internet Explorer 4 just can't handle "try". + eval("try\ + {\ + smfFadeScroller.filters.alpha.opacity = Math.round(tempPercent * 100);\ + done = true;\ + }\ + catch (err)\ + {\ + }"); + } + + if (!done) + { + // Get the new R, G, and B. (it should be bottom + (range of color * percent)...) + var r = Math.ceil(smfFadeTo.r + smfFadeRange.r * tempPercent); + var g = Math.ceil(smfFadeTo.g + smfFadeRange.g * tempPercent); + var b = Math.ceil(smfFadeTo.b + smfFadeRange.b * tempPercent); + + // Set the color in the style, thereby fading it. + smfFadeScroller.style.color = 'rgb(' + r + ', ' + g + ', ' + b + ')'; + } + } + } + + // Keep going. + window.setTimeout('smfFader();', 20); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts-compat.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/fonts-compat.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,16 @@ +body, td, .normaltext, .windowbg, .windowbg2, .titlebg, .bordercolor, .tborder, .catbg, .catbg2, .windowbg td, .windowbg2 td, .titlebg td +{ + font-size: x-small; +} +.smalltext, td.smalltext, i.smalltext, div.smalltext, .smalltext td, .quote, .quoteheader, .codeheader , .middletext +{ + font-size: xx-small; +} +.code +{ + font-size: xx-small; +} +.largetext +{ + font-size: medium; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice.gdf Binary file forum/Themes/default/fonts/Candice.gdf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/a.gif Binary file forum/Themes/default/fonts/Candice/a.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/b.gif Binary file forum/Themes/default/fonts/Candice/b.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/c.gif Binary file forum/Themes/default/fonts/Candice/c.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/d.gif Binary file forum/Themes/default/fonts/Candice/d.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/e.gif Binary file forum/Themes/default/fonts/Candice/e.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/f.gif Binary file forum/Themes/default/fonts/Candice/f.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/g.gif Binary file forum/Themes/default/fonts/Candice/g.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/h.gif Binary file forum/Themes/default/fonts/Candice/h.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/i.gif Binary file forum/Themes/default/fonts/Candice/i.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/fonts/Candice/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/j.gif Binary file forum/Themes/default/fonts/Candice/j.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/k.gif Binary file forum/Themes/default/fonts/Candice/k.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/l.gif Binary file forum/Themes/default/fonts/Candice/l.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/m.gif Binary file forum/Themes/default/fonts/Candice/m.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/n.gif Binary file forum/Themes/default/fonts/Candice/n.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/o.gif Binary file forum/Themes/default/fonts/Candice/o.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/p.gif Binary file forum/Themes/default/fonts/Candice/p.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/q.gif Binary file forum/Themes/default/fonts/Candice/q.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/r.gif Binary file forum/Themes/default/fonts/Candice/r.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/s.gif Binary file forum/Themes/default/fonts/Candice/s.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/t.gif Binary file forum/Themes/default/fonts/Candice/t.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/u.gif Binary file forum/Themes/default/fonts/Candice/u.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/v.gif Binary file forum/Themes/default/fonts/Candice/v.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/w.gif Binary file forum/Themes/default/fonts/Candice/w.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/x.gif Binary file forum/Themes/default/fonts/Candice/x.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/y.gif Binary file forum/Themes/default/fonts/Candice/y.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Candice/z.gif Binary file forum/Themes/default/fonts/Candice/z.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Forgottb.ttf Binary file forum/Themes/default/fonts/Forgottb.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie.gdf Binary file forum/Themes/default/fonts/Hootie.gdf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/a.gif Binary file forum/Themes/default/fonts/Hootie/a.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/b.gif Binary file forum/Themes/default/fonts/Hootie/b.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/c.gif Binary file forum/Themes/default/fonts/Hootie/c.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/d.gif Binary file forum/Themes/default/fonts/Hootie/d.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/e.gif Binary file forum/Themes/default/fonts/Hootie/e.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/f.gif Binary file forum/Themes/default/fonts/Hootie/f.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/g.gif Binary file forum/Themes/default/fonts/Hootie/g.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/h.gif Binary file forum/Themes/default/fonts/Hootie/h.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/i.gif Binary file forum/Themes/default/fonts/Hootie/i.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/fonts/Hootie/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/j.gif Binary file forum/Themes/default/fonts/Hootie/j.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/k.gif Binary file forum/Themes/default/fonts/Hootie/k.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/l.gif Binary file forum/Themes/default/fonts/Hootie/l.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/m.gif Binary file forum/Themes/default/fonts/Hootie/m.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/n.gif Binary file forum/Themes/default/fonts/Hootie/n.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/o.gif Binary file forum/Themes/default/fonts/Hootie/o.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/p.gif Binary file forum/Themes/default/fonts/Hootie/p.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/q.gif Binary file forum/Themes/default/fonts/Hootie/q.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/r.gif Binary file forum/Themes/default/fonts/Hootie/r.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/s.gif Binary file forum/Themes/default/fonts/Hootie/s.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/t.gif Binary file forum/Themes/default/fonts/Hootie/t.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/u.gif Binary file forum/Themes/default/fonts/Hootie/u.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/v.gif Binary file forum/Themes/default/fonts/Hootie/v.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/w.gif Binary file forum/Themes/default/fonts/Hootie/w.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/x.gif Binary file forum/Themes/default/fonts/Hootie/x.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/y.gif Binary file forum/Themes/default/fonts/Hootie/y.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Hootie/z.gif Binary file forum/Themes/default/fonts/Hootie/z.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Kimbalt.ttf Binary file forum/Themes/default/fonts/Kimbalt.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President.gdf Binary file forum/Themes/default/fonts/President.gdf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/a.gif Binary file forum/Themes/default/fonts/President/a.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/b.gif Binary file forum/Themes/default/fonts/President/b.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/c.gif Binary file forum/Themes/default/fonts/President/c.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/d.gif Binary file forum/Themes/default/fonts/President/d.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/e.gif Binary file forum/Themes/default/fonts/President/e.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/f.gif Binary file forum/Themes/default/fonts/President/f.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/g.gif Binary file forum/Themes/default/fonts/President/g.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/h.gif Binary file forum/Themes/default/fonts/President/h.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/i.gif Binary file forum/Themes/default/fonts/President/i.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/fonts/President/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/j.gif Binary file forum/Themes/default/fonts/President/j.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/k.gif Binary file forum/Themes/default/fonts/President/k.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/l.gif Binary file forum/Themes/default/fonts/President/l.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/m.gif Binary file forum/Themes/default/fonts/President/m.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/n.gif Binary file forum/Themes/default/fonts/President/n.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/o.gif Binary file forum/Themes/default/fonts/President/o.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/p.gif Binary file forum/Themes/default/fonts/President/p.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/q.gif Binary file forum/Themes/default/fonts/President/q.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/r.gif Binary file forum/Themes/default/fonts/President/r.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/s.gif Binary file forum/Themes/default/fonts/President/s.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/t.gif Binary file forum/Themes/default/fonts/President/t.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/u.gif Binary file forum/Themes/default/fonts/President/u.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/v.gif Binary file forum/Themes/default/fonts/President/v.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/w.gif Binary file forum/Themes/default/fonts/President/w.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/x.gif Binary file forum/Themes/default/fonts/President/x.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/y.gif Binary file forum/Themes/default/fonts/President/y.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/President/z.gif Binary file forum/Themes/default/fonts/President/z.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Screenge.ttf Binary file forum/Themes/default/fonts/Screenge.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Venusris.ttf Binary file forum/Themes/default/fonts/Venusris.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/Walshes.ttf Binary file forum/Themes/default/fonts/Walshes.ttf has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/fonts/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/a.english.wav Binary file forum/Themes/default/fonts/sound/a.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/b.english.wav Binary file forum/Themes/default/fonts/sound/b.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/c.english.wav Binary file forum/Themes/default/fonts/sound/c.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/d.english.wav Binary file forum/Themes/default/fonts/sound/d.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/e.english.wav Binary file forum/Themes/default/fonts/sound/e.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/f.english.wav Binary file forum/Themes/default/fonts/sound/f.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/g.english.wav Binary file forum/Themes/default/fonts/sound/g.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/h.english.wav Binary file forum/Themes/default/fonts/sound/h.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/i.english.wav Binary file forum/Themes/default/fonts/sound/i.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/fonts/sound/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/j.english.wav Binary file forum/Themes/default/fonts/sound/j.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/k.english.wav Binary file forum/Themes/default/fonts/sound/k.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/l.english.wav Binary file forum/Themes/default/fonts/sound/l.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/m.english.wav Binary file forum/Themes/default/fonts/sound/m.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/n.english.wav Binary file forum/Themes/default/fonts/sound/n.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/o.english.wav Binary file forum/Themes/default/fonts/sound/o.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/p.english.wav Binary file forum/Themes/default/fonts/sound/p.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/q.english.wav Binary file forum/Themes/default/fonts/sound/q.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/r.english.wav Binary file forum/Themes/default/fonts/sound/r.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/s.english.wav Binary file forum/Themes/default/fonts/sound/s.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/t.english.wav Binary file forum/Themes/default/fonts/sound/t.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/u.english.wav Binary file forum/Themes/default/fonts/sound/u.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/v.english.wav Binary file forum/Themes/default/fonts/sound/v.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/w.english.wav Binary file forum/Themes/default/fonts/sound/w.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/x.english.wav Binary file forum/Themes/default/fonts/sound/x.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/y.english.wav Binary file forum/Themes/default/fonts/sound/y.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/fonts/sound/z.english.wav Binary file forum/Themes/default/fonts/sound/z.english.wav has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/help.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/help.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,69 @@ +h1 +{ + font-size: 18px; + margin: 1ex 1ex 1ex 1ex; +} +h2 +{ + font-size: 18px; + border-bottom: solid 1px; + padding-bottom: 1ex; +} +h3, h4, h5, h6 +{ + font-size: 16px; + margin: 0; +} +p +{ + margin: 0 0 1.5em 0; +} +.board, .board:link +{ + text-decoration: underline; +} +.board:visited, .board:hover +{ + text-decoration: underline; +} +div.footer +{ + text-align: center; + padding: 3pt; +} +#menu +{ + line-height: 1; +} +#menu p +{ + margin: 0 0 1em 0; + line-height: 2; +} +table#reference1, table#reference2 +{ + border: 1px solid; +} +table#reference1 td, table#reference2 td +{ + vertical-align: top; + border: 1px solid; + font-size: x-small; + } +table#reference1 th, table#reference2 th +{ + font-size: x-small; +} +ol +{ + font-weight: bold; + list-style-type: square; + margin-bottom: 2ex; + margin-top: 3ex; +} +ol.la +{ + font-weight: normal; + list-style-type: circle; + margin: 0 0 2ex 4ex; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/Female.gif Binary file forum/Themes/default/images/Female.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/Male.gif Binary file forum/Themes/default/images/Male.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/administration.gif Binary file forum/Themes/default/images/admin/administration.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/attachment.gif Binary file forum/Themes/default/images/admin/attachment.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/ban.gif Binary file forum/Themes/default/images/admin/ban.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/boards.gif Binary file forum/Themes/default/images/admin/boards.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/calendar.gif Binary file forum/Themes/default/images/admin/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/change_menu.png Binary file forum/Themes/default/images/admin/change_menu.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/change_menu2.png Binary file forum/Themes/default/images/admin/change_menu2.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/corefeatures.gif Binary file forum/Themes/default/images/admin/corefeatures.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/current_theme.gif Binary file forum/Themes/default/images/admin/current_theme.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/engines.gif Binary file forum/Themes/default/images/admin/engines.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/feature_cd.png Binary file forum/Themes/default/images/admin/feature_cd.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/feature_cp.png Binary file forum/Themes/default/images/admin/feature_cp.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/feature_k.png Binary file forum/Themes/default/images/admin/feature_k.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/feature_ml.png Binary file forum/Themes/default/images/admin/feature_ml.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/feature_pm.png Binary file forum/Themes/default/images/admin/feature_pm.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/feature_ps.png Binary file forum/Themes/default/images/admin/feature_ps.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/feature_rg.png Binary file forum/Themes/default/images/admin/feature_rg.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/feature_sp.png Binary file forum/Themes/default/images/admin/feature_sp.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/feature_w.png Binary file forum/Themes/default/images/admin/feature_w.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/features.gif Binary file forum/Themes/default/images/admin/features.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/features_and_options.png Binary file forum/Themes/default/images/admin/features_and_options.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/forum_maintenance.png Binary file forum/Themes/default/images/admin/forum_maintenance.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/ignore.gif Binary file forum/Themes/default/images/admin/ignore.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/images/admin/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/languages.gif Binary file forum/Themes/default/images/admin/languages.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/logs.gif Binary file forum/Themes/default/images/admin/logs.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/mail.gif Binary file forum/Themes/default/images/admin/mail.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/maintain.gif Binary file forum/Themes/default/images/admin/maintain.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/membergroups.gif Binary file forum/Themes/default/images/admin/membergroups.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/members.gif Binary file forum/Themes/default/images/admin/members.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/members.png Binary file forum/Themes/default/images/admin/members.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/modifications.gif Binary file forum/Themes/default/images/admin/modifications.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/news.gif Binary file forum/Themes/default/images/admin/news.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/package_ops.gif Binary file forum/Themes/default/images/admin/package_ops.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/packages.gif Binary file forum/Themes/default/images/admin/packages.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/packages.png Binary file forum/Themes/default/images/admin/packages.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/paid.gif Binary file forum/Themes/default/images/admin/paid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/permissions.gif Binary file forum/Themes/default/images/admin/permissions.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/permissions.png Binary file forum/Themes/default/images/admin/permissions.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/post_moderation_allow.gif Binary file forum/Themes/default/images/admin/post_moderation_allow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/post_moderation_deny.gif Binary file forum/Themes/default/images/admin/post_moderation_deny.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/post_moderation_moderate.gif Binary file forum/Themes/default/images/admin/post_moderation_moderate.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/posts.gif Binary file forum/Themes/default/images/admin/posts.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/regcenter.gif Binary file forum/Themes/default/images/admin/regcenter.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/reports.gif Binary file forum/Themes/default/images/admin/reports.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/scheduled.gif Binary file forum/Themes/default/images/admin/scheduled.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/search.gif Binary file forum/Themes/default/images/admin/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/security.gif Binary file forum/Themes/default/images/admin/security.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/server.gif Binary file forum/Themes/default/images/admin/server.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/smiley.gif Binary file forum/Themes/default/images/admin/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/smilies_and_messageicons.png Binary file forum/Themes/default/images/admin/smilies_and_messageicons.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/subsection.gif Binary file forum/Themes/default/images/admin/subsection.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/support.gif Binary file forum/Themes/default/images/admin/support.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/support_and_credits.png Binary file forum/Themes/default/images/admin/support_and_credits.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/switch_off.png Binary file forum/Themes/default/images/admin/switch_off.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/switch_on.png Binary file forum/Themes/default/images/admin/switch_on.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/themes.gif Binary file forum/Themes/default/images/admin/themes.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/admin/themes_and_layout.png Binary file forum/Themes/default/images/admin/themes_and_layout.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/aim.gif Binary file forum/Themes/default/images/aim.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bar.gif Binary file forum/Themes/default/images/bar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bar_stats.png Binary file forum/Themes/default/images/bar_stats.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/bbc_bg.gif Binary file forum/Themes/default/images/bbc/bbc_bg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/bbc_hoverbg.gif Binary file forum/Themes/default/images/bbc/bbc_hoverbg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/bold.gif Binary file forum/Themes/default/images/bbc/bold.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/center.gif Binary file forum/Themes/default/images/bbc/center.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/code.gif Binary file forum/Themes/default/images/bbc/code.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/divider.gif Binary file forum/Themes/default/images/bbc/divider.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/email.gif Binary file forum/Themes/default/images/bbc/email.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/face.gif Binary file forum/Themes/default/images/bbc/face.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/flash.gif Binary file forum/Themes/default/images/bbc/flash.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/ftp.gif Binary file forum/Themes/default/images/bbc/ftp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/glow.gif Binary file forum/Themes/default/images/bbc/glow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/hr.gif Binary file forum/Themes/default/images/bbc/hr.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/img.gif Binary file forum/Themes/default/images/bbc/img.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/images/bbc/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/italicize.gif Binary file forum/Themes/default/images/bbc/italicize.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/left.gif Binary file forum/Themes/default/images/bbc/left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/list.gif Binary file forum/Themes/default/images/bbc/list.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/move.gif Binary file forum/Themes/default/images/bbc/move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/orderlist.gif Binary file forum/Themes/default/images/bbc/orderlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/pre.gif Binary file forum/Themes/default/images/bbc/pre.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/quote.gif Binary file forum/Themes/default/images/bbc/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/resize-handle.gif Binary file forum/Themes/default/images/bbc/resize-handle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/right.gif Binary file forum/Themes/default/images/bbc/right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/shadow.gif Binary file forum/Themes/default/images/bbc/shadow.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/size.gif Binary file forum/Themes/default/images/bbc/size.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/strike.gif Binary file forum/Themes/default/images/bbc/strike.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/sub.gif Binary file forum/Themes/default/images/bbc/sub.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/sup.gif Binary file forum/Themes/default/images/bbc/sup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/table.gif Binary file forum/Themes/default/images/bbc/table.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/td.gif Binary file forum/Themes/default/images/bbc/td.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/tele.gif Binary file forum/Themes/default/images/bbc/tele.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/toggle.gif Binary file forum/Themes/default/images/bbc/toggle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/tr.gif Binary file forum/Themes/default/images/bbc/tr.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/underline.gif Binary file forum/Themes/default/images/bbc/underline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/unformat.gif Binary file forum/Themes/default/images/bbc/unformat.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bbc/url.gif Binary file forum/Themes/default/images/bbc/url.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/bdaycake.gif Binary file forum/Themes/default/images/bdaycake.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/blank.gif Binary file forum/Themes/default/images/blank.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/board.gif Binary file forum/Themes/default/images/board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/board_select_spot.gif Binary file forum/Themes/default/images/board_select_spot.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/board_select_spot_child.gif Binary file forum/Themes/default/images/board_select_spot_child.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/box_bg.gif Binary file forum/Themes/default/images/box_bg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buddy_useroff.gif Binary file forum/Themes/default/images/buddy_useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buddy_useron.gif Binary file forum/Themes/default/images/buddy_useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/approve.gif Binary file forum/Themes/default/images/buttons/approve.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/calendarpe.gif Binary file forum/Themes/default/images/buttons/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/close.gif Binary file forum/Themes/default/images/buttons/close.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/delete.gif Binary file forum/Themes/default/images/buttons/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/details.gif Binary file forum/Themes/default/images/buttons/details.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/ignore.gif Binary file forum/Themes/default/images/buttons/ignore.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/im_reply.gif Binary file forum/Themes/default/images/buttons/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/im_reply_all.gif Binary file forum/Themes/default/images/buttons/im_reply_all.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/images/buttons/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/merge.gif Binary file forum/Themes/default/images/buttons/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/modify.gif Binary file forum/Themes/default/images/buttons/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/notify_sm.gif Binary file forum/Themes/default/images/buttons/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/quote.gif Binary file forum/Themes/default/images/buttons/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/reply.gif Binary file forum/Themes/default/images/buttons/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/reply_sm.gif Binary file forum/Themes/default/images/buttons/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/restore_topic.gif Binary file forum/Themes/default/images/buttons/restore_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/search.gif Binary file forum/Themes/default/images/buttons/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/buttons/split.gif Binary file forum/Themes/default/images/buttons/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/cake.png Binary file forum/Themes/default/images/cake.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/catbg.jpg Binary file forum/Themes/default/images/catbg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/catbg2.jpg Binary file forum/Themes/default/images/catbg2.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/collapse.gif Binary file forum/Themes/default/images/collapse.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/construction.gif Binary file forum/Themes/default/images/construction.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/construction.png Binary file forum/Themes/default/images/construction.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/addpoll.gif Binary file forum/Themes/default/images/dutch/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/admin.gif Binary file forum/Themes/default/images/dutch/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/admin_lock.gif Binary file forum/Themes/default/images/dutch/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/admin_move.gif Binary file forum/Themes/default/images/dutch/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/admin_rem.gif Binary file forum/Themes/default/images/dutch/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/admin_remove_poll.gif Binary file forum/Themes/default/images/dutch/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/admin_sticky.gif Binary file forum/Themes/default/images/dutch/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/calendar.gif Binary file forum/Themes/default/images/dutch/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/calendarpe.gif Binary file forum/Themes/default/images/dutch/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/delete.gif Binary file forum/Themes/default/images/dutch/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/delete_selected.gif Binary file forum/Themes/default/images/dutch/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/go_down.gif Binary file forum/Themes/default/images/dutch/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/go_up.gif Binary file forum/Themes/default/images/dutch/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/help.gif Binary file forum/Themes/default/images/dutch/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/home.gif Binary file forum/Themes/default/images/dutch/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/im_delete.gif Binary file forum/Themes/default/images/dutch/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/im_inbox.gif Binary file forum/Themes/default/images/dutch/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/im_new.gif Binary file forum/Themes/default/images/dutch/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/im_outbox.gif Binary file forum/Themes/default/images/dutch/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/im_reload.gif Binary file forum/Themes/default/images/dutch/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/im_reply.gif Binary file forum/Themes/default/images/dutch/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/keystats.gif Binary file forum/Themes/default/images/dutch/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/linktocal.gif Binary file forum/Themes/default/images/dutch/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/login.gif Binary file forum/Themes/default/images/dutch/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/logout.gif Binary file forum/Themes/default/images/dutch/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/markread.gif Binary file forum/Themes/default/images/dutch/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/markunread.gif Binary file forum/Themes/default/images/dutch/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/memberlist.gif Binary file forum/Themes/default/images/dutch/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/merge.gif Binary file forum/Themes/default/images/dutch/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/modify.gif Binary file forum/Themes/default/images/dutch/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/new.gif Binary file forum/Themes/default/images/dutch/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/new_poll.gif Binary file forum/Themes/default/images/dutch/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/new_topic.gif Binary file forum/Themes/default/images/dutch/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/newsbox.gif Binary file forum/Themes/default/images/dutch/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/notify.gif Binary file forum/Themes/default/images/dutch/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/notify_sm.gif Binary file forum/Themes/default/images/dutch/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/print.gif Binary file forum/Themes/default/images/dutch/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/profile.gif Binary file forum/Themes/default/images/dutch/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/quote.gif Binary file forum/Themes/default/images/dutch/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/register.gif Binary file forum/Themes/default/images/dutch/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/reply.gif Binary file forum/Themes/default/images/dutch/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/reply_sm.gif Binary file forum/Themes/default/images/dutch/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/search.gif Binary file forum/Themes/default/images/dutch/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/sendtopic.gif Binary file forum/Themes/default/images/dutch/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/split.gif Binary file forum/Themes/default/images/dutch/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/userinfo.gif Binary file forum/Themes/default/images/dutch/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/dutch/who.gif Binary file forum/Themes/default/images/dutch/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/email_sm.gif Binary file forum/Themes/default/images/email_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/addpoll.gif Binary file forum/Themes/default/images/english/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/admin.gif Binary file forum/Themes/default/images/english/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/admin_lock.gif Binary file forum/Themes/default/images/english/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/admin_move.gif Binary file forum/Themes/default/images/english/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/admin_rem.gif Binary file forum/Themes/default/images/english/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/admin_remove_poll.gif Binary file forum/Themes/default/images/english/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/admin_sticky.gif Binary file forum/Themes/default/images/english/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/calendar.gif Binary file forum/Themes/default/images/english/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/calendarpe.gif Binary file forum/Themes/default/images/english/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/delete.gif Binary file forum/Themes/default/images/english/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/delete_selected.gif Binary file forum/Themes/default/images/english/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/go_down.gif Binary file forum/Themes/default/images/english/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/go_up.gif Binary file forum/Themes/default/images/english/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/help.gif Binary file forum/Themes/default/images/english/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/home.gif Binary file forum/Themes/default/images/english/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/im_delete.gif Binary file forum/Themes/default/images/english/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/im_inbox.gif Binary file forum/Themes/default/images/english/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/im_new.gif Binary file forum/Themes/default/images/english/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/im_outbox.gif Binary file forum/Themes/default/images/english/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/im_reload.gif Binary file forum/Themes/default/images/english/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/im_reply.gif Binary file forum/Themes/default/images/english/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/images/english/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/keystats.gif Binary file forum/Themes/default/images/english/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/linktocal.gif Binary file forum/Themes/default/images/english/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/login.gif Binary file forum/Themes/default/images/english/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/logout.gif Binary file forum/Themes/default/images/english/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/markread.gif Binary file forum/Themes/default/images/english/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/markunread.gif Binary file forum/Themes/default/images/english/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/memberlist.gif Binary file forum/Themes/default/images/english/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/merge.gif Binary file forum/Themes/default/images/english/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/modify.gif Binary file forum/Themes/default/images/english/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/new.gif Binary file forum/Themes/default/images/english/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/new_poll.gif Binary file forum/Themes/default/images/english/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/new_topic.gif Binary file forum/Themes/default/images/english/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/newsbox.gif Binary file forum/Themes/default/images/english/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/notify.gif Binary file forum/Themes/default/images/english/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/notify_sm.gif Binary file forum/Themes/default/images/english/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/print.gif Binary file forum/Themes/default/images/english/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/profile.gif Binary file forum/Themes/default/images/english/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/quote.gif Binary file forum/Themes/default/images/english/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/register.gif Binary file forum/Themes/default/images/english/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/reply.gif Binary file forum/Themes/default/images/english/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/reply_sm.gif Binary file forum/Themes/default/images/english/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/search.gif Binary file forum/Themes/default/images/english/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/sendtopic.gif Binary file forum/Themes/default/images/english/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/split.gif Binary file forum/Themes/default/images/english/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/userinfo.gif Binary file forum/Themes/default/images/english/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/english/who.gif Binary file forum/Themes/default/images/english/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/expand.gif Binary file forum/Themes/default/images/expand.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/filter.gif Binary file forum/Themes/default/images/filter.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/addpoll.gif Binary file forum/Themes/default/images/german/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/admin.gif Binary file forum/Themes/default/images/german/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/admin_lock.gif Binary file forum/Themes/default/images/german/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/admin_move.gif Binary file forum/Themes/default/images/german/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/admin_rem.gif Binary file forum/Themes/default/images/german/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/admin_remove_poll.gif Binary file forum/Themes/default/images/german/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/admin_sticky.gif Binary file forum/Themes/default/images/german/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/calendar.gif Binary file forum/Themes/default/images/german/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/calendarpe.gif Binary file forum/Themes/default/images/german/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/delete.gif Binary file forum/Themes/default/images/german/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/delete_selected.gif Binary file forum/Themes/default/images/german/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/go_down.gif Binary file forum/Themes/default/images/german/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/go_up.gif Binary file forum/Themes/default/images/german/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/help.gif Binary file forum/Themes/default/images/german/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/home.gif Binary file forum/Themes/default/images/german/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/im_delete.gif Binary file forum/Themes/default/images/german/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/im_inbox.gif Binary file forum/Themes/default/images/german/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/im_new.gif Binary file forum/Themes/default/images/german/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/im_outbox.gif Binary file forum/Themes/default/images/german/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/im_reload.gif Binary file forum/Themes/default/images/german/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/im_reply.gif Binary file forum/Themes/default/images/german/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/keystats.gif Binary file forum/Themes/default/images/german/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/linktocal.gif Binary file forum/Themes/default/images/german/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/login.gif Binary file forum/Themes/default/images/german/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/logout.gif Binary file forum/Themes/default/images/german/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/markread.gif Binary file forum/Themes/default/images/german/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/markunread.gif Binary file forum/Themes/default/images/german/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/memberlist.gif Binary file forum/Themes/default/images/german/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/merge.gif Binary file forum/Themes/default/images/german/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/modify.gif Binary file forum/Themes/default/images/german/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/new.gif Binary file forum/Themes/default/images/german/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/new_poll.gif Binary file forum/Themes/default/images/german/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/new_topic.gif Binary file forum/Themes/default/images/german/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/newsbox.gif Binary file forum/Themes/default/images/german/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/notify.gif Binary file forum/Themes/default/images/german/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/notify_sm.gif Binary file forum/Themes/default/images/german/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/print.gif Binary file forum/Themes/default/images/german/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/profile.gif Binary file forum/Themes/default/images/german/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/quote.gif Binary file forum/Themes/default/images/german/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/register.gif Binary file forum/Themes/default/images/german/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/reply.gif Binary file forum/Themes/default/images/german/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/reply_sm.gif Binary file forum/Themes/default/images/german/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/search.gif Binary file forum/Themes/default/images/german/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/sendtopic.gif Binary file forum/Themes/default/images/german/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/split.gif Binary file forum/Themes/default/images/german/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/userinfo.gif Binary file forum/Themes/default/images/german/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/german/who.gif Binary file forum/Themes/default/images/german/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/h_powered-mysql.gif Binary file forum/Themes/default/images/h_powered-mysql.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/h_powered-php.gif Binary file forum/Themes/default/images/h_powered-php.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/h_valid-css.gif Binary file forum/Themes/default/images/h_valid-css.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/h_valid-xhtml10.gif Binary file forum/Themes/default/images/h_valid-xhtml10.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/helplogo.jpg Binary file forum/Themes/default/images/helplogo.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/helptopics.gif Binary file forum/Themes/default/images/helptopics.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/assist.gif Binary file forum/Themes/default/images/icons/assist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/bullet_grin.gif Binary file forum/Themes/default/images/icons/bullet_grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/calendar.gif Binary file forum/Themes/default/images/icons/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/clip.gif Binary file forum/Themes/default/images/icons/clip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/config_sm.gif Binary file forum/Themes/default/images/icons/config_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/delete.gif Binary file forum/Themes/default/images/icons/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/field_check.gif Binary file forum/Themes/default/images/icons/field_check.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/field_invalid.gif Binary file forum/Themes/default/images/icons/field_invalid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/field_valid.gif Binary file forum/Themes/default/images/icons/field_valid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/folder_open.gif Binary file forum/Themes/default/images/icons/folder_open.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/im_newmsg.gif Binary file forum/Themes/default/images/icons/im_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/images/icons/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/info.gif Binary file forum/Themes/default/images/icons/info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/last_post.gif Binary file forum/Themes/default/images/icons/last_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/linktree_main.gif Binary file forum/Themes/default/images/icons/linktree_main.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/linktree_side.gif Binary file forum/Themes/default/images/icons/linktree_side.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/login.gif Binary file forum/Themes/default/images/icons/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/login_sm.gif Binary file forum/Themes/default/images/icons/login_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/members.gif Binary file forum/Themes/default/images/icons/members.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/modify_inline.gif Binary file forum/Themes/default/images/icons/modify_inline.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/modify_small.gif Binary file forum/Themes/default/images/icons/modify_small.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/notify_sm.gif Binary file forum/Themes/default/images/icons/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/online.gif Binary file forum/Themes/default/images/icons/online.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/package_installed.gif Binary file forum/Themes/default/images/icons/package_installed.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/package_old.gif Binary file forum/Themes/default/images/icons/package_old.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/pm_read.gif Binary file forum/Themes/default/images/icons/pm_read.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/pm_replied.gif Binary file forum/Themes/default/images/icons/pm_replied.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/profile_sm.gif Binary file forum/Themes/default/images/icons/profile_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/quick_lock.gif Binary file forum/Themes/default/images/icons/quick_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/quick_move.gif Binary file forum/Themes/default/images/icons/quick_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/quick_remove.gif Binary file forum/Themes/default/images/icons/quick_remove.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/quick_sticky.gif Binary file forum/Themes/default/images/icons/quick_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/quick_sticky_lock.gif Binary file forum/Themes/default/images/icons/quick_sticky_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icons/show_sticky.gif Binary file forum/Themes/default/images/icons/show_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/icq.gif Binary file forum/Themes/default/images/icq.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/im_off.gif Binary file forum/Themes/default/images/im_off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/im_on.gif Binary file forum/Themes/default/images/im_on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/im_sm_newmsg.gif Binary file forum/Themes/default/images/im_sm_newmsg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/im_sm_prefs.gif Binary file forum/Themes/default/images/im_sm_prefs.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/im_switch.gif Binary file forum/Themes/default/images/im_switch.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/images/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/ip.gif Binary file forum/Themes/default/images/ip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/loading.gif Binary file forum/Themes/default/images/loading.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/maintab_active_back.gif Binary file forum/Themes/default/images/maintab_active_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/maintab_active_first.gif Binary file forum/Themes/default/images/maintab_active_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/maintab_active_last.gif Binary file forum/Themes/default/images/maintab_active_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/maintab_back.gif Binary file forum/Themes/default/images/maintab_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/maintab_first.gif Binary file forum/Themes/default/images/maintab_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/maintab_last.gif Binary file forum/Themes/default/images/maintab_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/menubg.gif Binary file forum/Themes/default/images/menubg.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/message_sm.gif Binary file forum/Themes/default/images/message_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/mirrortab_active_back.gif Binary file forum/Themes/default/images/mirrortab_active_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/mirrortab_active_first.gif Binary file forum/Themes/default/images/mirrortab_active_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/mirrortab_active_last.gif Binary file forum/Themes/default/images/mirrortab_active_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/mirrortab_back.gif Binary file forum/Themes/default/images/mirrortab_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/mirrortab_first.gif Binary file forum/Themes/default/images/mirrortab_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/mirrortab_last.gif Binary file forum/Themes/default/images/mirrortab_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/modtab_back.gif Binary file forum/Themes/default/images/modtab_back.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/modtab_first.gif Binary file forum/Themes/default/images/modtab_first.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/modtab_last.gif Binary file forum/Themes/default/images/modtab_last.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/msntalk.gif Binary file forum/Themes/default/images/msntalk.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/new_bar.gif Binary file forum/Themes/default/images/new_bar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/new_none.gif Binary file forum/Themes/default/images/new_none.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/new_none.png Binary file forum/Themes/default/images/new_none.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/new_redirect.png Binary file forum/Themes/default/images/new_redirect.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/new_some.gif Binary file forum/Themes/default/images/new_some.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/new_some.png Binary file forum/Themes/default/images/new_some.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/off.gif Binary file forum/Themes/default/images/off.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/off.png Binary file forum/Themes/default/images/off.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/on.gif Binary file forum/Themes/default/images/on.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/on.png Binary file forum/Themes/default/images/on.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/on2.gif Binary file forum/Themes/default/images/on2.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/on2.png Binary file forum/Themes/default/images/on2.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/openid.gif Binary file forum/Themes/default/images/openid.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/pm_recipient_delete.gif Binary file forum/Themes/default/images/pm_recipient_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/poll_left.gif Binary file forum/Themes/default/images/poll_left.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/poll_middle.gif Binary file forum/Themes/default/images/poll_middle.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/poll_right.gif Binary file forum/Themes/default/images/poll_right.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/angry.gif Binary file forum/Themes/default/images/post/angry.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/cheesy.gif Binary file forum/Themes/default/images/post/cheesy.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/clip.gif Binary file forum/Themes/default/images/post/clip.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/exclamation.gif Binary file forum/Themes/default/images/post/exclamation.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/grin.gif Binary file forum/Themes/default/images/post/grin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/images/post/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/lamp.gif Binary file forum/Themes/default/images/post/lamp.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/last_post.gif Binary file forum/Themes/default/images/post/last_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/moved.gif Binary file forum/Themes/default/images/post/moved.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/question.gif Binary file forum/Themes/default/images/post/question.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/recycled.gif Binary file forum/Themes/default/images/post/recycled.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/sad.gif Binary file forum/Themes/default/images/post/sad.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/smiley.gif Binary file forum/Themes/default/images/post/smiley.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/thumbdown.gif Binary file forum/Themes/default/images/post/thumbdown.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/thumbup.gif Binary file forum/Themes/default/images/post/thumbup.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/wink.gif Binary file forum/Themes/default/images/post/wink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/wireless.gif Binary file forum/Themes/default/images/post/wireless.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/post/xx.gif Binary file forum/Themes/default/images/post/xx.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/powered-mysql.gif Binary file forum/Themes/default/images/powered-mysql.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/powered-php.gif Binary file forum/Themes/default/images/powered-php.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/redirect.png Binary file forum/Themes/default/images/redirect.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/selected.gif Binary file forum/Themes/default/images/selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/smflogo.gif Binary file forum/Themes/default/images/smflogo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/smflogo.png Binary file forum/Themes/default/images/smflogo.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/smiley_select_spot.gif Binary file forum/Themes/default/images/smiley_select_spot.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/sort_down.gif Binary file forum/Themes/default/images/sort_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/sort_up.gif Binary file forum/Themes/default/images/sort_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/addpoll.gif Binary file forum/Themes/default/images/spanish/addpoll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/admin.gif Binary file forum/Themes/default/images/spanish/admin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/admin_lock.gif Binary file forum/Themes/default/images/spanish/admin_lock.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/admin_move.gif Binary file forum/Themes/default/images/spanish/admin_move.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/admin_rem.gif Binary file forum/Themes/default/images/spanish/admin_rem.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/admin_remove_poll.gif Binary file forum/Themes/default/images/spanish/admin_remove_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/admin_sticky.gif Binary file forum/Themes/default/images/spanish/admin_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/calendar.gif Binary file forum/Themes/default/images/spanish/calendar.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/calendarpe.gif Binary file forum/Themes/default/images/spanish/calendarpe.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/delete.gif Binary file forum/Themes/default/images/spanish/delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/delete_selected.gif Binary file forum/Themes/default/images/spanish/delete_selected.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/go_down.gif Binary file forum/Themes/default/images/spanish/go_down.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/go_up.gif Binary file forum/Themes/default/images/spanish/go_up.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/help.gif Binary file forum/Themes/default/images/spanish/help.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/home.gif Binary file forum/Themes/default/images/spanish/home.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/im_delete.gif Binary file forum/Themes/default/images/spanish/im_delete.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/im_inbox.gif Binary file forum/Themes/default/images/spanish/im_inbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/im_new.gif Binary file forum/Themes/default/images/spanish/im_new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/im_outbox.gif Binary file forum/Themes/default/images/spanish/im_outbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/im_reload.gif Binary file forum/Themes/default/images/spanish/im_reload.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/im_reply.gif Binary file forum/Themes/default/images/spanish/im_reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/keystats.gif Binary file forum/Themes/default/images/spanish/keystats.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/linktocal.gif Binary file forum/Themes/default/images/spanish/linktocal.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/login.gif Binary file forum/Themes/default/images/spanish/login.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/logout.gif Binary file forum/Themes/default/images/spanish/logout.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/markread.gif Binary file forum/Themes/default/images/spanish/markread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/markunread.gif Binary file forum/Themes/default/images/spanish/markunread.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/memberlist.gif Binary file forum/Themes/default/images/spanish/memberlist.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/merge.gif Binary file forum/Themes/default/images/spanish/merge.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/modify.gif Binary file forum/Themes/default/images/spanish/modify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/new.gif Binary file forum/Themes/default/images/spanish/new.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/new_poll.gif Binary file forum/Themes/default/images/spanish/new_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/new_topic.gif Binary file forum/Themes/default/images/spanish/new_topic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/newsbox.gif Binary file forum/Themes/default/images/spanish/newsbox.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/notify.gif Binary file forum/Themes/default/images/spanish/notify.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/notify_sm.gif Binary file forum/Themes/default/images/spanish/notify_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/print.gif Binary file forum/Themes/default/images/spanish/print.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/profile.gif Binary file forum/Themes/default/images/spanish/profile.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/quote.gif Binary file forum/Themes/default/images/spanish/quote.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/register.gif Binary file forum/Themes/default/images/spanish/register.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/reply.gif Binary file forum/Themes/default/images/spanish/reply.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/reply_sm.gif Binary file forum/Themes/default/images/spanish/reply_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/search.gif Binary file forum/Themes/default/images/spanish/search.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/sendtopic.gif Binary file forum/Themes/default/images/spanish/sendtopic.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/split.gif Binary file forum/Themes/default/images/spanish/split.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/userinfo.gif Binary file forum/Themes/default/images/spanish/userinfo.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/spanish/who.gif Binary file forum/Themes/default/images/spanish/who.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/split_deselect.gif Binary file forum/Themes/default/images/split_deselect.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/split_select.gif Binary file forum/Themes/default/images/split_select.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/star.gif Binary file forum/Themes/default/images/star.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/staradmin.gif Binary file forum/Themes/default/images/staradmin.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stargmod.gif Binary file forum/Themes/default/images/stargmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/starmod.gif Binary file forum/Themes/default/images/starmod.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stats_board.gif Binary file forum/Themes/default/images/stats_board.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stats_boards.gif Binary file forum/Themes/default/images/stats_boards.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stats_history.gif Binary file forum/Themes/default/images/stats_history.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stats_info.gif Binary file forum/Themes/default/images/stats_info.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stats_pie.png Binary file forum/Themes/default/images/stats_pie.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stats_pie_rtl.png Binary file forum/Themes/default/images/stats_pie_rtl.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stats_posters.gif Binary file forum/Themes/default/images/stats_posters.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stats_replies.gif Binary file forum/Themes/default/images/stats_replies.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/stats_views.gif Binary file forum/Themes/default/images/stats_views.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/theme/backdrop.png Binary file forum/Themes/default/images/theme/backdrop.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/theme/frame_repeat.png Binary file forum/Themes/default/images/theme/frame_repeat.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/theme/loadingbar.png Binary file forum/Themes/default/images/theme/loadingbar.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/theme/main_block.png Binary file forum/Themes/default/images/theme/main_block.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/theme/menu_gfx.png Binary file forum/Themes/default/images/theme/menu_gfx.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/theme/quickbuttons.png Binary file forum/Themes/default/images/theme/quickbuttons.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/theme/quote.png Binary file forum/Themes/default/images/theme/quote.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/theme/submit_bg.png Binary file forum/Themes/default/images/theme/submit_bg.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/thumbnail.gif Binary file forum/Themes/default/images/thumbnail.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/titlebg.jpg Binary file forum/Themes/default/images/titlebg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topbg.jpg Binary file forum/Themes/default/images/topbg.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/hot_poll.gif Binary file forum/Themes/default/images/topic/hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/hot_poll_locked.gif Binary file forum/Themes/default/images/topic/hot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/hot_poll_locked_sticky.gif Binary file forum/Themes/default/images/topic/hot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/hot_poll_sticky.gif Binary file forum/Themes/default/images/topic/hot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/hot_post.gif Binary file forum/Themes/default/images/topic/hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/hot_post_locked.gif Binary file forum/Themes/default/images/topic/hot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/hot_post_locked_sticky.gif Binary file forum/Themes/default/images/topic/hot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/hot_post_sticky.gif Binary file forum/Themes/default/images/topic/hot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/images/topic/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_hot_poll.gif Binary file forum/Themes/default/images/topic/my_hot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_hot_poll_locked.gif Binary file forum/Themes/default/images/topic/my_hot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_hot_poll_locked_sticky.gif Binary file forum/Themes/default/images/topic/my_hot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_hot_poll_sticky.gif Binary file forum/Themes/default/images/topic/my_hot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_hot_post.gif Binary file forum/Themes/default/images/topic/my_hot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_hot_post_locked.gif Binary file forum/Themes/default/images/topic/my_hot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_hot_post_locked_sticky.gif Binary file forum/Themes/default/images/topic/my_hot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_hot_post_sticky.gif Binary file forum/Themes/default/images/topic/my_hot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_normal_poll.gif Binary file forum/Themes/default/images/topic/my_normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_normal_poll_locked.gif Binary file forum/Themes/default/images/topic/my_normal_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_normal_poll_locked_sticky.gif Binary file forum/Themes/default/images/topic/my_normal_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_normal_poll_sticky.gif Binary file forum/Themes/default/images/topic/my_normal_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_normal_post.gif Binary file forum/Themes/default/images/topic/my_normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_normal_post_locked.gif Binary file forum/Themes/default/images/topic/my_normal_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_normal_post_locked_sticky.gif Binary file forum/Themes/default/images/topic/my_normal_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_normal_post_sticky.gif Binary file forum/Themes/default/images/topic/my_normal_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_veryhot_poll.gif Binary file forum/Themes/default/images/topic/my_veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_veryhot_poll_locked.gif Binary file forum/Themes/default/images/topic/my_veryhot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_veryhot_poll_locked_sticky.gif Binary file forum/Themes/default/images/topic/my_veryhot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_veryhot_poll_sticky.gif Binary file forum/Themes/default/images/topic/my_veryhot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_veryhot_post.gif Binary file forum/Themes/default/images/topic/my_veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_veryhot_post_locked.gif Binary file forum/Themes/default/images/topic/my_veryhot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_veryhot_post_locked_sticky.gif Binary file forum/Themes/default/images/topic/my_veryhot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/my_veryhot_post_sticky.gif Binary file forum/Themes/default/images/topic/my_veryhot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/normal_poll.gif Binary file forum/Themes/default/images/topic/normal_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/normal_poll_locked.gif Binary file forum/Themes/default/images/topic/normal_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/normal_poll_locked_sticky.gif Binary file forum/Themes/default/images/topic/normal_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/normal_poll_sticky.gif Binary file forum/Themes/default/images/topic/normal_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/normal_post.gif Binary file forum/Themes/default/images/topic/normal_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/normal_post_locked.gif Binary file forum/Themes/default/images/topic/normal_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/normal_post_locked_sticky.gif Binary file forum/Themes/default/images/topic/normal_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/normal_post_sticky.gif Binary file forum/Themes/default/images/topic/normal_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/veryhot_poll.gif Binary file forum/Themes/default/images/topic/veryhot_poll.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/veryhot_poll_locked.gif Binary file forum/Themes/default/images/topic/veryhot_poll_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/veryhot_poll_locked_sticky.gif Binary file forum/Themes/default/images/topic/veryhot_poll_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/veryhot_poll_sticky.gif Binary file forum/Themes/default/images/topic/veryhot_poll_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/veryhot_post.gif Binary file forum/Themes/default/images/topic/veryhot_post.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/veryhot_post_locked.gif Binary file forum/Themes/default/images/topic/veryhot_post_locked.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/veryhot_post_locked_sticky.gif Binary file forum/Themes/default/images/topic/veryhot_post_locked_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/topic/veryhot_post_sticky.gif Binary file forum/Themes/default/images/topic/veryhot_post_sticky.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/upshrink.gif Binary file forum/Themes/default/images/upshrink.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/upshrink.png Binary file forum/Themes/default/images/upshrink.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/upshrink2.gif Binary file forum/Themes/default/images/upshrink2.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/upshrink2.png Binary file forum/Themes/default/images/upshrink2.png has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/useroff.gif Binary file forum/Themes/default/images/useroff.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/useron.gif Binary file forum/Themes/default/images/useron.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/valid-css.gif Binary file forum/Themes/default/images/valid-css.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/valid-xhtml10.gif Binary file forum/Themes/default/images/valid-xhtml10.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/warn.gif Binary file forum/Themes/default/images/warn.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/warning_moderate.gif Binary file forum/Themes/default/images/warning_moderate.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/warning_mute.gif Binary file forum/Themes/default/images/warning_mute.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/warning_watch.gif Binary file forum/Themes/default/images/warning_watch.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/www.gif Binary file forum/Themes/default/images/www.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/www_sm.gif Binary file forum/Themes/default/images/www_sm.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/images/yim.gif Binary file forum/Themes/default/images/yim.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/index.template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/index.template.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,505 @@ + + +'; + + // The ?fin20 part of this link is just here to make sure browsers don't cache it wrongly. + echo ' + '; + + // Some browsers need an extra stylesheet due to bugs/compatibility issues. + foreach (array('ie7', 'ie6', 'webkit') as $cssfix) + if ($context['browser']['is_' . $cssfix]) + echo ' + '; + + // RTL languages require an additional stylesheet. + if ($context['right_to_left']) + echo ' + '; + + // Here comes the JavaScript bits! + echo ' + + + '; + + echo ' + + ', !empty($context['meta_keywords']) ? ' + ' : '', ' + ', $context['page_title_html_safe'], ''; + + // Please don't index these Mr Robot. + if (!empty($context['robot_no_index'])) + echo ' + '; + + // Present a canonical url for search engines to prevent duplicate content in their indices. + if (!empty($context['canonical_url'])) + echo ' + '; + + // Show all the relative links, such as help, search, contents, and the like. + echo ' + + + '; + + // If RSS feeds are enabled, advertise the presence of one. + if (!empty($modSettings['xmlnews_enable']) && (!empty($modSettings['allow_guestAccess']) || $context['user']['is_logged'])) + echo ' + '; + + // If we're viewing a topic, these should be the previous and next topics, respectively. + if (!empty($context['current_topic'])) + echo ' + + '; + + // If we're in a board, or a topic for that matter, the index will be the board's index. + if (!empty($context['current_board'])) + echo ' + '; + + // Output any remaining HTML headers. (from mods, maybe?) + echo $context['html_headers']; + + echo ' + +'; +} + +function template_body_above() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo !empty($settings['forum_width']) ? ' +
    ' : '', ' + '; + + // The main content should go here. + echo ' +
    +
    '; + + // Custom banners and shoutboxes should be placed here, before the linktree. + + // Show the navigation tree. + theme_linktree(); +} + +function template_body_below() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +
    +
    '; + + // Show the "Powered by" and "Valid" logos, as well as the copyright. Remember, the copyright must be somewhere! + echo ' + ', !empty($settings['forum_width']) ? ' +
    ' : ''; +} + +function template_html_below() +{ + global $context, $settings, $options, $scripturl, $txt, $modSettings; + + echo ' +'; +} + +// Show a linktree. This is that thing that shows "My Community | General Category | General Discussion".. +function theme_linktree($force_show = false) +{ + global $context, $settings, $options, $shown_linktree; + + // If linktree is empty, just return - also allow an override. + if (empty($context['linktree']) || (!empty($context['dont_default_linktree']) && !$force_show)) + return; + + echo ' + '; + + $shown_linktree = true; +} + +// Show the menu up top. Something like [home] [help] [profile] [logout]... +function template_menu() +{ + global $context, $settings, $options, $scripturl, $txt; + + echo ' + '; +} + +// Generate a strip of buttons. +function template_button_strip($button_strip, $direction = 'top', $strip_options = array()) +{ + global $settings, $context, $txt, $scripturl; + + if (!is_array($strip_options)) + $strip_options = array(); + + // List the buttons in reverse order for RTL languages. + if ($context['right_to_left']) + $button_strip = array_reverse($button_strip, true); + + // Create the buttons... + $buttons = array(); + foreach ($button_strip as $key => $value) + { + if (!isset($value['test']) || !empty($context[$value['test']])) + $buttons[] = ' +
  38. ' . $txt[$value['text']] . '
  39. '; + } + + // No buttons? No button strip either. + if (empty($buttons)) + return; + + // Make the last one, as easy as possible. + $buttons[count($buttons) - 1] = str_replace('', '', $buttons[count($buttons) - 1]); + + echo ' + '; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Admin.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Admin.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,619 @@ +Blank a box to remove that word.'; +$txt['admin_reserved_names'] = 'Reserved Names'; +$txt['admin_template_edit'] = 'Edit Your Forum Template'; +$txt['admin_modifications'] = 'Modification Settings'; +$txt['admin_security_moderation'] = 'Security and Moderation'; +$txt['admin_server_settings'] = 'Server Settings'; +$txt['admin_reserved_set'] = 'Set Reserved Names'; +$txt['admin_reserved_line'] = 'One reserved word per line.'; +$txt['admin_basic_settings'] = 'This page allows you to change the basic settings for your forum. Be very careful with these settings, as they may render the forum dysfunctional.'; +$txt['admin_maintain'] = 'Enable Maintenance Mode'; +$txt['admin_title'] = 'Forum Title'; +$txt['admin_url'] = 'Forum URL'; +$txt['cookie_name'] = 'Cookie Name'; +$txt['admin_webmaster_email'] = 'Webmaster Email Address'; +$txt['boarddir'] = 'SMF Directory'; +$txt['sourcesdir'] = 'Sources Directory'; +$txt['cachedir'] = 'Cache Directory'; +$txt['admin_news'] = 'Enable News'; +$txt['admin_guest_post'] = 'Enable Guest Posting'; +$txt['admin_manage_members'] = 'Members'; +$txt['admin_main'] = 'Main'; +$txt['admin_config'] = 'Configuration'; +$txt['admin_version_check'] = 'Detailed Version Check'; +$txt['admin_smffile'] = 'SMF File'; +$txt['admin_smfpackage'] = 'SMF Package'; +$txt['admin_maintenance'] = 'Maintenance'; +$txt['admin_image_text'] = 'Show buttons as images instead of text'; +$txt['admin_credits'] = 'Credits'; +$txt['admin_agreement'] = 'Show and require agreement letter when registering'; +$txt['admin_agreement_default'] = 'Default'; +$txt['admin_agreement_select_language'] = 'Language to edit'; +$txt['admin_agreement_select_language_change'] = 'Change'; +$txt['admin_delete_members'] = 'Delete Selected Members'; +$txt['admin_repair'] = 'Repair All Boards and Topics'; +$txt['admin_main_welcome'] = 'This is your "%1$s". From here, you can edit settings, maintain your forum, view logs, install packages, manage themes, and many other things.
    If you have any trouble, please look at the "Support & Credits" page. If the information there doesn\'t help you, feel free to look to us for help with the problem.
    You may also find answers to your questions or problems by clicking the %2$s symbols for more information on the related functions.'; +$txt['admin_news_desc'] = 'Please place one news item per box. BBC tags, such as [b], [i] and [u] are allowed in your news, as well as smileys. Clear a news item\'s text box to remove it.'; +$txt['administrators'] = 'Forum Administrators'; +$txt['admin_reserved_desc'] = 'Reserved names will keep members from registering certain usernames or using these words in their displayed names. Choose the options you wish to use from the bottom before submitting.'; +$txt['admin_activation_email'] = 'Send activation email to new members upon registration'; +$txt['admin_match_whole'] = 'Match whole name only. If unchecked, search within names.'; +$txt['admin_match_case'] = 'Match case. If unchecked, search will be case insensitive.'; +$txt['admin_check_user'] = 'Check username.'; +$txt['admin_check_display'] = 'Check display name.'; +$txt['admin_newsletter_send'] = 'You can email anyone from this page. The email addresses of the selected membergroups should appear below, but you may remove or add any email addresses you wish. Be sure that each address is separated in this fashion: \'address1; address2\'.'; +$txt['admin_fader_delay'] = 'Fading delay between items for the news fader'; +$txt['admin_bbc'] = 'Show BBC Buttons on Posting and PM Send Pages'; + +$txt['admin_backup_fail'] = 'Failed to make backup of Settings.php - make sure Settings_bak.php exists and is writable.'; +$txt['modSettings_info'] = 'Change or set options that control how this forum operates.'; +$txt['database_server'] = 'Database Server'; +$txt['database_user'] = 'Database Username'; +$txt['database_password'] = 'Database Password'; +$txt['database_name'] = 'Database Name'; +$txt['registration_agreement'] = 'Registration Agreement'; +$txt['registration_agreement_desc'] = 'This agreement is shown when a user registers an account on this forum and has to be accepted before users can continue registration.'; +$txt['database_prefix'] = 'Database Tables Prefix'; +$txt['errors_list'] = 'Listing of forum errors'; +$txt['errors_found'] = 'The following errors are fouling up your forum'; +$txt['errors_fix'] = 'Would you like to attempt to fix these errors?'; +$txt['errors_do_recount'] = 'All errors fixed - a salvage area has been created! Please click the button below to recount some key statistics.'; +$txt['errors_recount_now'] = 'Recount Statistics'; +$txt['errors_fixing'] = 'Fixing forum errors'; +$txt['errors_fixed'] = 'All errors fixed! Please check on any categories, boards, or topics created to decide what to do with them.'; +$txt['attachments_avatars'] = 'Attachments and Avatars'; +$txt['attachments_desc'] = 'From here you can administer the attached files on your system. You can delete attachments by size and by date from your system. Statistics on attachments are also displayed below.'; +$txt['attachment_stats'] = 'File Attachment Statistics'; +$txt['attachment_integrity_check'] = 'Attachment Integrity Check'; +$txt['attachment_integrity_check_desc'] = 'This function will check the integrity and sizes of attachments and filenames listed in the database and, if necessary, fix errors it encounters.'; +$txt['attachment_check_now'] = 'Run check now'; +$txt['attachment_pruning'] = 'Attachment Pruning'; +$txt['attachment_pruning_message'] = 'Message to add to post'; +$txt['attachment_pruning_warning'] = 'Are you sure you want to delete these attachments?\\nThis cannot be undone!'; +$txt['attachment_total'] = 'Total Attachments'; +$txt['attachmentdir_size'] = 'Total Size of Attachment Directory'; +$txt['attachmentdir_size_current'] = 'Total Size of Current Attachment Directory'; +$txt['attachment_space'] = 'Total Space Available in Attachment Directory'; +$txt['attachment_space_current'] = 'Total Space Available in Current Attachment Directory'; +$txt['attachment_options'] = 'File Attachment Options'; +$txt['attachment_log'] = 'Attachment Log'; +$txt['attachment_remove_old'] = 'Remove attachments older than'; +$txt['attachment_remove_size'] = 'Remove attachments larger than'; +$txt['attachment_name'] = 'Attachment Name'; +$txt['attachment_file_size'] = 'File Size'; +$txt['attachmentdir_size_not_set'] = 'No maximum directory size is currently set'; +$txt['attachment_delete_admin'] = '[attachment deleted by admin]'; +$txt['live'] = 'Live from Simple Machines...'; +$txt['remove_all'] = 'Remove All'; +$txt['approve_new_members'] = 'Admin must approve all new members'; +$txt['agreement_not_writable'] = 'Warning - agreement.txt is not writable, any changes you make will NOT be saved.'; + +$txt['version_check_desc'] = 'This shows you the versions of your installation\'s files versus those of the latest version. If any of these files are out of date, you should download and upgrade to the latest version at www.simplemachines.org.'; +$txt['version_check_more'] = '(more detailed)'; + +$txt['lfyi'] = 'You are unable to connect to simplemachines.org\'s latest news file.'; + +$txt['manage_calendar'] = 'Calendar'; +$txt['manage_search'] = 'Search'; + +$txt['smileys_manage'] = 'Smileys and Message Icons'; +$txt['smileys_manage_info'] = 'Install new smiley sets, add smileys to existing ones, or manage your message icons.'; +$txt['package_info'] = 'Install new features or modify existing ones with this interface.'; +$txt['theme_admin'] = 'Themes and Layout'; +$txt['theme_admin_info'] = 'Setup and manage your themes and set or reset theme options.'; +$txt['registration_center'] = 'Registration'; +$txt['member_center_info'] = 'View the member list, search for members and manage not-yet-approved members and members who haven\'t activated their account yet.'; + +$txt['viewmembers_name'] = 'Username (display name)'; +$txt['viewmembers_online'] = 'Last Online'; +$txt['viewmembers_today'] = 'Today'; +$txt['viewmembers_day_ago'] = 'day ago'; +$txt['viewmembers_days_ago'] = 'days ago'; + +$txt['display_name'] = 'Display name'; +$txt['email_address'] = 'Email Address'; +$txt['ip_address'] = 'IP address'; +$txt['member_id'] = 'ID'; + +$txt['unknown'] = 'unknown'; +$txt['security_wrong'] = 'Administration login attempt!' . "\n" . 'Referer: %1$s' . "\n" . 'User agent: %2$s' . "\n" . 'IP: %3$s'; + +$txt['email_as_html'] = 'Send in HTML format. (with this you can put normal HTML in the email.)'; +$txt['email_parsed_html'] = 'Add <br />s and &nbsp;s to this message.'; +$txt['email_variables'] = 'In this message you can use a few "variables". Click here for more information.'; +$txt['email_force'] = 'Send this to members even if they have chosen not to receive announcements.'; +$txt['email_as_pms'] = 'Send this to these groups using personal messages.'; +$txt['email_continue'] = 'Continue'; +$txt['email_done'] = 'done.'; + +$txt['ban_title'] = 'Ban List'; +$txt['ban_ip'] = 'IP banning: (e.g. 192.168.12.213 or 128.0.*.*) - one entry per line'; +$txt['ban_email'] = 'Email banning: (e.g. badguy@somewhere.com) - one entry per line'; +$txt['ban_username'] = 'User name banning: (e.g. l33tuser) - one entry per line'; + +$txt['ban_description'] = 'Here you can ban troublesome people either by IP, hostname, username, or email.'; +$txt['ban_add_new'] = 'Add New Ban'; +$txt['ban_banned_entity'] = 'Banned entity'; +$txt['ban_on_ip'] = 'Ban on IP (e.g. 192.168.10-20.*)'; +$txt['ban_on_hostname'] = 'Ban on Hostname (e.g. *.mil)'; +$txt['ban_on_email'] = 'Ban on Email Address (e.g. *@badsite.com)'; +$txt['ban_on_username'] = 'Ban on Username'; +$txt['ban_notes'] = 'Notes'; +$txt['ban_restriction'] = 'Restriction'; +$txt['ban_full_ban'] = 'Full ban'; +$txt['ban_partial_ban'] = 'Partial ban'; +$txt['ban_cannot_post'] = 'Cannot post'; +$txt['ban_cannot_register'] = 'Cannot register'; +$txt['ban_cannot_login'] = 'Cannot login'; +$txt['ban_add'] = 'Add'; +$txt['ban_edit_list'] = 'Ban List'; +$txt['ban_type'] = 'Ban Type'; +$txt['ban_days'] = 'day(s)'; +$txt['ban_will_expire_within'] = 'Ban will expire after'; +$txt['ban_added'] = 'Added'; +$txt['ban_expires'] = 'Expires'; +$txt['ban_hits'] = 'Hits'; +$txt['ban_actions'] = 'Actions'; +$txt['ban_expiration'] = 'Expiration'; +$txt['ban_reason_desc'] = 'Reason for ban, to be displayed to banned member.'; +$txt['ban_notes_desc'] = 'Notes that may assist other staff members.'; +$txt['ban_remove_selected'] = 'Remove selected'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['ban_remove_selected_confirm'] = 'Are you sure you want to remove the selected bans?'; +$txt['ban_modify'] = 'Modify'; +$txt['ban_name'] = 'Ban name'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['ban_edit'] = 'Edit ban'; +$txt['ban_add_notes'] = 'Note: after creating the above ban, you can add additional entries that trigger the ban, like IP addresses, hostnames and email addresses.'; +$txt['ban_expired'] = 'Expired / disabled'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['ban_restriction_empty'] = 'No restriction selected.'; + +$txt['ban_triggers'] = 'Triggers'; +$txt['ban_add_trigger'] = 'Add ban trigger'; +$txt['ban_add_trigger_submit'] = 'Add'; +$txt['ban_edit_trigger'] = 'Modify'; +$txt['ban_edit_trigger_title'] = 'Edit ban trigger'; +$txt['ban_edit_trigger_submit'] = 'Modify'; +$txt['ban_remove_selected_triggers'] = 'Remove selected ban triggers'; +$txt['ban_no_entries'] = 'There are currently no bans in effect.'; + +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['ban_remove_selected_triggers_confirm'] = 'Are you sure you want to remove the selected ban triggers?'; +$txt['ban_trigger_browse'] = 'Browse Ban Triggers'; +$txt['ban_trigger_browse_description'] = 'This screen shows all banned entities grouped by IP address, hostname, email address and username.'; + +$txt['ban_log'] = 'Ban Log'; +$txt['ban_log_description'] = 'The ban log shows all attempts to enter the forum by banned users (\'full ban\' and \'cannot register\' ban only).'; +$txt['ban_log_no_entries'] = 'There are currently no ban log entries.'; +$txt['ban_log_ip'] = 'IP'; +$txt['ban_log_email'] = 'Email address'; +$txt['ban_log_member'] = 'Member'; +$txt['ban_log_date'] = 'Date'; +$txt['ban_log_remove_all'] = 'Remove all'; +$txt['ban_log_remove_all_confirm'] = 'Are you sure you want to delete all ban log entries?'; +$txt['ban_log_remove_selected'] = 'Remove selected'; +$txt['ban_log_remove_selected_confirm'] = 'Are you sure you want to delete all selected ban log entries?'; +$txt['ban_no_triggers'] = 'There are currently no ban triggers.'; + +$txt['settings_not_writable'] = 'These settings cannot be changed because Settings.php is read only.'; + +$txt['maintain_title'] = 'Forum Maintenance'; +$txt['maintain_info'] = 'Optimize tables, make backups, check for errors, and prune boards with these tools.'; +$txt['maintain_sub_database'] = 'Database'; +$txt['maintain_sub_routine'] = 'Routine'; +$txt['maintain_sub_members'] = 'Members'; +$txt['maintain_sub_topics'] = 'Topics'; +$txt['maintain_done'] = 'The maintenance task \'%1$s\' was executed successfully.'; +$txt['maintain_no_errors'] = 'Congratulations, no errors found! Thanks for checking.'; + +$txt['maintain_tasks'] = 'Scheduled Tasks'; +$txt['maintain_tasks_desc'] = 'Manage all the tasks scheduled by SMF.'; + +$txt['scheduled_log'] = 'Task Log'; +$txt['scheduled_log_desc'] = 'Lists logs of the tasks that have be ran.'; +$txt['admin_log'] = 'Administration Log'; +$txt['admin_log_desc'] = 'Lists administrative tasks that have been performed by admins of your forum.'; +$txt['moderation_log'] = 'Moderation Log'; +$txt['moderation_log_desc'] = 'Lists moderation activities that have been performed by moderators on your forum.'; +$txt['spider_log_desc'] = 'Review the entries related to search engine spider activity on your forum.'; +$txt['pruning_log_desc'] = 'Use these tools to prune older entries in the various logs.'; + +$txt['mailqueue_title'] = 'Mail'; + +$txt['db_error_send'] = 'Send emails on database connection error'; +$txt['db_persist'] = 'Use a persistent connection'; +$txt['ssi_db_user'] = 'Database username to use in SSI mode'; +$txt['ssi_db_passwd'] = 'Database password to use in SSI mode'; + +$txt['default_language'] = 'Default Forum Language'; + +$txt['maintenance_subject'] = 'Subject for display'; +$txt['maintenance_message'] = 'Message for display'; + +$txt['errlog_desc'] = 'The error log tracks every error encountered by your forum. To delete any errors from the database, mark the checkbox, and click the %1$s button at the bottom of the page.'; +$txt['errlog_no_entries'] = 'There are currently no error log entries.'; + +$txt['theme_settings'] = 'Theme Settings'; +$txt['theme_current_settings'] = 'Current Theme'; + +$txt['dvc_your'] = 'Your Version'; +$txt['dvc_current'] = 'Current Version'; +$txt['dvc_sources'] = 'Sources'; +$txt['dvc_default'] = 'Default Templates'; +$txt['dvc_templates'] = 'Current Templates'; +$txt['dvc_languages'] = 'Language Files'; + +$txt['smileys_default_set_for_theme'] = 'Select default smiley set for this theme'; +$txt['smileys_no_default'] = '(use global default smiley set)'; + +$txt['censor_test'] = 'Test Censored Words'; +$txt['censor_test_save'] = 'Test'; +$txt['censor_case'] = 'Ignore case when censoring'; +$txt['censor_whole_words'] = 'Check only whole words'; + +$txt['admin_confirm_password'] = '(confirm)'; +$txt['admin_incorrect_password'] = 'Incorrect Password'; + +$txt['date_format'] = '(YYYY-MM-DD)'; +$txt['undefined_gender'] = 'Undefined'; +$txt['age'] = 'User age'; +$txt['activation_status'] = 'Activation Status'; +$txt['activated'] = 'Activated'; +$txt['not_activated'] = 'Not activated'; +$txt['primary'] = 'Primary'; +$txt['additional'] = 'Additional'; +$txt['messenger_address'] = 'Messenger Address'; +$txt['wild_cards_allowed'] = 'wildcard characters * and ? are allowed'; +$txt['search_for'] = 'Search for'; +$txt['member_part_of_these_membergroups'] = 'Member is part of these membergroups'; +$txt['membergroups'] = 'Membergroups'; +$txt['confirm_delete_members'] = 'Are you sure you want to delete the selected members?'; + +$txt['support_credits_title'] = 'Support and Credits'; +$txt['support_credits_info'] = 'Get support on common issues and version information to give if you have problems.'; +$txt['support_title'] = 'Support Information'; +$txt['support_versions_current'] = 'Current SMF version'; +$txt['support_versions_forum'] = 'Forum version'; +$txt['support_versions_php'] = 'PHP version'; +$txt['support_versions_db'] = '%1$s version'; +$txt['support_versions_server'] = 'Server version'; +$txt['support_versions_gd'] = 'GD version'; +$txt['support_versions'] = 'Version Information'; +$txt['support_resources'] = 'Support Resources'; +$txt['support_resources_p1'] = 'Our Online Manual provides the main documentation for SMF. The SMF Online Manual has many documents to help answer support questions and explain Features, Settings, Themes, Packages, etc. The Online Manual documents each area of SMF thoroughly and should answer most questions quickly.'; +$txt['support_resources_p2'] = 'If you can\'t find the answers to your questions in the Online Manual, you may want to search our Support Community or ask for assistance in either our English or one of our many international support boards. The SMF Support Community can be used for support, customization, and many other things such as discussing SMF, finding a host, and discussing administrative issues with other forum administrators.'; + +$txt['support_latest'] = 'Common Support & Issues'; +$txt['support_latest_fetch'] = 'Retrieving support information...'; + +$txt['edit_permissions_info'] = 'Change restrictions and available features globally or to specific boards.'; +$txt['membergroups_members'] = 'Regular Members'; +$txt['membergroups_guests'] = 'Guests'; +$txt['membergroups_guests_na'] = 'n/a'; +$txt['membergroups_add_group'] = 'Add group'; +$txt['membergroups_permissions'] = 'Permissions'; + +$txt['permitgroups_restrict'] = 'Restrictive'; +$txt['permitgroups_standard'] = 'Standard'; +$txt['permitgroups_moderator'] = 'Moderator'; +$txt['permitgroups_maintenance'] = 'Maintenance'; +$txt['permitgroups_inherit'] = 'Inherit'; + +$txt['confirm_delete_attachments_all'] = 'Are you sure you want to delete all attachments?'; +$txt['confirm_delete_attachments'] = 'Are you sure you want to delete the selected attachments?'; +$txt['attachment_manager_browse_files'] = 'Browse Files'; +$txt['attachment_manager_repair'] = 'Maintain'; +$txt['attachment_manager_avatars'] = 'Avatars'; +$txt['attachment_manager_attachments'] = 'Attachments'; +$txt['attachment_manager_thumbs'] = 'Thumbnails'; +$txt['attachment_manager_last_active'] = 'Last Active'; +$txt['attachment_manager_member'] = 'Member'; +$txt['attachment_manager_avatars_older'] = 'Remove avatars from members not active for more than'; +$txt['attachment_manager_total_avatars'] = 'Total Avatars'; + +$txt['attachment_manager_avatars_no_entries'] = 'There are currently no avatars.'; +$txt['attachment_manager_attachments_no_entries'] = 'There are currently no attachments.'; +$txt['attachment_manager_thumbs_no_entries'] = 'There are currently no thumbnails.'; + +$txt['attachment_manager_settings'] = 'Attachment Settings'; +$txt['attachment_manager_avatar_settings'] = 'Avatar Settings'; +$txt['attachment_manager_browse'] = 'Browse Files'; +$txt['attachment_manager_maintenance'] = 'File Maintenance'; +$txt['attachment_manager_save'] = 'Save'; + +$txt['attachmentEnable'] = 'Attachments mode'; +$txt['attachmentEnable_deactivate'] = 'Disable attachments'; +$txt['attachmentEnable_enable_all'] = 'Enable all attachments'; +$txt['attachmentEnable_disable_new'] = 'Disable new attachments'; +$txt['attachmentCheckExtensions'] = 'Check attachment\'s extension'; +$txt['attachmentExtensions'] = 'Allowed attachment extensions'; +$txt['attachmentRecodeLineEndings'] = 'Recode line endings in textual attachments'; +$txt['attachmentShowImages'] = 'Display image attachments as pictures under post'; +$txt['attachmentEncryptFilenames'] = 'Encrypt stored filenames'; +$txt['attachmentUploadDir'] = 'Attachments directory'; +$txt['attachmentUploadDir_multiple'] = 'Attachments directory'; +$txt['attachmentUploadDir_multiple_configure'] = '[Configure multiple attachment directories]'; +$txt['attachmentDirSizeLimit'] = 'Max attachment folder space
    (0 for no limit)
    '; +$txt['attachmentPostLimit'] = 'Max attachment size per post
    (0 for no limit)
    '; +$txt['attachmentSizeLimit'] = 'Max size per attachment
    (0 for no limit)
    '; +$txt['attachmentNumPerPostLimit'] = 'Max number of attachments per post
    (0 for no limit)
    '; +$txt['attachment_gd_warning'] = 'The GD module is currently not installed. Image re-encoding is not possible.'; +$txt['attachment_image_reencode'] = 'Re-encode potentially dangerous image attachments'; +$txt['attachment_image_reencode_note'] = '(requires GD module)'; +$txt['attachment_image_paranoid_warning'] = 'The extensive security checks can result in a large number of rejected attachments.'; +$txt['attachment_image_paranoid'] = 'Perform extensive security checks on uploaded image attachments'; +$txt['attachmentThumbnails'] = 'Resize images when showing under posts'; +$txt['attachment_thumb_png'] = 'Save thumbnails as PNG'; +$txt['attachmentThumbWidth'] = 'Maximum width of thumbnails'; +$txt['attachmentThumbHeight'] = 'Maximum height of thumbnails'; + +$txt['attach_dir_does_not_exist'] = 'Does Not Exist'; +$txt['attach_dir_not_writable'] = 'Not Writable'; +$txt['attach_dir_files_missing'] = 'Files Missing (Repair)'; +$txt['attach_dir_unused'] = 'Unused'; +$txt['attach_dir_ok'] = 'OK'; + +$txt['attach_path_manage'] = 'Manage Attachment Paths'; +$txt['attach_paths'] = 'Attachment Paths'; +$txt['attach_current_dir'] = 'Current Directory'; +$txt['attach_path'] = 'Path'; +$txt['attach_current_size'] = 'Current Size (KB)'; +$txt['attach_num_files'] = 'Files'; +$txt['attach_dir_status'] = 'Status'; +$txt['attach_add_path'] = 'Add Path'; +$txt['attach_path_current_bad'] = 'Invalid current attachment path.'; + +$txt['mods_cat_avatars'] = 'Avatars'; +$txt['avatar_directory'] = 'Avatars directory'; +$txt['avatar_url'] = 'Avatars URL'; +$txt['avatar_dimension_note'] = '(0 = no limit)'; +$txt['avatar_max_width_external'] = 'Maximum width of external avatar
    (0 for no limit)
    '; +$txt['avatar_max_height_external'] = 'Maximum height of external avatar
    (0 for no limit)
    '; +$txt['avatar_action_too_large'] = 'If the avatar is too large...'; +$txt['option_refuse'] = 'Refuse it'; +$txt['option_html_resize'] = 'Let the HTML resize it'; +$txt['option_js_resize'] = 'Resize it with JavaScript'; +$txt['option_download_and_resize'] = 'Download and resize it (requires GD module)'; +$txt['avatar_max_width_upload'] = 'Maximum width of uploaded avatar
    (0 for no limit)
    '; +$txt['avatar_max_height_upload'] = 'Maximum height of uploaded avatar
    (0 for no limit)
    '; +$txt['avatar_resize_upload'] = 'Resize oversized large avatars'; +$txt['avatar_resize_upload_note'] = '(requires GD module)'; +$txt['avatar_download_png'] = 'Use PNG for resized avatars'; +$txt['avatar_gd_warning'] = 'The GD module is currently not installed. Some avatar features are disabled.'; +$txt['avatar_external'] = 'External avatars'; +$txt['avatar_upload'] = 'Uploadable avatars'; +$txt['avatar_server_stored'] = 'Server-stored avatars'; +$txt['avatar_server_stored_groups'] = 'Membergroups allowed to select a server stored avatar'; +$txt['avatar_upload_groups'] = 'Membergroups allowed to upload an avatar to the server'; +$txt['avatar_external_url_groups'] = 'Membergroups allowed to select an external URL'; +$txt['avatar_select_permission'] = 'Select permissions for each group'; +$txt['avatar_download_external'] = 'Download avatar at given URL'; +$txt['custom_avatar_enabled'] = 'Upload avatars to...'; +$txt['option_attachment_dir'] = 'Attachment directory'; +$txt['option_specified_dir'] = 'Specific directory...'; +$txt['custom_avatar_dir'] = 'Upload directory'; +$txt['custom_avatar_dir_desc'] = 'This should not be the same as the server-stored directory.'; +$txt['custom_avatar_url'] = 'Upload URL'; +$txt['custom_avatar_check_empty'] = 'The custom avatar directory you have specified may be empty or invalid. Please ensure these settings are correct.'; +$txt['avatar_reencode'] = 'Re-encode potentially dangerous avatars'; +$txt['avatar_reencode_note'] = '(requires GD module)'; +$txt['avatar_paranoid_warning'] = 'The extensive security checks can result in a large number of rejected avatars.'; +$txt['avatar_paranoid'] = 'Perform extensive security checks on uploaded avatars'; + +$txt['repair_attachments'] = 'Maintain Attachments'; +$txt['repair_attachments_complete'] = 'Maintenance Complete'; +$txt['repair_attachments_complete_desc'] = 'All selected errors have now been corrected'; +$txt['repair_attachments_no_errors'] = 'No errors were found!'; +$txt['repair_attachments_error_desc'] = 'The follow errors were found during maintenance. Check the box next to the errors you wish to fix and hit continue.'; +$txt['repair_attachments_continue'] = 'Continue'; +$txt['repair_attachments_cancel'] = 'Cancel'; +$txt['attach_repair_missing_thumbnail_parent'] = '%1$d thumbnails are missing a parent attachment'; +$txt['attach_repair_parent_missing_thumbnail'] = '%1$d parents are flagged as having thumbnails but don\'t'; +$txt['attach_repair_file_missing_on_disk'] = '%1$d attachments/avatars have an entry but no longer exist on disk'; +$txt['attach_repair_file_wrong_size'] = '%1$d attachments/avatars are being reported as the wrong filesize'; +$txt['attach_repair_file_size_of_zero'] = '%1$d attachments/avatars have a size of zero on disk. (These will be deleted)'; +$txt['attach_repair_attachment_no_msg'] = '%1$d attachments no longer have a message associated with them'; +$txt['attach_repair_avatar_no_member'] = '%1$d avatars no longer have a member associated with them'; +$txt['attach_repair_wrong_folder'] = '%1$d attachments are in the wrong folder'; + +$txt['news_title'] = 'News and Newsletters'; +$txt['news_settings_desc'] = 'Here you can change the settings and permissions related to news and newsletters.'; +$txt['news_settings_submit'] = 'Save'; +$txt['news_mailing_desc'] = 'From this menu you can send messages to all members who\'ve registered and entered their email addresses. You may edit the distribution list, or send messages to all. Useful for important update/news information.'; +$txt['groups_edit_news'] = 'Groups allowed to edit news items'; +$txt['groups_send_mail'] = 'Groups allowed to send out forum newsletters'; +$txt['xmlnews_enable'] = 'Enable XML/RSS news'; +$txt['xmlnews_maxlen'] = 'Maximum message length:
    (0 to disable, bad idea.)
    '; +$txt['editnews_clickadd'] = 'Click here to add another item.'; +$txt['editnews_remove_selected'] = 'Remove selected'; +$txt['editnews_remove_confirm'] = 'Are you sure you want to delete the selected news items?'; +$txt['censor_clickadd'] = 'Click here to add another word.'; + +$txt['layout_controls'] = 'Forum'; +$txt['logs'] = 'Logs'; +$txt['generate_reports'] = 'Reports'; + +$txt['update_available'] = 'Update Available!'; +$txt['update_message'] = 'You\'re using an outdated version of SMF, which contains some bugs which have since been fixed. + It is recommended that you update your forum to the latest version as soon as possible. It only takes a minute!'; + +$txt['manageposts'] = 'Posts and Topics'; +$txt['manageposts_title'] = 'Manage Posts and Topics'; +$txt['manageposts_description'] = 'Here you can manage all settings related to topics and posts.'; + +$txt['manageposts_seconds'] = 'seconds'; +$txt['manageposts_minutes'] = 'minutes'; +$txt['manageposts_characters'] = 'characters'; +$txt['manageposts_days'] = 'days'; +$txt['manageposts_posts'] = 'posts'; +$txt['manageposts_topics'] = 'topics'; + +$txt['manageposts_settings'] = 'Post Settings'; +$txt['manageposts_settings_description'] = 'Here you can set everything related to posts and posting.'; +$txt['manageposts_settings_submit'] = 'Save'; + +$txt['manageposts_bbc_settings'] = 'Bulletin Board Code'; +$txt['manageposts_bbc_settings_description'] = 'Bulletin board code can be used to add markup to forum messages. For example, to highlight the word \'house\' you can type [b]house[/b]. All Bulletin board code tags are surrounded by square brackets (\'[\' and \']\').'; +$txt['manageposts_bbc_settings_title'] = 'Bulletin Board Code Settings'; +$txt['manageposts_bbc_settings_submit'] = 'Save'; + +$txt['manageposts_topic_settings'] = 'Topic Settings'; +$txt['manageposts_topic_settings_description'] = 'Here you can set all settings involving topics.'; +$txt['manageposts_topic_settings_submit'] = 'Save'; + +$txt['removeNestedQuotes'] = 'Remove nested quotes when quoting'; +$txt['enableEmbeddedFlash'] = 'Embed flash into posts'; +$txt['enableEmbeddedFlash_warning'] = 'may be a security risk!'; +$txt['enableSpellChecking'] = 'Enable spell checking'; +$txt['enableSpellChecking_warning'] = 'this does not work on all servers!'; +$txt['disable_wysiwyg'] = 'Disable WYSIWYG editor'; +$txt['max_messageLength'] = 'Maximum allowed post size'; +$txt['max_messageLength_zero'] = '0 for no max.'; +$txt['fixLongWords'] = 'Break up words with more letters than'; +$txt['fixLongWords_zero'] = '0 to disable.'; +$txt['fixLongWords_warning'] = 'this does not work on all servers!'; +$txt['topicSummaryPosts'] = 'Posts to show on topic summary'; +$txt['spamWaitTime'] = 'Time required between posts from the same IP'; +$txt['edit_wait_time'] = 'Courtesy edit wait time'; +$txt['edit_disable_time'] = 'Maximum time after posting to allow edit'; +$txt['edit_disable_time_zero'] = '0 to disable'; + +$txt['enableBBC'] = 'Enable bulletin board code (BBC)'; +$txt['enablePostHTML'] = 'Enable basic HTML in posts'; +$txt['autoLinkUrls'] = 'Automatically link posted URLs'; +$txt['disabledBBC'] = 'Enabled BBC tags'; +$txt['bbcTagsToUse'] = 'Enabled BBC tags'; +$txt['bbcTagsToUse_select'] = 'Select the tags allowed to be used'; +$txt['bbcTagsToUse_select_all'] = 'Select all tags'; + +$txt['enableStickyTopics'] = 'Enable sticky topics'; +$txt['enableParticipation'] = 'Enable participation icons'; +$txt['oldTopicDays'] = 'Time before topic is warned as old on reply'; +$txt['oldTopicDays_zero'] = '0 to disable'; +$txt['defaultMaxTopics'] = 'Number of topics per page in the message index'; +$txt['defaultMaxMessages'] = 'Number of posts per page in a topic page'; +$txt['hotTopicPosts'] = 'Number of posts for a hot topic'; +$txt['hotTopicVeryPosts'] = 'Number of posts for a very hot topic'; +$txt['enableAllMessages'] = 'Max topic size to show "All" posts'; +$txt['enableAllMessages_zero'] = '0 to never show "All"'; +$txt['disableCustomPerPage'] = 'Disable user defined topic/message count per page'; +$txt['enablePreviousNext'] = 'Enable previous/next topic links'; + +$txt['not_done_title'] = 'Not done yet!'; +$txt['not_done_reason'] = 'To avoid overloading your server, the process has been temporarily paused. It should automatically continue in a few seconds. If it doesn\'t, please click continue below.'; +$txt['not_done_continue'] = 'Continue'; + +$txt['general_settings'] = 'General'; +$txt['database_paths_settings'] = 'Database and Paths'; +$txt['cookies_sessions_settings'] = 'Cookies and Sessions'; +$txt['caching_settings'] = 'Caching'; +$txt['load_balancing_settings'] = 'Load Balancing'; + +$txt['language_configuration'] = 'Languages'; +$txt['language_description'] = 'This section allows you to edit languages installed on your forum, download new ones from the Simple Machines website. You may also edit language-related settings here.'; +$txt['language_edit'] = 'Edit Languages'; +$txt['language_add'] = 'Add Language'; +$txt['language_settings'] = 'Settings'; + +$txt['advanced'] = 'Advanced'; +$txt['simple'] = 'Simple'; + +$txt['admin_news_select_recipients'] = 'Please select who should receive a copy of the newsletter'; +$txt['admin_news_select_group'] = 'Membergroups'; +$txt['admin_news_select_group_desc'] = 'Select the groups to receive this newsletter.'; +$txt['admin_news_select_members'] = 'Members'; +$txt['admin_news_select_members_desc'] = 'Additional members to receive newsletter.'; +$txt['admin_news_select_excluded_members'] = 'Excluded Members'; +$txt['admin_news_select_excluded_members_desc'] = 'Members who should not receive newsletter.'; +$txt['admin_news_select_excluded_groups'] = 'Excluded Groups'; +$txt['admin_news_select_excluded_groups_desc'] = 'Select groups who should definitely not receive the newsletter.'; +$txt['admin_news_select_email'] = 'Email Addresses'; +$txt['admin_news_select_email_desc'] = 'A semi-colon separated list of email addresses which should be sent newsletter. (i.e. address1; address2)'; +$txt['admin_news_select_override_notify'] = 'Override Notification Settings'; +// Use entities in below. +$txt['admin_news_cannot_pm_emails_js'] = 'You cannot send a personal message to an email address. If you continue all entered email addresses will be ignored.\\n\\nAre you sure you wish to do this?'; + +$txt['mailqueue_browse'] = 'Browse Queue'; +$txt['mailqueue_settings'] = 'Settings'; + +$txt['admin_search'] = 'Quick Search'; +$txt['admin_search_type_internal'] = 'Task/Setting'; +$txt['admin_search_type_member'] = 'Member'; +$txt['admin_search_type_online'] = 'Online Manual'; +$txt['admin_search_go'] = 'Go'; +$txt['admin_search_results'] = 'Search Results'; +$txt['admin_search_results_desc'] = 'Results for search: "%1$s"'; +$txt['admin_search_results_again'] = 'Search again'; +$txt['admin_search_results_none'] = 'No results found!'; + +$txt['admin_search_section_sections'] = 'Section'; +$txt['admin_search_section_settings'] = 'Setting'; + +$txt['core_settings_title'] = 'Core Features'; +$txt['mods_cat_features'] = 'General'; +$txt['mods_cat_security_general'] = 'General'; +$txt['antispam_title'] = 'Anti-Spam'; +$txt['mods_cat_modifications_misc'] = 'Miscellaneous'; +$txt['mods_cat_layout'] = 'Layout'; +$txt['karma'] = 'Karma'; +$txt['moderation_settings_short'] = 'Moderation'; +$txt['signature_settings_short'] = 'Signatures'; +$txt['custom_profile_shorttitle'] = 'Profile Fields'; +$txt['pruning_title'] = 'Log Pruning'; + +$txt['boardsEdit'] = 'Modify Boards'; +$txt['mboards_new_cat'] = 'Create New Category'; +$txt['manage_holidays'] = 'Manage Holidays'; +$txt['calendar_settings'] = 'Calendar Settings'; +$txt['search_weights'] = 'Weights'; +$txt['search_method'] = 'Search Method'; + +$txt['smiley_sets'] = 'Smiley Sets'; +$txt['smileys_add'] = 'Add Smiley'; +$txt['smileys_edit'] = 'Edit Smileys'; +$txt['smileys_set_order'] = 'Set Smiley Order'; +$txt['icons_edit_message_icons'] = 'Edit Message Icons'; + +$txt['membergroups_new_group'] = 'Add Membergroup'; +$txt['membergroups_edit_groups'] = 'Edit Membergroups'; +$txt['permissions_groups'] = 'General Permissions'; +$txt['permissions_boards'] = 'Board Permissions'; +$txt['permissions_profiles'] = 'Edit Profiles'; +$txt['permissions_post_moderation'] = 'Post Moderation'; + +$txt['browse_packages'] = 'Browse Packages'; +$txt['download_packages'] = 'Download Packages'; +$txt['installed_packages'] = 'Installed Packages'; +$txt['package_file_perms'] = 'File Permissions'; +$txt['package_settings'] = 'Options'; +$txt['themeadmin_admin_title'] = 'Manage and Install'; +$txt['themeadmin_list_title'] = 'Theme Settings'; +$txt['themeadmin_reset_title'] = 'Member Options'; +$txt['themeadmin_edit_title'] = 'Modify Themes'; +$txt['admin_browse_register_new'] = 'Register New Member'; + +$txt['search_engines'] = 'Search Engines'; +$txt['spiders'] = 'Spiders'; +$txt['spider_logs'] = 'Spider Log'; +$txt['spider_stats'] = 'Stats'; + +$txt['paid_subscriptions'] = 'Paid Subscriptions'; +$txt['paid_subs_view'] = 'View Subscriptions'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/EmailTemplates.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/EmailTemplates.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1062 @@ + array( + /* + @additional_params: resend_activate_message + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + ACTIVATIONLINK: The url link to activate the member's account. + ACTIVATIONCODE: The code needed to activate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. If you forget your password, you can reset it by visiting {FORGOTPASSWORDLINK}. + +Before you can login, you must first activate your account by selecting the following link: + +{ACTIVATIONLINK} + +Should you have any problems with the activation, please visit {ACTIVATIONLINKWITHOUTCODE} and enter the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + + 'resend_pending_message' => array( + /* + @additional_params: resend_pending_message + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Your registration request at {FORUMNAME} has been received, {REALNAME}. + +The username you registered with was {USERNAME}. + +Before you can login and start using the forum, your request will be reviewed and approved. When this happens, you will receive another email from this address. + +{REGARDS}', + ), + 'mc_group_approve' => array( + /* + @additional_params: mc_group_approve + USERNAME: The user name for the member receiving the email. + GROUPNAME: The name of the membergroup that the user was accepted into. + @description: The request to join a particular membergroup has been accepted. + */ + 'subject' => 'Group Membership Approval', + 'body' => '{USERNAME}, + +We\'re pleased to notify you that your application to join the "{GROUPNAME}" group at {FORUMNAME} has been accepted, and your account has been updated to include this new membergroup. + +{REGARDS}', + ), + 'mc_group_reject' => array( + /* + @additional_params: mc_group_reject + USERNAME: The user name for the member receiving the email. + GROUPNAME: The name of the membergroup that the user was rejected from. + @description: The request to join a particular membergroup has been rejected. + */ + 'subject' => 'Group Membership Rejection', + 'body' => '{USERNAME}, + +We\'re sorry to notify you that your application to join the "{GROUPNAME}" group at {FORUMNAME} has been rejected. + +{REGARDS}', + ), + 'mc_group_reject_reason' => array( + /* + @additional_params: mc_group_reject_reason + USERNAME: The user name for the member receiving the email. + GROUPNAME: The name of the membergroup that the user was rejected from. + REASON: Reason for the rejection. + @description: The request to join a particular membergroup has been rejected with a reason given. + */ + 'subject' => 'Group Membership Rejection', + 'body' => '{USERNAME}, + +We\'re sorry to notify you that your application to join the "{GROUPNAME}" group at {FORUMNAME} has been rejected. + +This is due to the following reason: {REASON} + +{REGARDS}', + ), + 'admin_approve_accept' => array( + /* + @additional_params: admin_approve_accept + NAME: The display name of the member. + USERNAME: The user name for the member receiving the email. + PROFILELINK: The URL of the profile page. + FORGOTPASSWORDLINK: The URL of the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Welcome, {NAME}! + +Your account has been activated manually by the admin and you can now login and post. Your username is: {USERNAME}. If you forget your password, you can change it at {FORGOTPASSWORDLINK}. + +{REGARDS}', + ), + 'admin_approve_activation' => array( + /* + @additional_params: admin_approve_activation + USERNAME: The user name for the member receiving the email. + ACTIVATIONLINK: The url link to activate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The activation code. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Welcome, {USERNAME}! + +Your account on {FORUMNAME} has been approved by the forum administrator. Before you can login, you must first activate your account by selecting the following link: + +{ACTIVATIONLINK} + +Should you have any problems with the activation, please visit {ACTIVATIONLINKWITHOUTCODE} and enter the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'admin_approve_reject' => array( + /* + @additional_params: admin_approve_reject + USERNAME: The user name for the member receiving the email. + @description: + */ + 'subject' => 'Registration Rejected', + 'body' => '{USERNAME}, + +Regrettably, your application to join {FORUMNAME} has been rejected. + +{REGARDS}', + ), + 'admin_approve_delete' => array( + /* + @additional_params: admin_approve_delete + USERNAME: The user name for the member receiving the email. + @description: + */ + 'subject' => 'Account Deleted', + 'body' => '{USERNAME}, + +Your account on {FORUMNAME} has been deleted. This may be because you never activated your account, in which case you should be able to register again. + +{REGARDS}', + ), + 'admin_approve_remind' => array( + /* + @additional_params: admin_approve_remind + USERNAME: The user name for the member receiving the email. + ACTIVATIONLINK: The url link to activate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The activation code. + @description: + */ + 'subject' => 'Registration Reminder', + 'body' => '{USERNAME}, +You still have not activated your account at {FORUMNAME}. + +Please use the link below to activate your account: +{ACTIVATIONLINK} + +Should you have any problems with the activation, please visit {ACTIVATIONLINKWITHOUTCODE} and enter the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'admin_register_activate' => array( + /* + @additional_params: + USERNAME: The user name for the member receiving the email. + ACTIVATIONLINK: The url link to activate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The activation code. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME} and your password is {PASSWORD}. + +Before you can login, you must first activate your account by selecting the following link: + +{ACTIVATIONLINK} + +Should you have any problems with the activation, please visit {ACTIVATIONLINKWITHOUTCODE} and enter the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'admin_register_immediate' => array( + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME} and your password is {PASSWORD}. + +{REGARDS}', + ), + 'new_announcement' => array( + /* + @additional_params: new_announcement + TOPICSUBJECT: The subject of the topic being announced. + MESSAGE: The message body of the first post of the announced topic. + TOPICLINK: A link to the topic being announced. + @description: + + */ + 'subject' => 'New announcement: {TOPICSUBJECT}', + 'body' => '{MESSAGE} + +To unsubscribe from these announcements, login to the forum and uncheck "Receive forum announcements and important notifications by email." in your profile. + +You can view the full announcement by following this link: +{TOPICLINK} + +{REGARDS}', + ), + 'notify_boards_once_body' => array( + /* + @additional_params: notify_boards_once_body + TOPICSUBJECT: The subject of the topic causing the notification + TOPICLINK: A link to the topic. + MESSAGE: This is the body of the message. + UNSUBSCRIBELINK: Link to unsubscribe from notifications. + @description: + */ + 'subject' => 'New Topic: {TOPICSUBJECT}', + 'body' => 'A new topic, \'{TOPICSUBJECT}\', has been made on a board you are watching. + +You can see it at +{TOPICLINK} + +More topics may be posted, but you won\'t receive more email notifications until you return to the board and read some of them. + +The text of the topic is shown below: +{MESSAGE} + +Unsubscribe to new topics from this board by using this link: +{UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notify_boards_once' => array( + /* + @additional_params: notify_boards_once + TOPICSUBJECT: The subject of the topic causing the notification + TOPICLINK: A link to the topic. + UNSUBSCRIBELINK: Link to unsubscribe from notifications. + @description: + */ + 'subject' => 'New Topic: {TOPICSUBJECT}', + 'body' => 'A new topic, \'{TOPICSUBJECT}\', has been made on a board you are watching. + +You can see it at +{TOPICLINK} + +More topics may be posted, but you won\'t receive more email notifications until you return to the board and read some of them. + +Unsubscribe to new topics from this board by using this link: +{UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notify_boards_body' => array( + /* + @additional_params: notify_boards_body + TOPICSUBJECT: The subject of the topic causing the notification + TOPICLINK: A link to the topic. + MESSAGE: This is the body of the message. + UNSUBSCRIBELINK: Link to unsubscribe from notifications. + @description: + */ + 'subject' => 'New Topic: {TOPICSUBJECT}', + 'body' => 'A new topic, \'{TOPICSUBJECT}\', has been made on a board you are watching. + +You can see it at +{TOPICLINK} + +The text of the topic is shown below: +{MESSAGE} + +Unsubscribe to new topics from this board by using this link: +{UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notify_boards' => array( + /* + @additional_params: notify_boards + TOPICSUBJECT: The subject of the topic causing the notification + TOPICLINK: A link to the topic. + UNSUBSCRIBELINK: Link to unsubscribe from notifications. + @description: + */ + 'subject' => 'New Topic: {TOPICSUBJECT}', + 'body' => 'A new topic, \'{TOPICSUBJECT}\', has been made on a board you are watching. + +You can see it at +{TOPICLINK} + +Unsubscribe to new topics from this board by using this link: +{UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'request_membership' => array( + /* + @additional_params: request_membership + RECPNAME: The name of the person recieving the email + APPYNAME: The name of the person applying for group membership + GROUPNAME: The name of the group being applied to. + REASON: The reason given by the applicant for wanting to join the group. + MODLINK: Link to the group moderation page. + @description: + */ + 'subject' => 'New Group Application', + 'body' => '{RECPNAME}, + +{APPYNAME} has requested membership to the "{GROUPNAME}" group. The user has given the following reason: + +{REASON} + +You can approve or reject this application by clicking the link below: + +{MODLINK} + +{REGARDS}', + ), + 'paid_subscription_reminder' => array( + /* + @additional_params: scheduled_approval + REALNAME: The real (display) name of the person receiving the email. + PROFILE_LINK: Link to profile of member receiving email where can renew. + SUBSCRIPTION: Name of the subscription. + END_DATE: Date it expires. + @description: + */ + 'subject' => 'Subscription about to expire at {FORUMNAME}', + 'body' => '{REALNAME}, + +A subscription you are subscribed to at {FORUMNAME} is about to expire. If when you took out the subscription you selected to auto-renew you need take no action - otherwise you may wish to consider subscribing once more. Details are below: + +Subscription Name: {SUBSCRIPTION} +Expires: {END_DATE} + +To edit your subscriptions visit the following URL: +{PROFILE_LINK} + +{REGARDS}', + ), + 'activate_reactivate' => array( + /* + @additional_params: activate_reactivate + ACTIVATIONLINK: The url link to reactivate the member's account. + ACTIVATIONCODE: The code needed to reactivate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + @description: + */ + 'subject' => 'Welcome back to {FORUMNAME}', + 'body' => 'In order to re-validate your email address, your account has been deactivated. Click the following link to activate it again: +{ACTIVATIONLINK} + +Should you have any problems with activation, please visit {ACTIVATIONLINKWITHOUTCODE} and use the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'forgot_password' => array( + /* + @additional_params: forgot_password + REALNAME: The real (display) name of the person receiving the reminder. + REMINDLINK: The link to reset the password. + IP: The IP address of the requester. + MEMBERNAME: + @description: + */ + 'subject' => 'New password for {FORUMNAME}', + 'body' => 'Dear {REALNAME}, +This mail was sent because the \'forgot password\' function has been applied to your account. To set a new password, click the following link: +{REMINDLINK} + +IP: {IP} +Username: {MEMBERNAME} + +{REGARDS}', + ), + 'forgot_openid' => array( + /* + @additional_params: forgot_password + REALNAME: The real (display) name of the person receiving the reminder. + IP: The IP address of the requester. + OPENID: The members OpenID identity. + @description: + */ + 'subject' => 'OpenID reminder for {FORUMNAME}', + 'body' => 'Dear {REALNAME}, +This mail was sent because the \'forgot OpenID\' function has been applied to your account. Below is the OpenID that your account is associated with: +{OPENID} + +IP: {IP} +Username: {MEMBERNAME} + +{REGARDS}', + ), + 'scheduled_approval' => array( + /* + @additional_params: scheduled_approval + REALNAME: The real (display) name of the person receiving the email. + BODY: The generated body of the mail. + @description: + */ + 'subject' => 'Summary of posts awaiting approval at {FORUMNAME}', + 'body' => '{REALNAME}, + +This email contains a summary of all items awaiting approval at {FORUMNAME}. + +{BODY} + +Please log in to the forum to review these items. +{SCRIPTURL} + +{REGARDS}', + ), + 'send_topic' => array( + /* + @additional_params: send_topic + TOPICSUBJECT: The subject of the topic being sent. + SENDERNAME: The name of the member sending the topic. + RECPNAME: The name of the person receiving the email. + TOPICLINK: A link to the topic being sent. + @description: + */ + 'subject' => 'Topic: {TOPICSUBJECT} (From: {SENDERNAME})', + 'body' => 'Dear {RECPNAME}, +I want you to check out "{TOPICSUBJECT}" on {FORUMNAME}. To view it, please click this link: + +{TOPICLINK} + +Thanks, + +{SENDERNAME}', + ), + 'send_topic_comment' => array( + /* + @additional_params: send_topic_comment + TOPICSUBJECT: The subject of the topic being sent. + SENDERNAME: The name of the member sending the topic. + RECPNAME: The name of the person receiving the email. + TOPICLINK: A link to the topic being sent. + COMMENT: A comment left by the sender. + @description: + */ + 'subject' => 'Topic: {TOPICSUBJECT} (From: {SENDERNAME})', + 'body' => 'Dear {RECPNAME}, +I want you to check out "{TOPICSUBJECT}" on {FORUMNAME}. To view it, please click this link: + +{TOPICLINK} + +A comment has also been added regarding this topic: +{COMMENT} + +Thanks, + +{SENDERNAME}', + ), + 'send_email' => array( + /* + @additional_params: send_email + EMAILSUBJECT: The subject the user wants to email. + EMAILBODY: The body the user wants to email. + SENDERNAME: The name of the member sending the email. + RECPNAME: The name of the person receiving the email. + @description: + */ + 'subject' => '{EMAILSUBJECT}', + 'body' => '{EMAILBODY}', + ), + 'report_to_moderator' => array( + /* + @additional_params: report_to_moderator + TOPICSUBJECT: The subject of the reported post. + POSTERNAME: The report post's author's name. + REPORTERNAME: The name of the person reporting the post. + TOPICLINK: The url of the post that is being reported. + REPORTLINK: The url of the moderation center report. + COMMENT: The comment left by the reporter, hopefully to explain why they are reporting the post. + @description: When a user reports a post this email is sent out to moderators and admins of that board. + */ + 'subject' => 'Reported post: {TOPICSUBJECT} by {POSTERNAME}', + 'body' => 'The following post, "{TOPICSUBJECT}" by {POSTERNAME} has been reported by {REPORTERNAME} on a board you moderate: + +The topic: {TOPICLINK} +Moderation center: {REPORTLINK} + +The reporter has made the following comment: +{COMMENT} + +{REGARDS}', + ), + 'change_password' => array( + /* + @additional_params: change_password + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + @description: + */ + 'subject' => 'New Password Details', + 'body' => 'Hey, {USERNAME}! + +Your login details at {FORUMNAME} have been changed and your password reset. Below are your new login details. + +Your username is "{USERNAME}" and your password is "{PASSWORD}". + +You may change it after you login by going to the profile page, or by visiting this page after you login: +{SCRIPTURL}?action=profile + +{REGARDS}', + ), + 'register_activate' => array( + /* + @additional_params: register_activate + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + ACTIVATIONLINK: The url link to reactivate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The code needed to reactivate the member's account. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. If you forget your password, you can reset it by visiting {FORGOTPASSWORDLINK}. + +Before you can login, you first need to activate your account. To do so, please follow this link: + +{ACTIVATIONLINK} + +Should you have any problems with activation, please visit {ACTIVATIONLINKWITHOUTCODE} use the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'register_openid_activate' => array( + /* + @additional_params: register_activate + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + OPENID: The openID identity for the member. + ACTIVATIONLINK: The url link to reactivate the member's account. + ACTIVATIONLINKWITHOUTCODE: The url to the page where the activation code can be entered. + ACTIVATIONCODE: The code needed to reactivate the member's account. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. You have chosen to authenticate using the following OpenID identity: +{OPENID} + +Before you can login, you first need to activate your account. To do so, please follow this link: + +{ACTIVATIONLINK} + +Should you have any problems with activation, please visit {ACTIVATIONLINKWITHOUTCODE} and use the code "{ACTIVATIONCODE}". + +{REGARDS}', + ), + 'register_coppa' => array( + /* + @additional_params: register_coppa + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + COPPALINK: The url link to the coppa form. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. If you forget your password, you can change it at {FORGOTPASSWORDLINK} + +Before you can login, the admin requires consent from your parent/guardian for you to join the community. You can obtain more information at the link below: + +{COPPALINK} + +{REGARDS}', + ), + 'register_openid_coppa' => array( + /* + @additional_params: register_coppa + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + OPENID: The openID identity for the member. + COPPALINK: The url link to the coppa form. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. + +You have chosen to authenticate using the following OpenID identity: +{OPENID} + +Before you can login, the admin requires consent from your parent/guardian for you to join the community. You can obtain more information at the link below: + +{COPPALINK} + +{REGARDS}', + ), + 'register_immediate' => array( + /* + @additional_params: register_immediate + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. If you forget your password, you may change it at {FORGOTPASSWORDLINK}. + +{REGARDS}', + ), + 'register_openid_immediate' => array( + /* + @additional_params: register_immediate + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + OPENID: The openID identity for the member. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Thank you for registering at {FORUMNAME}. Your username is {USERNAME}. + +You have chosen to authenticate using the following OpenID identity: +{OPENID} + +You may update your profile by visiting this page after you login: + +{SCRIPTURL}?action=profile + +{REGARDS}', + ), + 'register_pending' => array( + /* + @additional_params: register_pending + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + PASSWORD: The password for the member. + FORGOTPASSWORDLINK: The url to the "forgot password" page. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Your registration request at {FORUMNAME} has been received, {REALNAME}. + +The username you registered with was {USERNAME}. If you forget your password, you can change it at {FORGOTPASSWORDLINK}. + +Before you can login and start using the forum, your request will be reviewed and approved. When this happens, you will receive another email from this address. + +{REGARDS}', + ), + 'register_openid_pending' => array( + /* + @additional_params: register_pending + REALNAME: The display name for the member receiving the email. + USERNAME: The user name for the member receiving the email. + OPENID: The openID identity for the member. + @description: + */ + 'subject' => 'Welcome to {FORUMNAME}', + 'body' => 'Your registration request at {FORUMNAME} has been received, {REALNAME}. + +The username you registered with was {USERNAME}. + +You have chosen to authenticate using the following OpenID identity: +{OPENID} + +Before you can login and start using the forum, your request will be reviewed and approved. When this happens, you will receive another email from this address. + +{REGARDS}', + ), + 'notification_reply' => array( + /* + @additional_params: notification_reply + TOPICSUBJECT: + POSTERNAME: + TOPICLINK: + UNSUBSCRIBELINK: + @description: + */ + 'subject' => 'Topic reply: {TOPICSUBJECT}', + 'body' => 'A reply has been posted to a topic you are watching by {POSTERNAME}. + +View the reply at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_reply_body' => array( + /* + @additional_params: notification_reply_body + TOPICSUBJECT: + POSTERNAME: + TOPICLINK: + UNSUBSCRIBELINK: + MESSAGE: + @description: + */ + 'subject' => 'Topic reply: {TOPICSUBJECT}', + 'body' => 'A reply has been posted to a topic you are watching by {POSTERNAME}. + +View the reply at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +The text of the reply is shown below: +{MESSAGE} + +{REGARDS}', + ), + 'notification_reply_once' => array( + /* + @additional_params: notification_reply_once + TOPICSUBJECT: + POSTERNAME: + TOPICLINK: + UNSUBSCRIBELINK: + @description: + */ + 'subject' => 'Topic reply: {TOPICSUBJECT}', + 'body' => 'A reply has been posted to a topic you are watching by {POSTERNAME}. + +View the reply at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +More replies may be posted, but you won\'t receive any more notifications until you read the topic. + +{REGARDS}', + ), + 'notification_reply_body_once' => array( + /* + @additional_params: notification_reply_body_once + TOPICSUBJECT: + POSTERNAME: + TOPICLINK: + UNSUBSCRIBELINK: + MESSAGE: + @description: + */ + 'subject' => 'Topic reply: {TOPICSUBJECT}', + 'body' => 'A reply has been posted to a topic you are watching by {POSTERNAME}. + +View the reply at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +The text of the reply is shown below: +{MESSAGE} + +More replies may be posted, but you won\'t receive any more notifications until you read the topic. + +{REGARDS}', + ), + 'notification_sticky' => array( + /* + @additional_params: notification_sticky + @description: + */ + 'subject' => 'Topic stickied: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been marked as a sticky topic by {POSTERNAME}. + +View the topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_lock' => array( + /* + @additional_params: notification_lock + @description: + */ + 'subject' => 'Topic locked: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been locked by {POSTERNAME}. + +View the topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_unlock' => array( + /* + @additional_params: notification_unlock + @description: + */ + 'subject' => 'Topic unlocked: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been unlocked by {POSTERNAME}. + +View the topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_remove' => array( + /* + @additional_params: notification_remove + @description: + */ + 'subject' => 'Topic removed: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been removed by {POSTERNAME}. + +{REGARDS}', + ), + 'notification_move' => array( + /* + @additional_params: notification_move + @description: + */ + 'subject' => 'Topic moved: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been moved to another board by {POSTERNAME}. + +View the topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_merge' => array( + /* + @additional_params: notification_merged + @description: + */ + 'subject' => 'Topic merged: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been merged with another topic by {POSTERNAME}. + +View the new merged topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'notification_split' => array( + /* + @additional_params: notification_split + @description: + */ + 'subject' => 'Topic split: {TOPICSUBJECT}', + 'body' => 'A topic you are watching has been split into two or more topics by {POSTERNAME}. + +View what remains of this topic at: {TOPICLINK} + +Unsubscribe to this topic by using this link: {UNSUBSCRIBELINK} + +{REGARDS}', + ), + 'admin_notify' => array( + /* + @additional_params: admin_notify + USERNAME: + PROFILELINK: + @description: + */ + 'subject' => 'A new member has joined', + 'body' => '{USERNAME} has just signed up as a new member of your forum. Click the link below to view their profile. +{PROFILELINK} + +{REGARDS}', + ), + 'admin_notify_approval' => array( + /* + @additional_params: admin_notify_approval + USERNAME: + PROFILELINK: + APPROVALLINK: + @description: + */ + 'subject' => 'A new member has joined', + 'body' => '{USERNAME} has just signed up as a new member of your forum. Click the link below to view their profile. +{PROFILELINK} + +Before this member can begin posting they must first have their account approved. Click the link below to go to the approval screen. +{APPROVALLINK} + +{REGARDS}', + ), + 'admin_attachments_full' => array( + /* + @additional_params: admin_attachments_full + REALNAME: + @description: + */ + 'subject' => 'Urgent! Attachments folder almost full', + 'body' => '{REALNAME}, + +The attachments folder at {FORUMNAME} is almost full. Please visit the forum to resolve this problem. + +Once the attachments folder reaches it\'s maximum permitted size users will not be able to continue to post attachments or upload custom avatars (If enabled). + +{REGARDS}', + ), + 'paid_subscription_refund' => array( + /* + @additional_params: paid_subscription_refund + NAME: Subscription title. + REALNAME: Recipients name + REFUNDUSER: Username who took out the subscription. + REFUNDNAME: User's display name who took out the subscription. + DATE: Today's date. + PROFILELINK: Link to members profile. + @description: + */ + 'subject' => 'Refunded Paid Subscription', + 'body' => '{REALNAME}, + +A member has received a refund on a paid subscription. Below are the details of this subscription: + + Subscription: {NAME} + User Name: {REFUNDNAME} ({REFUNDUSER}) + Date: {DATE} + +You can view this members profile by clicking the link below: +{PROFILELINK} + +{REGARDS}', + ), + 'paid_subscription_new' => array( + /* + @additional_params: paid_subscription_new + NAME: Subscription title. + REALNAME: Recipients name + SUBEMAIL: Email address of the user who took out the subscription + SUBUSER: Username who took out the subscription. + SUBNAME: User's display name who took out the subscription. + DATE: Today's date. + PROFILELINK: Link to members profile. + @description: + */ + 'subject' => 'New Paid Subscription', + 'body' => '{REALNAME}, + +A member has taken out a new paid subscription. Below are the details of this subscription: + + Subscription: {NAME} + User Name: {SUBNAME} ({SUBUSER}) + User Email: {SUBEMAIL} + Price: {PRICE} + Date: {DATE} + +You can view this members profile by clicking the link below: +{PROFILELINK} + +{REGARDS}', + ), + 'paid_subscription_error' => array( + /* + @additional_params: paid_subscription_error + ERROR: Error message. + REALNAME: Recipients name + @description: + */ + 'subject' => 'Paid Subscription Error Occurred', + 'body' => '{REALNAME}, + +The following error occurred when processing a paid subscription +--------------------------------------------------------------- +{ERROR} + +{REGARDS}', + ), +); + +/* + @additional_params: happy_birthday + REALNAME: The real (display) name of the person receiving the birthday message. + @description: A message sent to members on their birthday. +*/ +$birthdayEmails = array( + 'happy_birthday' => array( + 'subject' => 'Happy birthday from {FORUMNAME}.', + 'body' => 'Dear {REALNAME}, + +We here at {FORUMNAME} would like to wish you a happy birthday. May this day and the year to follow be full of joy. + +{REGARDS}', + 'author' => 'Thantos', + ), + 'karlbenson1' => array( + 'subject' => 'On your Birthday...', + 'body' => 'We could have sent you a birthday card. We could have sent you some flowers or a cake. + +But we didn\'t. + +We could have even sent you one of those automatically generated messages to wish you happy birthday where we don\'t even have to replace INSERT NAME. + +But we didn\'t + +We wrote this birthday greeting just for you. + +We would like to wish you a very special birthday. + +{REGARDS} + +//:: This message was automatically generated :://', + 'author' => 'karlbenson', + ), + 'nite0859' => array( + 'subject' => 'Happy Birthday!', + 'body' => 'Your friends at {FORUMNAME} would like to take a moment of your time to wish you a happy birthday, {REALNAME}. If you have not done so recently, please visit our community in order for others to have the opportunity to pass along their warm regards. + +Even though today is your birthday, {REALNAME}, we would like to remind you that your membership in our community has been the best gift to us thus far. + +Best Wishes, +The Staff of {FORUMNAME}', + 'author' => 'nite0859', + ), + 'zwaldowski' => array( + 'subject' => 'Birthday Wishes to {REALNAME}', + 'body' => 'Dear {REALNAME}, + +Another year in your life has passed. We at {FORUMNAME} hope it has been filled with happiness, and wish you luck in the coming one. + +{REGARDS}', + 'author' => 'zwaldowski', + ), + 'geezmo' => array( + 'subject' => 'Happy birthday, {REALNAME}!', + 'body' => 'Do you know who\'s having a birthday today, {REALNAME}? + +We know... YOU! + +Happy birthday! + +You\'re now a year older but we hope you\'re a lot happier than last year. + +Enjoy your day today, {REALNAME}! + +- From your {FORUMNAME} family', + 'author' => 'geezmo', + ), + 'karlbenson2' => array( + 'subject' => 'Your Birthday Greeting', + 'body' => 'We hope your birthday is the best ever cloudy, sunny or whatever the weather. +Have lots of birthday cake and fun, and tell us what you have done. + +We hope this message brought you cheer, and make it last, until same time same place, next year. + +{REGARDS}', + 'author' => 'karlbenson', + ), +); +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Errors.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Errors.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,407 @@ +register.'; +$txt['passwords_dont_match'] = 'Passwords aren\'t the same.'; +$txt['register_to_use'] = 'Sorry, you must register before using this feature.'; +$txt['password_invalid_character'] = 'Invalid character used in password.'; +$txt['name_invalid_character'] = 'Invalid character used in name.'; +$txt['email_invalid_character'] = 'Invalid character used in email.'; +$txt['username_reserved'] = 'The username you tried to use contains the reserved name \'%1$s\'. Please try another username.'; +$txt['numbers_one_to_nine'] = 'This field only accepts numbers from 0-9'; +$txt['not_a_user'] = 'The user whose profile you are trying to view does not exist.'; +$txt['not_a_topic'] = 'This topic doesn\'t exist on this board.'; +$txt['not_approved_topic'] = 'This topic has not been approved yet.'; +$txt['email_in_use'] = 'That email address (%1$s) is being used by a registered member already. If you feel this is a mistake, go to the login page and use the password reminder with that address.'; + +$txt['didnt_select_vote'] = 'You didn\'t select a vote option.'; +$txt['poll_error'] = 'Either that poll doesn\'t exist, the poll has been locked, or you tried to vote twice.'; +$txt['members_only'] = 'This option is only available to registered members.'; +$txt['locked_by_admin'] = 'This was locked by an administrator. You cannot unlock it.'; +$txt['not_enough_posts_karma'] = 'Sorry, you don\'t have enough posts to modify karma - you need at least %1$d.'; +$txt['cant_change_own_karma'] = 'Sorry, you are not permitted to modify your own karma.'; +$txt['karma_wait_time'] = 'Sorry, you can\'t repeat a karma action without waiting %1$s %2$s.'; +$txt['feature_disabled'] = 'Sorry, this feature is disabled.'; +$txt['cant_access_upload_path'] = 'Cannot access attachments upload path!'; +$txt['file_too_big'] = 'Your file is too large. The maximum attachment size allowed is %1$d KB.'; +$txt['attach_timeout'] = 'Your attachment couldn\'t be saved. This might happen because it took too long to upload or the file is bigger than the server will allow.

    Please consult your server administrator for more information.'; +$txt['filename_exists'] = 'Sorry! There is already an attachment with the same filename as the one you tried to upload. Please rename the file and try again.'; +$txt['bad_attachment'] = 'Your attachment has failed security checks and cannot be uploaded. Please consult the forum administrator.'; +$txt['ran_out_of_space'] = 'The upload folder is full. Please try a smaller file and/or contact an administrator.'; +$txt['couldnt_connect'] = 'Could not connect to server or could not find file'; +$txt['no_board'] = 'The board you specified doesn\'t exist'; +$txt['cant_split'] = 'You are not allowed to split topics'; +$txt['cant_merge'] = 'You are not allowed to merge topics'; +$txt['no_topic_id'] = 'You specified an invalid topic ID.'; +$txt['split_first_post'] = 'You cannot split a topic at the first post.'; +$txt['topic_one_post'] = 'This topic only contains one message and cannot be split.'; +$txt['no_posts_selected'] = 'No messages selected'; +$txt['selected_all_posts'] = 'Unable to split. You have selected every message.'; +$txt['cant_find_messages'] = 'Unable to find messages'; +$txt['cant_find_user_email'] = 'Unable to find user\'s email address.'; +$txt['cant_insert_topic'] = 'Unable to insert topic'; +$txt['already_a_mod'] = 'You have chosen a username of an already existing moderator. Please choose another username'; +$txt['session_timeout'] = 'Your session timed out while posting. Please go back and try again.'; +$txt['session_verify_fail'] = 'Session verification failed. Please try logging out and back in again, and then try again.'; +$txt['verify_url_fail'] = 'Unable to verify referring url. Please go back and try again.'; +$txt['guest_vote_disabled'] = 'Guests cannot vote in this poll.'; + +$txt['cannot_access_mod_center'] = 'You do not have permission to access the moderation center.'; +$txt['cannot_admin_forum'] = 'You are not allowed to administrate this forum.'; +$txt['cannot_announce_topic'] = 'You are not allowed to announce topics on this board.'; +$txt['cannot_approve_posts'] = 'You do not have permission to approve items.'; +$txt['cannot_post_unapproved_attachments'] = 'You do not have permission to post unapproved attachments.'; +$txt['cannot_post_unapproved_topics'] = 'You do not have permission to post unapproved topics.'; +$txt['cannot_post_unapproved_replies_own'] = 'You do not have permission to post unapproved replies to your topics.'; +$txt['cannot_post_unapproved_replies_any'] = 'You do not have permission to post unapproved replies to other users\' topics.'; +$txt['cannot_calendar_edit_any'] = 'You cannot edit calendar events.'; +$txt['cannot_calendar_edit_own'] = 'You don\'t have the privileges necessary to edit your own events.'; +$txt['cannot_calendar_post'] = 'Event posting isn\'t allowed - sorry.'; +$txt['cannot_calendar_view'] = 'Sorry, but you are not allowed to view the calendar.'; +$txt['cannot_remove_any'] = 'Sorry, but you don\'t have the privilege to remove just any topic. Check to make sure this topic wasn\'t just moved to another board.'; +$txt['cannot_remove_own'] = 'You cannot delete your own topics in this board. Check to make sure this topic wasn\'t just moved to another board.'; +$txt['cannot_edit_news'] = 'You are not allowed to edit news items on this forum.'; +$txt['cannot_pm_read'] = 'Sorry, you can\'t read your personal messages.'; +$txt['cannot_pm_send'] = 'You are not allowed to send personal messages.'; +$txt['cannot_karma_edit'] = 'You aren\'t permitted to modify other people\'s karma.'; +$txt['cannot_lock_any'] = 'You are not allowed to lock just any topic here.'; +$txt['cannot_lock_own'] = 'Apologies, but you cannot lock your own topics here.'; +$txt['cannot_make_sticky'] = 'You don\'t have permission to sticky this topic.'; +$txt['cannot_manage_attachments'] = 'You\'re not allowed to manage attachments or avatars.'; +$txt['cannot_manage_bans'] = 'You\'re not allowed to change the list of bans.'; +$txt['cannot_manage_boards'] = 'You are not allowed to manage boards and categories.'; +$txt['cannot_manage_membergroups'] = 'You don\'t have permission to modify or assign membergroups.'; +$txt['cannot_manage_permissions'] = 'You don\'t have permission to manage permissions.'; +$txt['cannot_manage_smileys'] = 'You\'re not allowed to manage smileys and message icons.'; +$txt['cannot_mark_any_notify'] = 'You don\'t have the permissions necessary to get notifications from this topic.'; +$txt['cannot_mark_notify'] = 'Sorry, but you are not permitted to request notifications from this board.'; +$txt['cannot_merge_any'] = 'You aren\'t allowed to merge topics on one of the selected board(s).'; +$txt['cannot_moderate_forum'] = 'You are not allowed to moderate this forum.'; +$txt['cannot_moderate_board'] = 'You are not allowed to moderate this board.'; +$txt['cannot_modify_any'] = 'You aren\'t allowed to modify just any post.'; +$txt['cannot_modify_own'] = 'Sorry, but you aren\'t allowed to edit your own posts.'; +$txt['cannot_modify_replies'] = 'Even though this post is a reply to your topic, you cannot edit it.'; +$txt['cannot_move_own'] = 'You are not allowed to move your own topics in this board.'; +$txt['cannot_move_any'] = 'You are not allowed to move topics in this board.'; +$txt['cannot_poll_add_own'] = 'Sorry, you aren\'t allowed to add polls to your own topics in this board.'; +$txt['cannot_poll_add_any'] = 'You don\'t have the access to add polls to this topic.'; +$txt['cannot_poll_edit_own'] = 'You cannot edit this poll, even though it is your own.'; +$txt['cannot_poll_edit_any'] = 'You have been denied access to editing polls in this board.'; +$txt['cannot_poll_lock_own'] = 'You are not allowed to lock your own polls in this board.'; +$txt['cannot_poll_lock_any'] = 'Sorry, but you aren\'t allowed to lock just any poll.'; +$txt['cannot_poll_post'] = 'You aren\'t allowed to post polls in the current board.'; +$txt['cannot_poll_remove_own'] = 'You are not permitted to remove this poll from your topic.'; +$txt['cannot_poll_remove_any'] = 'You cannot remove just any poll on this board.'; +$txt['cannot_poll_view'] = 'You are not allowed to view polls in this board.'; +$txt['cannot_poll_vote'] = 'Sorry, but you cannot vote in polls in this board.'; +$txt['cannot_post_attachment'] = 'You don\'t have permission to post attachments here.'; +$txt['cannot_post_new'] = 'Sorry, you cannot post new topics in this board.'; +$txt['cannot_post_reply_any'] = 'You are not permitted to post replies to topics on this board.'; +$txt['cannot_post_reply_own'] = 'You are not allowed to post replies even to your own topics in this board.'; +$txt['cannot_profile_remove_own'] = 'Sorry, but you aren\'t allowed to delete your own account.'; +$txt['cannot_profile_remove_any'] = 'You don\'t have the permissions to go about removing people\'s accounts!'; +$txt['cannot_profile_extra_any'] = 'You are not permitted to modify profile settings.'; +$txt['cannot_profile_identity_any'] = 'You aren\'t allowed to edit account settings.'; +$txt['cannot_profile_title_any'] = 'You cannot edit people\'s custom titles.'; +$txt['cannot_profile_extra_own'] = 'Sorry, but you don\'t have the necessary permissions to edit your profile data.'; +$txt['cannot_profile_identity_own'] = 'You can\'t change your identity at the current moment.'; +$txt['cannot_profile_title_own'] = 'You are not allowed to change your custom title.'; +$txt['cannot_profile_server_avatar'] = 'You are not permitted to use a server stored avatar.'; +$txt['cannot_profile_upload_avatar'] = 'You do not have permission to upload an avatar.'; +$txt['cannot_profile_remote_avatar'] = 'You don\'t have the privilege of using a remote avatar.'; +$txt['cannot_profile_view_own'] = 'Many apologies, but you can\'t view your own profile.'; +$txt['cannot_profile_view_any'] = 'Many apologies, but you can\'t view just any profile.'; +$txt['cannot_delete_own'] = 'You are not, on this board, allowed to delete your own posts.'; +$txt['cannot_delete_replies'] = 'Sorry, but you cannot remove these posts, even though they are replies to your topic.'; +$txt['cannot_delete_any'] = 'Deleting just any posts in this board is not allowed.'; +$txt['cannot_report_any'] = 'You are not allowed to report posts in this board.'; +$txt['cannot_search_posts'] = 'You are not allowed to search for posts in this forum.'; +$txt['cannot_send_mail'] = 'You don\'t have the privilege of sending out emails to everyone.'; +$txt['cannot_issue_warning'] = 'Sorry, you do not have permission to issue warnings to members.'; +$txt['cannot_send_topic'] = 'Sorry, but the administrator has disallowed sending topics on this board.'; +$txt['cannot_split_any'] = 'Splitting just any topic is not allowed in this board.'; +$txt['cannot_view_attachments'] = 'It seems that you are not allowed to download or view attachments on this board.'; +$txt['cannot_view_mlist'] = 'You can\'t view the memberlist because you don\'t have permission to.'; +$txt['cannot_view_stats'] = 'You aren\'t allowed to view the forum statistics.'; +$txt['cannot_who_view'] = 'Sorry - you don\'t have the proper permissions to view the Who\'s Online list.'; + +$txt['no_theme'] = 'That theme does not exist.'; +$txt['theme_dir_wrong'] = 'The default theme\'s directory is wrong, please correct it by clicking this text.'; +$txt['registration_disabled'] = 'Sorry, registration is currently disabled.'; +$txt['registration_no_secret_question'] = 'Sorry, there is no secret question set for this member.'; +$txt['poll_range_error'] = 'Sorry, the poll must run for more than 0 days.'; +$txt['delFirstPost'] = 'You are not allowed to delete the first post in a topic.

    If you want to delete this topic, click on the Remove Topic link, or ask a moderator/administrator to do it for you.

    '; +$txt['parent_error'] = 'Unable to create board!'; +$txt['login_cookie_error'] = 'You were unable to login. Please check your cookie settings.'; +$txt['incorrect_answer'] = 'Sorry, but you did not answer your question correctly. Please click back to try again, or click back twice to use the default method of obtaining your password.'; +$txt['no_mods'] = 'No moderators found!'; +$txt['parent_not_found'] = 'Board structure corrupt: unable to find parent board'; +$txt['modify_post_time_passed'] = 'You may not modify this post as the time limit for edits has passed.'; + +$txt['calendar_off'] = 'You cannot access the calendar right now because it is disabled.'; +$txt['invalid_month'] = 'Invalid month value.'; +$txt['invalid_year'] = 'Invalid year value.'; +$txt['invalid_day'] = 'Invalid day value.'; +$txt['event_month_missing'] = 'Event month is missing.'; +$txt['event_year_missing'] = 'Event year is missing.'; +$txt['event_day_missing'] = 'Event day is missing.'; +$txt['event_title_missing'] = 'Event title is missing.'; +$txt['invalid_date'] = 'Invalid date.'; +$txt['no_event_title'] = 'No event title was entered.'; +$txt['missing_event_id'] = 'Missing event ID.'; +$txt['cant_edit_event'] = 'You do not have permission to edit this event.'; +$txt['missing_board_id'] = 'Board ID is missing.'; +$txt['missing_topic_id'] = 'Topic ID is missing.'; +$txt['topic_doesnt_exist'] = 'Topic doesn\'t exist.'; +$txt['not_your_topic'] = 'You are not the owner of this topic.'; +$txt['board_doesnt_exist'] = 'The board does not exist.'; +$txt['no_span'] = 'The span feature is currently disabled.'; +$txt['invalid_days_numb'] = 'Invalid number of days to span.'; + +$txt['moveto_noboards'] = 'There are no boards to move this topic to!'; + +$txt['already_activated'] = 'Your account has already been activated.'; +$txt['still_awaiting_approval'] = 'Your account is still awaiting admin approval.'; + +$txt['invalid_email'] = 'Invalid email address / email address range.
    Example of a valid email address: evil.user@badsite.com.
    Example of a valid email address range: *@*.badsite.com'; +$txt['invalid_expiration_date'] = 'Expiration date is not valid'; +$txt['invalid_hostname'] = 'Invalid host name / host name range.
    Example of a valid host name: proxy4.badhost.com
    Example of a valid host name range: *.badhost.com'; +$txt['invalid_ip'] = 'Invalid IP / IP range.
    Example of a valid IP address: 127.0.0.1
    Example of a valid IP range: 127.0.0-20.*'; +$txt['invalid_tracking_ip'] = 'Invalid IP / IP range.
    Example of a valid IP address: 127.0.0.1
    Example of a valid IP range: 127.0.0.*'; +$txt['invalid_username'] = 'Member name not found'; +$txt['no_ban_admin'] = 'You may not ban an admin - You must demote them first!'; +$txt['no_bantype_selected'] = 'No ban type was selected'; +$txt['ban_not_found'] = 'Ban not found'; +$txt['ban_unknown_restriction_type'] = 'Restriction type unknown'; +$txt['ban_name_empty'] = 'The name of the ban was left empty'; +$txt['ban_name_exists'] = 'The name of this ban (%1$s) already exists. Please choose a different name.'; +$txt['ban_trigger_already_exists'] = 'This ban trigger (%1$s) already exists in %2$s.'; + +$txt['recycle_no_valid_board'] = 'No valid board selected for recycled topics'; + +$txt['login_threshold_fail'] = 'Sorry, you are out of login chances. Please come back and try again later.'; +$txt['login_threshold_brute_fail'] = 'Sorry, but you\'ve reached your login attempts threshold. Please wait 30 seconds and try again later.'; + +$txt['who_off'] = 'You cannot access Who\'s Online right now because it is disabled.'; + +$txt['merge_create_topic_failed'] = 'Error creating a new topic.'; +$txt['merge_need_more_topics'] = 'Merge topics require at least two topics to merge.'; + +$txt['postWaitTime_broken'] = 'The last posting from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['registerWaitTime_broken'] = 'You already registered just %1$d seconds ago!'; +$txt['loginWaitTime_broken'] = 'You will have to wait about %1$d seconds to login again, sorry.'; +$txt['pmWaitTime_broken'] = 'The last personal message from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['reporttmWaitTime_broken'] = 'The last topic report from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['sendtopcWaitTime_broken'] = 'The last topic sent from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['sendmailWaitTime_broken'] = 'The last email sent from your IP was less than %1$d seconds ago. Please try again later.'; +$txt['searchWaitTime_broken'] = 'Your last search was less than %1$d seconds ago. Please try again later.'; + +$txt['email_missing_data'] = 'You must enter something in both the subject and message boxes.'; + +$txt['topic_gone'] = 'The topic or board you are looking for appears to be either missing or off limits to you.'; +$txt['theme_edit_missing'] = 'The file you are trying to edit... can\'t even be found!'; + +$txt['attachments_no_write'] = 'The attachments upload directory is not writable. Your attachment or avatar cannot be saved.'; +$txt['attachments_limit_per_post'] = 'You may not upload more than %1$d attachments per post'; + +$txt['no_dump_database'] = 'Only administrators can make database backups!'; +$txt['pm_not_yours'] = 'The personal message you\'re trying to quote is not your own or does not exist, please go back and try again.'; +$txt['mangled_post'] = 'Mangled form data - please go back and try again.'; +$txt['quoted_post_deleted'] = 'The post you are trying to quote either does not exist, was deleted, or is no longer viewable by you.'; +$txt['pm_too_many_per_hour'] = 'You have exceeded the limit of %1$d personal messages per hour.'; +$txt['labels_too_many'] = 'Sorry, %1$s messages already had the maximum amount of labels allowed!'; + +$txt['register_only_once'] = 'Sorry, but you\'re not allowed to register multiple accounts at the same time from the same computer.'; +$txt['admin_setting_coppa_require_contact'] = 'You must enter either a postal or fax contact if parent/guardian approval is required.'; + +$txt['error_long_name'] = 'The name you tried to use was too long.'; +$txt['error_no_name'] = 'No name was provided.'; +$txt['error_bad_name'] = 'The name you submitted cannot be used, because it is or contains a reserved name.'; +$txt['error_no_email'] = 'No email address was provided.'; +$txt['error_bad_email'] = 'An invalid email address was given.'; +$txt['error_no_event'] = 'No event name has been given.'; +$txt['error_no_subject'] = 'No subject was filled in.'; +$txt['error_no_question'] = 'No question was filled in for this poll.'; +$txt['error_no_message'] = 'The message body was left empty.'; +$txt['error_long_message'] = 'The message exceeds the maximum allowed length (%1$d characters).'; +$txt['error_no_comment'] = 'The comment field was left empty.'; +$txt['error_session_timeout'] = 'Your session timed out while posting. Please try to re-submit your message.'; +$txt['error_no_to'] = 'No recipients specified.'; +$txt['error_bad_to'] = 'One or more \'to\'-recipients could not be found.'; +$txt['error_bad_bcc'] = 'One or more \'bcc\'-recipients could not be found.'; +$txt['error_form_already_submitted'] = 'You already submitted this post! You might have accidentally double clicked or tried to refresh the page.'; +$txt['error_poll_few'] = 'You must have at least two choices!'; +$txt['error_need_qr_verification'] = 'Please complete the verification section below to complete your post.'; +$txt['error_wrong_verification_code'] = 'The letters you typed don\'t match the letters that were shown in the picture.'; +$txt['error_wrong_verification_answer'] = 'You did not answer the verification questions correctly.'; +$txt['error_need_verification_code'] = 'Please enter the verification code below to continue to the results.'; +$txt['error_bad_file'] = 'Sorry but the file specified could not be opened: %1$s'; +$txt['error_bad_line'] = 'The line you specified is invalid.'; + +$txt['smiley_not_found'] = 'Smiley not found.'; +$txt['smiley_has_no_code'] = 'No code for this smiley was given.'; +$txt['smiley_has_no_filename'] = 'No filename for this smiley was given.'; +$txt['smiley_not_unique'] = 'A smiley with that code already exists.'; +$txt['smiley_set_already_exists'] = 'A smiley set with that URL already exists'; +$txt['smiley_set_not_found'] = 'Smiley set not found'; +$txt['smiley_set_path_already_used'] = 'The URL of the smiley set is already being used by another smiley set.'; +$txt['smiley_set_unable_to_import'] = 'Unable to import smiley set. Either the directory is invalid or cannot be accessed.'; + +$txt['smileys_upload_error'] = 'Failed to upload file.'; +$txt['smileys_upload_error_blank'] = 'All smiley sets must have an image!'; +$txt['smileys_upload_error_name'] = 'All smileys must have the same filename!'; +$txt['smileys_upload_error_illegal'] = 'Illegal Type.'; + +$txt['search_invalid_weights'] = 'Search weights are not properly configured. At least one weight should be configure to be non-zero. Please report this error to an administrator.'; +$txt['unable_to_create_temporary'] = 'The search function was unable to create temporary tables. Please try again.'; + +$txt['package_no_file'] = 'Unable to find package file!'; +$txt['packageget_unable'] = 'Unable to connect to the server. Please try using this URL instead.'; +$txt['not_on_simplemachines'] = 'Sorry, packages can only be downloaded like this from the simplemachines.org server.'; +$txt['package_cant_uninstall'] = 'This package was either never installed or was already uninstalled - you can\'t uninstall it now.'; +$txt['package_cant_download'] = 'You cannot download or install new packages because the Packages directory or one of the files in it are not writable!'; +$txt['package_upload_error_nofile'] = 'You did not select a package to upload.'; +$txt['package_upload_error_failed'] = 'Could not upload package, please check directory permissions!'; +$txt['package_upload_error_exists'] = 'The file you are uploading already exists on the server. Please delete it first then try again.'; +$txt['package_upload_error_supports'] = 'The package manager currently allows only these file types: %1$s.'; +$txt['package_upload_error_broken'] = 'Package upload failed due to the following error:
    "%1$s"'; + +$txt['package_get_error_not_found'] = 'The package you are trying to install cannot be located. You may want to manually upload the package to your Packages directory.'; +$txt['package_get_error_missing_xml'] = 'The package you are attempting to install is missing the package-info.xml that must be in the root package directory.'; +$txt['package_get_error_is_zero'] = 'Although the package was downloaded to the server it appears to be empty. Please check the Packages directory, and the "temp" sub-directory are both writable. If you continue to experience this problem you should try extracting the package on your PC and uploading the extracted files into a subdirectory in your Packages directory and try again. For example, if the package was called shout.tar.gz you should:
    1) Download the package to your local PC and extract it into files.
    2) Using an FTP client create a new directory in your "Packages" folder, in this example you may call it "shout".
    3) Upload all the files from the extracted package to this directory.
    4) Go back to the package manager browse page and the package will be automatically found by SMF.'; +$txt['package_get_error_packageinfo_corrupt'] = 'SMF was unable to find any valid information within the package-info.xml file included within the Package. There may be an error with the modification, or the package may be corrupt.'; + +$txt['no_membergroup_selected'] = 'No membergroup selected'; +$txt['membergroup_does_not_exist'] = 'The membergroup doesn\'t exist or is invalid.'; + +$txt['at_least_one_admin'] = 'There must be at least one administrator on a forum!'; + +$txt['error_functionality_not_windows'] = 'Sorry, this functionality is currently not available for servers running Windows.'; + +// Don't use entities in the below string. +$txt['attachment_not_found'] = 'Attachment Not Found'; + +$txt['error_no_boards_selected'] = 'No valid boards were selected!'; +$txt['error_invalid_search_string'] = 'Did you forget to put something to search for?'; +$txt['error_invalid_search_string_blacklist'] = 'Your search query contained too trivial words. Please try again with a different query.'; +$txt['error_search_string_small_words'] = 'Each word must be at least two characters long.'; +$txt['error_query_not_specific_enough'] = 'Your search query didn\'t return any matches.'; +$txt['error_no_messages_in_time_frame'] = 'No messages found in selected time frame.'; +$txt['error_no_labels_selected'] = 'No labels were selected!'; +$txt['error_no_search_daemon'] = 'Unable to access the search daemon'; + +$txt['profile_errors_occurred'] = 'The following errors occurred when trying to save your profile'; +$txt['profile_error_bad_offset'] = 'The time offset is out of range'; +$txt['profile_error_no_name'] = 'The name field was left blank'; +$txt['profile_error_name_taken'] = 'The selected username/display name has already been taken'; +$txt['profile_error_name_too_long'] = 'The selected name is too long. It should be no greater than 60 characters long'; +$txt['profile_error_no_email'] = 'The email field was left blank'; +$txt['profile_error_bad_email'] = 'You have not entered a valid email address'; +$txt['profile_error_email_taken'] = 'Another user is already registered with that email address'; +$txt['profile_error_no_password'] = 'You did not enter your password'; +$txt['profile_error_bad_new_password'] = 'The new passwords you entered do not match'; +$txt['profile_error_bad_password'] = 'The password you entered was not correct'; +$txt['profile_error_bad_avatar'] = 'The avatar you have selected is either too large or not an avatar'; +$txt['profile_error_password_short'] = 'Your password must be at least ' . (empty($modSettings['password_strength']) ? 4 : 8) . ' characters long.'; +$txt['profile_error_password_restricted_words'] = 'Your password must not contain your username, email address or other commonly used words.'; +$txt['profile_error_password_chars'] = 'Your password must contain a mix of upper and lower case letters, as well as digits.'; +$txt['profile_error_already_requested_group'] = 'You already have an outstanding request for this group!'; +$txt['profile_error_openid_in_use'] = 'Another user is already using that OpenID authentication URL'; + +$txt['mysql_error_space'] = ' - check database storage space or contact the server administrator.'; + +$txt['icon_not_found'] = 'The icon image could not be found in the default theme - please ensure the image has been uploaded and try again.'; +$txt['icon_after_itself'] = 'The icon cannot be positioned after itself!'; +$txt['icon_name_too_long'] = 'Icon filenames cannot be more than 16 characters long'; + +$txt['name_censored'] = 'Sorry, the name you tried to use, %1$s, contains words which have been censored. Please try another name.'; + +$txt['poll_already_exists'] = 'A topic can only have one poll associated with it!'; +$txt['poll_not_found'] = 'There is no poll associated with this topic!'; + +$txt['error_while_adding_poll'] = 'The following error or errors occurred while adding this poll'; +$txt['error_while_editing_poll'] = 'The following error or errors occurred while editing this poll'; + +$txt['loadavg_search_disabled'] = 'Due to high stress on the server, the search function has been automatically and temporarily disabled. Please try again in a short while.'; +$txt['loadavg_generic_disabled'] = 'Sorry, because of high stress on the server, this feature is currently unavailable.'; +$txt['loadavg_allunread_disabled'] = 'The server\'s resources are temporarily under too high a demand to find all the topics you have not read.'; +$txt['loadavg_unreadreplies_disabled'] = 'The server is currently under high stress. Please try again shortly.'; +$txt['loadavg_show_posts_disabled'] = 'Please try again later. This member\'s posts are not currently available due to high load on the server.'; +$txt['loadavg_unread_disabled'] = 'The server\'s resources are temporarily under too high a demand to list out the topics you have not read.'; + +$txt['cannot_edit_permissions_inherited'] = 'You cannot edit inherited permissions directly, you must either edit the parent group or edit the membergroup inheritance.'; + +$txt['mc_no_modreport_specified'] = 'You need to specify which report you wish to view.'; +$txt['mc_no_modreport_found'] = 'The specified report either doesn\'t exist or is off limits to you'; + +$txt['st_cannot_retrieve_file'] = 'Could not retrieve the file %1$s.'; +$txt['admin_file_not_found'] = 'Could not load the requested file: %1$s.'; + +$txt['themes_none_selectable'] = 'At least one theme must be selectable.'; +$txt['themes_default_selectable'] = 'The overall forum default theme must be a selectable theme.'; +$txt['ignoreboards_disallowed'] = 'The option to ignore boards has not been enabled.'; + +$txt['mboards_delete_error'] = 'No category selected!'; +$txt['mboards_delete_board_error'] = 'No board selected!'; + +$txt['mboards_parent_own_child_error'] = 'Unable to make a parent its own child!'; +$txt['mboards_board_own_child_error'] = 'Unable to make a board its own child!'; + +$txt['smileys_upload_error_notwritable'] = 'The following smiley directories are not writable: %1$s'; +$txt['smileys_upload_error_types'] = 'Smiley images can only have the following extensions: %1$s.'; + +$txt['change_email_success'] = 'Your email address has been changed, and a new activation email has been sent to it.'; +$txt['resend_email_success'] = 'A new activation email has successfully been sent.'; + +$txt['custom_option_need_name'] = 'The profile option must have a name!'; +$txt['custom_option_not_unique'] = 'Field name is not unique!'; + +$txt['warning_no_reason'] = 'You must enter a reason for altering the warning state of a member'; +$txt['warning_notify_blank'] = 'You selected to notify the user but did not fill in the subject/message fields'; + +$txt['cannot_connect_doc_site'] = 'Could not connect to the Simple Machines Online Manual. Please check that your server configuration allows external internet connections and try again later.'; + +$txt['movetopic_no_reason'] = 'You must enter a reason for moving the topic, or uncheck the option to \'post a redirection topic\'.'; + +// OpenID error strings +$txt['openid_server_bad_response'] = 'The requested identifier did not return the proper information.'; +$txt['openid_return_no_mode'] = 'The identity provider did not respond with the OpenID mode.'; +$txt['openid_not_resolved'] = 'The identity provider did not approve your request.'; +$txt['openid_no_assoc'] = 'Could not find the requested association with the identity provider.'; +$txt['openid_sig_invalid'] = 'The signature from the identity provider is invalid.'; +$txt['openid_load_data'] = 'Could not load the data from your login request. Please try again.'; +$txt['openid_not_verified'] = 'The OpenID address given has not been verified yet. Please log in to verify.'; + +$txt['error_custom_field_too_long'] = 'The "%1$s" field cannot be greater than %2$d characters in length.'; +$txt['error_custom_field_invalid_email'] = 'The "%1$s" field must be a valid email address.'; +$txt['error_custom_field_not_number'] = 'The "%1$s" field must be numeric.'; +$txt['error_custom_field_inproper_format'] = 'The "%1$s" field is an invalid format.'; +$txt['error_custom_field_empty'] = 'The "%1$s" field cannot be left blank.'; + +$txt['email_no_template'] = 'The email template "%1$s" could not be found.'; + +$txt['search_api_missing'] = 'The search API could not be found! Please contact the admin to check they have uploaded the correct files.'; +$txt['search_api_not_compatible'] = 'The selected search API the forum is using is out of date - falling back to standard search. Please check file %1$s.'; + +// Restore topic/posts +$txt['cannot_restore_first_post'] = 'You cannot restore the first post in a topic.'; +$txt['parent_topic_missing'] = 'The parent topic of the post you are trying to restore has been deleted.'; +$txt['restored_disabled'] = 'The restoration of topics has been disabled.'; +$txt['restore_not_found'] = 'The following messages could not be restored; the original topic may have been removed:
      %1$s
    You will need to move these manually.'; + +$txt['error_invalid_dir'] = 'The directory you entered is invalid.'; + +$txt['error_sqlite_optimizing'] = 'Sqlite is optimizing the database, the forum can not be accessed until it has finished. Please try refreshing this page momentarily.'; +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Help.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Help.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,604 @@ +Edit Boards
    + In this menu you can create/reorder/remove boards, and the categories + above them. For example, if you had a wide-ranging + site that offered information on "Sports" and "Cars" and "Music", these + would be the top-level Categories you\'d create. Under each of these + categories you\'d likely want to create hierarchical "sub-categories", + or "Boards" for topics within each. It\'s a simple hierarchy, with this structure:
    +
      +
    • + Sports +  - A "category" +
    • +
        +
      • + Baseball +  - A board under the category of "Sports" +
      • +
          +
        • + Stats +  - A child board under the board of "Baseball" +
        • +
        +
      • Football +  - A board under the category of "Sports"
      • +
      +
    + Categories allow you to break down the board into broad topics ("Cars, + Sports"), and the "Boards" under them are the actual topics under which + members can post. A user interested in Pintos + would post a message under "Cars->Pinto". Categories allow people to + quickly find what their interests are: Instead of a "Store" you have + "Hardware" and "Clothing" stores you can go to. This simplifies your + search for "pipe joint compound" because you can go to the Hardware + Store "category" instead of the Clothing Store (where you\'re unlikely + to find pipe joint compound).
    + As noted above, a Board is a key topic underneath a broad category. + If you want to discuss "Pintos" you\'d go to the "Auto" category and + jump into the "Pinto" board to post your thoughts in that board.
    + Administrative functions for this menu item are to create new boards + under each category, to reorder them (put "Pinto" behind "Chevy"), or + to delete the board entirely.'; + +$helptxt['edit_news'] = ' +
      +
    • + News
      + This section allows you to set the text for news items displayed on the Board Index page. + Add any item you want (e.g., "Don\'t miss the conference this Tuesday"). Each news item is displayed randomly and should be placed in a separate box. +
    • +
    • + Newsletters
      + This section allows you to send out newsletters to the members of the forum via personal message or email. First select the groups that you want to receive the newsletter, and those you don\'t want to receive the newsletter. If you wish, you can add additional members and email addresses that will receive the newsletter. Finally, input the message you want to send and select whether you want it to be sent to members as a personal message or as an email. +
    • +
    • + Settings
      + This section contains a few settings that relate to news and newsletters, including selecting what groups can edit forum news or send newsletters. There is also an setting to configure whether you want news feeds enabled on the forum, as well as a setting to configure the length (how many characters are displayed) for each news post from a news feed. +
    • +
    '; + +$helptxt['view_members'] = ' +
      +
    • + View all Members
      + View all members in the board. You are presented with a hyperlinked list of member names. You may click + on any of the names to find details of the members (homepage, age, etc.), and as Administrator + you are able to modify these parameters. You have complete control over members, including the + ability to delete them from the forum.

      +
    • +
    • + Awaiting Approval
      + This section is only shown if you have enabled admin approval of all new registrations. Anyone who registers to join your + forum will only become a full member once they have been approved by an admin. The section lists all those members who + are still awaiting approval, along with their email and IP address. You can choose to either accept or reject (delete) + any member on the list by checking the box next to the member and choosing the action from the drop-down box at the bottom + of the screen. When rejecting a member you can choose to delete the member either with or without notifying them of your decision.

      +
    • +
    • + Awaiting Activation
      + This section will only be visible if you have activation of member accounts enabled on the forum. This section will list all + members who have still not activated their new accounts. From this screen you can choose to either accept, reject or remind + members with outstanding registrations. As above you can also choose to email the member to inform them of the + action you have taken.

      +
    • +
    '; + +$helptxt['ban_members'] = 'Ban Members
    + SMF provides the ability to "ban" users, to prevent people who have violated the trust of the board + by spamming, trolling, etc. This allows you to those users who are detrimental to your forum. As an admin, + when you view messages, you can see each user\'s IP address used to post at that time. In the ban list, + you simply type that IP address in, save, and they can no longer post from that location.
    You can also + ban people through their email address.'; + +$helptxt['featuresettings'] = 'Features and Options
    + There are several features in this section that can be changed to your preference.'; + +$helptxt['securitysettings'] = 'Security and Moderation
    + This section contains settings relating to the security and moderation of your forum.'; + +$helptxt['modsettings'] = 'Modification Settings
    + This section should contain any settings added by modifications installed on your forum.'; + +$helptxt['number_format'] = 'Number Format
    + You can use this setting to format the way in which numbers on your forum will be displayed to the user. The format of this setting is:
    +
    1,234.00

    + Where \',\' is the character used to split up groups of thousands, \'.\' is the character used as the decimal point and the number of zeros dictate the accuracy of rounding.'; + +$helptxt['time_format'] = 'Time Format
    + You have the power to adjust how the time and date look for yourself. There are a lot of little letters, but it\'s quite simple. + The conventions follow PHP\'s strftime function and are described as below (more details can be found at php.net).
    +
    + The following characters are recognized in the format string:
    + +   %a - abbreviated weekday name
    +   %A - full weekday name
    +   %b - abbreviated month name
    +   %B - full month name
    +   %d - day of the month (01 to 31)
    +   %D* - same as %m/%d/%y
    +   %e* - day of the month (1 to 31)
    +   %H - hour using a 24-hour clock (range 00 to 23)
    +   %I - hour using a 12-hour clock (range 01 to 12)
    +   %m - month as a number (01 to 12)
    +   %M - minute as a number
    +   %p - either "am" or "pm" according to the given time
    +   %R* - time in 24 hour notation
    +   %S - second as a decimal number
    +   %T* - current time, equal to %H:%M:%S
    +   %y - 2 digit year (00 to 99)
    +   %Y - 4 digit year
    +   %% - a literal \'%\' character
    +
    + * Does not work on Windows-based servers.
    '; + +$helptxt['live_news'] = 'Live announcements
    + This box shows recently updated announcements from www.simplemachines.org. + You should check here every now and then for updates, new releases, and important information from Simple Machines.'; + +$helptxt['registrations'] = 'Registration Management
    + This section contains all the functions that could be necessary to manage new registrations on the forum. It contains up to four + sections which are visible depending on your forum settings. These are:

    +
      +
    • + Register new member
      + From this screen you can choose to register accounts for new members on their behalf. This can be useful in forums where registration is closed + to new members, or in cases where the admin wishes to create a test account. If the option to require activation of the account + is selected the member will be emailed a activation link which must be clicked before they can use the account. Similarly you can + select to email the users new password to the stated email address.

      +
    • +
    • + Edit Registration Agreement
      + This allows you to set the text for the registration agreement displayed when members sign up for your forum. + You can add or remove anything from the default registration agreement, which is included in SMF.

      +
    • +
    • + Set Reserved Names
      + Using this interface you can specify words or names which may not be used by your users.

      +
    • +
    • + Settings
      + This section will only be visible if you have permission to administrate the forum. From this screen you can decide on the registration method + is use on your forum, as well as other registration related settings. +
    • +
    '; + +$helptxt['modlog'] = 'Moderation Log
    + This section allows members of the moderation team to track all the moderation actions that the forum moderators have performed. To ensure that + moderators cannot remove references to the actions they have performed, entries may not be deleted until 24 hours after the action was taken.'; +$helptxt['adminlog'] = 'Administration Log
    + This section allows members of the admin team to track some of the administrative actions that have occurred on the forum. To ensure that + admins cannot remove references to the actions they have performed, entries may not be deleted until 24 hours after the action was taken.'; +$helptxt['warning_enable'] = 'User Warning System
    + This feature enables members of the admin and moderation team to issue warnings to members - and to use a members warning level to determine the + actions available to them on the forum. Upon enabling this feature a permission will be available within the permissions section to define + which groups may assign warnings to members. Warning levels can be adjusted from a members profile. The following additional options are available: +
      +
    • + Warning Level for Member Watch
      + This setting defines the percentage warning level a member must reach to automatically assign a "watch" to the member. + Any member who is being "watched" will appear in the relevant area of the moderation center. +
    • +
    • + Warning Level for Post Moderation
      + Any member passing the value of this setting will find all their posts require moderator approval before they appear to the forum + community. This will override any local board permissions which may exist related to post moderation. +
    • +
    • + Warning Level for Member Muting
      + If this warning level is passed by a member they will find themselves under a post ban. The member will lose all posting rights. +
    • +
    • + Maximum Member Warning Point per Day
      + This setting limits the amount of points a moderator may add/remove to any particular member in a twenty four hour period. This will + can be used to limit what a moderator can do in a small period of time. This setting can be disabled by setting to a value of zero. Note that + any member with administrator permissions are not affected by this value. +
    • +
    '; +$helptxt['error_log'] = 'Error Log
    + The error log tracks logs every serious error encountered by users using your forum. It lists all of these errors by date which can be sorted + by clicking the black arrow next to each date. Additionally you can filter the errors by clicking the picture next to each error statistic. This + allows you to filter, for example, by member. When a filter is active the only results that will be displayed will be those that match that filter.'; +$helptxt['theme_settings'] = 'Theme Settings
    + The settings screen allows you to change settings specific to a theme. These settings include options such as the themes directory and URL information but + also options that affect the layout of a theme on your forum. Most themes will have a variety of user configurable options, allowing you to adapt a theme + to suit your individual forum needs.'; +$helptxt['smileys'] = 'Smiley Center
    + Here you can add and remove smileys, and smiley sets. Note importantly that if a smiley is in one set, it\'s in all sets - otherwise, it might + get confusing for your users using different sets.

    + + You are also able to edit message icons from here, if you have them enabled on the settings page.'; +$helptxt['calendar'] = 'Manage Calendar
    + Here you can modify the current calendar settings as well as add and remove holidays that appear on the calendar.'; + +$helptxt['serversettings'] = 'Server Settings
    + Here you can perform the core configuration for your forum. This section includes the database and url settings, as well as other + important configuration items such as mail settings and caching. Think carefully whenever editing these settings as an error may + render the forum inaccessible'; +$helptxt['manage_files'] = ' +
      +
    • + Browse Files
      + Browse through all the attachments, avatars and thumbnails stored by SMF.

      +
    • + Attachment Settings
      + Configure where attachments are stored and set restrictions on the types of attachments.

      +
    • + Avatar Settings
      + Configure where avatars are stored and manage resizing of avatars.

      +
    • + File Maintenance
      + Check and repair any error in the attachment directory and delete selected attachments.

      +
    • +
    '; + +$helptxt['topicSummaryPosts'] = 'This allows you to set the number of previous posts shown in the topic summary at the reply screen.'; +$helptxt['enableAllMessages'] = 'Set this to the maximum number of posts a topic can have to show the all link. Setting this lower than "Maximum messages to display in a topic page" will simply mean it never gets shown, and setting it too high could slow down your forum.'; +$helptxt['enableStickyTopics'] = 'Stickies are topics that remain on top of the topic list. They are mostly used for important + messages. Although you can change this with permissions, by default only moderators and administrators can make topics sticky.'; +$helptxt['allow_guestAccess'] = 'Unchecking this box will stop guests from doing anything but very basic actions - login, register, password reminder, etc. - on your forum. This is not the same as disallowing guest access to boards.'; +$helptxt['userLanguage'] = 'Turning this option on will allow users to select which language file they use. It will not affect the + default selection.'; +$helptxt['trackStats'] = 'Stats:
    This will allow users to see the latest posts and the most popular topics on your forum. + It will also show several statistics, like the most members online, new members and new topics.
    + Page views:
    Adds another column to the stats page with the number of pageviews on your forum.'; +$helptxt['titlesEnable'] = 'Switching Custom Titles on will allow members with the relevant permission to create a special title for themselves. + This will be shown underneath the name.
    For example:
    Jeff
    Cool Guy'; +$helptxt['topbottomEnable'] = 'This will add go up and go down buttons, so that member can go to the top and bottom of a page + without scrolling.'; +$helptxt['onlineEnable'] = 'This will show an image to indicate whether the member is online or offline'; +$helptxt['todayMod'] = 'This will show "Today" or "Yesterday" instead of the date.

    + Examples:

    +
    +
    Disabled
    +
    October 3, 2009 at 12:59:18 am
    +
    Only Today
    +
    Today at 12:59:18 am
    +
    Today & Yesterday
    +
    Yesterday at 09:36:55 pm
    + '; +$helptxt['disableCustomPerPage'] = 'Check this option to stop users from customizing the amount of messages and topics to display per page on the Message Index and Topic Display page respectively.'; +$helptxt['enablePreviousNext'] = 'This will show a link to the next and previous topic.'; +$helptxt['pollMode'] = 'This selects whether polls are enabled or not. If polls are disabled, any existing polls will be hidden + from the topic listing. You can choose to continue to show the regular topic without their polls by selecting + "Show Existing Polls as Topics".

    To choose who can post polls, view polls, and similar, you + can allow and disallow those permissions. Remember this if polls are not working.'; +$helptxt['enableVBStyleLogin'] = 'This will show a more compact login on every page of the forum for guests.'; +$helptxt['enableCompressedOutput'] = 'This option will compress output to lower bandwidth consumption, but it requires + zlib to be installed.'; +$helptxt['disableTemplateEval'] = 'By default, templates are evaluated instead of just included. This helps with showing more useful debug information in case a template contains an error.

    + On large forums however, this customised inclusion process may be significantly slower. Therefore, advanced users may wish to disable it.'; +$helptxt['databaseSession_enable'] = 'This option makes use of the database for session storage - it is best for load balanced servers, but helps with all timeout issues and can make the forum faster.'; +$helptxt['databaseSession_loose'] = 'Turning this on will decrease the bandwidth your forum uses, and make it so clicking back will not reload the page - the downside is that the (new) icons won\'t update, among other things. (unless you click to that page instead of going back to it.)'; +$helptxt['databaseSession_lifetime'] = 'This is the number of seconds for sessions to last after they haven\'t been accessed. If a session is not accessed for too long, it is said to have "timed out". Anything higher than 2400 is recommended.'; +$helptxt['enableErrorLogging'] = 'This will log any errors, like a failed login, so you can see what went wrong.'; +$helptxt['enableErrorQueryLogging'] = 'This will include the full query sent to the database in the error log. Requires error logging to be turned on.

    Note: This will affect the ability to filter the error log by the error message.'; +$helptxt['allow_disableAnnounce'] = 'This will allow users to opt out of notification of topics you announce by checking the "announce topic" checkbox when posting.'; +$helptxt['disallow_sendBody'] = 'This option removes the option to receive the text of replies and posts in notification emails.

    Often, members will reply to the notification email, which in most cases means the webmaster receives the reply.'; +$helptxt['compactTopicPagesEnable'] = 'This will just show a selection of the number of pages.
    Example: + "3" to display: 1 ... 4 [5] 6 ... 9
    + "5" to display: 1 ... 3 4 [5] 6 7 ... 9'; +$helptxt['timeLoadPageEnable'] = 'This will show the time in seconds SMF took to create that page at the bottom of the board.'; +$helptxt['removeNestedQuotes'] = 'This will strip nested quotes from a post when citing the post in question via a quote link.'; +$helptxt['simpleSearch'] = 'This will show a simple search form and a link to a more advanced form.'; +$helptxt['max_image_width'] = 'This allows you to set a maximum size for posted pictures. Pictures smaller than the maximum will not be affected.'; +$helptxt['mail_type'] = 'This setting allows you to choose either PHP\'s default settings, or to override those settings with SMTP. PHP doesn\'t support using authentication with SMTP (which many hosts require, now) so if you want that you should select SMTP. Please note that SMTP can be slower, and some servers will not take usernames and passwords.

    You don\'t need to fill in the SMTP settings if this is set to PHP\'s default.'; +$helptxt['attachment_manager_settings'] = 'Attachments are files that members can upload, and attach to a post.

    + Check attachment extension:
    Do you want to check the extension of the files?
    + Allowed attachment extensions:
    You can set the allowed extensions of attached files.
    + Attachments directory:
    The path to your attachment folder
    (example: /home/sites/yoursite/www/forum/attachments)
    + Max attachment folder space (in KB):
    Select how large the attachment folder can be, including all files within it.
    + Max attachment size per post (in KB):
    Select the maximum filesize of all attachments made per post. If this is lower than the per-attachment limit, this will be the limit.
    + Max size per attachment (in KB):
    Select the maximum filesize of each separate attachment.
    + Max number of attachments per post:
    Select the number of attachments a person can make, per post.
    + Display attachment as picture in posts:
    If the uploaded file is a picture, this will show it underneath the post.
    + Resize images when showing under posts:
    If the above option is selected, this will save a separate (smaller) attachment for the thumbnail to decrease bandwidth.
    + Maximum width and height of thumbnails:
    Only used with the "Resize images when showing under posts" option, the maximum width and height to resize attachments down from. They will be resized proportionally.'; +$helptxt['attachment_image_paranoid'] = 'Selecting this option will enable very strict security checks on image attachments. Warning! These extensive checks can fail on valid images too. It is strongly recommended to only use this option together with image re-encoding, in order to have SMF try to resample the images which fail the security checks: if successful, they will be sanitized and uploaded. Otherwise, if image re-encoding is not enabled, all attachments failing checks will be rejected.'; +$helptxt['attachment_image_reencode'] = 'Selecting this option will enable trying to re-encode the uploaded image attachments. Image re-encoding offers better security. Note however that image re-encoding also renders all animated images static.
    This feature is only possible if the GD module is installed on your server.'; +$helptxt['avatar_paranoid'] = 'Selecting this option will enable very strict security checks on avatars. Warning! These extensive checks can fail on valid images too. It is strongly recommended to only use this option together with avatars re-encoding, in order to have SMF try to resample the images which fail the security checks: if successful, they will be sanitized and uploaded. Otherwise, if re-encoding of avatars is not enabled, all avatars failing checks will be rejected.'; +$helptxt['avatar_reencode'] = 'Selecting this option will enable trying to re-encode the uploaded avatars. Image re-encoding offers better security. Note however that image re-encoding also renders all animated images static.
    This feature is only possible if the GD module is installed on your server.'; +$helptxt['karmaMode'] = 'Karma is a feature that shows the popularity of a member. Members, if allowed, can + \'applaud\' or \'smite\' other members, which is how their popularity is calculated. You can change the + number of posts needed to have a "karma", the time between smites or applauds, and if administrators + have to wait this time as well.

    Whether or not groups of members can smite others is controlled by + a permission. If you have trouble getting this feature to work for everyone, double check your permissions.'; +$helptxt['cal_enabled'] = 'The calendar can be used for showing birthdays, or for showing important moments happening in your community.

    + Show days as link to \'Post Event\':
    This will allow members to post events for that day, when they click on that date
    + Max days in advance on board index:
    If this is set to 7, the next week\'s worth of events will be shown.
    + Show holidays on board index:
    Show today\'s holidays in a calendar bar on the board index.
    + Show birthdays on board index:
    Show today\'s birthdays in a calendar bar on the board index.
    + Show events on board index:
    Show today\'s events in a calendar bar on the board index.
    + Default Board to Post In:
    What\'s the default board to post events in?
    + Allow events not linked to posts:
    Allow members to post events without requiring it to be linked with a post in a board.
    + Minimum year:
    Select the "first" year on the calendar list.
    + Maximum year:
    Select the "last" year on the calendar list
    + Allow events to span multiple days:
    Check to allow events to span multiple days.
    + Max number of days an event can span:
    Select the maximum days that an event can span.

    + Remember that usage of the calendar (posting events, viewing events, etc.) is controlled by permissions set on the permissions screen.'; +$helptxt['localCookies'] = 'SMF uses cookies to store login information on the client computer. + Cookies can be stored globally (myserver.com) or locally (myserver.com/path/to/forum).
    + Check this option if you\'re experiencing problems with users getting logged out automatically.
    + Globally stored cookies are less secure when used on a shared webserver (like Tripod).
    + Local cookies don\'t work outside the forum folder so, if your forum is stored at www.myserver.com/forum, pages like www.myserver.com/index.php cannot access the account information. + Especially when using SSI.php, global cookies are recommended.'; +$helptxt['enableBBC'] = 'Selecting this option will allow your members to use Bulletin Board Code (BBC) throughout the forum, allowing users to format their posts with images, type formatting and more.'; +$helptxt['time_offset'] = 'Not all forum administrators want their forum to use the same time zone as the server upon which it is hosted. Use this option to specify a time difference (in hours) from which the forum should operate from the server time. Negative and decimal values are permitted.'; +$helptxt['default_timezone'] = 'The server timezone tells PHP where your server is located. You should ensure this is set correctly, preferably to the country/city in which the city is located. You can find out more information on the PHP Site.'; +$helptxt['spamWaitTime'] = 'Here you can select the amount of time that must pass between postings. This can be used to stop people from "spamming" your forum by limiting how often they can post.'; + +$helptxt['enablePostHTML'] = 'This will allow the posting of some basic HTML tags: +
      +
    • <b>, <u>, <i>, <s>, <em>, <ins>, <del>
    • +
    • <a href="">
    • +
    • <img src="" alt="" />
    • +
    • <br />, <hr />
    • +
    • <pre>, <blockquote>
    • +
    '; + +$helptxt['themes'] = 'Here you can select whether the default theme can be chosen, what theme guests will use, + as well as other options. Click on a theme to the right to change the settings for it.'; +$helptxt['theme_install'] = 'This allows you to install new themes. You can do this from an already created directory, by uploading an archive for the theme, or by copying the default theme.

    Note that the archive or directory must have a theme_info.xml definition file.'; +$helptxt['enableEmbeddedFlash'] = 'This option will allow your users to use Flash directly inside their posts, + just like images. This could pose a security risk, although few have successfully exploited it. + USE AT YOUR OWN RISK!'; +// !!! Add more information about how to use them here. +$helptxt['xmlnews_enable'] = 'Allows people to link to Recent news + and similar data. It is also recommended that you limit the size of recent posts/news because, when rss data + is displayed in some clients, like Trillian, it is expected to be truncated.'; +$helptxt['hotTopicPosts'] = 'Change the number of posts for a topic to reach the state of a "hot" or + "very hot" topic.'; +$helptxt['globalCookies'] = 'Makes log in cookies available across subdomains. For example, if...
    + Your site is at http://www.simplemachines.org/,
    + And your forum is at http://forum.simplemachines.org/,
    + Using this option will allow you to access the forum\'s cookie on your site. Do not enable this if there are other subdomains (like hacker.simplemachines.org) not controlled by you.'; +$helptxt['secureCookies'] = 'Enabling this option will force the cookies created for users on your forum to be marked as secure. Only enable this option if you are using HTTPS throughout your site as it will break cookie handling otherwise!'; +$helptxt['securityDisable'] = 'This disables the additional password check for the administration section. This is not recommended!'; +$helptxt['securityDisable_why'] = 'This is your current password. (the same one you use to login.)

    Having to type this helps ensure that you want to do whatever administration you are doing, and that it is you doing it.'; +$helptxt['emailmembers'] = 'In this message you can use a few "variables". These are:
    + {$board_url} - The URL to your forum.
    + {$current_time} - The current time.
    + {$member.email} - The current member\'s email.
    + {$member.link} - The current member\'s link.
    + {$member.id} - The current member\'s id.
    + {$member.name} - The current member\'s name. (for personalization.)
    + {$latest_member.link} - The most recently registered member\'s link.
    + {$latest_member.id} - The most recently registered member\'s id.
    + {$latest_member.name} - The most recently registered member\'s name.'; +$helptxt['attachmentEncryptFilenames'] = 'Encrypting attachment filenames allows you to have more than one attachment of the + same name, to safely use .php files for attachments, and heightens security. It, however, could make it more + difficult to rebuild your database if something drastic happened.'; + +$helptxt['failed_login_threshold'] = 'Set the number of failed login attempts before directing the user to the password reminder screen.'; +$helptxt['oldTopicDays'] = 'If this option is enabled a warning will be displayed to the user when attempting to reply to a topic which has not had any new replies for the amount of time, in days, specified by this setting. Set this setting to 0 to disable the feature.'; +$helptxt['edit_wait_time'] = 'Number of seconds allowed for a post to be edited before logging the last edit date.'; +$helptxt['edit_disable_time'] = 'Number of minutes allowed to pass before a user can no longer edit a post they have made. Set to 0 disable.

    Note: This will not affect any user who has permission to edit other people\'s posts.'; +$helptxt['posts_require_captcha'] = 'This setting will force users to pass anti-spam bot verification each time they make a post to a board. Only users with a post count below the number set will need to enter the code - this should help combat automated spamming scripts.'; +$helptxt['enableSpellChecking'] = 'Enable spell checking. You MUST have the pspell library installed on your server and your PHP configuration set up to use the pspell library. Your server ' . (function_exists('pspell_new') ? 'DOES' : 'DOES NOT') . ' appear to have this set up.'; +$helptxt['disable_wysiwyg'] = 'This setting disallows all users from using the WYSIWYG ("What You See Is What You Get") editor on the post page.'; +$helptxt['lastActive'] = 'Set the number of minutes to show people are active in X number of minutes on the board index. Default is 15 minutes.'; + +$helptxt['customoptions'] = 'This section defines the options that a user may choose from a drop down list. There are a few key points to note in this section: +
      +
    • Default Option: Whichever option box has the "radio button" next to it selected will be the default selection for the user when they enter their profile.
    • +
    • Removing Options: To remove an option simply empty the text box for that option - all users with that selected will have their option cleared.
    • +
    • Reordering Options: You can reorder the options by moving text around between the boxes. However - an important note - you must make sure you do not change the text when reordering options as otherwise user data will be lost.
    • +
    '; + +$helptxt['autoOptDatabase'] = 'This option optimizes the database every so many days. Set it to 1 to make a daily optimization. You can also specify a maximum number of online users, so that you won\'t overload your server or inconvenience too many users.'; +$helptxt['autoFixDatabase'] = 'This will automatically fix broken tables and resume like nothing happened. This can be useful, because the only way to fix it is to REPAIR the table, and this way your forum won\'t be down until you notice. It does email you when this happens.'; + +$helptxt['enableParticipation'] = 'This shows a little icon on the topics the user has posted in.'; + +$helptxt['db_persist'] = 'Keeps the connection active to increase performance. If you aren\'t on a dedicated server, this may cause you problems with your host.'; +$helptxt['ssi_db_user'] = 'Optional setting to use a different database user and password when you are using SSI.php.'; + +$helptxt['queryless_urls'] = 'This changes the format of URLs a little so search engines will like them better. They will look like index.php/topic,1.0.html.

    This feature will ' . (isset($_SERVER['SERVER_SOFTWARE']) && (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false) ? '' : 'not') . ' work on your server.'; +$helptxt['countChildPosts'] = 'Checking this option will mean that posts and topics in a board\'s child board will count toward its totals on the index page.

    This will make things notably slower, but means that a parent with no posts in it won\'t show \'0\'.'; +$helptxt['fixLongWords'] = 'This option breaks words longer than a certain length into pieces so they don\'t disturb the forum\'s layout. (as much...) This option should not be set to a value under 40. This option will not work with forums using UTF-8 and PHP less than 4.4.0. This ' . (empty($GLOBALS['context']['utf8']) || version_compare(PHP_VERSION, '4.4.0') != -1 ? 'WILL' : 'WILL NOT') . ' work on your server'; +$helptxt['allow_ignore_boards'] = 'Checking this option will allow users to select boards they wish to ignore.'; + +$helptxt['who_enabled'] = 'This option allows you to turn on or off the ability for users to see who is browsing the forum and what they are doing.'; + +$helptxt['recycle_enable'] = '"Recycles" deleted topics and posts to the specified board.'; + +$helptxt['enableReportPM'] = 'This option allows your users to report personal messages they receive to the administration team. This may be useful in helping to track down any abuse of the personal messaging system.'; +$helptxt['max_pm_recipients'] = 'This option allows you to set the maximum amount of recipients allowed in a single personal message sent by a forum member. This may be used to help stop spam abuse of the PM system. Note that users with permission to send newsletters are exempt from this restriction. Set to zero for no limit.'; +$helptxt['pm_posts_verification'] = 'This setting will force users to enter a code shown on a verification image each time they are sending a personal message. Only users with a post count below the number set will need to enter the code - this should help combat automated spamming scripts.'; +$helptxt['pm_posts_per_hour'] = 'This will limit the number of personal messages which may be sent by a user in a one hour period. This does not affect admins or moderators.'; + +$helptxt['default_personal_text'] = 'Sets the default text a new user will have as their "personal text."'; + +$helptxt['modlog_enabled'] = 'Logs all moderation actions.'; + +$helptxt['guest_hideContacts'] = 'If selected this option will hide the email addresses and messenger contact details + of all members from any guests on your forum'; + +$helptxt['registration_method'] = 'This option determines what method of registration is used for people wishing to join your forum. You can select from:

    +
      +
    • + Registration Disabled
      + Disables the registration process, which means that no new members can register to join your forum.
      +
    • + Immediate Registration
      + New members can login and post immediately after registering on your forum.
      +
    • + Email Activation
      + When this option is enabled any members registering with the forum will have an activation link emailed to them which they must click before they can become full members.
      +
    • + Admin Approval
      + This option will make it so all new members registering to your forum will need to be approved by the admin before they become members. +
    • +
    '; +$helptxt['register_openid'] = 'Authenticate with OpenID
    + OpenID is a means of using one username across different websites, to simplify the online experience. To use OpenID you first need to create an OpenID account - a list of providers can be found on the OpenID Official Site

    + Once you have an OpenID account simply enter your unique identification URL into the OpenID input box and submit. You will then be taken to your providers site to verify your identity before being passed back to this site.

    + On your first visit to this site you will be asked to confirm a couple of details before you will be recognized, after which you can login to this site and change your profile settings using just your OpenID.

    + For more information please visit the OpenID Official Site'; + +$helptxt['send_validation_onChange'] = 'When this option is checked all members who change their email address in their profile will have to reactivate their account from an email sent to that address'; +$helptxt['send_welcomeEmail'] = 'When this option is enabled all new members will be sent an email welcoming them to your community'; +$helptxt['password_strength'] = 'This setting determines the strength required for passwords selected by your forum users. The stronger the password, the harder it should be to compromise member\'s accounts. + Its possible options are: +
      +
    • Low: The password must be at least four characters long.
    • +
    • Medium: The password must be at least eight characters long, and can not be part of a users name or email address.
    • +
    • High: As for medium, except the password must also contain a mixture of upper and lower case letters, and at least one number.
    • +
    '; + +$helptxt['coppaAge'] = 'The value specified in this box will determine the minimum age that new members must be to be granted immediate access to the forums. + On registration they will be prompted to confirm whether they are over this age, and if not will either have their application rejected or suspended awaiting parental approval - dependant on the type of restriction chosen. + If a value of 0 is chosen for this setting then all other age restriction settings shall be ignored.'; +$helptxt['coppaType'] = 'If age restrictions are enabled, then this setting will define that happens when a user below the minimum age attempts to register with your forum. There are two possible choices: +
      +
    • + Reject Their Registration:
      + Any new member below the minimum age will have their registration rejected immediately.
      +
    • + Require Parent/Guardian Approval
      + Any new member who attempts to register and is below the minimum permitted age will have their account marked as awaiting approval, and will be presented with a form upon which their parents must give permission to become a member of the forums. + They will also be presented with the forum contact details entered on the settings page, so they can send the form to the administrator by mail or fax. +
    • +
    '; +$helptxt['coppaPost'] = 'The contact boxes are required so that forms granting permission for underage registration can be sent to the forum administrator. These details will be shown to all new minors, and are required for parent/guardian approval. At the very least a postal address or fax number must be provided.'; + +$helptxt['allow_hideOnline'] = 'With this option enabled all members will be able to hide their online status from other users (except administrators). If disabled only users who can moderate the forum can hide their presence. Note that disabling this option will not change any existing member\'s status - it just stops them from hiding themselves in the future.'; +$helptxt['make_email_viewable'] = 'If this option is enabled instead of users email addresses being hidden to normal members and guests they will be publicly viewable on the forum. Enabling this will put your users at greater risk of being victims of spam as a result of email harvesters visiting your forum. Note this setting does not override the user setting for hiding their email address from users. Enabling this setting is not recommended.'; +$helptxt['meta_keywords'] = 'These keywords are sent in the output of every page to indicate to search engines (etc) the key content of your site. They should be a comma separated list of words, and should not use HTML.'; + +$helptxt['latest_support'] = 'This panel shows you some of the most common problems and questions on your server configuration. Don\'t worry, this information isn\'t logged or anything.

    If this stays as "Retrieving support information...", your computer probably cannot connect to www.simplemachines.org.'; +$helptxt['latest_packages'] = 'Here you can see some of the most popular and some random packages or mods, with quick and easy installations.

    If this section doesn\'t show up, your computer probably cannot connect to www.simplemachines.org.'; +$helptxt['latest_themes'] = 'This area shows a few of the latest and most popular themes from www.simplemachines.org. It may not show up properly if your computer can\'t find www.simplemachines.org, though.'; + +$helptxt['secret_why_blank'] = 'For your security, your password and the answer to your secret question are encrypted so that the SMF software will never tell you, or anyone else, what they are.'; +$helptxt['moderator_why_missing'] = 'Since moderation is done on a board-by-board basis, you have to make members moderators from the board management interface.'; + +$helptxt['permissions'] = 'Permissions are how you either allow groups to, or deny groups from, doing specific things.

    You can modify multiple boards at once with the checkboxes, or look at the permissions for a specific group by clicking \'Modify.\''; +$helptxt['permissions_board'] = 'If a board is set to \'Global,\' it means that the board will not have any special permissions. \'Local\' means it will have its own permissions - separate from the global ones. This allows you to have a board that has more or less permissions than another, without requiring you to set them for each and every board.'; +$helptxt['permissions_quickgroups'] = 'These allow you to use the "default" permission setups - standard means \'nothing special\', restrictive means \'like a guest\', moderator means \'what a moderator has\', and lastly \'maintenance\' means permissions very close to those of an administrator.'; +$helptxt['permissions_deny'] = 'Denying permissions can be useful when you want take away permission from certain members. You can add a membergroup with a \'deny\'-permission to the members you wish to deny a permission.

    Use with care, a denied permission will stay denied no matter what other membergroups the member is in.'; +$helptxt['permissions_postgroups'] = 'Enabling permissions for post count based groups will allow you to attribute permissions to members that have posted a certain amount of messages. The permissions of the post count based groups are added to the permissions of the regular membergroups.'; +$helptxt['membergroup_guests'] = 'The Guests membergroup are all users that are not logged in.'; +$helptxt['membergroup_regular_members'] = 'The Regular Members are all members that are logged in, but that have no primary membergroup assigned.'; +$helptxt['membergroup_administrator'] = 'The administrator can, per definition, do anything and see any board. There are no permission settings for the administrator.'; +$helptxt['membergroup_moderator'] = 'The Moderator membergroup is a special membergroup. Permissions and settings assigned to this group apply to moderators but only on the boards they moderate. Outside these boards they\'re just like any other member.'; +$helptxt['membergroups'] = 'In SMF there are two types of groups that your members can be part of. These are: +
      +
    • Regular Groups: A regular group is a group to which members are not automatically put into. To assign a member to be in a group simply go to their profile and click "Account Settings". From here you can assign them any number of regular groups to which they will be part.
    • +
    • Post Groups: Unlike regular groups post based groups cannot be assigned. Instead, members are automatically assigned to a post based group when they reach the minimum number of posts required to be in that group.
    • +
    '; + +$helptxt['calendar_how_edit'] = 'You can edit these events by clicking on the red asterisk (*) next to their names.'; + +$helptxt['maintenance_backup'] = 'This area allows you to save a copy of all the posts, settings, members, and other information in your forum to a very large file.

    It is recommended that you do this often, perhaps weekly, for safety and security.'; +$helptxt['maintenance_rot'] = 'This allows you to completely and irrevocably remove old topics. It is recommended that you try to make a backup first, just in case you remove something you didn\'t mean to.

    Use this option with care.'; +$helptxt['maintenance_members'] = 'This allows you to completely and irrevocably remove member accounts from your forum. It is highly recommended that you try to make a backup first, just in case you remove something you didn\'t mean to.

    Use this option with care.'; + +$helptxt['avatar_server_stored'] = 'This allows your members to pick from avatars stored on your server itself. They are, generally, in the same place as SMF under the avatars folder.
    As a tip, if you create directories in that folder, you can make "categories" of avatars.'; +$helptxt['avatar_external'] = 'With this enabled, your members can type in a URL to their own avatar. The downside of this is that, in some cases, they may use avatars that are overly large or portray images you don\'t want on your forum.'; +$helptxt['avatar_download_external'] = 'With this option enabled, the URL given by the user is accessed to download the avatar at that location. On success, the avatar will be treated as uploadable avatar.'; +$helptxt['avatar_upload'] = 'This option is much like "Allow members to select an external avatar", except that you have better control over the avatars, a better time resizing them, and your members do not have to have somewhere to put avatars.

    However, the downside is that it can take a lot of space on your server.'; +$helptxt['avatar_download_png'] = 'PNGs are larger, but offer better quality compression. If this is unchecked, JPEG will be used instead - which is often smaller, but also of lesser or blurry quality.'; + +$helptxt['disableHostnameLookup'] = 'This disables host name lookups, which on some servers are very slow. Note that this will make banning less effective.'; + +$helptxt['search_weight_frequency'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor counts the amount of matching messages and divides them by the total number of messages within a topic.'; +$helptxt['search_weight_age'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor rates the age of the last matching message within a topic. The more recent this message is, the higher the score.'; +$helptxt['search_weight_length'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor is based on the topic size. The more messages are within the topic, the higher the score.'; +$helptxt['search_weight_subject'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor looks whether a search term can be found within the subject of a topic.'; +$helptxt['search_weight_first_message'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor looks whether a match can be found in the first message of a topic.'; +$helptxt['search_weight_sticky'] = 'Weight factors are used to determine the relevancy of a search result. Change these weight factors to match the things that are specifically important for your forum. For instance, a forum of a news site, might want a relatively high value for \'age of last matching message\'. All values are relative in relation to each other and should be positive integers.

    This factor looks whether a topic is sticky and increases the relevancy score if it is.'; +$helptxt['search'] = 'Adjust all settings for the search function here.'; +$helptxt['search_why_use_index'] = 'A search index can greatly improve the performance of searches on your forum. Especially when the number of messages on a forum grows bigger, searching without an index can take a long time and increase the pressure on your database. If your forum is bigger than 50.000 messages, you might want to consider creating a search index to assure peak performance of your forum.

    Note that a search index can take up quite some space. A fulltext index is a built-in index of MySQL. It\'s relatively compact (approximately the same size as the message table), but a lot of words aren\'t indexed and it can, in some search queries, turn out to be very slow. The custom index is often bigger (depending on your configuration it can be up to 3 times the size of the messages table) but it\'s performance is better than fulltext and relatively stable.'; + +$helptxt['see_admin_ip'] = 'IP addresses are shown to administrators and moderators to facilitate moderation and to make it easier to track people up to no good. Remember that IP addresses may not always be identifying, and most people\'s IP addresses change periodically.

    Members are also allowed to see their own IPs.'; +$helptxt['see_member_ip'] = 'Your IP address is shown only to you and moderators. Remember that this information is not identifying, and that most IPs change periodically.

    You cannot see other members\' IP addresses, and they cannot see yours.'; +$helptxt['whytwoip'] = 'SMF uses various methods to detect user IP addresses. Usually these two methods result in the same address but in some cases more than one address may be detected. In this case SMF logs both addresses, and uses them both for ban checks (etc). You can click on either address to track that IP and ban if necessary.'; + +$helptxt['ban_cannot_post'] = 'The \'cannot post\' restriction turns the forum into read-only mode for the banned user. The user cannot create new topics, or reply to existing ones, send personal messages or vote in polls. The banned user can however still read personal messages and topics.

    A warning message is shown to the users that are banned this way.'; + +$helptxt['posts_and_topics'] = ' +
      +
    • + Post Settings
      + Modify the settings related to the posting of messages and the way messages are shown. You can also enable the spell check here. +
    • + Bulletin Board Code
      + Enable the code that shows forum messages in the right layout. Also adjust which codes are allowed and which aren\'t. +
    • + Censored Words + In order to keep the language on your forum under control, you can censor certain words. This function allows you to convert forbidden words into innocent versions. +
    • + Topic Settings + Modify the settings related to topics. The number of topics per page, whether sticky topics are enabled or not, the number of messages needed for a topic to be hot, etc. +
    • +
    '; +$helptxt['spider_group'] = 'By selecting a restrictive group, when a guest is detected as a search crawler it will automatically be assigned any "deny" deny permissions of this group in addition to the normal permissions of a guest. You can use this to provide lesser access to a search engine than you would a normal guest. You might for example wish to create a new group called "Spiders" and select that here. You could then deny permission for that group to view profiles to stop spiders indexing your members profiles.
    Note: Spider detection is not perfect and can be simulated by users so this feature is not guaranteed to restrict content only to those search engines you have added.'; +$helptxt['show_spider_online'] = 'This setting allows you to select whether spiders should be listed in the who\'s online list on the board index and "Who\'s Online" page. Options are: +
      +
    • + Not at All
      + Spiders will simply appear as guests to all users. +
    • + Show Spider Quantity
      + The Board Index will display the number of spiders currently visiting the forum. +
    • + Show Spider Names
      + Each spider name will be revealed, so users can see how many of each spider is currently visiting the forum - this takes effect in both the Board Index and Who\'s Online page. +
    • + Show Spider Names - Admin Only
      + As above except only Administrators can see spider status - to all other users spiders appear as guests. +
    • +
    '; + +$helptxt['birthday_email'] = 'Choose the index of the birthday email message to use. A preview will be shown in the Email Subject and Email Body fields.
    Note: Setting this option does not automatically enable birthday emails. To enable birthday emails use the Scheduled Tasks page and enable the birthday email task.'; +$helptxt['pm_bcc'] = 'When sending a personal message you can choose to add a recipient as BCC or "Blind Carbon Copy". BCC recipients do not have their identities revealed to other recipients of the message.'; + +$helptxt['move_topics_maintenance'] = 'This will allow you to move all the posts from one board to another board.'; +$helptxt['maintain_reattribute_posts'] = 'You can use this function to attribute guest posts on your board to a registered member. This is useful if, for example, a user deleted their account and changed their mind and wished to have their old posts associated with their account.'; +$helptxt['chmod_flags'] = 'You can manually set the permissions you wish to set the selected files to. To do this enter the chmod value as a numeric (octet) value. Note - these flags will have no effect on Microsoft Windows operating systems.'; + +$helptxt['postmod'] = 'This section allows members of the moderation team (with sufficient permissions) to approve any posts and topics before they are shown.'; + +$helptxt['field_show_enclosed'] = 'Encloses the user input between some text or html. This will allow you to add more instant message providers, images or an embed etc. For example:

    + <a href="http://website.com/{INPUT}"><img src="{DEFAULT_IMAGES_URL}/icon.gif" alt="{INPUT}" /></a>

    + Note that you can use the following variables:
    +
      +
    • {INPUT} - The input specified by the user.
    • +
    • {SCRIPTURL} - Web address of forum.
    • +
    • {IMAGES_URL} - Url to images folder in the users current theme.
    • +
    • {DEFAULT_IMAGES_URL} - Url to the images folder in the default theme.
    • +
    '; + +$helptxt['custom_mask'] = 'The input mask is important for your forum\'s security. Validating the input from a user can help ensure that data is not used in a way you do not expect. We have provided some simple regular expressions as hints.

    +
    + "[A-Za-z]+" - Match all upper and lower case alphabet characters.
    + "[0-9]+" - Match all numeric characters.
    + "[A-Za-z0-9]{7}" - Match all upper and lower case alphabet and numeric characters seven times.
    + "[^0-9]?" - Forbid any number from being matched.
    + "^([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$" - Only allow 3 or 6 character hexcodes.
    +


    + Additionally, special metacharacters ?+*^$ and {xx} can be defined. +
    + ? - None or one match of previous expression.
    + + - One or more of previous expression.
    + * - None or more of previous expression.
    + {xx} - An exact number from previous expression.
    + {xx,} - An exact number or more from previous expression.
    + {,xx} - An exact number or less from previous expression.
    + {xx,yy} - An exact match between the two numbers from previous expression.
    + ^ - Start of string.
    + $ - End of string.
    + \ - Escapes the next character.
    +


    + More information and advanced techniques may be found on the internet.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Install.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Install.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,235 @@ +help is available if you need it.'; +$txt['still_writable'] = 'Your installation directory is still writable. It\'s a good idea to chmod it so that it is not writable for security reasons.'; +$txt['delete_installer'] = 'Click here to delete this install.php file now.'; +$txt['delete_installer_maybe'] = '(doesn\'t work on all servers.)'; +$txt['go_to_your_forum'] = 'Now you can see your newly installed forum and begin to use it. You should first make sure you are logged in, after which you will be able to access the administration center.'; +$txt['good_luck'] = 'Good luck!
    Simple Machines'; + +$txt['install_welcome'] = 'Welcome'; +$txt['install_welcome_desc'] = 'Welcome to SMF. This script will guide you through the process for installing %1$s. We\'ll gather a few details about your forum over the next few steps, and after a couple of minutes your forum will be ready for use.'; +$txt['install_all_lovely'] = 'We\'ve completed some initial tests on your server and everything appears to be in order. Simply click the "Continue" button below to get started.'; + +$txt['user_refresh_install'] = 'Forum Refreshed'; +$txt['user_refresh_install_desc'] = 'While installing, the installer found that (with the details you provided) one or more of the tables this installer might create already existed.
    Any missing tables in your installation have been recreated with the default data, but no data was deleted from existing tables.'; + +$txt['default_topic_subject'] = 'Welcome to SMF!'; +$txt['default_topic_message'] = 'Welcome to Simple Machines Forum!

    We hope you enjoy using your forum.  If you have any problems, please feel free to [url=http://www.simplemachines.org/community/index.php]ask us for assistance[/url].

    Thanks!
    Simple Machines'; +$txt['default_board_name'] = 'General Discussion'; +$txt['default_board_description'] = 'Feel free to talk about anything and everything in this board.'; +$txt['default_category_name'] = 'General Category'; +$txt['default_time_format'] = '%B %d, %Y, %I:%M:%S %p'; +$txt['default_news'] = 'SMF - Just Installed!'; +$txt['default_karmaLabel'] = 'Karma:'; +$txt['default_karmaSmiteLabel'] = '[smite]'; +$txt['default_karmaApplaudLabel'] = '[applaud]'; +$txt['default_reserved_names'] = 'Admin\nWebmaster\nGuest\nroot'; +$txt['default_smileyset_name'] = 'Alienine\'s Set'; +$txt['default_aaron_smileyset_name'] = 'Aaron\'s Set'; +$txt['default_akyhne_smileyset_name'] = 'Akyhne\'s Set'; +$txt['default_theme_name'] = 'SMF Default Theme - Curve'; +$txt['default_core_theme_name'] = 'Core Theme'; +$txt['default_classic_theme_name'] = 'Classic YaBB SE Theme'; +$txt['default_babylon_theme_name'] = 'Babylon Theme'; + +$txt['default_administrator_group'] = 'Administrator'; +$txt['default_global_moderator_group'] = 'Global Moderator'; +$txt['default_moderator_group'] = 'Moderator'; +$txt['default_newbie_group'] = 'Newbie'; +$txt['default_junior_group'] = 'Jr. Member'; +$txt['default_full_group'] = 'Full Member'; +$txt['default_senior_group'] = 'Sr. Member'; +$txt['default_hero_group'] = 'Hero Member'; + +$txt['default_smiley_smiley'] = 'Smiley'; +$txt['default_wink_smiley'] = 'Wink'; +$txt['default_cheesy_smiley'] = 'Cheesy'; +$txt['default_grin_smiley'] = 'Grin'; +$txt['default_angry_smiley'] = 'Angry'; +$txt['default_sad_smiley'] = 'Sad'; +$txt['default_shocked_smiley'] = 'Shocked'; +$txt['default_cool_smiley'] = 'Cool'; +$txt['default_huh_smiley'] = 'Huh?'; +$txt['default_roll_eyes_smiley'] = 'Roll Eyes'; +$txt['default_tongue_smiley'] = 'Tongue'; +$txt['default_embarrassed_smiley'] = 'Embarrassed'; +$txt['default_lips_sealed_smiley'] = 'Lips Sealed'; +$txt['default_undecided_smiley'] = 'Undecided'; +$txt['default_kiss_smiley'] = 'Kiss'; +$txt['default_cry_smiley'] = 'Cry'; +$txt['default_evil_smiley'] = 'Evil'; +$txt['default_azn_smiley'] = 'Azn'; +$txt['default_afro_smiley'] = 'Afro'; +$txt['default_laugh_smiley'] = 'Laugh'; +$txt['default_police_smiley'] = 'Police'; +$txt['default_angel_smiley'] = 'Angel'; + +$txt['error_message_click'] = 'Click here'; +$txt['error_message_try_again'] = 'to try this step again.'; +$txt['error_message_bad_try_again'] = 'to try installing anyway, but note that this is strongly discouraged.'; + +$txt['install_settings'] = 'Forum Settings'; +$txt['install_settings_info'] = 'This page requires you to define a few key settings for your forum. SMF has automatically detected key settings for you.'; +$txt['install_settings_name'] = 'Forum name'; +$txt['install_settings_name_info'] = 'This is the name of your forum, ie. "The Testing Forum".'; +$txt['install_settings_name_default'] = 'My Community'; +$txt['install_settings_url'] = 'Forum URL'; +$txt['install_settings_url_info'] = 'This is the URL to your forum without the trailing \'/\'!.
    In most cases, you can leave the default value in this box alone - it is usually right.'; +$txt['install_settings_compress'] = 'Gzip Output'; +$txt['install_settings_compress_title'] = 'Compress output to save bandwidth.'; +// In this string, you can translate the word "PASS" to change what it says when the test passes. +$txt['install_settings_compress_info'] = 'This function does not work properly on all servers, but can save you a lot of bandwidth.
    Click here to test it. (it should just say "PASS".)'; +$txt['install_settings_dbsession'] = 'Database Sessions'; +$txt['install_settings_dbsession_title'] = 'Use the database for sessions instead of using files.'; +$txt['install_settings_dbsession_info1'] = 'This feature is almost always for the best, as it makes sessions more dependable.'; +$txt['install_settings_dbsession_info2'] = 'This feature is generally a good idea, but may not work properly on this server.'; +$txt['install_settings_utf8'] = 'UTF-8 Character Set'; +$txt['install_settings_utf8_title'] = 'Use UTF-8 as default character set'; +$txt['install_settings_utf8_info'] = 'This feature lets both the database and the forum use an international character set, UTF-8. This can be useful when working with multiple languages that use different character sets.'; +$txt['install_settings_stats'] = 'Allow Stat Collection'; +$txt['install_settings_stats_title'] = 'Allow Simple Machines to Collect Basic Stats Monthly'; +$txt['install_settings_stats_info'] = 'If enabled, this will allow Simple Machines to visit your site once a month to collect basic statistics. This will help us make decisions as to which configurations to optimize the software for. For more information please visit our info page.'; +$txt['install_settings_proceed'] = 'Proceed'; + +$txt['db_settings'] = 'Database Server Settings'; +$txt['db_settings_info'] = 'These are the settings to use for your database server. If you don\'t know the values, you should ask your host what they are.'; +$txt['db_settings_type'] = 'Database type'; +$txt['db_settings_type_info'] = 'Multiple supported database types were detected - which do you wish to use. Please note that running pre-SMF 2.0 RC3 along with newer SMF versions in the same PostgreSQL database is not supported. You need to upgrade your older installations in that case.'; +$txt['db_settings_server'] = 'Server name'; +$txt['db_settings_server_info'] = 'This is nearly always localhost - so if you don\'t know, try localhost.'; +$txt['db_settings_username'] = 'Username'; +$txt['db_settings_username_info'] = 'Fill in the username you need to connect to your database here.
    If you don\'t know what it is, try the username of your ftp account, most of the time they are the same.'; +$txt['db_settings_password'] = 'Password'; +$txt['db_settings_password_info'] = 'Here, put the password you need to connect to your database.
    If you don\'t know this, you should try the password to your ftp account.'; +$txt['db_settings_database'] = 'Database name'; +$txt['db_settings_database_info'] = 'Fill in the name of the database you want to use for SMF to store its data in.'; +$txt['db_settings_database_info_note'] = 'If this database does not exist, this installer will try to create it.'; +$txt['db_settings_database_file'] = 'Database filename'; +$txt['db_settings_database_file_info'] = 'This is the name of the file in which to store the SMF data. We recommend you use the randomly generated name for this and set the path of this file to be outside of the public area of your webserver.'; +$txt['db_settings_prefix'] = 'Table prefix'; +$txt['db_settings_prefix_info'] = 'The prefix for every table in the database. Do not install two forums with the same prefix!
    This value allows for multiple installations in one database.'; +$txt['db_sqlite_warning'] = 'Only recommended for small, low volume and/or intranet-type forums'; +$txt['db_populate'] = 'Populated Database'; +$txt['db_populate_info'] = 'Your settings have now been saved and the database has been populated with all the data required to get your forum up and running. Summary of population:'; +$txt['db_populate_info2'] = 'Click "Continue" to progress to the admin account creation page.'; +$txt['db_populate_inserts'] = 'Inserted %1$d rows.'; +$txt['db_populate_tables'] = 'Created %1$d tables.'; +$txt['db_populate_insert_dups'] = 'Ignored %1$d duplicated inserts.'; +$txt['db_populate_table_dups'] = 'Ignored %1$d duplicated tables.'; + +$txt['user_settings'] = 'Create Your Account'; +$txt['user_settings_info'] = 'The installer will now create a new administrator account for you.'; +$txt['user_settings_username'] = 'Your username'; +$txt['user_settings_username_info'] = 'Choose the name you want to login with.
    This can\'t be changed later, but your display name can be.'; +$txt['user_settings_password'] = 'Password'; +$txt['user_settings_password_info'] = 'Fill in your preferred password here, and remember it well!'; +$txt['user_settings_again'] = 'Password'; +$txt['user_settings_again_info'] = '(just for verification.)'; +$txt['user_settings_email'] = 'Email Address'; +$txt['user_settings_email_info'] = 'Provide your email address as well. This must be a valid email address.'; +$txt['user_settings_database'] = 'Database Password'; +$txt['user_settings_database_info'] = 'The installer requires that you supply the database password to create an administrator account, for security reasons.'; +$txt['user_settings_skip'] = 'Skip'; +$txt['user_settings_skip_sure'] = 'Are you sure you wish to skip admin account creation?'; +$txt['user_settings_proceed'] = 'Finish'; + +$txt['ftp_checking_writable'] = 'Checking Files are Writable'; +$txt['ftp_setup'] = 'FTP Connection Information'; +$txt['ftp_setup_info'] = 'This installer can connect via FTP to fix the files that need to be writable and are not. If this doesn\'t work for you, you will have to go in manually and make the files writable. Please note that this doesn\'t support SSL right now.'; +$txt['ftp_server'] = 'Server'; +$txt['ftp_server_info'] = 'This should be the server and port for your FTP server.'; +$txt['ftp_port'] = 'Port'; +$txt['ftp_username'] = 'Username'; +$txt['ftp_username_info'] = 'The username to login with. This will not be saved anywhere.'; +$txt['ftp_password'] = 'Password'; +$txt['ftp_password_info'] = 'The password to login with. This will not be saved anywhere.'; +$txt['ftp_path'] = 'Install Path'; +$txt['ftp_path_info'] = 'This is the relative path you use in your FTP server.'; +$txt['ftp_path_found_info'] = 'The path in the box above was automatically detected.'; +$txt['ftp_connect'] = 'Connect'; +$txt['ftp_setup_why'] = 'What is this step for?'; +$txt['ftp_setup_why_info'] = 'Some files need to be writable for SMF to work properly. This step allows you to let the installer make them writable for you. However, in some cases it won\'t work - in that case, please make the following files 777 (writable, 755 on some hosts):'; +$txt['ftp_setup_again'] = 'to test if these files are writable again.'; + +$txt['error_php_too_low'] = 'Warning! You do not appear to have a version of PHP installed on your webserver that meets SMF\'s minimum installations requirements.
    If you are not the host, you will need to ask your host to upgrade, or use a different host - otherwise, please upgrade PHP to a recent version.

    If you know for a fact that your PHP version is high enough you may continue, although this is strongly discouraged.'; +$txt['error_missing_files'] = 'Unable to find crucial installation files in the directory of this script!

    Please make sure you uploaded the entire installation package, including the sql file, and then try again.'; +$txt['error_session_save_path'] = 'Please inform your host that the session.save_path specified in php.ini is not valid! It needs to be changed to a directory that exists, and is writable by the user PHP is running under.
    '; +$txt['error_windows_chmod'] = 'You\'re on a windows server, and some crucial files are not writable. Please ask your host to give write permissions to the user PHP is running under for the files in your SMF installation. The following files or directories need to be writable:'; +$txt['error_ftp_no_connect'] = 'Unable to connect to FTP server with this combination of details.'; +$txt['error_db_file'] = 'Cannot find database source script! Please check file %1$s is within your forum source directory.'; +$txt['error_db_connect'] = 'Cannot connect to the database server with the supplied data.

    If you are not sure about what to type in, please contact your host.'; +$txt['error_db_too_low'] = 'The version of your database server is very old, and does not meet SMF\'s minimum requirements.

    Please ask your host to either upgrade it or supply a new one, and if they won\'t, please try a different host.'; +$txt['error_db_database'] = 'The installer was unable to access the "%1$s" database. With some hosts, you have to create the database in your administration panel before SMF can use it. Some also add prefixes - like your username - to your database names.'; +$txt['error_db_queries'] = 'Some of the queries were not executed properly. This could be caused by an unsupported (development or old) version of your database software.

    Technical information about the queries:'; +$txt['error_db_queries_line'] = 'Line #'; +$txt['error_db_missing'] = 'The installer was unable to detect any database support in PHP. Please ask your host to ensure that PHP was compiled with the desired database, or that the proper extension is being loaded.'; +$txt['error_db_script_missing'] = 'The installer could not find any install script files for the detected databases. Please check you have uploaded the necessary install script files to your forum directory, for example "%1$s"'; +$txt['error_session_missing'] = 'The installer was unable to detect sessions support in your server\'s installation of PHP. Please ask your host to ensure that PHP was compiled with session support (in fact, it has to be explicitly compiled without it.)'; +$txt['error_user_settings_again_match'] = 'You typed in two completely different passwords!'; +$txt['error_user_settings_no_password'] = 'Your password must be at least four characters long.'; +$txt['error_user_settings_taken'] = 'Sorry, a member is already registered with that username and/or email address.

    A new account has not been created.'; +$txt['error_user_settings_query'] = 'A database error occurred while trying to create an administrator. This error was:'; +$txt['error_subs_missing'] = 'Unable to find the Sources/Subs.php file. Please make sure it was uploaded properly, and then try again.'; +$txt['error_db_alter_priv'] = 'The database account you specified does not have permission to ALTER, CREATE, and/or DROP tables in the database; this is necessary for SMF to function properly.'; +$txt['error_versions_do_not_match'] = 'The installer has detected another version of SMF already installed with the specified information. If you are trying to upgrade, you should use the upgrader, not the installer.

    Otherwise, you may wish to use different information, or create a backup and then delete the data currently in the database.'; +$txt['error_mod_security'] = 'The installer has detected the mod_security module is installed on your web server. Mod_security will block submitted forms even before SMF gets a say in anything. SMF has a built-in security scanner that will work more effectively than mod_security and that won\'t block submitted forms.

    More information about disabling mod_security'; +$txt['error_mod_security_no_write'] = 'The installer has detected the mod_security module is installed on your web server. Mod_security will block submitted forms even before SMF gets a say in anything. SMF has a built-in security scanner that will work more effectively than mod_security and that won\'t block submitted forms.

    More information about disabling mod_security

    Alternatively, you may wish to use your ftp client to chmod .htaccess in the forum directory to be writable (777), and then refresh this page.'; +$txt['error_utf8_version'] = 'The current version of your database doesn\'t support the use of the UTF-8 character set. You can still install SMF without any problems, but only with UTF-8 support unchecked. If you would like to switch over to UTF-8 in the future (e.g. after the database server of your forum has been upgraded to version >= %1$s), you can convert your forum to UTF-8 through the admin panel.'; +$txt['error_valid_email_needed'] = 'You have not entered a valid email address.'; +$txt['error_already_installed'] = 'The installer has detected that you already have SMF installed. It is strongly advised that you do not try to overwrite an existing installation - continuing with installation may result in the loss or corruption of existing data.

    If you wish to upgrade please visit the Simple Machines Website and download the latest upgrade package.

    If you wish to overwrite your existing installation, including all data, it\'s recommended that you delete the existing database tables and replace Settings.php and try again.'; +$txt['error_warning_notice'] = 'Warning!'; +$txt['error_script_outdated'] = 'This install script is out of date! The current version of SMF is %1$s but this install script is for %2$s.

    + It is recommended that you visit the Simple Machines website to ensure you are installing the latest version.'; +$txt['error_db_filename'] = 'You must enter a name for the database file name for SQLite.'; +$txt['error_db_prefix_numeric'] = 'The selected database type does not support the use of numeric prefixes.'; +$txt['error_invalid_characters_username'] = 'Invalid character used in Username.'; +$txt['error_username_too_long'] = 'Username must be less than 25 characters long.'; +$txt['error_username_left_empty'] = 'Username field was left empty.'; +$txt['error_db_filename_exists'] = 'The database that you are trying to create exists. Please delete the current database file or enter another name.'; +$txt['error_db_prefix_reserved'] = 'The prefix that you entered is a reserved prefix. Please enter another prefix.'; + +$txt['upgrade_upgrade_utility'] = 'SMF Upgrade Utility'; +$txt['upgrade_warning'] = 'Warning!'; +$txt['upgrade_critical_error'] = 'Critical Error!'; +$txt['upgrade_continue'] = 'Continue'; +$txt['upgrade_skip'] = 'Skip'; +$txt['upgrade_note'] = 'Note!'; +$txt['upgrade_step'] = 'Step'; +$txt['upgrade_steps'] = 'Steps'; +$txt['upgrade_progress'] = 'Progress'; +$txt['upgrade_overall_progress'] = 'Overall Progress'; +$txt['upgrade_step_progress'] = 'Step Progress'; +$txt['upgrade_time_elapsed'] = 'Time Elapsed'; +$txt['upgrade_time_mins'] = 'mins'; +$txt['upgrade_time_secs'] = 'seconds'; + +$txt['upgrade_incomplete'] = 'Incomplete'; +$txt['upgrade_not_quite_done'] = 'Not quite done yet!'; +$txt['upgrade_paused_overload'] = 'This upgrade has been paused to avoid overloading your server. Don\'t worry, nothing\'s wrong - simply click the below to keep going.'; + +$txt['upgrade_ready_proceed'] = 'Thank you for choosing to upgrade to SMF %1$s. All files appear to be in place, and we\'re ready to proceed.'; + +$txt['upgrade_error_script_js'] = 'The upgrade script cannot find script.js or it is out of date. Make sure your theme paths are correct. You can download a setting checker tool from the Simple Machines Website'; + +$txt['upgrade_warning_lots_data'] = 'This upgrade script has detected that your forum contains a lot of data which needs upgrading. This process may take quite some time depending on your server and forum size, and for very large forums (~300,000 messages) may take several hours to complete.'; +$txt['upgrade_warning_out_of_date'] = 'This upgrade script is out of date! The current version of SMF is ?? but this upgrade script is for %1$s.

    It is recommended that you visit the Simple Machines website to ensure you are upgrading to the latest version.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Login.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Login.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,151 @@ +If you wish to restore your account, please check the "Reactivate my account" box, and login again.'; +$txt['undelete_account'] = 'Reactivate my account'; + +// Use numeric entities in the below three strings. +$txt['change_password'] = 'New Password Details'; +$txt['change_password_login'] = 'Your login details at'; +$txt['change_password_new'] = 'have been changed and your password reset. Below are your new login details.'; + +$txt['in_maintain_mode'] = 'This board is in Maintenance Mode.'; + +// These two are used as a javascript alert; please use international characters directly, not as entities. +$txt['register_agree'] = 'Please read and accept the agreement before registering.'; +$txt['register_passwords_differ_js'] = 'The two passwords you entered are not the same!'; + +$txt['approval_after_registration'] = 'Thank you for registering. The admin must approve your registration before you may begin to use your account, you will receive an email shortly advising you of the admins decision.'; + +$txt['admin_settings_desc'] = 'Here you can change a variety of settings related to registration of new members.'; + +$txt['setting_enableOpenID'] = 'Allow users to register using OpenID'; + +$txt['setting_registration_method'] = 'Method of registration employed for new members'; +$txt['setting_registration_disabled'] = 'Registration Disabled'; +$txt['setting_registration_standard'] = 'Immediate Registration'; +$txt['setting_registration_activate'] = 'Email Activation'; +$txt['setting_registration_approval'] = 'Admin Approval'; +$txt['setting_notify_new_registration'] = 'Notify administrators when a new member joins'; +$txt['setting_send_welcomeEmail'] = 'Send welcome email to new members'; + +$txt['setting_coppaAge'] = 'Age below which to apply registration restrictions'; +$txt['setting_coppaAge_desc'] = '(Set to 0 to disable)'; +$txt['setting_coppaType'] = 'Action to take when a user below minimum age registers'; +$txt['setting_coppaType_reject'] = 'Reject their registration'; +$txt['setting_coppaType_approval'] = 'Require parent/guardian approval'; +$txt['setting_coppaPost'] = 'Postal address to which approval forms should be sent'; +$txt['setting_coppaPost_desc'] = 'Only applies if age restriction is in place'; +$txt['setting_coppaFax'] = 'Fax number to which approval forms should be faxed'; +$txt['setting_coppaPhone'] = 'Contact number for parents to contact with age restriction queries'; + +$txt['admin_register'] = 'Registration of new member'; +$txt['admin_register_desc'] = 'From here you can register new members into the forum, and if desired, email them their details.'; +$txt['admin_register_username'] = 'New Username'; +$txt['admin_register_email'] = 'Email Address'; +$txt['admin_register_password'] = 'Password'; +$txt['admin_register_username_desc'] = 'Username for the new member'; +$txt['admin_register_email_desc'] = 'Email address of the member'; +$txt['admin_register_password_desc'] = 'Password for new member'; +$txt['admin_register_email_detail'] = 'Email new password to user'; +$txt['admin_register_email_detail_desc'] = 'Email address required even if unchecked'; +$txt['admin_register_email_activate'] = 'Require user to activate the account'; +$txt['admin_register_group'] = 'Primary Membergroup'; +$txt['admin_register_group_desc'] = 'Primary membergroup new member will belong to'; +$txt['admin_register_group_none'] = '(no primary membergroup)'; +$txt['admin_register_done'] = 'Member %1$s has been registered successfully!'; + +$txt['coppa_title'] = 'Age Restricted Forum'; +$txt['coppa_after_registration'] = 'Thank you for registering with ' . $context['forum_name_html_safe'] . '.

    Because you fall under the age of {MINIMUM_AGE}, it is a legal requirement + to obtain your parent or guardian\'s permission before you may begin to use your account. To arrange for account activation please print off the form below:'; +$txt['coppa_form_link_popup'] = 'Load Form In New Window'; +$txt['coppa_form_link_download'] = 'Download Form as Text File'; +$txt['coppa_send_to_one_option'] = 'Then arrange for your parent/guardian to send the completed form by:'; +$txt['coppa_send_to_two_options'] = 'Then arrange for your parent/guardian to send the completed form by either:'; +$txt['coppa_send_by_post'] = 'Post, to the following address:'; +$txt['coppa_send_by_fax'] = 'Fax, to the following number:'; +$txt['coppa_send_by_phone'] = 'Alternatively, arrange for them to phone the administrator at {PHONE_NUMBER}.'; + +$txt['coppa_form_title'] = 'Permission form for registration at ' . $context['forum_name_html_safe']; +$txt['coppa_form_address'] = 'Address'; +$txt['coppa_form_date'] = 'Date'; +$txt['coppa_form_body'] = 'I {PARENT_NAME},

    Give permission for {CHILD_NAME} (child name) to become a fully registered member of the forum: ' . $context['forum_name_html_safe'] . ', with the username: {USER_NAME}.

    I understand that certain personal information entered by {USER_NAME} may be shown to other users of the forum.

    Signed:
    {PARENT_NAME} (Parent/Guardian).'; + +$txt['visual_verification_sound_again'] = 'Play again'; +$txt['visual_verification_sound_close'] = 'Close window'; +$txt['visual_verification_sound_direct'] = 'Having problems hearing this? Try a direct link to it.'; + +// Use numeric entities in the below. +$txt['registration_username_available'] = 'Username is available'; +$txt['registration_username_unavailable'] = 'Username is not available'; +$txt['registration_username_check'] = 'Check if username is available'; +$txt['registration_password_short'] = 'Password is too short'; +$txt['registration_password_reserved'] = 'Password contains your username/email'; +$txt['registration_password_numbercase'] = 'Password must contain both upper and lower case, and numbers'; +$txt['registration_password_no_match'] = 'Passwords do not match'; +$txt['registration_password_valid'] = 'Password is valid'; + +$txt['registration_errors_occurred'] = 'The following errors were detected in your registration. Please correct them to continue:'; + +$txt['authenticate_label'] = 'Authentication Method'; +$txt['authenticate_password'] = 'Password'; +$txt['authenticate_openid'] = 'OpenID'; +$txt['authenticate_openid_url'] = 'OpenID Authentication URL'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManageBoards.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManageBoards.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,98 @@ +"username", "username". (these must be usernames not display names!)
    To create a new board, click the Add Board button. To make the new board a child of a current board, select "Child of..." from the Order drop down menu when creating the board.'; +$txt['parent_members_only'] = 'Regular Members'; +$txt['parent_guests_only'] = 'Guests'; +$txt['catConfirm'] = 'Do you really want to delete this category?'; +$txt['boardConfirm'] = 'Do you really want to delete this board?'; + +$txt['catEdit'] = 'Edit Category'; +$txt['collapse_enable'] = 'Collapsible'; +$txt['collapse_desc'] = 'Allow users to collapse this category'; +$txt['catModify'] = '(modify)'; + +$txt['mboards_order_after'] = 'After '; +$txt['mboards_order_inside'] = 'Inside '; +$txt['mboards_order_first'] = 'In first place'; + +$txt['mboards_new_board'] = 'Add Board'; +$txt['mboards_new_cat_name'] = 'New Category'; +$txt['mboards_add_cat_button'] = 'Add Category'; +$txt['mboards_new_board_name'] = 'New Board'; + +$txt['mboards_name'] = 'Name'; +$txt['mboards_modify'] = 'modify'; +$txt['mboards_permissions'] = 'permissions'; +// Don't use entities in the below string. +$txt['mboards_permissions_confirm'] = 'Are you sure you want to switch this board to use local permissions?'; + +$txt['mboards_delete_cat'] = 'Delete Category'; +$txt['mboards_delete_board'] = 'Delete Board'; + +$txt['mboards_delete_cat_contains'] = 'Deleting this category will also delete the below boards, including all topics, posts and attachments within each board'; +$txt['mboards_delete_option1'] = 'Delete category and all boards contained within.'; +$txt['mboards_delete_option2'] = 'Delete category and move all boards contained within to'; +$txt['mboards_delete_board_contains'] = 'Deleting this board will also move the child boards below, including all topics, posts and attachments within each board'; +$txt['mboards_delete_board_option1'] = 'Delete board and move child boards contained within to category level.'; +$txt['mboards_delete_board_option2'] = 'Delete board and move all child boards contained within to'; +$txt['mboards_delete_what_do'] = 'Please select what you would like to do with these boards'; +$txt['mboards_delete_confirm'] = 'Confirm'; +$txt['mboards_delete_cancel'] = 'Cancel'; + +$txt['mboards_category'] = 'Category'; +$txt['mboards_description'] = 'Description'; +$txt['mboards_description_desc'] = 'A short description of your board.'; +$txt['mboards_groups'] = 'Allowed Groups'; +$txt['mboards_groups_desc'] = 'Groups allowed to access this board.
    Note: if the member is in any group or post group checked, they will have access to this board.'; +$txt['mboards_groups_regular_members'] = 'This group contains all members that have no primary group set.'; +$txt['mboards_groups_post_group'] = 'This group is a post count based group.'; +$txt['mboards_moderators'] = 'Moderators'; +$txt['mboards_moderators_desc'] = 'Additional members to have moderation privileges on this board. Note that administrators don\'t have to be listed here.'; +$txt['mboards_count_posts'] = 'Count Posts'; +$txt['mboards_count_posts_desc'] = 'Makes new replies and topics raise members\' post counts.'; +$txt['mboards_unchanged'] = 'Unchanged'; +$txt['mboards_theme'] = 'Board Theme'; +$txt['mboards_theme_desc'] = 'This allows you to change the look of your forum inside only this board.'; +$txt['mboards_theme_default'] = '(overall forum default.)'; +$txt['mboards_override_theme'] = 'Override Member\'s Theme'; +$txt['mboards_override_theme_desc'] = 'Use this board\'s theme even if the member didn\'t choose to use the defaults.'; + +$txt['mboards_redirect'] = 'Redirect to a web address'; +$txt['mboards_redirect_desc'] = 'Enable this option to redirect anyone who clicks on this board to another web address.'; +$txt['mboards_redirect_url'] = 'Address to redirect users to'; +$txt['mboards_redirect_url_desc'] = 'For example: "http://www.simplemachines.org".'; +$txt['mboards_redirect_reset'] = 'Reset redirect count'; +$txt['mboards_redirect_reset_desc'] = 'Selecting this will reset the redirection count for this board to zero.'; +$txt['mboards_current_redirects'] = 'Currently: %1$s'; +$txt['mboards_redirect_disabled'] = 'Note: Board must be empty of topics to enable this option.'; +$txt['mboards_redirect_disabled_recycle'] = 'Note: You cannot set the recycle bin board to be a redirection board.'; + +$txt['mboards_order_before'] = 'Before'; +$txt['mboards_order_child_of'] = 'Child of'; +$txt['mboards_order_in_category'] = 'In category'; +$txt['mboards_current_position'] = 'Current Position'; +$txt['no_valid_parent'] = 'Board %1$s does not have a valid parent. Use the \'find and repair errors\' function to fix this.'; + +$txt['mboards_recycle_disabled_delete'] = 'Note: You must select an alternative recycle bin board or disable recycling before you can delete this board.'; + +$txt['mboards_settings_desc'] = 'Edit general board and category settings.'; +$txt['groups_manage_boards'] = 'Membergroups allowed to manage boards and categories'; +$txt['mboards_settings_submit'] = 'Save'; +$txt['recycle_enable'] = 'Enable recycling of deleted topics'; +$txt['recycle_board'] = 'Board for recycled topics'; +$txt['recycle_board_unselected_notice'] = 'You have enabled the recycling of topics without specifying a board to place them in. This feature will not be enabled until you specify a board to place recycled topics into.'; +$txt['countChildPosts'] = 'Count child\'s posts in parent\'s totals'; +$txt['allow_ignore_boards'] = 'Allow boards to be ignored'; + +$txt['mboards_select_destination'] = 'Select destination for board \'%1$s\''; +$txt['mboards_cancel_moving'] = 'Cancel moving'; +$txt['mboards_move'] = 'move'; + +$txt['mboards_no_cats'] = 'There are currently no categories or boards configured.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManageCalendar.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManageCalendar.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,45 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManageMail.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManageMail.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,50 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManageMaintenance.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManageMaintenance.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,214 @@ +Critical
    '; +$txt['errortype_critical_desc'] = 'Critical errors. These should be taken care of as quickly as possible. Ignoring these errors can result in your forum failing and possibly security issues'; +$txt['errortype_database'] = 'Database'; +$txt['errortype_database_desc'] = 'Errors caused by faulty queries. These should be looked at and reported to the SMF team.'; +$txt['errortype_undefined_vars'] = 'Undefined'; +$txt['errortype_undefined_vars_desc'] = 'Errors caused by the use of undefined variables, indexes, or offsets.'; +$txt['errortype_template'] = 'Template'; +$txt['errortype_template_desc'] = 'Errors related to the loading of templates.'; +$txt['errortype_user'] = 'User'; +$txt['errortype_user_desc'] = 'Errors resulting from user errors. Includes failed passwords, trying to login when banned, and trying to do an action for which they do not have permission.'; + +$txt['maintain_recount'] = 'Recount all forum totals and statistics'; +$txt['maintain_recount_info'] = 'Should the total replies of a topic or the number of PMs in your inbox be incorrect: this function will recount all saved counts and statistics for you.'; +$txt['maintain_errors'] = 'Find and repair any errors'; +$txt['maintain_errors_info'] = 'If, for example, posts or topics are missing after a server crash, this function may help finding them again.'; +$txt['maintain_logs'] = 'Empty out unimportant logs'; +$txt['maintain_logs_info'] = 'This function will empty out all unimportant logs. This should be avoided unless something\'s wrong, but it doesn\'t hurt anything.'; +$txt['maintain_cache'] = 'Empty the file cache'; +$txt['maintain_cache_info'] = 'This function will empty out the file cache should you need it to be cleared.'; +$txt['maintain_optimize'] = 'Optimize all tables'; +$txt['maintain_optimize_info'] = 'This task allows you to optimize all tables. This will get rid of overhead, effectively making the tables smaller in size and your forum faster!'; +$txt['maintain_version'] = 'Check all files against current versions'; +$txt['maintain_version_info'] = 'This maintenance task allows you to do a detailed version check of all forum files against the official list of latest versions.'; +$txt['maintain_run_now'] = 'Run task now'; +$txt['maintain_return'] = 'Back to Forum Maintenance'; + +$txt['maintain_backup'] = 'Backup Database'; +$txt['maintain_backup_info'] = 'Download a backup copy of your forums database in case of emergency.'; +$txt['maintain_backup_struct'] = 'Save the table structure.'; +$txt['maintain_backup_data'] = 'Save the table data (the important stuff).'; +$txt['maintain_backup_gz'] = 'Compress the file with gzip.'; +$txt['maintain_backup_save'] = 'Download'; + +$txt['maintain_old'] = 'Remove Old Posts'; +$txt['maintain_old_since_days1'] = 'Remove all topics not posted in for '; +$txt['maintain_old_since_days2'] = ' days, which are:'; +$txt['maintain_old_nothing_else'] = 'Any sort of topic.'; +$txt['maintain_old_are_moved'] = 'Moved topic notices.'; +$txt['maintain_old_are_locked'] = 'Locked.'; +$txt['maintain_old_are_not_stickied'] = 'But don\'t count stickied topics.'; +$txt['maintain_old_all'] = 'All Boards (click to select specific boards)'; +$txt['maintain_old_choose'] = 'Specific Boards (click to select all)'; +$txt['maintain_old_remove'] = 'Remove now'; +$txt['maintain_old_confirm'] = 'Are you really sure you want to delete old posts now?\\n\\nThis cannot be undone!'; + +$txt['maintain_members'] = 'Remove Inactive Members'; +$txt['maintain_members_ungrouped'] = 'Ungrouped Members (Members with no assigned groups)'; +$txt['maintain_members_since1'] = 'Remove all members who have not'; +$txt['maintain_members_since2'] = 'for'; +$txt['maintain_members_since3'] = 'days.'; +$txt['maintain_members_activated'] = 'activated their account'; +$txt['maintain_members_logged_in'] = 'logged in'; +$txt['maintain_members_all'] = 'All Membergroups'; +$txt['maintain_members_choose'] = 'Selected Groups'; +$txt['maintain_members_confirm'] = 'Are you sure you really want to delete these member accounts?\\n\\nThis cannot be undone!'; + +$txt['utf8_title'] = 'Convert the database and data to UTF-8'; +$txt['utf8_introduction'] = 'UTF-8 is an international character set covering nearly all languages around the world. Converting your database and data to UTF-8 can make it easier to support multiple languages on the same board. It also can enhance search and sorting capabilities for languages with non-latin characters.'; +$txt['utf8_warning'] = 'If you want to convert your data and database to UTF-8, be aware of the following: +
      +
    • Converting character sets might be harmful for your data! Make sure you have backed up your database before converting.
    • +
    • Because UTF-8 is a richer character set than most other character sets, there\'s no way back, unless by restoring your database to before the conversion.
    • +
    • After converting your data and database to UTF-8, you will need UTF-8 compatible language files.
    • +
    '; +$txt['utf8_charset_not_supported'] = 'Conversion from %1$s to UTF-8 is not supported.'; +$txt['utf8_detected_charset'] = 'Based on your default language file (\'%1$s\'), the character set of your data would most likely be \'%2$s\'.'; +$txt['utf8_already_utf8'] = 'Your database and data already seem to be configured as UTF-8 data. No conversion is needed.'; +$txt['utf8_source_charset'] = 'Data character set'; +$txt['utf8_proceed'] = 'Proceed'; +$txt['utf8_database_charset'] = 'Database character set'; +$txt['utf8_target_charset'] = 'Convert data and database to'; +$txt['utf8_utf8'] = 'UTF-8'; +$txt['utf8_db_version_too_low'] = 'The version of MySQL that your database server is using is not high enough to support UTF-8 properly. A minimum version of 4.1.2 is required.'; +$txt['utf8_cannot_convert_fulltext'] = 'Your messages table is using a fulltext index for use when searching. You cannot proceed in converting to UTF-8 until that index is removed. You can re-create it after the conversion has been completed.'; + +$txt['entity_convert_title'] = 'Convert HTML-entities to UTF-8 characters'; +$txt['entity_convert_only_utf8'] = 'The database needs to be in UTF-8 format before HTML-entities can be converted to UTF-8'; +$txt['entity_convert_introduction'] = 'This function will convert all characters that are stored in the database as HTML-entities to UTF-8 characters. This is especially useful when you have just converted your forum from a character set like ISO-8859-1 while non-latin characters were used on the forum. The browser then sends all characters as HTML-entities. For example, the HTML-entity &#945; represents the greek letter α (alpha). Converting entities to UTF-8 will improve searching and sorting of text and reduce storage size.'; +$txt['entity_convert_proceed'] = 'Proceed'; + +// Move topics out. +$txt['move_topics_maintenance'] = 'Move Topics'; +$txt['move_topics_select_board'] = 'Select Board'; +$txt['move_topics_from'] = 'Move topics from'; +$txt['move_topics_to'] = 'to'; +$txt['move_topics_now'] = 'Move now'; +$txt['move_topics_confirm'] = 'Are you sure you want to move ALL the topics from "%board_from%" to "%board_to%"?'; + +$txt['maintain_reattribute_posts'] = 'Reattribute User Posts'; +$txt['reattribute_guest_posts'] = 'Attribute guest posts made with'; +$txt['reattribute_email'] = 'Email address of'; +$txt['reattribute_username'] = 'Username of'; +$txt['reattribute_current_member'] = 'Attribute posts to member'; +$txt['reattribute_increase_posts'] = 'Add posts to users post count'; +$txt['reattribute'] = 'Reattribute'; +// Don't use entities in the below string. +$txt['reattribute_confirm'] = 'Are you sure you want to attribute all guest posts with %type% of "%find%" to member "%member_to%"?'; +$txt['reattribute_confirm_username'] = 'a username'; +$txt['reattribute_confirm_email'] = 'an email address'; +$txt['reattribute_cannot_find_member'] = 'Could not find member to attribute posts to.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManageMembers.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManageMembers.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,129 @@ +Note: normally, post groups don\'t need access because the group the member is in will give them access.'; +$txt['membergroups_new_as_inherit'] = 'inherit from'; +$txt['membergroups_new_as_type'] = 'by type'; +$txt['membergroups_new_as_copy'] = 'based off of'; +$txt['membergroups_new_copy_none'] = '(none)'; +$txt['membergroups_can_edit_later'] = 'You can edit them later.'; + +$txt['membergroups_edit_group'] = 'Edit Membergroup'; +$txt['membergroups_edit_name'] = 'Group name'; +$txt['membergroups_edit_inherit_permissions'] = 'Inherit Permissions'; +$txt['membergroups_edit_inherit_permissions_desc'] = 'Select "No" to enable group to have own permission set.'; +$txt['membergroups_edit_inherit_permissions_no'] = 'No - Use Unique Permissions'; +$txt['membergroups_edit_inherit_permissions_from'] = 'Inherit From'; +$txt['membergroups_edit_hidden'] = 'Visibility'; +$txt['membergroups_edit_hidden_no'] = 'Visible'; +$txt['membergroups_edit_hidden_boardindex'] = 'Visible - Except in Group Key'; +$txt['membergroups_edit_hidden_all'] = 'Invisible'; +// Do not use numeric entities in the below string. +$txt['membergroups_edit_hidden_warning'] = 'Are you sure you want to disallow assignment of this group as a users primary group?\\n\\nDoing so will restrict assignment to additional groups only, and will update all current "primary" members to have it as an additional group only.'; +$txt['membergroups_edit_desc'] = 'Group description'; +$txt['membergroups_edit_group_type'] = 'Group Type'; +$txt['membergroups_edit_select_group_type'] = 'Select Group Type'; +$txt['membergroups_group_type_private'] = 'Private (Membership must be assigned)'; +$txt['membergroups_group_type_protected'] = 'Protected (Only administrators can manage and assign)'; +$txt['membergroups_group_type_request'] = 'Requestable (User may request membership)'; +$txt['membergroups_group_type_free'] = 'Free (User may leave and join group at will)'; +$txt['membergroups_group_type_post'] = 'Post Based (Membership based on post count)'; +$txt['membergroups_min_posts'] = 'Required posts'; +$txt['membergroups_online_color'] = 'Color in online list'; +$txt['membergroups_star_count'] = 'Number of star images'; +$txt['membergroups_star_image'] = 'Star image filename'; +$txt['membergroups_star_image_note'] = 'you can use $language for the language of the user'; +$txt['membergroups_max_messages'] = 'Max personal messages'; +$txt['membergroups_max_messages_note'] = '0 = unlimited'; +$txt['membergroups_edit_save'] = 'Save'; +$txt['membergroups_delete'] = 'Delete'; +$txt['membergroups_confirm_delete'] = 'Are you sure you want to delete this group?!'; + +$txt['membergroups_members_title'] = 'Viewing Group'; +$txt['membergroups_members_group_members'] = 'Group Members'; +$txt['membergroups_members_no_members'] = 'This group is currently empty'; +$txt['membergroups_members_add_title'] = 'Add a member to this group'; +$txt['membergroups_members_add_desc'] = 'List of Members to Add'; +$txt['membergroups_members_add'] = 'Add Members'; +$txt['membergroups_members_remove'] = 'Remove from Group'; +$txt['membergroups_members_last_active'] = 'Last Active'; +$txt['membergroups_members_additional_only'] = 'Add as additional group only.'; +$txt['membergroups_members_group_moderators'] = 'Group Moderators'; +$txt['membergroups_members_description'] = 'Description'; +// Use javascript escaping in the below. +$txt['membergroups_members_deadmin_confirm'] = 'Are you sure you wish to remove yourself from the Administration group?'; + +$txt['membergroups_postgroups'] = 'Post groups'; +$txt['membergroups_settings'] = 'Membergroup Settings'; +$txt['groups_manage_membergroups'] = 'Groups allowed to change membergroups'; +$txt['membergroups_select_permission_type'] = 'Select permission profile'; +$txt['membergroups_images_url'] = '{theme URL}/images/'; +$txt['membergroups_select_visible_boards'] = 'Show boards'; +$txt['membergroups_members_top'] = 'Members'; +$txt['membergroups_name'] = 'Name'; +$txt['membergroups_stars'] = 'Stars'; + +$txt['admin_browse_approve'] = 'Members whose accounts are awaiting approval'; +$txt['admin_browse_approve_desc'] = 'From here you can manage all members who are waiting to have their accounts approved.'; +$txt['admin_browse_activate'] = 'Members whose accounts are awaiting activation'; +$txt['admin_browse_activate_desc'] = 'This screen lists all the members who have still not activated their accounts at your forum.'; +$txt['admin_browse_awaiting_approval'] = 'Awaiting Approval (%1$d)'; +$txt['admin_browse_awaiting_activate'] = 'Awaiting Activation (%1$d)'; + +$txt['admin_browse_username'] = 'Username'; +$txt['admin_browse_email'] = 'Email Address'; +$txt['admin_browse_ip'] = 'IP Address'; +$txt['admin_browse_registered'] = 'Registered'; +$txt['admin_browse_id'] = 'ID'; +$txt['admin_browse_with_selected'] = 'With Selected'; +$txt['admin_browse_no_members_approval'] = 'No members currently await approval.'; +$txt['admin_browse_no_members_activate'] = 'No members currently have not activated their accounts.'; + +// Don't use entities in the below strings, except the main ones. (lt, gt, quot.) +$txt['admin_browse_warn'] = 'all selected members?'; +$txt['admin_browse_outstanding_warn'] = 'all affected members?'; +$txt['admin_browse_w_approve'] = 'Approve'; +$txt['admin_browse_w_activate'] = 'Activate'; +$txt['admin_browse_w_delete'] = 'Delete'; +$txt['admin_browse_w_reject'] = 'Reject'; +$txt['admin_browse_w_remind'] = 'Remind'; +$txt['admin_browse_w_approve_deletion'] = 'Approve (Delete Accounts)'; +$txt['admin_browse_w_email'] = 'and send email'; +$txt['admin_browse_w_approve_require_activate'] = 'Approve and Require Activation'; + +$txt['admin_browse_filter_by'] = 'Filter By'; +$txt['admin_browse_filter_show'] = 'Displaying'; +$txt['admin_browse_filter_type_0'] = 'Unactivated New Accounts'; +$txt['admin_browse_filter_type_2'] = 'Unactivated Email Changes'; +$txt['admin_browse_filter_type_3'] = 'Unapproved New Accounts'; +$txt['admin_browse_filter_type_4'] = 'Unapproved Account Deletions'; +$txt['admin_browse_filter_type_5'] = 'Unapproved "Under Age" Accounts'; + +$txt['admin_browse_outstanding'] = 'Outstanding Members'; +$txt['admin_browse_outstanding_days_1'] = 'With all members who registered longer than'; +$txt['admin_browse_outstanding_days_2'] = 'days ago'; +$txt['admin_browse_outstanding_perform'] = 'Perform the following action'; +$txt['admin_browse_outstanding_go'] = 'Perform Action'; + +$txt['check_for_duplicate'] = 'Check for Duplicates'; +$txt['dont_check_for_duplicate'] = 'Don\'t Check for Duplicates'; +$txt['duplicates'] = 'Duplicates'; + +$txt['not_activated'] = 'Not activated'; +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManagePaid.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManagePaid.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,209 @@ +Note:
    For subscriptions to be automatically updated for your users, you + will need to setup a return URL for each of your payment methods. For all payment types, this return URL should be set as:

    +   •  ' . $boardurl . '/subscriptions.php

    + You can edit the link for paypal directly, by clicking here.
    + For the other gateways (If installed) you can normally find it in your customer panels, usually under the term "Return URL" or "Callback URL".'; + +// View subscription strings. +$txt['paid_name'] = 'Name'; +$txt['paid_status'] = 'Status'; +$txt['paid_cost'] = 'Cost'; +$txt['paid_duration'] = 'Duration'; +$txt['paid_active'] = 'Active'; +$txt['paid_pending'] = 'Pending Payment'; +$txt['paid_finished'] = 'Finished'; +$txt['paid_total'] = 'Total'; +$txt['paid_is_active'] = 'Activated'; +$txt['paid_none_yet'] = 'You haven\'t set up any subscriptions yet.'; +$txt['paid_payments_pending'] = 'Payments Pending'; +$txt['paid_order'] = 'Order'; + +$txt['yes'] = 'Yes'; +$txt['no'] = 'No'; + +// Add/Edit/Delete subscription. +$txt['paid_add_subscription'] = 'Add Subscription'; +$txt['paid_edit_subscription'] = 'Edit Subscription'; +$txt['paid_delete_subscription'] = 'Delete Subscription'; + +$txt['paid_mod_name'] = 'Subscription Name'; +$txt['paid_mod_desc'] = 'Description'; +$txt['paid_mod_reminder'] = 'Send Reminder Email'; +$txt['paid_mod_reminder_desc'] = 'Days before subscription is due to expire to send reminder. (In days, 0 to disable)'; +$txt['paid_mod_email'] = 'Email to Send upon Completion'; +$txt['paid_mod_email_desc'] = 'Where {NAME} is members name; {FORUM} is community name. Email subject should be on first line. Blank for no email notification.'; +$txt['paid_mod_cost_usd'] = 'Cost (USD)'; +$txt['paid_mod_cost_eur'] = 'Cost (EUR)'; +$txt['paid_mod_cost_gbp'] = 'Cost (GBP)'; +$txt['paid_mod_cost_blank'] = 'Leave this blank to not offer this currency.'; +$txt['paid_mod_span'] = 'Length of Subscription'; +$txt['paid_mod_span_days'] = 'Days'; +$txt['paid_mod_span_weeks'] = 'Weeks'; +$txt['paid_mod_span_months'] = 'Months'; +$txt['paid_mod_span_years'] = 'Years'; +$txt['paid_mod_active'] = 'Active'; +$txt['paid_mod_active_desc'] = 'A subscription must be active for new members to join.'; +$txt['paid_mod_prim_group'] = 'Primary Group upon Subscription'; +$txt['paid_mod_prim_group_desc'] = 'Primary group to put the user into when they subscribe.'; +$txt['paid_mod_add_groups'] = 'Additional Groups upon Subscription'; +$txt['paid_mod_add_groups_desc'] = 'Additional groups to add the user to after subscription.'; +$txt['paid_mod_no_group'] = 'Don\'t Change'; +$txt['paid_mod_edit_note'] = 'Note that as this group has existing subscribers the group settings cannot be changed!'; +$txt['paid_mod_delete_warning'] = 'WARNING

    If you delete this subscription all users currently subscribed will lose any access rights granted by the subscription. Unless you are sure you want to do this it is recommended that you simply deactivate a subscription rather than delete it.
    '; +$txt['paid_mod_repeatable'] = 'Allow user to auto-renew this subscription'; +$txt['paid_mod_allow_partial'] = 'Allow partial subscription'; +$txt['paid_mod_allow_partial_desc'] = 'If this option is enabled, in the case where the user pays less than required they will be granted a subscription for the percentage of the duration they have paid for.'; +$txt['paid_mod_fixed_price'] = 'Subscription for fixed price and period'; +$txt['paid_mod_flexible_price'] = 'Subscription price varies on duration ordered'; +$txt['paid_mod_price_breakdown'] = 'Flexible Price Breakdown'; +$txt['paid_mod_price_breakdown_desc'] = 'Define here how much the subscription should cost dependant on the period they subscribe for. For example, it could cost 12USD to subscribe for a month, but only 100USD for a year. If you don\'t want to define a price for a particular period of time leave it blank.'; +$txt['flexible'] = 'Flexible'; + +$txt['paid_per_day'] = 'Price Per Day'; +$txt['paid_per_week'] = 'Price Per Week'; +$txt['paid_per_month'] = 'Price Per Month'; +$txt['paid_per_year'] = 'Price Per Year'; +$txt['day'] = 'Day'; +$txt['week'] = 'Week'; +$txt['month'] = 'Month'; +$txt['year'] = 'Year'; + +// View subscribed users. +$txt['viewing_users_subscribed'] = 'Viewing Users'; +$txt['view_users_subscribed'] = 'Viewing users subscribed to: "%1$s"'; +$txt['no_subscribers'] = 'There are currently no subscribers to this subscription!'; +$txt['add_subscriber'] = 'Add New Subscriber'; +$txt['edit_subscriber'] = 'Edit Subscriber'; +$txt['delete_selected'] = 'Delete Selected'; +$txt['complete_selected'] = 'Complete Selected'; + +// !!! These strings are used in conjunction with JavaScript. Use numeric entities. +$txt['delete_are_sure'] = 'Are you sure you want to delete all records of the selected subscriptions?'; +$txt['complete_are_sure'] = 'Are you sure you want to complete the selected subscriptions?'; + +$txt['start_date'] = 'Start Date'; +$txt['end_date'] = 'End Date'; +$txt['start_date_and_time'] = 'Start Date and Time'; +$txt['end_date_and_time'] = 'End Date and Time'; +$txt['edit'] = 'EDIT'; +$txt['one_username'] = 'Please enter one username only.'; +$txt['hour'] = 'Hour'; +$txt['minute'] = 'Minute'; +$txt['error_member_not_found'] = 'The member entered could not be found'; +$txt['member_already_subscribed'] = 'This member is already subscribed to this subscription. Please edit their existing subscription.'; +$txt['search_sub'] = 'Find User'; + +// Make payment. +$txt['paid_confirm_payment'] = 'Confirm Payment'; +$txt['paid_confirm_desc'] = 'To continue through to payment please check the details below and hit "Order"'; +$txt['paypal'] = 'PayPal'; +$txt['paid_confirm_paypal'] = 'To pay using PayPal please click the button below. You will be directed to the PayPal site for payment.'; +$txt['paid_paypal_order'] = 'Order with PayPal'; +$txt['worldpay'] = 'WorldPay'; +$txt['paid_confirm_worldpay'] = 'To pay using WorldPay please click the button below. You will be directed to the WorldPay site for payment.'; +$txt['paid_worldpay_order'] = 'Order with WorldPay'; +$txt['nochex'] = 'Nochex'; +$txt['paid_confirm_nochex'] = 'To pay using Nochex please click the button below. You will be directed to the Nochex site for payment.'; +$txt['paid_nochex_order'] = 'Order with Nochex'; +$txt['authorize'] = 'Authorize.Net'; +$txt['paid_confirm_authorize'] = 'To pay using Authorize.Net please click the button below. You will be directed to the Authorize.Net site for payment.'; +$txt['paid_authorize_order'] = 'Order with Authorize.Net'; +$txt['2co'] = '2checkout'; +$txt['paid_confirm_2co'] = 'To pay using 2co.com please click the button below. You will be directed to the 2co.com site for payment.'; +$txt['paid_2co_order'] = 'Order with 2co.com'; +$txt['paid_done'] = 'Payment Complete'; +$txt['paid_done_desc'] = 'Thank you for your payment. Once the transaction has been verified the subscription will be activated.'; +$txt['paid_sub_return'] = 'Return to Subscriptions'; +$txt['paid_current_desc'] = 'Below is a list of all your current and previous subscriptions. To extend an existing subscription simply select it from the list above.'; +$txt['paid_admin_add'] = 'Add This Subscription'; + +$txt['paid_not_set_currency'] = 'You have not setup your currency yet. Please do so from the Settings section before continuing.'; +$txt['paid_no_cost_value'] = 'You must enter a cost and subscription length.'; +$txt['paid_all_freq_blank'] = 'You must enter a cost for at least one of the four durations.'; + +// Some error strings. +$txt['paid_no_data'] = 'No valid data was sent to the script.'; + +$txt['paypal_could_not_connect'] = 'Could not connect to PayPal server'; +$txt['paid_sub_not_active'] = 'That subscription is not taking any new users!'; +$txt['paid_disabled'] = 'Paid subscriptions are currently disabled!'; +$txt['paid_unknown_transaction_type'] = 'Unknown Paid Subscriptions transaction type.'; +$txt['paid_empty_member'] = 'Paid subscription handler could not recover member ID'; +$txt['paid_could_not_find_member'] = 'Paid subscription handler could not find member with ID: %1$d'; +$txt['paid_count_not_find_subscription'] = 'Paid subscription handler could not find subscription for member ID: %1$s, subscription ID: %2$s'; +$txt['paid_count_not_find_subscription_log'] = 'Paid subscription handler could not find subscription log entry for member ID: %1$s, subscription ID: %2$s'; +$txt['paid_count_not_find_outstanding_payment'] = 'Could not find outstanding payment entry for member ID: %1$s, subscription ID: %2$s so ignoring'; +$txt['paid_admin_not_setup_gateway'] = 'Sorry, the admin has not yet finished setting up paid subscriptions. Please check back later.'; +$txt['paid_make_recurring'] = 'Make this a recurring payment'; + +$txt['subscriptions'] = 'Subscriptions'; +$txt['subscription'] = 'Subscription'; +$txt['paid_subs_desc'] = 'Below is a list of all the subscriptions which are available on this forum.'; +$txt['paid_subs_none'] = 'There are currently no paid subscriptions available!'; + +$txt['paid_current'] = 'Existing Subscriptions'; +$txt['pending_payments'] = 'Pending Payments'; +$txt['pending_payments_desc'] = 'This member has attempted to make the following payments for this subscription but the confirmation has not been received by the forum. If you are sure the payment has been received click "accept" to action to subscription. Alternatively you can click "Remove" to remove all reference to the payment.'; +$txt['pending_payments_value'] = 'Value'; +$txt['pending_payments_accept'] = 'Accept'; +$txt['pending_payments_remove'] = 'Remove'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManagePermissions.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManagePermissions.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,345 @@ +Note: You cannot edit this permission profile as it is a predefined profile included within the forum software by default. If you wish to change the permissions of this profile you must first create a duplicate profile. You can carry out this task by clicking here.'; + +$txt['permissions_for_profile'] = 'Permissions for Profile'; +$txt['permissions_boards_desc'] = 'The list below shows which set of permissions has been assigned to each board on your forum. You may edit the assigned permission profile by either clicking the board name or select "edit all" from the bottom of the page. To edit the profile itself simply click the profile name.'; +$txt['permissions_board_all'] = 'Edit All'; +$txt['permission_profile'] = 'Permission Profile'; +$txt['permission_profile_desc'] = 'Which permission set the board should use.'; +$txt['permission_profile_inherit'] = 'Inherit from parent board'; + +$txt['permissions_profile'] = 'Profile'; +$txt['permissions_profiles_desc'] = 'Permission profiles are assigned to individual boards to allow you to easily manage your security settings. From this area you can create, edit and delete permission profiles.'; +$txt['permissions_profiles_change_for_board'] = 'Edit Permission Profile For: "%1$s"'; +$txt['permissions_profile_default'] = 'Default'; +$txt['permissions_profile_no_polls'] = 'No Polls'; +$txt['permissions_profile_reply_only'] = 'Reply Only'; +$txt['permissions_profile_read_only'] = 'Read Only'; + +$txt['permissions_profile_rename'] = 'Rename'; +$txt['permissions_profile_edit'] = 'Edit Profiles'; +$txt['permissions_profile_new'] = 'New Profile'; +$txt['permissions_profile_new_create'] = 'Create'; +$txt['permissions_profile_name'] = 'Profile Name'; +$txt['permissions_profile_used_by'] = 'Used By'; +$txt['permissions_profile_used_by_one'] = '1 Board'; +$txt['permissions_profile_used_by_many'] = '%1$d Boards'; +$txt['permissions_profile_used_by_none'] = 'No Boards'; +$txt['permissions_profile_do_edit'] = 'Edit'; +$txt['permissions_profile_do_delete'] = 'Delete'; +$txt['permissions_profile_copy_from'] = 'Copy Permissions From'; + +$txt['permissions_includes_inherited'] = 'Inherited Groups'; + +$txt['permissions_all'] = 'all'; +$txt['permissions_none'] = 'none'; +$txt['permissions_set_permissions'] = 'Set permissions'; + +$txt['permissions_advanced_options'] = 'Advanced Options'; +$txt['permissions_with_selection'] = 'With selection'; +$txt['permissions_apply_pre_defined'] = 'Apply pre-defined permission set'; +$txt['permissions_select_pre_defined'] = 'Select a pre-defined profile'; +$txt['permissions_copy_from_board'] = 'Copy permissions from this board'; +$txt['permissions_select_board'] = 'Select a board'; +$txt['permissions_like_group'] = 'Set permissions like this group'; +$txt['permissions_select_membergroup'] = 'Select a membergroup'; +$txt['permissions_add'] = 'Add permission'; +$txt['permissions_remove'] = 'Clear permission'; +$txt['permissions_deny'] = 'Deny permission'; +$txt['permissions_select_permission'] = 'Select a permission'; + +// All of the following block of strings should not use entities, instead use \\" for " etc. +$txt['permissions_only_one_option'] = 'You can only select one action to modify the permissions'; +$txt['permissions_no_action'] = 'No action selected'; +$txt['permissions_deny_dangerous'] = 'You are about to deny one or more permissions.\\nThis can be dangerous and cause unexpected results if you haven\'t made sure no one is \\"accidentally\\" in the group or groups you are denying permissions to.\\n\\nAre you sure you want to continue?'; + +$txt['permissions_modify_group'] = 'Modify Group'; +$txt['permissions_general'] = 'General Permissions'; +$txt['permissions_board'] = 'Default Board Profile Permissions'; +$txt['permissions_board_desc'] = 'Note: changing these board permissions will affect all boards currently assigned the "Default" permissions profile. Boards not using the "Default" profile will not be affected by changes to this page.'; +$txt['permissions_commit'] = 'Save changes'; +$txt['permissions_on'] = 'in profile'; +$txt['permissions_local_for'] = 'Permissions for group'; +$txt['permissions_option_on'] = 'A'; +$txt['permissions_option_off'] = 'X'; +$txt['permissions_option_deny'] = 'D'; +$txt['permissions_option_desc'] = 'For each permission you can pick either \'Allow\' (A), \'Disallow\' (X), or \'Deny\' (D).

    Remember that if you deny a permission, any member - whether moderator or otherwise - that is in that group will be denied that as well.
    For this reason, you should use deny carefully, only when necessary. Disallow, on the other hand, denies unless otherwise granted.'; +$txt['permissions_change_view'] = 'Change View'; +$txt['permissions_view_simple'] = 'Simple'; +$txt['permissions_view_classic'] = 'Classic'; + +$txt['permissiongroup_general'] = 'General'; +$txt['permissionname_view_stats'] = 'View forum statistics'; +$txt['permissionhelp_view_stats'] = 'The forum statistics is a page summarizing all statistics of the forum, like member count, daily number of posts, and several top 10 statistics. Enabling this permission adds a link to the bottom of the board index (\'[More Stats]\').'; +$txt['permissionname_view_mlist'] = 'View the memberlist and groups'; +$txt['permissionhelp_view_mlist'] = 'The memberlist shows all members that have registered on your forum. The list can be sorted and searched. The memberlist is linked from both the boardindex and the stats page, by clicking on the number of members. It also applies to the groups page which is a mini memberlist of people in that group.'; +$txt['permissionname_who_view'] = 'View Who\'s Online'; +$txt['permissionhelp_who_view'] = 'Who\'s online shows all members that are currently online and what they are doing at that moment. This permission will only work if you also have enabled it in \'Features and Options\'. You can access the \'Who\'s Online\' screen by clicking the link in the \'Users Online\' section of the board index. Even if this is denied, members will still be able to see who\'s online, just not where they are.'; +$txt['permissionname_search_posts'] = 'Search for posts and topics'; +$txt['permissionhelp_search_posts'] = 'The Search permission allows the user to search all boards he or she is allowed to access. When the search permission is enabled, a \'Search\' button will be added to the forum button bar.'; +$txt['permissionname_karma_edit'] = 'Change other people\'s karma'; +$txt['permissionhelp_karma_edit'] = 'Karma is a feature that shows the popularity of a member. In order to use this feature, you need to have it enabled in \'Features and Options\'. This permission will allow a membergroup to cast a vote. This permission has no effect on guests.'; + +$txt['permissiongroup_pm'] = 'Personal Messaging'; +$txt['permissionname_pm_read'] = 'Read personal messages'; +$txt['permissionhelp_pm_read'] = 'This permission allows users to access the Personal Messages section and read their Personal Messages. Without this permission a user is unable to send Personal Messages.'; +$txt['permissionname_pm_send'] = 'Send personal messages'; +$txt['permissionhelp_pm_send'] = 'Send personal messages to other registered members. Requires the \'Read personal messages\' permission.'; + +$txt['permissiongroup_calendar'] = 'Calendar'; +$txt['permissionname_calendar_view'] = 'View the calendar'; +$txt['permissionhelp_calendar_view'] = 'The calendar shows for each month the birthdays, events and holidays. This permission allows access to this calendar. When this permission is enabled, a button will be added to the top button bar and a list will be shown at the bottom of the board index with current and upcoming birthdays, events and holidays. The calendar needs be enabled from \'Configuration - Core Features\'.'; +$txt['permissionname_calendar_post'] = 'Create events in the calendar'; +$txt['permissionhelp_calendar_post'] = 'An Event is a topic linked to a certain date or date range. Creating events can be done from the calendar. An event can only be created if the user that creates the event is allowed to post new topics.'; +$txt['permissionname_calendar_edit'] = 'Edit events in the calendar'; +$txt['permissionhelp_calendar_edit'] = 'An Event is a topic linked to a certain date or date range. The event can be edited by clicking the red asterisk (*) next to the event in the calendar view. In order to be able to edit an event, a user must have sufficient permissions to edit the first message of the topic that is linked to the event.'; +$txt['permissionname_calendar_edit_own'] = 'Own events'; +$txt['permissionname_calendar_edit_any'] = 'Any events'; + +$txt['permissiongroup_maintenance'] = 'Forum administration'; +$txt['permissionname_admin_forum'] = 'Administrate forum and database'; +$txt['permissionhelp_admin_forum'] = 'This permission allows a user to:
    • change forum, database and theme settings
    • manage packages
    • use the forum and database maintenance tools
    • view the error and mod logs
    Use this permission with caution, as it is very powerful.'; +$txt['permissionname_manage_boards'] = 'Manage boards and categories'; +$txt['permissionhelp_manage_boards'] = 'This permission allows creation, editing and removal of boards and categories.'; +$txt['permissionname_manage_attachments'] = 'Manage attachments and avatars'; +$txt['permissionhelp_manage_attachments'] = 'This permission allows access to the attachment center, where all forum attachments and avatars are listed and can be removed.'; +$txt['permissionname_manage_smileys'] = 'Manage smileys and message icons'; +$txt['permissionhelp_manage_smileys'] = 'This allows access to the smiley center. In the smiley center you can add, edit and remove smileys and smiley sets. If you\'ve enabled customized message icons you are also able to add and edit message icons with this permission.'; +$txt['permissionname_edit_news'] = 'Edit news'; +$txt['permissionhelp_edit_news'] = 'The news function allows a random news line to appear on each screen. In order to use the news function, enabled it in the forum settings.'; +$txt['permissionname_access_mod_center'] = 'Access the moderation center'; +$txt['permissionhelp_access_mod_center'] = 'With this permission any members of this group can access the moderation center from where they will have access to functionality to ease moderation. Note that this does not in itself grant any moderation privileges.'; + +$txt['permissiongroup_member_admin'] = 'Member administration'; +$txt['permissionname_moderate_forum'] = 'Moderate forum members'; +$txt['permissionhelp_moderate_forum'] = 'This permission includes all important member moderation functions:
    • access to registration management
    • access to the view/delete members screen
    • extensive profile info, including track IP/user and (hidden) online status
    • activate accounts
    • get approval notifications and approve accounts
    • immune to ignore PM
    • several small things
    '; +$txt['permissionname_manage_membergroups'] = 'Manage and assign membergroups'; +$txt['permissionhelp_manage_membergroups'] = 'This permission allows a user to edit membergroups and assign membergroups to other members.'; +$txt['permissionname_manage_permissions'] = 'Manage permissions'; +$txt['permissionhelp_manage_permissions'] = 'This permission allows a user to edit all permissions of a membergroup, globally or for individual boards.'; +$txt['permissionname_manage_bans'] = 'Manage ban list'; +$txt['permissionhelp_manage_bans'] = 'This permission allows a user to add or remove usernames, IP addresses, hostnames and email addresses to a list of banned users. It also allows a user to view and remove log entries of banned users that attempted to login.'; +$txt['permissionname_send_mail'] = 'Send a forum email to members'; +$txt['permissionhelp_send_mail'] = 'Mass mail all forum members, or just a few membergroups by email or personal message (the latter requires \'Send Personal Message\' permission).'; +$txt['permissionname_issue_warning'] = 'Issue warnings to members'; +$txt['permissionhelp_issue_warning'] = 'Issue a warning to members of the forum and change that members\' warning level. Requires the warning system to be enabled.'; + +$txt['permissiongroup_profile'] = 'Member Profiles'; +$txt['permissionname_profile_view'] = 'View profile summary and stats'; +$txt['permissionhelp_profile_view'] = 'This permission allows users clicking on a username to see a summary of profile settings, some statistics and all posts of the user.'; +$txt['permissionname_profile_view_own'] = 'Own profile'; +$txt['permissionname_profile_view_any'] = 'Any profile'; +$txt['permissionname_profile_identity'] = 'Edit account settings'; +$txt['permissionhelp_profile_identity'] = 'Account settings are the basic settings of a profile, like password, email address, membergroup and preferred language.'; +$txt['permissionname_profile_identity_own'] = 'Own profile'; +$txt['permissionname_profile_identity_any'] = 'Any profile'; +$txt['permissionname_profile_extra'] = 'Edit additional profile settings'; +$txt['permissionhelp_profile_extra'] = 'Additional profile settings include settings for avatars, theme preferences, notifications and Personal Messages.'; +$txt['permissionname_profile_extra_own'] = 'Own profile'; +$txt['permissionname_profile_extra_any'] = 'Any profile'; +$txt['permissionname_profile_title'] = 'Edit custom title'; +$txt['permissionhelp_profile_title'] = 'The custom title is shown on the topic display page, under the profile of each user that has a custom title.'; +$txt['permissionname_profile_title_own'] = 'Own profile'; +$txt['permissionname_profile_title_any'] = 'Any profile'; +$txt['permissionname_profile_remove'] = 'Delete account'; +$txt['permissionhelp_profile_remove'] = 'This permission allows a user to delete his account, when set to \'Own Account\'.'; +$txt['permissionname_profile_remove_own'] = 'Own account'; +$txt['permissionname_profile_remove_any'] = 'Any account'; +$txt['permissionname_profile_server_avatar'] = 'Select an avatar from the server'; +$txt['permissionhelp_profile_server_avatar'] = 'If enabled this will allow a user to select an avatar from the avatar collections installed on the server.'; +$txt['permissionname_profile_upload_avatar'] = 'Upload an avatar to the server'; +$txt['permissionhelp_profile_upload_avatar'] = 'This permission will allow a user to upload their personal avatar to the server.'; +$txt['permissionname_profile_remote_avatar'] = 'Choose a remotely stored avatar'; +$txt['permissionhelp_profile_remote_avatar'] = 'Because avatars might influence the page creation time negatively, it is possible to disallow certain membergroups to use avatars from external servers.'; + +$txt['permissiongroup_general_board'] = 'General'; +$txt['permissionname_moderate_board'] = 'Moderate board'; +$txt['permissionhelp_moderate_board'] = 'The moderate board permission adds a few small permissions that make a moderator a real moderator. Permissions include replying to locked topics, changing the poll expire time and viewing poll results.'; + +$txt['permissiongroup_topic'] = 'Topics'; +$txt['permissionname_post_new'] = 'Post new topics'; +$txt['permissionhelp_post_new'] = 'This permission allows users to post new topics. It doesn\'t allow to post replies to topics.'; +$txt['permissionname_merge_any'] = 'Merge any topic'; +$txt['permissionhelp_merge_any'] = 'Merge two or more topic into one. The order of messages within the merged topic will be based on the time the messages were created. A user can only merge topics on those boards a user is allowed to merge. In order to merge multiple topics at once, a user has to enable quickmoderation in their profile settings.'; +$txt['permissionname_split_any'] = 'Split any topic'; +$txt['permissionhelp_split_any'] = 'Split a topic into two separate topics.'; +$txt['permissionname_send_topic'] = 'Send topics to friends'; +$txt['permissionhelp_send_topic'] = 'This permission allows a user to mail a topic to a friend, by entering their email address and allows adding a message.'; +$txt['permissionname_make_sticky'] = 'Make topics sticky'; +$txt['permissionhelp_make_sticky'] = 'Sticky topics are topics that always remain on top of a board. They can be useful for announcements or other important messages.'; +$txt['permissionname_move'] = 'Move topic'; +$txt['permissionhelp_move'] = 'Move a topic from one board to the other. Users can only select target boards they are allowed to access.'; +$txt['permissionname_move_own'] = 'Own topic'; +$txt['permissionname_move_any'] = 'Any topic'; +$txt['permissionname_lock'] = 'Lock topics'; +$txt['permissionhelp_lock'] = 'This permission allows a user to lock a topic. This can be done in order to make sure no one can reply to a topic. Only uses with a \'Moderate board\' permission can still post in locked topics.'; +$txt['permissionname_lock_own'] = 'Own topic'; +$txt['permissionname_lock_any'] = 'Any topic'; +$txt['permissionname_remove'] = 'Remove topics'; +$txt['permissionhelp_remove'] = 'Delete topics as a whole. Note that this permission doesn\'t allow to delete specific messages within the topic!'; +$txt['permissionname_remove_own'] = 'Own topic'; +$txt['permissionname_remove_any'] = 'Any topics'; +$txt['permissionname_post_reply'] = 'Post replies to topics'; +$txt['permissionhelp_post_reply'] = 'This permission allows replying to topics.'; +$txt['permissionname_post_reply_own'] = 'Own topic'; +$txt['permissionname_post_reply_any'] = 'Any topic'; +$txt['permissionname_modify_replies'] = 'Modify replies to own topics'; +$txt['permissionhelp_modify_replies'] = 'This permission allows a user that started a topic to modify all replies to their topic.'; +$txt['permissionname_delete_replies'] = 'Delete replies to own topics'; +$txt['permissionhelp_delete_replies'] = 'This permission allows a user that started a topic to remove all replies to their topic.'; +$txt['permissionname_announce_topic'] = 'Announce topic'; +$txt['permissionhelp_announce_topic'] = 'This allows a user to send an announcement e-mail about a topic to all members or to a few membergroups.'; + +$txt['permissiongroup_post'] = 'Posts'; +$txt['permissionname_delete'] = 'Delete posts'; +$txt['permissionhelp_delete'] = 'Remove posts. This does not allow a user to delete the first post of a topic.'; +$txt['permissionname_delete_own'] = 'Own post'; +$txt['permissionname_delete_any'] = 'Any post'; +$txt['permissionname_modify'] = 'Modify posts'; +$txt['permissionhelp_modify'] = 'Edit posts'; +$txt['permissionname_modify_own'] = 'Own post'; +$txt['permissionname_modify_any'] = 'Any post'; +$txt['permissionname_report_any'] = 'Report posts to the moderators'; +$txt['permissionhelp_report_any'] = 'This permission adds a link to each message, allowing a user to report a post to a moderator. On reporting, all moderators on that board will receive an email with a link to the reported post and a description of the problem (as given by the reporting user).'; + +$txt['permissiongroup_poll'] = 'Polls'; +$txt['permissionname_poll_view'] = 'View polls'; +$txt['permissionhelp_poll_view'] = 'This permission allows a user to view a poll. Without this permission, the user will only see the topic.'; +$txt['permissionname_poll_vote'] = 'Vote in polls'; +$txt['permissionhelp_poll_vote'] = 'This permission allows a (registered) user to cast one vote. It doesn\'t apply to guests.'; +$txt['permissionname_poll_post'] = 'Post polls'; +$txt['permissionhelp_poll_post'] = 'This permission allows a user to post a new poll. The user needs to have the \'Post new topics\' permission.'; +$txt['permissionname_poll_add'] = 'Add poll to topics'; +$txt['permissionhelp_poll_add'] = 'Add poll to topics allows a user to add a poll after the topic has been created. This permission requires sufficient rights to edit the first post of a topic.'; +$txt['permissionname_poll_add_own'] = 'Own topics'; +$txt['permissionname_poll_add_any'] = 'Any topics'; +$txt['permissionname_poll_edit'] = 'Edit polls'; +$txt['permissionhelp_poll_edit'] = 'This permission allows a user to edit the options of a poll and to reset the poll. In order to edit the maximum number of votes and the expiration time, a user needs to have the \'Moderate board\' permission.'; +$txt['permissionname_poll_edit_own'] = 'Own poll'; +$txt['permissionname_poll_edit_any'] = 'Any poll'; +$txt['permissionname_poll_lock'] = 'Lock polls'; +$txt['permissionhelp_poll_lock'] = 'Locking polls prevents the poll from accepting any more votes.'; +$txt['permissionname_poll_lock_own'] = 'Own poll'; +$txt['permissionname_poll_lock_any'] = 'Any poll'; +$txt['permissionname_poll_remove'] = 'Remove polls'; +$txt['permissionhelp_poll_remove'] = 'This permission allows removal of polls.'; +$txt['permissionname_poll_remove_own'] = 'Own poll'; +$txt['permissionname_poll_remove_any'] = 'Any poll'; + +$txt['permissiongroup_approval'] = 'Post Moderation'; +$txt['permissionname_approve_posts'] = 'Approve items awaiting moderation'; +$txt['permissionhelp_approve_posts'] = 'This permission allows a user to approve all unapproved items on a board.'; +$txt['permissionname_post_unapproved_replies'] = 'Post replies to topics, but hide until approved'; +$txt['permissionhelp_post_unapproved_replies'] = 'This permission allows a user to post replies to a topic. The replies will not be shown until approved by a moderator.'; +$txt['permissionname_post_unapproved_replies_own'] = 'Own topic'; +$txt['permissionname_post_unapproved_replies_any'] = 'Any topic'; +$txt['permissionname_post_unapproved_topics'] = 'Post new topics, but hide until approved'; +$txt['permissionhelp_post_unapproved_topics'] = 'This permission allows a user to post a new topic which will require approval before being shown.'; +$txt['permissionname_post_unapproved_attachments'] = 'Post attachments, but hide until approved'; +$txt['permissionhelp_post_unapproved_attachments'] = 'This permission allows a user to attach files to their posts. The attached files will then require approval before being shown to other users.'; + +$txt['permissiongroup_notification'] = 'Notifications'; +$txt['permissionname_mark_any_notify'] = 'Request notification on replies'; +$txt['permissionhelp_mark_any_notify'] = 'This feature allows users to receive a notification whenever someone replies to a topic they subscribed to.'; +$txt['permissionname_mark_notify'] = 'Request notification on new topics'; +$txt['permissionhelp_mark_notify'] = 'Notification on new topics is a feature that allows a user to receive an email every time a new topic is created on the board they subscribe to.'; + +$txt['permissiongroup_attachment'] = 'Attachments'; +$txt['permissionname_view_attachments'] = 'View attachments'; +$txt['permissionhelp_view_attachments'] = 'Attachments are files that are attached to posted messages. This feature can be enabled and configured in \'Attachments and avatars\'. Since attachments are not directly accessed, you can protect them from being downloaded by users that don\'t have this permission.'; +$txt['permissionname_post_attachment'] = 'Post attachments'; +$txt['permissionhelp_post_attachment'] = 'Attachments are files that are attached to posted messages. One message can contain multiple attachments.'; + +$txt['permissiongroup_simple_view_basic_info'] = 'Use basic forum functionality'; +$txt['permissiongroup_simple_use_pm_system'] = 'Contact members using the personal messaging system'; +$txt['permissiongroup_simple_post_calendar'] = 'Post events onto the calendar'; +$txt['permissiongroup_simple_edit_profile'] = 'Personalize their profile'; +$txt['permissiongroup_simple_delete_account'] = 'Delete their account'; +$txt['permissiongroup_simple_use_avatar'] = 'Select or upload an avatar'; +$txt['permissiongroup_simple_moderate_general'] = 'Moderate the entire forum'; +$txt['permissiongroup_simple_administrate'] = 'Carry out administrative duties'; + +$txt['permissionname_simple_calendar_edit_own'] = 'Edit their own calendar events'; +$txt['permissionname_simple_calendar_edit_any'] = 'Edit other people\'s calendar events'; +$txt['permissionname_simple_profile_view_own'] = 'View their own profile'; +$txt['permissionname_simple_profile_view_any'] = 'View other people\'s profiles'; +$txt['permissionname_simple_profile_identity_own'] = 'Edit their account settings'; +$txt['permissionname_simple_profile_identity_any'] = 'Edit other people\'s account settings'; +$txt['permissionname_simple_profile_extra_own'] = 'Edit their additional profile options'; +$txt['permissionname_simple_profile_extra_any'] = 'Edit other people\'s profile options'; +$txt['permissionname_simple_profile_title_own'] = 'Choose a custom title for themselves'; +$txt['permissionname_simple_profile_title_any'] = 'Edit other people\'s custom titles'; +$txt['permissionname_simple_profile_remove_own'] = 'Delete their own account'; +$txt['permissionname_simple_profile_remove_any'] = 'Delete other user\'s accounts'; + +$txt['permissiongroup_simple_make_unapproved_posts'] = 'Post topics and replies to the board only after they have been approved'; +$txt['permissiongroup_simple_make_posts'] = 'Post topics and replies to the board'; +$txt['permissiongroup_simple_post_polls'] = 'Make new polls'; +$txt['permissiongroup_simple_participate'] = 'View additional board content'; +$txt['permissiongroup_simple_modify'] = 'Modify their posts'; +$txt['permissiongroup_simple_notification'] = 'Request notifications'; +$txt['permissiongroup_simple_attach'] = 'Post attachments'; +$txt['permissiongroup_simple_moderate'] = 'Moderate the board'; + +$txt['permissionname_simple_post_unapproved_replies_own'] = 'Post replies to their own topic - but require approval'; +$txt['permissionname_simple_post_unapproved_replies_any'] = 'Post replies to any topic - but require approval'; +$txt['permissionname_simple_post_reply_own'] = 'Post replies to a topic they started'; +$txt['permissionname_simple_post_reply_any'] = 'Post replies to any topic'; +$txt['permissionname_simple_move_own'] = 'Move their own topics'; +$txt['permissionname_simple_move_any'] = 'Move anyone\'s topic'; +$txt['permissionname_simple_lock_own'] = 'Lock their own topic'; +$txt['permissionname_simple_lock_any'] = 'Lock anyone\'s topic'; +$txt['permissionname_simple_remove_own'] = 'Remove their own topic'; +$txt['permissionname_simple_remove_any'] = 'Remove anyone\'s topic'; +$txt['permissionname_simple_delete_own'] = 'Delete a post that they made'; +$txt['permissionname_simple_delete_any'] = 'Delete a post made by anyone'; +$txt['permissionname_simple_modify_own'] = 'Modify their own post'; +$txt['permissionname_simple_modify_any'] = 'Modify someone else\'s post'; +$txt['permissionname_simple_poll_add_own'] = 'Add a poll to a topic they created'; +$txt['permissionname_simple_poll_add_any'] = 'Add a poll to any topic'; +$txt['permissionname_simple_poll_edit_own'] = 'Edit a poll they created'; +$txt['permissionname_simple_poll_edit_any'] = 'Edit anyone\'s poll'; +$txt['permissionname_simple_poll_lock_own'] = 'Lock their own poll'; +$txt['permissionname_simple_poll_lock_any'] = 'Lock anyone\'s poll'; +$txt['permissionname_simple_poll_remove_own'] = 'Remove a poll they created'; +$txt['permissionname_simple_poll_remove_any'] = 'Remove anyone\'s poll'; + +$txt['permissionicon'] = ''; + +$txt['permission_settings_title'] = 'Permission Settings'; +$txt['groups_manage_permissions'] = 'Membergroups allowed to manage permissions'; +$txt['permission_settings_submit'] = 'Save'; +$txt['permission_settings_enable_deny'] = 'Enable the option to deny permissions'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['permission_disable_deny_warning'] = 'Turning off this option will update \\\'Deny\\\'-permissions to \\\'Disallow\\\'.'; +$txt['permission_by_board_desc'] = 'Here you can set which permissions profile a board uses. You can create new permission profiles from the "Edit Profiles" menu.'; +$txt['permission_settings_desc'] = 'Here you can set who has permission to change permissions, as well as how sophisticated the permission system should be.'; +$txt['permission_settings_enable_postgroups'] = 'Enable permissions for post count based groups'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['permission_disable_postgroups_warning'] = 'Disabling this setting will remove permissions currently set to post count based groups.'; + +$txt['permissions_post_moderation_desc'] = 'From this page you can easily change which groups have their posts moderated for a particular permissions profile.'; +$txt['permissions_post_moderation_deny_note'] = 'Note that while you have advanced permissions enabled you cannot apply the "deny" permission from this page. Please edit the permissions directly if you wish to apply a deny permission.'; +$txt['permissions_post_moderation_select'] = 'Select Profile'; +$txt['permissions_post_moderation_new_topics'] = 'New Topics'; +$txt['permissions_post_moderation_replies_own'] = 'Own Replies'; +$txt['permissions_post_moderation_replies_any'] = 'Any Replies'; +$txt['permissions_post_moderation_attachments'] = 'Attachments'; +$txt['permissions_post_moderation_legend'] = 'Legend'; +$txt['permissions_post_moderation_allow'] = 'Can create'; +$txt['permissions_post_moderation_moderate'] = 'Can create but requires approval'; +$txt['permissions_post_moderation_disallow'] = 'Cannot create'; +$txt['permissions_post_moderation_group'] = 'Group'; + +$txt['auto_approve_topics'] = 'Post new topics, without requiring approval'; +$txt['auto_approve_replies'] = 'Post replies to topics, without requiring approval'; +$txt['auto_approve_attachments'] = 'Post attachments, without requiring approval'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManageScheduledTasks.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManageScheduledTasks.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,57 @@ +Note: All times given below are server time and do not take any time offsets setup within SMF into account.'; +$txt['scheduled_tasks_were_run'] = 'All selected tasks were completed'; + +$txt['scheduled_tasks_na'] = 'N/A'; +$txt['scheduled_task_approval_notification'] = 'Approval Notifications'; +$txt['scheduled_task_desc_approval_notification'] = 'Send out emails to all moderators summarizing posts awaiting approval.'; +$txt['scheduled_task_auto_optimize'] = 'Optimize Database'; +$txt['scheduled_task_desc_auto_optimize'] = 'Optimize the database to resolve fragmentation issues.'; +$txt['scheduled_task_daily_maintenance'] = 'Daily Maintenance'; +$txt['scheduled_task_desc_daily_maintenance'] = 'Runs essential daily maintenance on the forum - should not be disabled.'; +$txt['scheduled_task_daily_digest'] = 'Daily Notification Summary'; +$txt['scheduled_task_desc_daily_digest'] = 'Emails out the daily digest for notification subscribers.'; +$txt['scheduled_task_weekly_digest'] = 'Weekly Notification Summary'; +$txt['scheduled_task_desc_weekly_digest'] = 'Emails out the weekly digest for notification subscribers.'; +$txt['scheduled_task_fetchSMfiles'] = 'Fetch Simple Machines Files'; +$txt['scheduled_task_desc_fetchSMfiles'] = 'Retrieves javascript files containing notifications of updates and other information.'; +$txt['scheduled_task_birthdayemails'] = 'Send Birthday Emails'; +$txt['scheduled_task_desc_birthdayemails'] = 'Sends out emails wishing members a happy birthday.'; +$txt['scheduled_task_weekly_maintenance'] = 'Weekly Maintenance'; +$txt['scheduled_task_desc_weekly_maintenance'] = 'Runs essential weekly maintenance on the forum - should not be disabled.'; +$txt['scheduled_task_paid_subscriptions'] = 'Paid Subscription Checks'; +$txt['scheduled_task_desc_paid_subscriptions'] = 'Sends out any necessary paid subscription reminders and removes expired member subscriptions.'; + +$txt['scheduled_task_reg_starting'] = 'Starting at %1$s'; +$txt['scheduled_task_reg_repeating'] = 'repeating every %1$d %2$s'; +$txt['scheduled_task_reg_unit_m'] = 'minute(s)'; +$txt['scheduled_task_reg_unit_h'] = 'hour(s)'; +$txt['scheduled_task_reg_unit_d'] = 'day(s)'; +$txt['scheduled_task_reg_unit_w'] = 'week(s)'; + +$txt['scheduled_task_edit'] = 'Edit Scheduled Task'; +$txt['scheduled_task_edit_repeat'] = 'Repeat task every'; +$txt['scheduled_task_edit_pick_unit'] = 'Pick Unit'; +$txt['scheduled_task_edit_interval'] = 'Interval'; +$txt['scheduled_task_edit_start_time'] = 'Start Time'; +$txt['scheduled_task_edit_start_time_desc'] = 'Time the first instance of the day should start (hours:minutes)'; +$txt['scheduled_task_time_offset'] = 'Note the start time should be the offset against the current server time. Current server time is: %1$s'; + +$txt['scheduled_view_log'] = 'View Log'; +$txt['scheduled_log_empty'] = 'There are currently no task log entries.'; +$txt['scheduled_log_time_run'] = 'Time Run'; +$txt['scheduled_log_time_taken'] = 'Time taken'; +$txt['scheduled_log_time_taken_seconds'] = '%1$d seconds'; +$txt['scheduled_log_empty_log'] = 'Empty Log'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManageSettings.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManageSettings.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,359 @@ +theme settings for more options. Click the help icons for more information about a setting.'; +$txt['security_settings_desc'] = 'This page allows you to set options specifically related to the security and moderation of your forum, including anti-spam options.'; +$txt['modification_settings_desc'] = 'This page contains settings added by any modifications to your forum'; + +$txt['modification_no_misc_settings'] = 'There are no modifications installed that have added any settings to this area yet.'; + +$txt['pollMode'] = 'Poll mode'; +$txt['disable_polls'] = 'Disable polls'; +$txt['enable_polls'] = 'Enable polls'; +$txt['polls_as_topics'] = 'Show existing polls as topics'; +$txt['allow_guestAccess'] = 'Allow guests to browse the forum'; +$txt['userLanguage'] = 'Enable user-selectable language support'; +$txt['allow_editDisplayName'] = 'Allow users to edit their displayed name'; +$txt['allow_hideOnline'] = 'Allow non-administrators to hide their online status'; +$txt['guest_hideContacts'] = 'Do not reveal contact details of members to guests'; +$txt['titlesEnable'] = 'Enable custom titles'; +$txt['enable_buddylist'] = 'Enable buddy/ignore lists'; +$txt['default_personal_text'] = 'Default personal text
    Personal text to assign to newly registered members.
    '; +$txt['number_format'] = 'Default number format'; +$txt['time_format'] = 'Default time format'; +$txt['setting_time_offset'] = 'Overall time offset
    (added to the member specific option.)
    '; +$txt['setting_default_timezone'] = 'Server timezone'; +$txt['failed_login_threshold'] = 'Failed login threshold'; +$txt['lastActive'] = 'User online time threshold'; +$txt['trackStats'] = 'Track daily statistics'; +$txt['hitStats'] = 'Track daily page views (must have stats enabled)'; +$txt['enableCompressedOutput'] = 'Enable compressed output'; +$txt['disableTemplateEval'] = 'Disable evaluation of templates'; +$txt['databaseSession_enable'] = 'Use database driven sessions'; +$txt['databaseSession_loose'] = 'Allow browsers to go back to cached pages'; +$txt['databaseSession_lifetime'] = 'Seconds before an unused session timeout'; +$txt['enableErrorLogging'] = 'Enable error logging'; +$txt['enableErrorQueryLogging'] = 'Include database query in the error log'; +$txt['pruningOptions'] = 'Enable pruning of log entries'; +$txt['pruneErrorLog'] = 'Remove error log entries older than
    (0 to disable)
    '; +$txt['pruneModLog'] = 'Remove moderation log entries older than
    (0 to disable)
    '; +$txt['pruneBanLog'] = 'Remove ban hit log entries older than
    (0 to disable)
    '; +$txt['pruneReportLog'] = 'Remove report to moderator log entries older than
    (0 to disable)
    '; +$txt['pruneScheduledTaskLog'] = 'Remove scheduled task log entries older than
    (0 to disable)
    '; +$txt['pruneSpiderHitLog'] = 'Remove search engine hit logs older than
    (0 to disable)
    '; +$txt['cookieTime'] = 'Default login cookies length (in minutes)'; +$txt['localCookies'] = 'Enable local storage of cookies
    (SSI won\'t work well with this on.)
    '; +$txt['globalCookies'] = 'Use subdomain independent cookies
    (turn off local cookies first!)
    '; +$txt['secureCookies'] = 'Force cookies to be secure
    (This only applies if you are using HTTPS - don\'t use otherwise!)
    '; +$txt['securityDisable'] = 'Disable administration security'; +$txt['send_validation_onChange'] = 'Require reactivation after email change'; +$txt['approveAccountDeletion'] = 'Require admin approval when member deletes account'; +$txt['autoOptMaxOnline'] = 'Maximum users online when optimizing
    (0 for no max.)
    '; +$txt['autoFixDatabase'] = 'Automatically fix broken tables'; +$txt['allow_disableAnnounce'] = 'Allow users to disable announcements'; +$txt['disallow_sendBody'] = 'Don\'t allow post text in notifications'; +$txt['queryless_urls'] = 'Search engine friendly URLs
    Apache/Lighttpd only!
    '; +$txt['max_image_width'] = 'Max width of posted pictures (0 = disable)'; +$txt['max_image_height'] = 'Max height of posted pictures (0 = disable)'; +$txt['enableReportPM'] = 'Enable reporting of personal messages'; +$txt['max_pm_recipients'] = 'Maximum number of recipients allowed in a personal message
    (0 for no limit, admins are exempt)
    '; +$txt['pm_posts_verification'] = 'Post count under which users must pass verification when sending personal messages
    (0 for no limit, admins are exempt)
    '; +$txt['pm_posts_per_hour'] = 'Number of personal messages a user may send in an hour
    (0 for no limit, moderators are exempt)
    '; +$txt['compactTopicPagesEnable'] = 'Limit number of displayed page links'; +$txt['contiguous_page_display'] = 'Contiguous pages to display'; +$txt['to_display'] = 'to display'; +$txt['todayMod'] = 'Enable shorthand date display'; +$txt['today_disabled'] = 'Disabled'; +$txt['today_only'] = 'Only Today'; +$txt['yesterday_today'] = 'Today & Yesterday'; +$txt['topbottomEnable'] = 'Enable Go Up/Go Down buttons'; +$txt['onlineEnable'] = 'Show online/offline in posts and PMs'; +$txt['enableVBStyleLogin'] = 'Show a quick login on every page'; +$txt['defaultMaxMembers'] = 'Members per page in member list'; +$txt['timeLoadPageEnable'] = 'Display time taken to create every page'; +$txt['disableHostnameLookup'] = 'Disable hostname lookups'; +$txt['who_enabled'] = 'Enable who\'s online list'; +$txt['make_email_viewable'] = 'Allow viewable email addresses'; +$txt['meta_keywords'] = 'Meta keywords associated with forum
    For search engines. Leave blank for default.
    '; + +$txt['karmaMode'] = 'Karma mode'; +$txt['karma_options'] = 'Disable karma|Enable karma total|Enable karma positive/negative'; +$txt['karmaMinPosts'] = 'Set the minimum posts needed to modify karma'; +$txt['karmaWaitTime'] = 'Set wait time in hours'; +$txt['karmaTimeRestrictAdmins'] = 'Restrict administrators to wait time'; +$txt['karmaLabel'] = 'Karma label'; +$txt['karmaApplaudLabel'] = 'Karma applaud label'; +$txt['karmaSmiteLabel'] = 'Karma smite label'; + +$txt['caching_information'] = '
    Important! Read this first before enabling these features.

    + SMF supports caching through the use of accelerators. The currently supported accelerators include:
    +
      +
    • APC
    • +
    • eAccelerator
    • +
    • Turck MMCache
    • +
    • Memcached
    • +
    • Zend Platform/Performance Suite (Not Zend Optimizer)
    • +
    • XCache
    • +
    + Caching will work best if you have PHP compiled with one of the above optimizers, or have memcache + available. If you do not have any optimizer installed SMF will do file based caching.

    + SMF performs caching at a variety of levels. The higher the level of caching enabled the more CPU time will be spent + retrieving cached information. If caching is available on your machine it is recommended that you try caching at level 1 first. +

    + Note that if you use memcached you need to provide the server details in the setting below. This should be entered as a comma separated list + as shown in the example below:
    + "server1,server2,server3:port,server4"

    + Note that if no port is specified SMF will use port 11211. SMF will attempt to perform rough/random load balancing across the servers. +

    + %1$s'; + +$txt['detected_no_caching'] = 'SMF has not been able to detect a compatible accelerator on your server.'; +$txt['detected_APC'] = 'SMF has detected that your server has APC installed.'; +$txt['detected_eAccelerator'] = 'SMF has detected that your server has eAccelerator installed.'; +$txt['detected_MMCache'] = 'SMF has detected that your server has MMCache installed.'; +$txt['detected_Zend'] = 'SMF has detected that your server has Zend installed.'; +$txt['detected_Memcached'] = 'SMF has detected that your server has Memcached installed.'; +$txt['detected_XCache'] = 'SMF has detected that your server has XCache installed.'; + +$txt['cache_enable'] = 'Caching Level'; +$txt['cache_off'] = 'No caching'; +$txt['cache_level1'] = 'Level 1 Caching (Recommended)'; +$txt['cache_level2'] = 'Level 2 Caching'; +$txt['cache_level3'] = 'Level 3 Caching (Not Recommended)'; +$txt['cache_memcached'] = 'Memcache settings'; + +$txt['loadavg_warning'] = 'Please note: the settings below are to be edited with care. Setting any of them too low may render your forum unusable! The current load average is %01.2f'; +$txt['loadavg_enable'] = 'Enable load balancing by load averages'; +$txt['loadavg_auto_opt'] = 'Threshold to disabling automatic database optimization'; +$txt['loadavg_search'] = 'Threshold to disabling search'; +$txt['loadavg_allunread'] = 'Threshold to disabling all unread topics'; +$txt['loadavg_unreadreplies'] = 'Threshold to disabling unread replies'; +$txt['loadavg_show_posts'] = 'Threshold to disabling showing user posts'; +$txt['loadavg_forum'] = 'Threshold to disabling the forum completely'; +$txt['loadavg_disabled_windows'] = 'Load balancing support is not available on Windows.'; +$txt['loadavg_disabled_conf'] = 'Load balancing support is disabled by your host configuration.'; + +$txt['setting_password_strength'] = 'Required strength for user passwords'; +$txt['setting_password_strength_low'] = 'Low - 4 character minimum'; +$txt['setting_password_strength_medium'] = 'Medium - cannot contain username'; +$txt['setting_password_strength_high'] = 'High - mixture of different characters'; + +$txt['antispam_Settings'] = 'Anti-Spam Verification'; +$txt['antispam_Settings_desc'] = 'This section allows you to setup verification checks to ensure the user is a human (and not a bot), and tweak how and where these apply.'; +$txt['setting_reg_verification'] = 'Require verification on registration page'; +$txt['posts_require_captcha'] = 'Post count under which users must pass verification to make a post'; +$txt['posts_require_captcha_desc'] = '(0 for no limit, moderators are exempt)'; +$txt['search_enable_captcha'] = 'Require verification on all guest searches'; +$txt['setting_guests_require_captcha'] = 'Guests must pass verification when making a post'; +$txt['setting_guests_require_captcha_desc'] = '(Automatically set if you specify a minimum post count below)'; +$txt['guests_report_require_captcha'] = 'Guests must pass verification when reporting a post'; + +$txt['configure_verification_means'] = 'Configure Verification Methods'; +$txt['setting_qa_verification_number'] = 'Number of verification questions user must answer'; +$txt['setting_qa_verification_number_desc'] = '(0 to disable; questions are set below)'; +$txt['configure_verification_means_desc'] = 'Below you can set which anti-spam features you wish to have enabled whenever a user needs to verify they are a human. Note that the user will have to pass all verification so if you enable both a verification image and a question/answer test they need to complete both to proceed.'; +$txt['setting_visual_verification_type'] = 'Visual verification image to display'; +$txt['setting_visual_verification_type_desc'] = 'The more complex the image the harder it is for bots to bypass'; +$txt['setting_image_verification_off'] = 'None'; +$txt['setting_image_verification_vsimple'] = 'Very Simple - Plain text on image'; +$txt['setting_image_verification_simple'] = 'Simple - Overlapping colored letters, no noise'; +$txt['setting_image_verification_medium'] = 'Medium - Overlapping colored letters, with noise/lines'; +$txt['setting_image_verification_high'] = 'High - Angled letters, considerable noise/lines'; +$txt['setting_image_verification_extreme'] = 'Extreme - Angled letters, noise, lines and blocks'; +$txt['setting_image_verification_sample'] = 'Sample'; +$txt['setting_image_verification_nogd'] = 'Note: as this server does not have the GD library installed the different complexity settings will have no effect.'; +$txt['setup_verification_questions'] = 'Verification Questions'; +$txt['setup_verification_questions_desc'] = 'If you want users to answer verification questions in order to stop spam bots you should setup a number of questions in the table below. You should pick relatively simple questions; answers are not case sensitive. You may use BBC in the questions for formatting, to remove a question simply delete the contents of that line.'; +$txt['setup_verification_question'] = 'Question'; +$txt['setup_verification_answer'] = 'Answer'; +$txt['setup_verification_add_more'] = 'Add another question'; + +$txt['moderation_settings'] = 'Moderation Settings'; +$txt['setting_warning_enable'] = 'Enable User Warning System'; +$txt['setting_warning_watch'] = 'Warning level for user watch
    The user warning level after which a user watch is put in place - 0 to disable.
    '; +$txt['setting_warning_moderate'] = 'Warning level for post moderation
    The user warning level after which a user has all posts moderated - 0 to disable.
    '; +$txt['setting_warning_mute'] = 'Warning level for user muting
    The user warning level after which a user cannot post any further - 0 to disable.
    '; +$txt['setting_user_limit'] = 'Maximum user warning points per day
    This value is the maximum amount of warning points a single moderator can assign to a user in a 24 hour period - 0 for no limit.
    '; +$txt['setting_warning_decrement'] = 'Warning points to decrement from users every 24 hours
    Only applies to users not warned within last 24 hours - set to 0 to disable.
    '; +$txt['setting_warning_show'] = 'Users who can see warning status
    Determines who can see the warning level of users on the forum.
    '; +$txt['setting_warning_show_mods'] = 'Moderators Only'; +$txt['setting_warning_show_user'] = 'Moderators and Warned Users'; +$txt['setting_warning_show_all'] = 'All Users'; + +$txt['signature_settings'] = 'Signature Settings'; +$txt['signature_settings_desc'] = 'Use the settings on this page to decide how member signatures should be treated in SMF.'; +$txt['signature_settings_warning'] = 'Note that settings are not applied to existing signatures by default. Click here to apply rules to all existing signatures.'; +$txt['signature_enable'] = 'Enable signatures'; +$txt['signature_max_length'] = 'Maximum allowed characters
    (0 for no max.)
    '; +$txt['signature_max_lines'] = 'Maximum amount of lines
    (0 for no max)
    '; +$txt['signature_max_images'] = 'Maximum image count
    (0 for no max - excludes smileys)
    '; +$txt['signature_allow_smileys'] = 'Allow smileys in signatures'; +$txt['signature_max_smileys'] = 'Maximum smiley count
    (0 for no max)
    '; +$txt['signature_max_image_width'] = 'Maximum width of signature images (pixels)
    (0 for no max)
    '; +$txt['signature_max_image_height'] = 'Maximum height of signature images (pixels)
    (0 for no max)
    '; +$txt['signature_max_font_size'] = 'Maximum font size allowed in signatures
    (0 for no max, in pixels)
    '; +$txt['signature_bbc'] = 'Enabled BBC tags'; + +$txt['custom_profile_title'] = 'Custom Profile Fields'; +$txt['custom_profile_desc'] = 'From this page you can create your own custom profile fields that fit in with your own forums requirements'; +$txt['custom_profile_active'] = 'Active'; +$txt['custom_profile_fieldname'] = 'Field Name'; +$txt['custom_profile_fieldtype'] = 'Field Type'; +$txt['custom_profile_make_new'] = 'New Field'; +$txt['custom_profile_none'] = 'You have not created any custom profile fields yet!'; +$txt['custom_profile_icon'] = 'Icon'; + +$txt['custom_profile_type_text'] = 'Text'; +$txt['custom_profile_type_textarea'] = 'Large Text'; +$txt['custom_profile_type_select'] = 'Select Box'; +$txt['custom_profile_type_radio'] = 'Radio Button'; +$txt['custom_profile_type_check'] = 'Checkbox'; + +$txt['custom_add_title'] = 'Add Profile Field'; +$txt['custom_edit_title'] = 'Edit Profile Field'; +$txt['custom_edit_general'] = 'Display Settings'; +$txt['custom_edit_input'] = 'Input Settings'; +$txt['custom_edit_advanced'] = 'Advanced Settings'; +$txt['custom_edit_name'] = 'Name'; +$txt['custom_edit_desc'] = 'Description'; +$txt['custom_edit_profile'] = 'Profile Section'; +$txt['custom_edit_profile_desc'] = 'Section of profile this is edited in.'; +$txt['custom_edit_profile_none'] = 'None'; +$txt['custom_edit_registration'] = 'Show on Registration'; +$txt['custom_edit_registration_disable'] = 'No'; +$txt['custom_edit_registration_allow'] = 'Yes'; +$txt['custom_edit_registration_require'] = 'Yes, and require entry'; +$txt['custom_edit_display'] = 'Show on Topic View'; +$txt['custom_edit_picktype'] = 'Field Type'; + +$txt['custom_edit_max_length'] = 'Maximum Length'; +$txt['custom_edit_max_length_desc'] = '(0 for no limit)'; +$txt['custom_edit_dimension'] = 'Dimensions'; +$txt['custom_edit_dimension_row'] = 'Rows'; +$txt['custom_edit_dimension_col'] = 'Columns'; +$txt['custom_edit_bbc'] = 'Allow BBC'; +$txt['custom_edit_options'] = 'Options'; +$txt['custom_edit_options_desc'] = 'Leave option box blank to remove. Radio button selects default option.'; +$txt['custom_edit_options_more'] = 'More'; +$txt['custom_edit_default'] = 'Default State'; +$txt['custom_edit_active'] = 'Active'; +$txt['custom_edit_active_desc'] = 'If not selected this field will not be shown to anyone.'; +$txt['custom_edit_privacy'] = 'Privacy'; +$txt['custom_edit_privacy_desc'] = 'Who can see and edit this field.'; +$txt['custom_edit_privacy_all'] = 'Users can see this field; owner can edit it'; +$txt['custom_edit_privacy_see'] = 'Users can see this field; only admins can edit it'; +$txt['custom_edit_privacy_owner'] = 'Users cannot see this field; owner and admins can edit it.'; +$txt['custom_edit_privacy_none'] = 'This field is only visible to admins'; +$txt['custom_edit_can_search'] = 'Searchable'; +$txt['custom_edit_can_search_desc'] = 'Can this field be searched from the members list.'; +$txt['custom_edit_mask'] = 'Input Mask'; +$txt['custom_edit_mask_desc'] = 'For text fields an input mask can be selected to validate the data.'; +$txt['custom_edit_mask_email'] = 'Valid Email'; +$txt['custom_edit_mask_number'] = 'Numeric'; +$txt['custom_edit_mask_nohtml'] = 'No HTML'; +$txt['custom_edit_mask_regex'] = 'Regex (Advanced)'; +$txt['custom_edit_enclose'] = 'Show Enclosed Within Text (Optional)'; +$txt['custom_edit_enclose_desc'] = 'We strongly recommend to use an input mask to validate the input supplied by the user.'; + +$txt['custom_edit_placement'] = 'Choose Placement'; +$txt['custom_edit_placement_standard'] = 'Standard (with title)'; +$txt['custom_edit_placement_withicons'] = 'With Icons'; +$txt['custom_edit_placement_abovesignature'] = 'Above Signature'; +$txt['custom_profile_placement'] = 'Placement'; +$txt['custom_profile_placement_standard'] = 'Standard'; +$txt['custom_profile_placement_withicons'] = 'With Icons'; +$txt['custom_profile_placement_abovesignature'] = 'Above Signature'; + +// Use numeric entities in the string below! +$txt['custom_edit_delete_sure'] = 'Are you sure you wish to delete this field - all related user data will be lost!'; + +$txt['standard_profile_title'] = 'Standard Profile Fields'; +$txt['standard_profile_field'] = 'Field'; + +$txt['core_settings_welcome_msg'] = 'Welcome to Your New Forum'; +$txt['core_settings_welcome_msg_desc'] = 'To get you started we suggest you select which of SMF\'s core features you want to enable. We\'d recommend only enabling with those features you need!'; +$txt['core_settings_item_cd'] = 'Calendar'; +$txt['core_settings_item_cd_desc'] = 'Enabling this feature will open up a selection of options to enable your users to view the calendar, add and review events, see users birthdates on a calendar and much, much more.'; +$txt['core_settings_item_cp'] = 'Advanced Profile Fields'; +$txt['core_settings_item_cp_desc'] = 'This enables you to hide standard profile fields, add profile fields to registration, and create new profile fields for your forum.'; +$txt['core_settings_item_k'] = 'Karma'; +$txt['core_settings_item_k_desc'] = 'Karma is a feature that shows the popularity of a member. Members, if allowed, can \'applaud\' or \'smite\' other members, which is how their popularity is calculated.'; +$txt['core_settings_item_ml'] = 'Moderation, Administration and User Logs'; +$txt['core_settings_item_ml_desc'] = 'Enable the moderation and administration logs to keep an audit trail of all the key actions taken on your forum. Also allows forum moderators to view a history of key changes a user makes to their profile.'; +$txt['core_settings_item_pm'] = 'Post Moderation'; +$txt['core_settings_item_pm_desc'] = 'Post moderation enables you to select groups and boards within which posts must be approved before they become public. Upon enabling this feature be sure to visit the permission section to set up the relevant permissions.'; +$txt['core_settings_item_ps'] = 'Paid Subscriptions'; +$txt['core_settings_item_ps_desc'] = 'Paid subscriptions allow users to pay for subscriptions to change membergroup within the forum and thus change their access rights.'; +$txt['core_settings_item_rg'] = 'Report Generation'; +$txt['core_settings_item_rg_desc'] = 'This administration feature allows the generation of reports (Which can be printed) to present your current forum setup in an easy to view manner - particularly useful for large forums.'; +$txt['core_settings_item_sp'] = 'Search Engine Tracking'; +$txt['core_settings_item_sp_desc'] = 'Enabling this feature will allow administrators to track search engines as they index your forum.'; +$txt['core_settings_item_w'] = 'Warning System'; +$txt['core_settings_item_w_desc'] = 'This functionality allows administrators and moderators to issue warnings to users; it also includes advanced functionality for automatically removing user rights as their warning level increases. Note to take full advantage of this function "Post Moderation" should be enabled.'; +$txt['core_settings_switch_on'] = 'Click to Enable'; +$txt['core_settings_switch_off'] = 'Click to Disable'; +$txt['core_settings_enabled'] = 'Enabled'; +$txt['core_settings_disabled'] = 'Disabled'; + +$txt['languages_lang_name'] = 'Language Name'; +$txt['languages_locale'] = 'Locale'; +$txt['languages_default'] = 'Default'; +$txt['languages_character_set'] = 'Character Set'; +$txt['languages_users'] = 'Users'; +$txt['language_settings_writable'] = 'Warning: Settings.php is not writable so the default language setting cannot be saved.'; +$txt['edit_languages'] = 'Edit Languages'; +$txt['lang_file_not_writable'] = 'Warning: The primary language file (%1$s) is not writable. You must make this writable before you can make any changes.'; +$txt['lang_entries_not_writable'] = 'Warning: The language file you wish to edit (%1$s) is not writable. You must make this writable before you can make any changes.'; +$txt['languages_ltr'] = 'Right to Left'; + +$txt['add_language'] = 'Add Language'; +$txt['add_language_smf'] = 'Download from Simple Machines'; +$txt['add_language_smf_browse'] = 'Type name of language to search for or leave blank to search for all.'; +$txt['add_language_smf_install'] = 'Install'; +$txt['add_language_smf_found'] = 'The following languages were found. Click the install link next to the language you wish to install, you will then be taken to the package manager to install.'; +$txt['add_language_error_no_response'] = 'The Simple Machines site is not responding. Please try again later.'; +$txt['add_language_error_no_files'] = 'No files could be found.'; +$txt['add_language_smf_desc'] = 'Description'; +$txt['add_language_smf_utf8'] = 'UTF-8'; +$txt['add_language_smf_version'] = 'Version'; + +$txt['edit_language_entries_primary'] = 'Below are the primary language settings for this language pack.'; +$txt['edit_language_entries'] = 'Edit Language Entries'; +$txt['edit_language_entries_file'] = 'Select entries to edit'; +$txt['languages_dictionary'] = 'Dictionary'; +$txt['languages_spelling'] = 'Spelling'; +$txt['languages_for_pspell'] = 'This is for pSpell - if installed'; +$txt['languages_rtl'] = 'Enable "Right to Left" Mode'; + +$txt['lang_file_desc_index'] = 'General Strings'; +$txt['lang_file_desc_EmailTemplates'] = 'Email Templates'; + +$txt['languages_download'] = 'Download Language Pack'; +$txt['languages_download_note'] = 'This page lists all the files that are contained within the language pack and some useful information about each one. All files that have their associated check box marked will be copied.'; +$txt['languages_download_info'] = 'Note: +
      +
    • Files which have the status "Not Writable" means SMF will not be able to copy this file to the directory at the present and you must make the destination writable either using an FTP client or by filling in your details at the bottom of the page.
    • +
    • The Version information for a file displays the last SMF version which it was updated for. If it is indicated in green then this is a newer version than you have at current. If amber this indicates it\'s the same version number as at current, red indicates you have a newer version installed than contained in the pack.
    • +
    • Where a file already exists on your forum the "Already Exists" column will have one of two values. "Identical" indicates that the file already exists in an identical form and need not be overwritten. "Different" means that the contents vary in some way and overwriting is probably the optimum solution.
    • +
    '; + +$txt['languages_download_main_files'] = 'Primary Files'; +$txt['languages_download_theme_files'] = 'Theme-related Files'; +$txt['languages_download_filename'] = 'File Name'; +$txt['languages_download_dest'] = 'Destination'; +$txt['languages_download_writable'] = 'Writable'; +$txt['languages_download_version'] = 'Version'; +$txt['languages_download_older'] = 'You have a newer version of this file installed, overwriting is not recommended.'; +$txt['languages_download_exists'] = 'Already Exists'; +$txt['languages_download_exists_same'] = 'Identical'; +$txt['languages_download_exists_different'] = 'Different'; +$txt['languages_download_copy'] = 'Copy'; +$txt['languages_download_not_chmod'] = 'You cannot proceed with the installation until all files selected to be copied are writable.'; +$txt['languages_download_illegal_paths'] = 'Package contains illegal paths - please contact Simple Machines'; +$txt['languages_download_complete'] = 'Installation Complete'; +$txt['languages_download_complete_desc'] = 'Language pack installed successfully. Please click here to return to the languages page'; +$txt['languages_delete_confirm'] = 'Are you sure you want to delete this language?'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ManageSmileys.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ManageSmileys.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,95 @@ +Here you can change the name and location of each smiley set - remember, however, that all sets share the same smileys.'; +$txt['smiley_editsmileys_explain'] = 'Change your smileys here by clicking on the smiley you want to modify. Remember that these smileys all have to exist in all the sets or some smileys won\'t show up! Don\'t forget to save after you are done editing!'; +$txt['smiley_setorder_explain'] = 'Change the order of the smileys here.'; +$txt['smiley_addsmiley_explain'] = 'Here you can add a new smiley - either from an existing file or by uploading new ones.'; + +$txt['smiley_set_select_default'] = 'Default Smiley Set'; +$txt['smiley_set_new'] = 'Create new smiley set'; +$txt['smiley_set_modify_existing'] = 'Modify existing smiley set'; +$txt['smiley_set_modify'] = 'Modify'; +$txt['smiley_set_import_directory'] = 'Import smileys already in this directory'; +$txt['smiley_set_import_single'] = 'There is one smiley in this smiley set not yet imported. Click'; +$txt['smiley_set_import_multiple'] = 'There are %1$d smileys in the directory that have not yet been imported. Click'; +$txt['smiley_set_to_import_single'] = 'to import it now.'; +$txt['smiley_set_to_import_multiple'] = 'to import them now.'; + +$txt['smileys_location'] = 'Location'; +$txt['smileys_location_form'] = 'Post form'; +$txt['smileys_location_hidden'] = 'Hidden'; +$txt['smileys_location_popup'] = 'Popup'; +$txt['smileys_modify'] = 'Modify'; +$txt['smileys_not_found_in_set'] = 'Smiley not found in set(s)'; +$txt['smileys_default_description'] = '(Insert a description)'; +$txt['smiley_new'] = 'Add new smiley'; +$txt['smiley_modify_existing'] = 'Modify smiley'; +$txt['smiley_preview'] = 'Preview'; +$txt['smiley_preview_using'] = 'using smiley set'; +$txt['smileys_confirm'] = 'Are you sure you want to remove these smileys?\\n\\nNote: This won\\\'t remove the images, just the choices.'; +$txt['smileys_location_form_description'] = 'These smileys will appear above the text area, when posting a new forum message or Personal Message.'; +$txt['smileys_location_popup_description'] = 'These smileys will be shown in a popup, that is shown after a user has clicked \'[more]\''; +$txt['smileys_move_select_destination'] = 'Select smiley destination'; +$txt['smileys_move_select_smiley'] = 'Select smiley to move'; +$txt['smileys_move_here'] = 'Move smiley to this location'; +$txt['smileys_no_entries'] = 'There are currently no smileys configured.'; + +$txt['icons_edit_icons_explain'] = 'From here you can change which message icons are available throughout your board. You can add, edit and remove icons, as well as limit their use to certain boards.'; +$txt['icons_edit_icons_all_boards'] = 'Available In All Boards'; +$txt['icons_board'] = 'Board'; +$txt['icons_confirm'] = 'Are you sure you wish to remove these icons?\\n\\nNote this will only stop new posters from using the icons, the images will remain.'; +$txt['icons_add_new'] = 'Add New Icon'; + +$txt['icons_edit_icon'] = 'Edit Message Icon'; +$txt['icons_new_icon'] = 'New Message Icon'; +$txt['icons_location_first_icon'] = 'As First Icon'; +$txt['icons_location_after'] = 'After'; +$txt['icons_filename_all_gif'] = 'All files must be "gif" files'; +$txt['icons_no_entries'] = 'There are currently no message icons configured.'; +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Manual.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Manual.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,36 @@ +Simple Machines Documentation Wiki and check out the credits to find out who has made SMF what it is today.'; + +$txt['manual_section_registering_title'] = 'Registering'; +$txt['manual_section_logging_in_title'] = 'Logging In'; +$txt['manual_section_profile_title'] = 'Profile'; +$txt['manual_section_search_title'] = 'Search'; +$txt['manual_section_posting_title'] = 'Posting'; +$txt['manual_section_bbc_title'] = 'Bulletin Board Code (BBC)'; +$txt['manual_section_personal_messages_title'] = 'Personal Messages'; +$txt['manual_section_memberlist_title'] = 'Memberlist'; +$txt['manual_section_calendar_title'] = 'Calendar'; +$txt['manual_section_features_title'] = 'Features'; + +$txt['manual_section_registering_desc'] = 'Many forums require users to register to gain full access.'; +$txt['manual_section_logging_in_desc'] = 'Once registered, users must login to access their account.'; +$txt['manual_section_profile_desc'] = 'Each member has their own personal profile.'; +$txt['manual_section_search_desc'] = 'Searching is an extremely helpful tool for finding information in posts and topics.'; +$txt['manual_section_posting_desc'] = 'The whole point of a forum, posting allows users to express themselves.'; +$txt['manual_section_bbc_desc'] = 'Posts can be spiced up with a little BBC.'; +$txt['manual_section_personal_messages_desc'] = 'Users can send personal messages to each other.'; +$txt['manual_section_memberlist_desc'] = 'The memberlist shows all the members of a forum.'; +$txt['manual_section_calendar_desc'] = 'Users can keep track of events, holidays, and birthdays with the calendar.'; +$txt['manual_section_features_desc'] = 'Here is a list of the most popular features in SMF.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ModSettings.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ModSettings.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,121 @@ +theme settings for more options. Click the help icons for more information about a setting.'; + +$txt['mods_cat_features'] = 'Basic Features'; +$txt['pollMode'] = 'Poll mode'; +$txt['smf34'] = 'Disable polls'; +$txt['smf32'] = 'Enable polls'; +$txt['smf33'] = 'Show existing polls as topics'; +$txt['allow_guestAccess'] = 'Allow guests to browse the forum'; +$txt['userLanguage'] = 'Enable user-selectable language support'; +$txt['allow_editDisplayName'] = 'Allow users to edit their displayed name?'; +$txt['allow_hideOnline'] = 'Allow non-administrators to hide their online status?'; +$txt['allow_hideEmail'] = 'Allow users to hide their email from everyone except admins?'; +$txt['guest_hideContacts'] = 'Do not reveal contact details of members to guests'; +$txt['titlesEnable'] = 'Enable custom titles'; +$txt['enable_buddylist'] = 'Enable buddy lists'; +$txt['default_personalText'] = 'Default personal text'; +$txt['max_signatureLength'] = 'Maximum allowed characters in signatures
    (0 for no max.)
    '; +$txt['number_format'] = 'Default number format'; +$txt['time_format'] = 'Default time format'; +$txt['time_offset'] = 'Overall time offset
    (added to the member specific option.)
    '; +$txt['failed_login_threshold'] = 'Failed login threshold'; +$txt['lastActive'] = 'User online time threshold'; +$txt['trackStats'] = 'Track daily statistics'; +$txt['hitStats'] = 'Track daily page views (must have stats enabled)'; +$txt['enableCompressedOutput'] = 'Enable compressed output'; +$txt['databaseSession_enable'] = 'Use database driven sessions'; +$txt['databaseSession_loose'] = 'Allow browsers to go back to cached pages'; +$txt['databaseSession_lifetime'] = 'Seconds before an unused session timeout'; +$txt['enableErrorLogging'] = 'Enable error logging'; +$txt['cookieTime'] = 'Default login cookies length (in minutes)'; +$txt['localCookies'] = 'Enable local storage of cookies
    (SSI won\'t work well with this on.)
    '; +$txt['globalCookies'] = 'Use subdomain independent cookies
    (turn off local cookies first!)
    '; +$txt['securityDisable'] = 'Disable administration security'; +$txt['send_validation_onChange'] = 'Require reactivation after email change'; +$txt['approveAccountDeletion'] = 'Require admin approval when member deletes account'; +$txt['autoOptDatabase'] = 'Optimize tables every how many days?
    (0 to disable.)
    '; +$txt['autoOptMaxOnline'] = 'Maximum users online when optimizing
    (0 for no max.)
    '; +$txt['autoFixDatabase'] = 'Automatically fix broken tables'; +$txt['allow_disableAnnounce'] = 'Allow users to disable announcements'; +$txt['disallow_sendBody'] = 'Don\'t allow post text in notifications?'; +$txt['modlog_enabled'] = 'Log moderation actions'; +$txt['queryless_urls'] = 'Search engine friendly URLs
    Apache only!
    '; +$txt['max_image_width'] = 'Max width of posted pictures (0 = disable)'; +$txt['max_image_height'] = 'Max height of posted pictures (0 = disable)'; +$txt['mail_type'] = 'Mail type'; +$txt['mail_type_default'] = '(PHP default)'; +$txt['smtp_host'] = 'SMTP server'; +$txt['smtp_port'] = 'SMTP port'; +$txt['smtp_username'] = 'SMTP username'; +$txt['smtp_password'] = 'SMTP password'; +$txt['enableReportPM'] = 'Enable reporting of personal messages'; +$txt['max_pm_recipients'] = 'Maximum number of recipients allowed in a personal message.
    (0 for no limit, admins are exempt)
    '; +$txt['pm_posts_verification'] = 'Post count under which users must enter code when sending personal messages.
    (0 for no limit, admins are exempt)
    '; +$txt['pm_posts_per_hour'] = 'Number of personal messages a user may send in an hour.
    (0 for no limit, moderators are exempt)
    '; + +$txt['mods_cat_layout'] = 'Layout and Options'; +$txt['compactTopicPagesEnable'] = 'Limit number of displayed page links'; +$txt['smf235'] = 'Contiguous pages to display:'; +$txt['smf236'] = 'to display'; +$txt['todayMod'] = 'Enable "Today" feature'; +$txt['smf290'] = 'Disabled'; +$txt['smf291'] = 'Only Today'; +$txt['smf292'] = 'Today & Yesterday'; +$txt['topbottomEnable'] = 'Enable Go Up/Go Down buttons'; +$txt['onlineEnable'] = 'Show online/offline in posts and PMs'; +$txt['enableVBStyleLogin'] = 'Show a quick login on every page'; +$txt['defaultMaxMembers'] = 'Members per page in member list'; +$txt['timeLoadPageEnable'] = 'Display time taken to create every page'; +$txt['disableHostnameLookup'] = 'Disable hostname lookups?'; +$txt['who_enabled'] = 'Enable who\'s online list'; + +$txt['smf293'] = 'Karma'; +$txt['karmaMode'] = 'Karma mode'; +$txt['smf64'] = 'Disable karma|Enable karma total|Enable karma positive/negative'; +$txt['karmaMinPosts'] = 'Set the minimum posts needed to modify karma'; +$txt['karmaWaitTime'] = 'Set wait time in hours'; +$txt['karmaTimeRestrictAdmins'] = 'Restrict administrators to wait time'; +$txt['karmaLabel'] = 'Karma label'; +$txt['karmaApplaudLabel'] = 'Karma applaud label'; +$txt['karmaSmiteLabel'] = 'Karma smite label'; + +$txt['caching_information'] = '
    Important! Read this first before enabling these features.

    + SMF supports caching through the use of accelerators. The currently supported accelerators include:
    +
      +
    • APC
    • +
    • eAccelerator
    • +
    • Turck MMCache
    • +
    • Memcached
    • +
    • Zend Platform/Performance Suite (Not Zend Optimizer)
    • +
    + Caching will only work on your server if you have PHP compiled with one of the above optimizers, or have memcache + available.

    + SMF performs caching at a variety of levels. The higher the level of caching enabled the more CPU time will be spent + retrieving cached information. If caching is available on your machine it is recommended that you try caching at level 1 first. +

    + Note that if you use memcached you need to provide the server details in the setting below. This should be entered as a comma separated list + as shown in the example below:
    + "server1,server2,server3:port,server4"

    + Note that if no port is specified SMF will use port 11211. SMF will attempt to perform rough/random load balancing across the servers. +

    + %s +
    '; + +$txt['detected_no_caching'] = 'SMF has not been able to detect a compatible accelerator on your server.'; +$txt['detected_APC'] = 'SMF has detected that your server has APC installed.'; +$txt['detected_eAccelerator'] = 'SMF has detected that your server has eAccelerator installed.'; +$txt['detected_MMCache'] = 'SMF has detected that your server has MMCache installed.'; +$txt['detected_Zend'] = 'SMF has detected that your server has Zend installed.'; +$txt['detected_Memcached'] = 'SMF has detected that your server has Memcached installed.'; + +$txt['cache_enable'] = 'Caching Level'; +$txt['cache_off'] = 'No caching'; +$txt['cache_level1'] = 'Level 1 Caching'; +$txt['cache_level2'] = 'Level 2 Caching (Not Recommended)'; +$txt['cache_level3'] = 'Level 3 Caching (Not Recommended)'; +$txt['cache_memcached'] = 'Memcache settings'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/ModerationCenter.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/ModerationCenter.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,145 @@ +here.'; +$txt['mc_group_requests'] = 'Membergroup Requests'; +$txt['mc_unapproved_posts'] = 'Unapproved Posts'; +$txt['mc_watched_users'] = 'Recent Watched Members'; +$txt['mc_watched_topics'] = 'Watched Topics'; +$txt['mc_scratch_board'] = 'Moderator Scratch Board'; +$txt['mc_latest_news'] = 'Simple Machines Latest News'; +$txt['mc_recent_reports'] = 'Recent Topic Reports'; +$txt['mc_warnings'] = 'Warnings'; +$txt['mc_notes'] = 'Moderator Notes'; + +$txt['mc_cannot_connect_sm'] = 'You are unable to connect to simplemachines.org\'s latest news file.'; + +$txt['mc_recent_reports_none'] = 'There are no outstanding reports'; +$txt['mc_watched_users_none'] = 'There are not currently any watches in place.'; +$txt['mc_group_requests_none'] = 'There are no open requests for group membership.'; + +$txt['mc_seen'] = '%1$s last seen %2$s'; +$txt['mc_seen_never'] = '%1$s never seen'; +$txt['mc_groupr_by'] = 'by'; + +$txt['mc_reported_posts_desc'] = 'Here you can review all the post reports raised by members of the community.'; +$txt['mc_reportedp_active'] = 'Active Reports'; +$txt['mc_reportedp_closed'] = 'Old Reports'; +$txt['mc_reportedp_by'] = 'by'; +$txt['mc_reportedp_reported_by'] = 'Reported By'; +$txt['mc_reportedp_last_reported'] = 'Last Reported'; +$txt['mc_reportedp_none_found'] = 'No Reports Found'; + +$txt['mc_reportedp_details'] = 'Details'; +$txt['mc_reportedp_close'] = 'Close'; +$txt['mc_reportedp_open'] = 'Open'; +$txt['mc_reportedp_ignore'] = 'Ignore'; +$txt['mc_reportedp_unignore'] = 'Un-Ignore'; +// Do not use numeric entries in the below string. +$txt['mc_reportedp_ignore_confirm'] = 'Are you sure you wish to ignore further reports about this message?\\n\\nThis will turn off further reports for all moderators of the forum.'; +$txt['mc_reportedp_close_selected'] = 'Close Selected'; + +$txt['mc_groupr_group'] = 'Membergroups'; +$txt['mc_groupr_member'] = 'Member'; +$txt['mc_groupr_reason'] = 'Reason'; +$txt['mc_groupr_none_found'] = 'There are currently no outstanding membergroup requests.'; +$txt['mc_groupr_submit'] = 'Submit'; +$txt['mc_groupr_reason_desc'] = 'Reason to reject %1$s\'s request to join "%2$s"'; +$txt['mc_groups_reason_title'] = 'Reasons for Rejection'; +$txt['with_selected'] = 'With Selected'; +$txt['mc_groupr_approve'] = 'Approve Request'; +$txt['mc_groupr_reject'] = 'Reject Request (No Reason)'; +$txt['mc_groupr_reject_w_reason'] = 'Reject Request with Reason'; +// Do not use numeric entries in the below string. +$txt['mc_groupr_warning'] = 'Are you sure you wish to do this?'; + +$txt['mc_unapproved_attachments_none_found'] = 'There are currently no attachments awaiting approval'; +$txt['mc_unapproved_replies_none_found'] = 'There are currently no posts awaiting approval'; +$txt['mc_unapproved_topics_none_found'] = 'There are currently no topics awaiting approval'; +$txt['mc_unapproved_posts_desc'] = 'From here you can approve or delete any posts awaiting moderation.'; +$txt['mc_unapproved_replies'] = 'Replies'; +$txt['mc_unapproved_topics'] = 'Topics'; +$txt['mc_unapproved_by'] = 'by'; +$txt['mc_unapproved_sure'] = 'Are you sure you want to do this?'; +$txt['mc_unapproved_attach_name'] = 'Attachment Name'; +$txt['mc_unapproved_attach_size'] = 'Filesize'; +$txt['mc_unapproved_attach_poster'] = 'Poster'; +$txt['mc_viewmodreport'] = 'Moderation Report for %1$s by %2$s'; +$txt['mc_modreport_summary'] = 'There have been %1$d report(s) concerning this post. The last report was %2$s.'; +$txt['mc_modreport_whoreported_title'] = 'Members who have reported this post'; +$txt['mc_modreport_whoreported_data'] = 'Reported by %1$s on %2$s. They left the following message:'; +$txt['mc_modreport_modactions'] = 'Actions taken by other moderators'; +$txt['mc_modreport_mod_comments'] = 'Moderator Comments'; +$txt['mc_modreport_no_mod_comment'] = 'There are not currently any moderator comments'; +$txt['mc_modreport_add_mod_comment'] = 'Add Comment'; + +$txt['show_notice'] = 'Notice Text'; +$txt['show_notice_subject'] = 'Subject'; +$txt['show_notice_text'] = 'Text'; + +$txt['mc_watched_users_title'] = 'Watched Members'; +$txt['mc_watched_users_desc'] = 'Here you can keep a track of all members who have been assigned a "watch" by the moderation team.'; +$txt['mc_watched_users_post'] = 'View by Post'; +$txt['mc_watched_users_warning'] = 'Warning Level'; +$txt['mc_watched_users_last_login'] = 'Last Login'; +$txt['mc_watched_users_last_post'] = 'Last Post'; +$txt['mc_watched_users_no_posts'] = 'There are no posts from watched members.'; +// Don't use entities in the two strings below. +$txt['mc_watched_users_delete_post'] = 'Are you sure you want to delete this post?'; +$txt['mc_watched_users_delete_posts'] = 'Are you sure you want to delete these posts?'; +$txt['mc_watched_users_posted'] = 'Posted'; +$txt['mc_watched_users_member'] = 'Member'; + +$txt['mc_warnings_description'] = 'From this section you can see which warnings have been issued to members of the forum. You can also add and modify the notification templates used when sending a warning to a member.'; +$txt['mc_warning_log'] = 'Log'; +$txt['mc_warning_templates'] = 'Custom Templates'; +$txt['mc_warning_log_title'] = 'Viewing Warning Log'; +$txt['mc_warning_templates_title'] = 'Custom Warning Templates'; + +$txt['mc_warnings_none'] = 'No warnings have been issued yet!'; +$txt['mc_warnings_recipient'] = 'Recipient'; + +$txt['mc_warning_templates_none'] = 'No warning templates have been created yet'; +$txt['mc_warning_templates_time'] = 'Time Created'; +$txt['mc_warning_templates_name'] = 'Template'; +$txt['mc_warning_templates_creator'] = 'Created By'; +$txt['mc_warning_template_add'] = 'Add Template'; +$txt['mc_warning_template_modify'] = 'Edit Template'; +$txt['mc_warning_template_delete'] = 'Delete Selected'; +$txt['mc_warning_template_delete_confirm'] = 'Are you sure you want to delete the selected templates?'; + +$txt['mc_warning_template_desc'] = 'Use this page to fill in the details of the template. Note that the subject for the email is not part of the template. Note that as the notification is sent by PM you can use BBC within the template. Note if you use the {MESSAGE} variable then this template will not be available when issuing a generic warning (i.e. A warning not linked to a post).'; +$txt['mc_warning_template_title'] = 'Template Title'; +$txt['mc_warning_template_body_desc'] = 'The content of the notification message. Note that you can use the following shortcuts in this template.
    • {MEMBER} - Member Name.
    • {MESSAGE} - Link to Offending Post. (If Applicable)
    • {FORUMNAME} - Forum Name.
    • {SCRIPTURL} - Web address of forum.
    • {REGARDS} - Standard email sign-off.
    '; +$txt['mc_warning_template_body_default'] = '{MEMBER},' . "\n\n" . 'You have received a warning for inappropriate activity. Please cease these activities and abide by the forum rules otherwise we will take further action.' . "\n\n" . '{REGARDS}'; +$txt['mc_warning_template_personal'] = 'Personal Template'; +$txt['mc_warning_template_personal_desc'] = 'If you select this option only you will be able to see, edit and use this template. If not selected all moderators will be able to use this template.'; +$txt['mc_warning_template_error_empty'] = 'You must set both a title and notification body.'; + +$txt['mc_prefs'] = 'Preferences'; +$txt['mc_settings'] = 'Change Settings'; +$txt['mc_prefs_title'] = 'Moderation Preferences'; +$txt['mc_prefs_desc'] = 'This section allows you to set some personal preferences for moderation related activities such as email notifications.'; +$txt['mc_prefs_homepage'] = 'Items to show on moderation homepage'; +$txt['mc_prefs_latest_news'] = 'SM News'; +$txt['mc_prefs_show_reports'] = 'Show open report count in forum header'; +$txt['mc_prefs_notify_report'] = 'Notify of topic reports'; +$txt['mc_prefs_notify_report_never'] = 'Never'; +$txt['mc_prefs_notify_report_moderator'] = 'Only if it\'s a board I moderate'; +$txt['mc_prefs_notify_report_always'] = 'Always'; +$txt['mc_prefs_notify_approval'] = 'Notify of items awaiting approval'; + +// Use entities in the below string. +$txt['mc_click_add_note'] = 'Add a new note'; +$txt['mc_add_note'] = 'Add'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Modifications.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Modifications.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,4 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Modlog.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Modlog.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,87 @@ +Please note: Entries cannot be removed from this log until they are at least twenty-four hours old.'; +$txt['modlog_no_entries_found'] = 'There are currently no moderation log entries.'; +$txt['modlog_remove'] = 'Remove'; +$txt['modlog_removeall'] = 'Remove All'; +$txt['modlog_go'] = 'Go'; +$txt['modlog_add'] = 'Add'; +$txt['modlog_search'] = 'Quick Search'; +$txt['modlog_by'] = 'By'; +$txt['modlog_id'] = 'Deleted - ID:%1$d'; + +$txt['modlog_ac_add_warn_template'] = 'Added warning template: "{template}"'; +$txt['modlog_ac_modify_warn_template'] = 'Edited the warning template: "{template}"'; +$txt['modlog_ac_delete_warn_template'] = 'Deleted the warning template: "{template}"'; + +$txt['modlog_ac_ban'] = 'Added ban triggers:'; +$txt['modlog_ac_ban_trigger_member'] = ' Member: {member}'; +$txt['modlog_ac_ban_trigger_email'] = ' Email: {email}'; +$txt['modlog_ac_ban_trigger_ip_range'] = ' IP: {ip_range}'; +$txt['modlog_ac_ban_trigger_hostname'] = ' Hostname: {hostname}'; + +$txt['modlog_admin_log'] = 'Administration Log'; +$txt['modlog_admin_log_desc'] = 'Below is a list of administration actions which have been logged on your forum.
    Please note: Entries cannot be removed from this log until they are at least twenty-four hours old.'; +$txt['modlog_admin_log_no_entries_found'] = 'There are currently no administration log entries.'; + +// Admin type strings. +$txt['modlog_ac_upgrade'] = 'Upgraded the forum to version {version}'; +$txt['modlog_ac_install'] = 'Installed version {version}'; +$txt['modlog_ac_add_board'] = 'Added a new board: "{board}"'; +$txt['modlog_ac_edit_board'] = 'Edited the "{board}" board'; +$txt['modlog_ac_delete_board'] = 'Deleted the "{boardname}" board'; +$txt['modlog_ac_add_cat'] = 'Added a new category, "{catname}"'; +$txt['modlog_ac_edit_cat'] = 'Edited the "{catname}" category'; +$txt['modlog_ac_delete_cat'] = 'Deleted the "{catname}" category'; + +$txt['modlog_ac_delete_group'] = 'Deleted the "{group}" group'; +$txt['modlog_ac_add_group'] = 'Added the "{group}" group'; +$txt['modlog_ac_edited_group'] = 'Edited the "{group}" group'; +$txt['modlog_ac_added_to_group'] = 'Added "{member}" to the "{group}" group'; +$txt['modlog_ac_removed_from_group'] = 'Removed "{member}" from the "{group}" group'; +$txt['modlog_ac_removed_all_groups'] = 'Removed "{member}" from all groups'; + +$txt['modlog_ac_remind_member'] = 'Sent out a reminder to "{member}" to activate their account'; +$txt['modlog_ac_approve_member'] = 'Approved/Activated the account of "{member}"'; +$txt['modlog_ac_newsletter'] = 'Sent Newsletter'; + +$txt['modlog_ac_install_package'] = 'Installed new package: "{package}", version {version}'; +$txt['modlog_ac_upgrade_package'] = 'Upgraded package: "{package}" to version {version}'; +$txt['modlog_ac_uninstall_package'] = 'Uninstalled package: "{package}", version {version}'; + +// Restore topic. +$txt['modlog_ac_restore_topic'] = 'Restored topic "{topic}" from "{board}" to "{board_to}"'; +$txt['modlog_ac_restore_posts'] = 'Restored posts from "{subject}" to the topic "{topic}" in the "{board}" board.'; + +$txt['modlog_parameter_guest'] = 'Guest'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Packages.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Packages.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,262 @@ +strongly recommended that you do not continue with installation unless you know what you are doing, and have made a backup very recently. + This error may be caused by a conflict between the package you\'re trying to install and another package you have already installed, an error in the package, a package which requires another package that you don\'t have installed yet, or a package designed for another version of SMF.'; +// Don't use entities in the below string. +$txt['package_will_fail_popup'] = 'Are you sure you wish to continue installing this modification, even though it will not install successfully?'; +$txt['package_will_fail_popup_uninstall'] = 'Are you sure you wish to continue uninstalling this modification, even though it will not uninstall successfully?'; +$txt['package_install_now'] = 'Install Now'; +$txt['package_uninstall_now'] = 'Uninstall Now'; +$txt['package_other_themes'] = 'Install in Other Themes'; +$txt['package_other_themes_uninstall'] = 'UnInstall in Other Themes'; +$txt['package_other_themes_desc'] = 'To use this modification in themes other than the default, the package manager needs to make additional changes to the other themes. If you\'d like to install this modification in the other themes, please select these themes below.'; +// Don't use entities in the below string. +$txt['package_theme_failure_warning'] = 'At least one error was encountered during a test install of this theme. Are you sure you wish to attempt installation?'; + +$txt['package_bytes'] = 'bytes'; + +$txt['package_action_missing'] = 'File not found'; +$txt['package_action_error'] = 'Modification parse error'; +$txt['package_action_failure'] = 'Test failed'; +$txt['package_action_success'] = 'Test successful'; +$txt['package_action_skipping'] = 'Skipping file'; + +$txt['package_uninstall_actions'] = 'Uninstall Actions'; +$txt['package_uninstall_done'] = 'The package has been uninstalled, it should no longer take effect.'; +$txt['package_uninstall_cannot'] = 'This package cannot be uninstalled, because there is no uninstaller!

    Please contact the mod author for more information.'; + +$txt['package_install_options'] = 'Installation Options'; +$txt['package_install_options_ftp_why'] = 'Using the package manager\'s FTP functionality is the easiest way to avoid having to manually chmod the files writable through FTP yourself for the package manager to work.
    Here you can set the default values for some fields.'; +$txt['package_install_options_ftp_server'] = 'FTP Server'; +$txt['package_install_options_ftp_port'] = 'Port'; +$txt['package_install_options_ftp_user'] = 'Username'; +$txt['package_install_options_make_backups'] = 'Create Backup versions of replaced files with a tilde (~) on the end of their names.'; + +$txt['package_ftp_necessary'] = 'FTP Information Required'; +$txt['package_ftp_why'] = 'Some of the files the package manager needs to modify are not writable. This needs to be changed by logging into FTP and using it to chmod or create the files and folders. Your FTP information may be temporarily cached for proper operation of the package manager. Note you can also do this manually using an FTP client - to view a list of the affected files please click here.'; +$txt['package_ftp_why_file_list'] = 'The following files need to made writable to continue installation:'; +$txt['package_ftp_why_download'] = 'To download packages, the Packages directory and files in it need to be writable - and they are not currently. The package manager can use your FTP information to fix this.'; +$txt['package_ftp_server'] = 'FTP Server'; +$txt['package_ftp_port'] = 'Port'; +$txt['package_ftp_username'] = 'Username'; +$txt['package_ftp_password'] = 'Password'; +$txt['package_ftp_path'] = 'Local path to SMF'; +$txt['package_ftp_test'] = 'Test'; +$txt['package_ftp_test_connection'] = 'Test Connection'; +$txt['package_ftp_test_success'] = 'FTP connection established.'; +$txt['package_ftp_test_failed'] = 'Could not contact server.'; + +// For a break, use \\n instead of
    ... and don't use entities. +$txt['package_delete_bad'] = 'The package you are about to delete is currently installed! If you delete it, you may not be able to uninstall it later.\\n\\nAre you sure?'; + +$txt['package_examine_file'] = 'View file in package'; +$txt['package_file_contents'] = 'Contents of file'; + +$txt['package_upload_title'] = 'Upload a Package'; +$txt['package_upload_select'] = 'Package to Upload'; +$txt['package_upload'] = 'Upload'; +$txt['package_uploaded_success'] = 'Package uploaded successfully'; +$txt['package_uploaded_successfully'] = 'The package has been uploaded successfully'; + +$txt['package_modification_malformed'] = 'Malformed or invalid modification file.'; +$txt['package_modification_missing'] = 'The file could not be found.'; +$txt['package_no_zlib'] = 'Sorry, your PHP configuration doesn\'t have support for zlib. Without this, the package manager cannot function. Please contact your host about this for more information.'; + +$txt['package_cleanperms_title'] = 'Cleanup Permissions'; +$txt['package_cleanperms_desc'] = 'This interface allows you to reset the permissions for files throughout your installation, so as to increase security or solve any permission problems you may encounter while installing packages.'; +$txt['package_cleanperms_type'] = 'Change all file permissions throughout the forum such that'; +$txt['package_cleanperms_standard'] = 'Only the standard files are writable.'; +$txt['package_cleanperms_free'] = 'All files are writable.'; +$txt['package_cleanperms_restrictive'] = 'The minimum files are writable.'; +$txt['package_cleanperms_go'] = 'Change file permissions'; + +$txt['package_download_by_url'] = 'Download a package by url'; +$txt['package_download_filename'] = 'Name of the file'; +$txt['package_download_filename_info'] = 'Optional value. Should be used when the url does not end in the filename. For example: index.php?mod=5'; + +$txt['package_db_uninstall'] = 'Remove all data associated with this modification.'; +$txt['package_db_uninstall_details'] = 'Details'; +$txt['package_db_uninstall_actions'] = 'Checking this option will result in the following database changes'; +$txt['package_db_remove_table'] = 'Drop table "%1$s"'; +$txt['package_db_remove_column'] = 'Remove column "%2$s" from "%1$s"'; +$txt['package_db_remove_index'] = 'Remove index "%1$s" from "%2$s"'; + +$txt['package_advanced_button'] = 'Advanced'; +$txt['package_advanced_options'] = 'Advanced Options'; +$txt['package_apply'] = 'Apply'; +$txt['package_emulate'] = 'Emulate Version'; +$txt['package_emulate_revert'] = 'Revert'; +$txt['package_emulate_desc'] = 'Sometimes packages are locked to early versions of SMF but remain compatible with a newer version. Here you can choose to "emulate" a different SMF version within the package manager.'; + +// Operations. +$txt['operation_find'] = 'Find'; +$txt['operation_replace'] = 'Replace'; +$txt['operation_after'] = 'Add After'; +$txt['operation_before'] = 'Add Before'; +$txt['operation_title'] = 'Operations'; +$txt['operation_ignore'] = 'Ignore Errors'; +$txt['operation_invalid'] = 'The operation that you selected is invalid.'; + +$txt['package_file_perms_desc'] = 'You can use this section to review the writable status of critical files and folders within your forum directory. Note this only considers key forum folders and files - use an FTP client for additional options.'; +$txt['package_file_perms_name'] = 'File/Folder Name'; +$txt['package_file_perms_status'] = 'Current Status'; +$txt['package_file_perms_new_status'] = 'New Status'; +$txt['package_file_perms_status_read'] = 'Read'; +$txt['package_file_perms_status_write'] = 'Write'; +$txt['package_file_perms_status_execute'] = 'Execute'; +$txt['package_file_perms_status_custom'] = 'Custom'; +$txt['package_file_perms_status_no_change'] = 'No Change'; +$txt['package_file_perms_writable'] = 'Writable'; +$txt['package_file_perms_not_writable'] = 'Not Writable'; +$txt['package_file_perms_chmod'] = 'chmod'; +$txt['package_file_perms_more_files'] = 'More Files'; + +$txt['package_file_perms_change'] = 'Change File Permissions'; +$txt['package_file_perms_predefined'] = 'Use predefined permission profile'; +$txt['package_file_perms_predefined_note'] = 'Note that this only applies the predefined profile to key SMF folders and files.'; +$txt['package_file_perms_apply'] = 'Apply individual file permissions settings selected above.'; +$txt['package_file_perms_custom'] = 'If "Custom" has been selected use chmod value of'; +$txt['package_file_perms_pre_restricted'] = 'Restricted - minimum files writable'; +$txt['package_file_perms_pre_standard'] = 'Standard - key files writable'; +$txt['package_file_perms_pre_free'] = 'Free - all files writable'; +$txt['package_file_perms_ftp_details'] = 'On most servers it is only possible to change file permissions using an FTP account. Please enter your FTP details below'; +$txt['package_file_perms_ftp_retain'] = 'Note, SMF will only retain the password information temporarily to aid operation of the package manager.'; +$txt['package_file_perms_go'] = 'Make Changes'; + +$txt['package_file_perms_applying'] = 'Applying Changes'; +$txt['package_file_perms_items_done'] = '%1$d of %2$d items completed'; +$txt['package_file_perms_skipping_ftp'] = 'Warning: Failed to connect to FTP server, attempting to change permissions without. This is likely to fail - please check the results upon completion and try again with correct FTP details if necessary.'; + +$txt['package_file_perms_dirs_done'] = '%1$d of %2$d directories completed'; +$txt['package_file_perms_files_done'] = '%1$d of %2$d files done in current directory'; + +$txt['chmod_value_invalid'] = 'You have tried to enter an invalid chmod value. Chmod must be between 0444 and 0777'; + +$txt['package_restore_permissions'] = 'Restore File Permissions'; +$txt['package_restore_permissions_desc'] = 'The following file permissions were changed by SMF to install the selected package(s). You can return these files back to their original status by clicking "Restore" below.'; +$txt['package_restore_permissions_restore'] = 'Restore'; +$txt['package_restore_permissions_filename'] = 'Filename'; +$txt['package_restore_permissions_orig_status'] = 'Original Status'; +$txt['package_restore_permissions_cur_status'] = 'Current Status'; +$txt['package_restore_permissions_result'] = 'Result'; +$txt['package_restore_permissions_pre_change'] = '%1$s (%3$s)'; +$txt['package_restore_permissions_post_change'] = '%2$s (%3$s - was %2$s)'; +$txt['package_restore_permissions_action_skipped'] = 'Skipped'; +$txt['package_restore_permissions_action_success'] = 'Success'; +$txt['package_restore_permissions_action_failure'] = 'Failed'; +$txt['package_restore_permissions_action_done'] = 'SMF has attempted to restore the selected files back to their original permissions, the results can be seen below. If a change failed, or for a more detailed view of file permissions, please see the File Permissions section.'; + +$txt['package_file_perms_warning'] = 'Please Note'; +$txt['package_file_perms_warning_desc'] = ' +
  40. Be careful when changing file permissions from this section - incorrect permissions can adversely affect the operation of your forum!
  41. +
  42. On some server configurations selecting the wrong permissions may stop SMF from operating.
  43. +
  44. Certain directories such as attachments need to be writable to use that functionality.
  45. +
  46. This functionality is mainly applicable on non-Windows based servers - it will not work as expected on Windows in regards to permission flags.
  47. +
  48. Before proceeding make sure you have an FTP client installed in case you do make an error and need to FTP into the server to remedy it.
  49. '; + +$txt['package_confirm_view_package_content'] = 'Are you sure you want to view the package contents from this location:

    %1$s'; +$txt['package_confirm_proceed'] = 'Proceed'; +$txt['package_confirm_go_back'] = 'Go back'; + +$txt['package_readme_default'] = 'Default'; +$txt['package_available_readme_language'] = 'Available Readme Languages:'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/PersonalMessage.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/PersonalMessage.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,196 @@ +Note: You appear to have javascript disabled. We highly recommend you enable javascript to use this feature.
    '; +$txt['pm_rule_criteria'] = 'Criteria'; +$txt['pm_rule_criteria_add'] = 'Add Criteria'; +$txt['pm_rule_criteria_pick'] = 'Choose Criteria'; +$txt['pm_rule_mid'] = 'Sender Name'; +$txt['pm_rule_gid'] = 'Sender\'s Group'; +$txt['pm_rule_sub'] = 'Message Subject Contains'; +$txt['pm_rule_msg'] = 'Message Body Contains'; +$txt['pm_rule_bud'] = 'Sender is Buddy'; +$txt['pm_rule_sel_group'] = 'Select Group'; +$txt['pm_rule_logic'] = 'When Checking Criteria'; +$txt['pm_rule_logic_and'] = 'All criteria must be met'; +$txt['pm_rule_logic_or'] = 'Any criteria can be met'; +$txt['pm_rule_actions'] = 'Actions'; +$txt['pm_rule_sel_action'] = 'Select an Action'; +$txt['pm_rule_add_action'] = 'Add Action'; +$txt['pm_rule_label'] = 'Label message with'; +$txt['pm_rule_sel_label'] = 'Select Label'; +$txt['pm_rule_delete'] = 'Delete Message'; +$txt['pm_rule_no_name'] = 'You forgot to enter a name for the rule.'; +$txt['pm_rule_no_criteria'] = 'A rule must have at least one criteria and one action set.'; +$txt['pm_rule_too_complex'] = 'The rule you are creating is too long for SMF to store. Try breaking it up into smaller rules.'; + +$txt['pm_readable_and'] = 'and'; +$txt['pm_readable_or'] = 'or'; +$txt['pm_readable_start'] = 'If '; +$txt['pm_readable_end'] = '.'; +$txt['pm_readable_member'] = 'message is from "{MEMBER}"'; +$txt['pm_readable_group'] = 'sender is from the "{GROUP}" group'; +$txt['pm_readable_subject'] = 'message subject contains "{SUBJECT}"'; +$txt['pm_readable_body'] = 'message body contains "{BODY}"'; +$txt['pm_readable_buddy'] = 'sender is a buddy'; +$txt['pm_readable_label'] = 'apply label "{LABEL}"'; +$txt['pm_readable_delete'] = 'delete the message'; +$txt['pm_readable_then'] = 'then'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Post.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Post.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,196 @@ + 'it\\\'s'. +$txt['bbc_quote'] = 'Insert Quote'; +$txt['list'] = 'Insert List'; +$txt['list_unordered'] = 'Insert Unordered List'; +$txt['list_ordered'] = 'Insert Ordered List'; + +$txt['change_color'] = 'Change Color'; +$txt['black'] = 'Black'; +$txt['red'] = 'Red'; +$txt['yellow'] = 'Yellow'; +$txt['pink'] = 'Pink'; +$txt['green'] = 'Green'; +$txt['orange'] = 'Orange'; +$txt['purple'] = 'Purple'; +$txt['blue'] = 'Blue'; +$txt['beige'] = 'Beige'; +$txt['brown'] = 'Brown'; +$txt['teal'] = 'Teal'; +$txt['navy'] = 'Navy'; +$txt['maroon'] = 'Maroon'; +$txt['lime_green'] = 'Lime Green'; +$txt['white'] = 'White'; +$txt['disable_smileys'] = 'Disable Smileys'; +$txt['dont_use_smileys'] = 'Don\'t use smileys.'; +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['posted_on'] = 'Posted on'; +$txt['standard'] = 'Standard'; +$txt['thumbs_up'] = 'Thumb Up'; +$txt['thumbs_down'] = 'Thumb Down'; +$txt['excamation_point'] = 'Exclamation point'; +$txt['question_mark'] = 'Question mark'; +$txt['lamp'] = 'Lamp'; +$txt['add_smileys'] = 'Add Smileys'; +$txt['flash'] = 'Insert Flash'; +$txt['ftp'] = 'Insert FTP Link'; +$txt['image'] = 'Insert Image'; +$txt['table'] = 'Insert Table'; +$txt['table_td'] = 'Insert Table Column'; +$txt['topic_notify_no'] = 'There are no topics with Notification.'; +$txt['marquee'] = 'Marquee'; +$txt['teletype'] = 'Teletype'; +$txt['strike'] = 'Strikethrough'; +$txt['glow'] = 'Glow'; +$txt['shadow'] = 'Shadow'; +$txt['preformatted'] = 'Preformatted Text'; +$txt['left_align'] = 'Left Align'; +$txt['right_align'] = 'Right Align'; +$txt['superscript'] = 'Superscript'; +$txt['subscript'] = 'Subscript'; +$txt['table_tr'] = 'Insert Table Row'; +$txt['post_too_long'] = 'Your message is too long. Please go back and shorten it, then try again.'; +$txt['horizontal_rule'] = 'Horizontal Rule'; +$txt['font_size'] = 'Font Size'; +$txt['font_face'] = 'Font Face'; +$txt['toggle_view'] = 'Toggle View'; +$txt['unformat_text'] = 'Remove Formatting'; + +$txt['rich_edit_wont_work'] = 'Your browser does not support Rich Text editing.'; +$txt['rich_edit_function_disabled'] = 'Your browser does not support this function.'; + +// Use numeric entities in the below five strings. +$txt['notifyUnsubscribe'] = 'Unsubscribe to this topic by clicking here'; + +$txt['lock_after_post'] = 'Lock after Post'; +$txt['notify_replies'] = 'Notify me of replies.'; +$txt['lock_topic'] = 'Lock this topic.'; +$txt['shortcuts'] = 'shortcuts: hit alt+s to submit/post or alt+p to preview'; +$txt['shortcuts_firefox'] = 'shortcuts: hit shift+alt+s to submit/post or shift+alt+p to preview'; +$txt['option'] = 'Option'; +$txt['reset_votes'] = 'Reset Vote Count'; +$txt['reset_votes_check'] = 'Check this if you want to reset all vote counts to 0.'; +$txt['votes'] = 'votes'; +$txt['attach'] = 'Attach'; +$txt['clean_attach'] = 'Clear Attachment'; +$txt['attached'] = 'Attached'; +$txt['allowed_types'] = 'Allowed file types'; +$txt['cant_upload_type'] = 'You cannot upload that type of file. The only allowed extensions are'; +$txt['uncheck_unwatchd_attach'] = 'Uncheck the attachments you no longer want attached'; +$txt['restricted_filename'] = 'That is a restricted filename. Please try a different filename.'; +$txt['topic_locked_no_reply'] = 'Warning: topic is currently/will be locked!
    Only admins and moderators can reply.'; +$txt['awaiting_approval'] = 'Awaiting approval'; +$txt['attachment_requires_approval'] = 'Note that any files attached will not be displayed until approved by a moderator.'; +$txt['error_temp_attachments'] = 'There are attachments found, which you have attached before but not posted. These attachments are now attached to this post. If you do not want to include them in this post, you can remove them here.'; +// Use numeric entities in the below string. +$txt['js_post_will_require_approval'] = 'Reminder: This post will not appear until approved by a moderator.'; + +$txt['enter_comment'] = 'Enter comment'; +// Use numeric entities in the below two strings. +$txt['reported_post'] = 'Reported post'; +$txt['reported_to_mod_by'] = 'by'; +$txt['rtm10'] = 'Submit'; +// Use numeric entities in the below four strings. +$txt['report_following_post'] = 'The following post, "%1$s" by'; +$txt['reported_by'] = 'has been reported by'; +$txt['board_moderate'] = 'on a board you moderate'; +$txt['report_comment'] = 'The reporter has made the following comment'; + +$txt['attach_restrict_attachmentPostLimit'] = 'maximum total size %1$dKB'; +$txt['attach_restrict_attachmentSizeLimit'] = 'maximum individual size %1$dKB'; +$txt['attach_restrict_attachmentNumPerPostLimit'] = '%1$d per post'; +$txt['attach_restrictions'] = 'Restrictions:'; + +$txt['post_additionalopt'] = 'Attachments and other options'; +$txt['sticky_after'] = 'Sticky this topic.'; +$txt['move_after2'] = 'Move this topic.'; +$txt['back_to_topic'] = 'Return to this topic.'; +$txt['approve_this_post'] = 'Approve this Post'; + +$txt['retrieving_quote'] = 'Retrieving Quote...'; + +$txt['post_visual_verification_label'] = 'Verification'; +$txt['post_visual_verification_desc'] = 'Please enter the code in the image above to make this post.'; + +$txt['poll_options'] = 'Poll Options'; +$txt['poll_run'] = 'Run the poll for'; +$txt['poll_run_limit'] = '(Leave blank for no limit.)'; +$txt['poll_results_visibility'] = 'Result visibility'; +$txt['poll_results_anyone'] = 'Show the poll\'s results to anyone.'; +$txt['poll_results_voted'] = 'Only show the results after someone has voted.'; +$txt['poll_results_after'] = 'Only show the results after the poll has expired.'; +$txt['poll_max_votes'] = 'Maximum votes per user'; +$txt['poll_do_change_vote'] = 'Allow users to change vote'; +$txt['poll_too_many_votes'] = 'You selected too many options. For this poll, you may only select %1$s options.'; +$txt['poll_add_option'] = 'Add Option'; +$txt['poll_guest_vote'] = 'Allow guests to vote'; + +$txt['spellcheck_done'] = 'Spell checking complete.'; +$txt['spellcheck_change_to'] = 'Change To:'; +$txt['spellcheck_suggest'] = 'Suggestions:'; +$txt['spellcheck_change'] = 'Change'; +$txt['spellcheck_change_all'] = 'Change All'; +$txt['spellcheck_ignore'] = 'Ignore'; +$txt['spellcheck_ignore_all'] = 'Ignore All'; + +$txt['more_attachments'] = 'more attachments'; +// Don't use entities in the below string. +$txt['more_attachments_error'] = 'Sorry, you aren\'t allowed to post any more attachments.'; + +$txt['more_smileys'] = 'more'; +$txt['more_smileys_title'] = 'Additional smileys'; +$txt['more_smileys_pick'] = 'Pick a smiley'; +$txt['more_smileys_close_window'] = 'Close Window'; + +$txt['error_new_reply'] = 'Warning - while you were typing a new reply has been posted. You may wish to review your post.'; +$txt['error_new_replies'] = 'Warning - while you were typing %1$d new replies have been posted. You may wish to review your post.'; +$txt['error_new_reply_reading'] = 'Warning - while you were reading a new reply has been posted. You may wish to review your post.'; +$txt['error_new_replies_reading'] = 'Warning - while you were reading %1$d new replies have been posted. You may wish to review your post.'; + +$txt['announce_this_topic'] = 'Send an announcement about this topic to the members:'; +$txt['announce_title'] = 'Send an announcement'; +$txt['announce_desc'] = 'This form allows you to send an announcement to the selected membergroups about this topic.'; +$txt['announce_sending'] = 'Sending announcement of topic'; +$txt['announce_done'] = 'done'; +$txt['announce_continue'] = 'Continue'; +$txt['announce_topic'] = 'Announce topic.'; +$txt['announce_regular_members'] = 'Regular Members'; + +$txt['digest_subject_daily'] = 'Daily Digest'; +$txt['digest_subject_weekly'] = 'Weekly Digest'; +$txt['digest_intro_daily'] = 'Below is a summary of all activity in your subscribed boards and topics at %1$s today. To unsubscribe please visit the link below.'; +$txt['digest_intro_weekly'] = 'Below is a summary of all activity in your subscribed boards and topics at %1$s this week. To unsubscribe please visit the link below.'; +$txt['digest_new_topics'] = 'The following topics have been started'; +$txt['digest_new_topics_line'] = '"%1$s" in "%2$s"'; +$txt['digest_new_replies'] = 'Replies have been made in the following topics'; +$txt['digest_new_replies_one'] = '1 reply in "%1$s"'; +$txt['digest_new_replies_many'] = '%1$d replies in "%2$s"'; +$txt['digest_mod_actions'] = 'The following moderation actions have taken place'; +$txt['digest_mod_act_sticky'] = '"%1$s" was stickied'; +$txt['digest_mod_act_lock'] = '"%1$s" was locked'; +$txt['digest_mod_act_unlock'] = '"%1$s" was unlocked'; +$txt['digest_mod_act_remove'] = '"%1$s" was removed'; +$txt['digest_mod_act_move'] = '"%1$s" was moved'; +$txt['digest_mod_act_merge'] = '"%1$s" was merged'; +$txt['digest_mod_act_split'] = '"%1$s" was split'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Profile.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Profile.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,471 @@ +http://www.mypage.com/mypic.gif)'; +$txt['my_own_pic'] = 'Specify avatar by URL'; +$txt['date_format'] = 'The format here will be used to show dates throughout this forum.'; +$txt['time_format'] = 'Time Format'; +$txt['display_name_desc'] = 'This is the displayed name that people will see.'; +$txt['personal_time_offset'] = 'Number of hours to +/- to make displayed time equal to your local time.'; +$txt['dob'] = 'Birthdate'; +$txt['dob_month'] = 'Month (MM)'; +$txt['dob_day'] = 'Day (DD)'; +$txt['dob_year'] = 'Year (YYYY)'; +$txt['password_strength'] = 'For best security, you should use eight or more characters with a combination of letters, numbers, and symbols.'; +$txt['include_website_url'] = 'This must be included if you specify a URL below.'; +$txt['complete_url'] = 'This must be a complete URL.'; +$txt['your_icq'] = 'This is your ICQ number.'; +$txt['your_aim'] = 'This is your AOL Instant Messenger nickname.'; +$txt['your_yim'] = 'This is your Yahoo! Instant Messenger nickname.'; +$txt['sig_info'] = 'Signatures are displayed at the bottom of each post or personal message. BBCode and smileys may be used in your signature.'; +$txt['max_sig_characters'] = 'Max characters: %1$d; characters remaining: '; +$txt['send_member_pm'] = 'Send this member a personal message'; +$txt['hidden'] = 'hidden'; +$txt['current_time'] = 'Current forum time'; +$txt['digits_only'] = 'The \'number of posts\' box can only contain digits.'; + +$txt['language'] = 'Language'; +$txt['avatar_too_big'] = 'Avatar image is too big, please resize it and try again (max'; +$txt['invalid_registration'] = 'Invalid Date Registered value, valid example:'; +$txt['msn_email_address'] = 'Your MSN messenger email address'; +$txt['current_password'] = 'Current Password'; +// Don't use entities in the below string, except the main ones. (lt, gt, quot.) +$txt['required_security_reasons'] = 'For security reasons, your current password is required to make changes to your account.'; + +$txt['timeoffset_autodetect'] = '(auto detect)'; + +$txt['secret_question'] = 'Secret Question'; +$txt['secret_desc'] = 'To help retrieve your password, enter a question here with an answer that only you know.'; +$txt['secret_desc2'] = 'Choose carefully, you wouldn\'t want someone guessing your answer!'; +$txt['secret_answer'] = 'Answer'; +$txt['secret_ask'] = 'Ask me my question'; +$txt['cant_retrieve'] = 'You can\'t retrieve your password, but you can set a new one by following a link sent to you by email. You also have the option of setting a new password by answering your secret question.'; +$txt['incorrect_answer'] = 'Sorry, but you did not specify a valid combination of Secret Question and Answer in your profile. Please click on the back button, and use the default method of obtaining your password.'; +$txt['enter_new_password'] = 'Please enter the answer to your question, and the password you would like to use. Your password will be changed to the one you select provided you answer the question correctly.'; +$txt['password_success'] = 'Your password was changed successfully.
    Click here to login.'; +$txt['secret_why_blank'] = 'why is this blank?'; + +$txt['authentication_reminder'] = 'Authentication Reminder'; +$txt['password_reminder_desc'] = 'If you\'ve forgotten your login details, don\'t worry, they can be retrieved. To start this process please enter your username or email address below.'; +$txt['authentication_options'] = 'Please select one of the two options below'; +$txt['authentication_openid_email'] = 'Email me a reminder of my OpenID identity'; +$txt['authentication_openid_secret'] = 'Answer my "secret question" to display my OpenID identity'; +$txt['authentication_password_email'] = 'Email me a new password'; +$txt['authentication_password_secret'] = 'Let me set a new password by answering my "secret question"'; +$txt['openid_secret_reminder'] = 'Please enter your answer to the question below. If you get it correct your OpenID identity will be shown.'; +$txt['reminder_openid_is'] = 'The OpenID identity associated with your account is:
        %1$s

    Please make a note of this for future reference.'; +$txt['reminder_continue'] = 'Continue'; + +$txt['current_theme'] = 'Current Theme'; +$txt['change'] = '(change)'; +$txt['theme_preferences'] = 'Theme preferences'; +$txt['theme_forum_default'] = 'Forum or Board Default'; +$txt['theme_forum_default_desc'] = 'This is the default theme, which means your theme will change along with the administrator\'s settings and the board you are viewing.'; + +$txt['profileConfirm'] = 'Do you really want to delete this member?'; + +$txt['custom_title'] = 'Custom Title'; + +$txt['lastLoggedIn'] = 'Last Active'; + +$txt['notify_settings'] = 'Notification Settings:'; +$txt['notify_save'] = 'Save settings'; +$txt['notify_important_email'] = 'Receive forum newsletters, announcements and important notifications by email.'; +$txt['notify_regularity'] = 'For topics and boards I\'ve requested notification on, notify me'; +$txt['notify_regularity_instant'] = 'Instantly'; +$txt['notify_regularity_first_only'] = 'Instantly - but only for the first unread reply'; +$txt['notify_regularity_daily'] = 'Daily'; +$txt['notify_regularity_weekly'] = 'Weekly'; +$txt['auto_notify'] = 'Turn notification on when you post or reply to a topic.'; +$txt['notify_send_types'] = 'For topics and boards I\'ve requested notification on, notify me of'; +$txt['notify_send_type_everything'] = 'Replies and moderation'; +$txt['notify_send_type_everything_own'] = 'Moderation only if I started the topic'; +$txt['notify_send_type_only_replies'] = 'Only replies'; +$txt['notify_send_type_nothing'] = 'Nothing at all'; +$txt['notify_send_body'] = 'When sending notification of a reply to a topic, send the post in the email (but please don\'t reply to these emails.)'; + +$txt['notifications_topics'] = 'Current Topic Notifications'; +$txt['notifications_topics_list'] = 'You are being notified of replies to the following topics'; +$txt['notifications_topics_none'] = 'You are not currently receiving any notifications from topics.'; +$txt['notifications_topics_howto'] = 'To receive notifications from a topic, click the "notify" button while viewing it.'; +$txt['notifications_boards'] = 'Current Board Notifications'; +$txt['notifications_boards_list'] = 'You are being notified of new topics posted in the following boards'; +$txt['notifications_boards_none'] = 'You aren\'t receiving notifications on any boards right now.'; +$txt['notifications_boards_howto'] = 'To request notifications from a specific board, click the "notify" button in the index of that board.'; +$txt['notifications_update'] = 'Unnotify'; + +$txt['statPanel_showStats'] = 'User statistics for: '; +$txt['statPanel_users_votes'] = 'Number of Votes Cast'; +$txt['statPanel_users_polls'] = 'Number of Polls Created'; +$txt['statPanel_total_time_online'] = 'Total Time Spent Online'; +$txt['statPanel_noPosts'] = 'No posts to speak of!'; +$txt['statPanel_generalStats'] = 'General Statistics'; +$txt['statPanel_posts'] = 'posts'; +$txt['statPanel_topics'] = 'topics'; +$txt['statPanel_total_posts'] = 'Total Posts'; +$txt['statPanel_total_topics'] = 'Total Topics Started'; +$txt['statPanel_votes'] = 'votes'; +$txt['statPanel_polls'] = 'polls'; +$txt['statPanel_topBoards'] = 'Most Popular Boards By Posts'; +$txt['statPanel_topBoards_posts'] = '%1$d posts of the board\'s %2$d posts (%3$01.2f%%)'; +$txt['statPanel_topBoards_memberposts'] = '%1$d posts of the member\'s %2$d posts (%3$01.2f%%)'; +$txt['statPanel_topBoardsActivity'] = 'Most Popular Boards By Activity'; +$txt['statPanel_activityTime'] = 'Posting Activity By Time'; +$txt['statPanel_activityTime_posts'] = '%1$d posts (%2$d%%)'; +$txt['statPanel_timeOfDay'] = 'Time of Day'; + +$txt['deleteAccount_warning'] = 'Warning - These actions are irreversible!'; +$txt['deleteAccount_desc'] = 'From this page you can delete this user\'s account and posts.'; +$txt['deleteAccount_member'] = 'Delete this member\'s account'; +$txt['deleteAccount_posts'] = 'Remove posts made by this member'; +$txt['deleteAccount_none'] = 'None'; +$txt['deleteAccount_all_posts'] = 'All Posts'; +$txt['deleteAccount_topics'] = 'Topics and Posts'; +$txt['deleteAccount_confirm'] = 'Are you completely sure you want to delete this account?'; +$txt['deleteAccount_approval'] = 'Please note that the forum moderators will have to approve this account\'s deletion before it will be removed.'; + +$txt['profile_of_username'] = 'Profile of %1$s'; +$txt['profileInfo'] = 'Profile Info'; +$txt['showPosts'] = 'Show Posts'; +$txt['showPosts_help'] = 'This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.'; +$txt['showMessages'] = 'Messages'; +$txt['showTopics'] = 'Topics'; +$txt['showAttachments'] = 'Attachments'; +$txt['statPanel'] = 'Show Stats'; +$txt['editBuddyIgnoreLists'] = 'Buddies/Ignore List'; +$txt['editBuddies'] = 'Edit Buddies'; +$txt['editIgnoreList'] = 'Edit Ignore List'; +$txt['trackUser'] = 'Track User'; +$txt['trackActivity'] = 'Activity'; +$txt['trackIP'] = 'IP Address'; + +$txt['authentication'] = 'Authentication'; +$txt['change_authentication'] = 'From this section you can change how you login to the forum. You may choose to either use an OpenID account for your authentication, or alternatively switch to use a username and password.'; + +$txt['profileEdit'] = 'Modify Profile'; +$txt['account_info'] = 'These are your account settings. This page holds all critical information that identifies you on this forum. For security reasons, you will need to enter your (current) password to make changes to this information.'; +$txt['forumProfile_info'] = 'You can change your personal information on this page. This information will be displayed throughout ' . $context['forum_name_html_safe'] . '. If you aren\'t comfortable with sharing some information, simply skip it - nothing here is required.'; +$txt['theme'] = 'Look and Layout'; +$txt['theme_info'] = 'This section allows you to customize the look and layout of the forum.'; +$txt['notification'] = 'Notifications'; +$txt['notification_info'] = 'SMF allows you to be notified of replies to posts, newly posted topics, and forum announcements. You can change those settings here, or oversee the topics and boards you are currently receiving notifications for.'; +$txt['groupmembership'] = 'Group Membership'; +$txt['groupMembership_info'] = 'In this section of your profile you can change which groups you belong to.'; +$txt['ignoreboards'] = 'Ignore Boards Options'; +$txt['ignoreboards_info'] = 'This page lets you ignore particular boards. When a board is ignored, the new post indicator will not show up on the board index. New posts will not show up using the "unread post" search link (when searching it will not look in those boards) however, ignored boards will still appear on the board index and upon entering will show which topics have new posts. When using the "unread replies" link, new posts in an ignored board will still be shown.'; +$txt['pmprefs'] = 'Personal Messaging'; + +$txt['profileAction'] = 'Actions'; +$txt['deleteAccount'] = 'Delete this account'; +$txt['profileSendIm'] = 'Send personal message'; +$txt['profile_sendpm_short'] = 'Send PM'; + +$txt['profileBanUser'] = 'Ban this user'; + +$txt['display_name'] = 'Display name'; +$txt['enter_ip'] = 'Enter IP (range)'; +$txt['errors_by'] = 'Error messages by'; +$txt['errors_desc'] = 'Below is a list of all the recent errors that this user has generated/experienced.'; +$txt['errors_from_ip'] = 'Error messages from IP (range)'; +$txt['errors_from_ip_desc'] = 'Below is a list of all recent error messages generated by this IP (range).'; +$txt['ip_address'] = 'IP address'; +$txt['ips_in_errors'] = 'IPs used in error messages'; +$txt['ips_in_messages'] = 'IPs used in recent posts'; +$txt['members_from_ip'] = 'Members from IP (range)'; +$txt['members_in_range'] = 'Members possibly in the same range'; +$txt['messages_from_ip'] = 'Messages posted from IP (range)'; +$txt['messages_from_ip_desc'] = 'Below is a list of all messages posted from this IP (range).'; +$txt['most_recent_ip'] = 'Most recent IP address'; +$txt['why_two_ip_address'] = 'Why are there two IP addresses listed?'; +$txt['no_errors_from_ip'] = 'No error messages from the specified IP (range) found'; +$txt['no_errors_from_user'] = 'No error messages from the specified user found'; +$txt['no_members_from_ip'] = 'No members from the specified IP (range) found'; +$txt['no_messages_from_ip'] = 'No messages from the specified IP (range) found'; +$txt['none'] = 'None'; +$txt['own_profile_confirm'] = 'Are you sure you want to delete your account?'; +$txt['view_ips_by'] = 'View IPs used by'; + +$txt['avatar_will_upload'] = 'Upload an avatar'; + +$txt['activate_changed_email_title'] = 'Email Address Changed'; +$txt['activate_changed_email_desc'] = 'You\'ve changed your email address. In order to validate this address you will receive an email. Click the link in that email to reactivate your account.'; + +// Use numeric entities in the below three strings. +$txt['no_reminder_email'] = 'Unable to send reminder email.'; +$txt['send_email'] = 'Send an email to'; +$txt['to_ask_password'] = 'to ask for your authentication details'; + +$txt['user_email'] = 'Username/Email'; + +// Use numeric entities in the below two strings. +$txt['reminder_subject'] = 'New password for ' . $context['forum_name']; +$txt['reminder_mail'] = 'This mail was sent because the \'forgot password\' function has been applied to your account. To set a new password, click the following link'; +$txt['reminder_sent'] = 'A mail has been sent to your email address. Click the link in that mail to set a new password.'; +$txt['reminder_openid_sent'] = 'Your current OpenID identity has been sent to your email address.'; +$txt['reminder_set_password'] = 'Set Password'; +$txt['reminder_password_set'] = 'Password successfully set'; +$txt['reminder_error'] = '%1$s failed to answer their secret question correctly when attempting to change a forgotten password.'; + +$txt['registration_not_approved'] = 'Sorry, this account has not yet been approved. If you need to change your email address please click'; +$txt['registration_not_activated'] = 'Sorry, this account has not yet been activated. If you need to resend the activation email please click'; + +$txt['primary_membergroup'] = 'Primary Membergroup'; +$txt['additional_membergroups'] = 'Additional Membergroups'; +$txt['additional_membergroups_show'] = '[ show additional groups ]'; +$txt['no_primary_membergroup'] = '(no primary membergroup)'; +$txt['deadmin_confirm'] = 'Are you sure you wish to irrevocably remove your admin status?'; + +$txt['account_activate_method_2'] = 'Account requires reactivation after email change'; +$txt['account_activate_method_3'] = 'Account is not approved'; +$txt['account_activate_method_4'] = 'Account is awaiting approval for deletion'; +$txt['account_activate_method_5'] = 'Account is an "under age" account awaiting approval'; +$txt['account_not_activated'] = 'Account is currently not activated'; +$txt['account_activate'] = 'activate'; +$txt['account_approve'] = 'approve'; +$txt['user_is_banned'] = 'User is currently banned'; +$txt['view_ban'] = 'View'; +$txt['user_banned_by_following'] = 'This user is currently affected by the following bans'; +$txt['user_cannot_due_to'] = 'User cannot %1$s as a result of ban: "%2$s"'; +$txt['ban_type_post'] = 'post'; +$txt['ban_type_register'] = 'register'; +$txt['ban_type_login'] = 'login'; +$txt['ban_type_access'] = 'access forum'; + +$txt['show_online'] = 'Show others my online status'; + +$txt['return_to_post'] = 'Return to topics after posting by default.'; +$txt['no_new_reply_warning'] = 'Don\'t warn on new replies made while posting.'; +$txt['posts_apply_ignore_list'] = 'Hide messages posted by members on my ignore list.'; +$txt['recent_posts_at_top'] = 'Show most recent posts at the top.'; +$txt['recent_pms_at_top'] = 'Show most recent personal messages at top.'; +$txt['wysiwyg_default'] = 'Show WYSIWYG editor on post page by default.'; + +$txt['timeformat_default'] = '(Forum Default)'; +$txt['timeformat_easy1'] = 'Month Day, Year, HH:MM:SS am/pm'; +$txt['timeformat_easy2'] = 'Month Day, Year, HH:MM:SS (24 hour)'; +$txt['timeformat_easy3'] = 'YYYY-MM-DD, HH:MM:SS'; +$txt['timeformat_easy4'] = 'DD Month YYYY, HH:MM:SS'; +$txt['timeformat_easy5'] = 'DD-MM-YYYY, HH:MM:SS'; + +$txt['poster'] = 'Poster'; + +$txt['board_desc_inside'] = 'Show board descriptions inside boards.'; +$txt['show_children'] = 'Show child boards on every page inside boards, not just the first.'; +$txt['use_sidebar_menu'] = 'Use sidebar menus instead of dropdown menus when possible.'; +$txt['show_no_avatars'] = 'Don\'t show users\' avatars.'; +$txt['show_no_signatures'] = 'Don\'t show users\' signatures.'; +$txt['show_no_censored'] = 'Leave words uncensored.'; +$txt['topics_per_page'] = 'Topics to display per page:'; +$txt['messages_per_page'] = 'Messages to display per page:'; +$txt['per_page_default'] = 'forum default'; +$txt['calendar_start_day'] = 'First day of the week on the calendar'; +$txt['display_quick_reply'] = 'Use quick reply on topic display: '; +$txt['display_quick_reply1'] = 'don\'t show at all'; +$txt['display_quick_reply2'] = 'show, off by default'; +$txt['display_quick_reply3'] = 'show, on by default'; +$txt['display_quick_mod'] = 'Show quick-moderation as '; +$txt['display_quick_mod_none'] = 'don\'t show.'; +$txt['display_quick_mod_check'] = 'checkboxes.'; +$txt['display_quick_mod_image'] = 'icons.'; + +$txt['whois_title'] = 'Look up IP on a regional whois-server'; +$txt['whois_afrinic'] = 'AfriNIC (Africa)'; +$txt['whois_apnic'] = 'APNIC (Asia Pacific region)'; +$txt['whois_arin'] = 'ARIN (North America, a portion of the Caribbean and sub-Saharan Africa)'; +$txt['whois_lacnic'] = 'LACNIC (Latin American and Caribbean region)'; +$txt['whois_ripe'] = 'RIPE (Europe, the Middle East and parts of Africa and Asia)'; + +$txt['moderator_why_missing'] = 'why isn\'t moderator here?'; +$txt['username_change'] = 'change'; +$txt['username_warning'] = 'To change this member\'s username, the forum must also reset their password, which will be emailed to the member with their new username.'; + +$txt['show_member_posts'] = 'View Member Posts'; +$txt['show_member_topics'] = 'View Member Topics'; +$txt['show_member_attachments'] = 'View Member Attachments'; +$txt['show_posts_none'] = 'No posts have been posted yet.'; +$txt['show_topics_none'] = 'No topics have been posted yet.'; +$txt['show_attachments_none'] = 'No attachments have been posted yet.'; +$txt['show_attach_filename'] = 'Filename'; +$txt['show_attach_downloads'] = 'Downloads'; +$txt['show_attach_posted'] = 'Posted'; + +$txt['showPermissions'] = 'Show Permissions'; +$txt['showPermissions_status'] = 'Permission status'; +$txt['showPermissions_help'] = 'This section allows you to view all permissions for this member (denied permissions are struck out).'; +$txt['showPermissions_given'] = 'Given by'; +$txt['showPermissions_denied'] = 'Denied by'; +$txt['showPermissions_permission'] = 'Permission (denied permissions are struck out)'; +$txt['showPermissions_none_general'] = 'This member has no general permissions set.'; +$txt['showPermissions_none_board'] = 'This member has no board specific permissions set.'; +$txt['showPermissions_all'] = 'As an administrator, this member has all possible permissions.'; +$txt['showPermissions_select'] = 'Board specific permissions for'; +$txt['showPermissions_general'] = 'General Permissions'; +$txt['showPermissions_global'] = 'All boards'; +$txt['showPermissions_restricted_boards'] = 'Restricted boards'; +$txt['showPermissions_restricted_boards_desc'] = 'The following boards are not accessible by this user'; + +$txt['local_time'] = 'Local Time'; +$txt['posts_per_day'] = 'per day'; + +$txt['buddy_ignore_desc'] = 'This area allows you to maintain your buddy and ignore lists for this forum. Adding members to these lists will, amongst other things, help control mail and PM traffic, depending on your preferences.'; + +$txt['buddy_add'] = 'Add To Buddy List'; +$txt['buddy_remove'] = 'Remove From Buddy List'; +$txt['buddy_add_button'] = 'Add'; +$txt['no_buddies'] = 'Your buddy list is currently empty'; + +$txt['ignore_add'] = 'Add To Ignore List'; +$txt['ignore_remove'] = 'Remove From Ignore List'; +$txt['ignore_add_button'] = 'Add'; +$txt['no_ignore'] = 'Your ignore list is currently empty'; + +$txt['regular_members'] = 'Registered Members'; +$txt['regular_members_desc'] = 'Every member of the forum is a member of this group.'; +$txt['group_membership_msg_free'] = 'Your group membership was successfully updated.'; +$txt['group_membership_msg_request'] = 'Your request has been submitted, please be patient while the request is considered.'; +$txt['group_membership_msg_primary'] = 'Your primary group has been updated'; +$txt['current_membergroups'] = 'Current Membergroups'; +$txt['available_groups'] = 'Available Groups'; +$txt['join_group'] = 'Join Group'; +$txt['leave_group'] = 'Leave Group'; +$txt['request_group'] = 'Request Membership'; +$txt['approval_pending'] = 'Approval Pending'; +$txt['make_primary'] = 'Make Primary Group'; + +$txt['request_group_membership'] = 'Request Group Membership'; +$txt['request_group_membership_desc'] = 'Before you can join this group your membership must be approved by the moderator. Please give a reason for joining this group'; +$txt['submit_request'] = 'Submit Request'; + +$txt['profile_updated_own'] = 'Your profile has been updated successfully.'; +$txt['profile_updated_else'] = 'The profile for %1$s has been updated successfully.'; + +$txt['profile_error_signature_max_length'] = 'Your signature cannot be greater than %1$d characters'; +$txt['profile_error_signature_max_lines'] = 'Your signature cannot span more than %1$d lines'; +$txt['profile_error_signature_max_image_size'] = 'Images in your signature must be no greater than %1$dx%2$d pixels'; +$txt['profile_error_signature_max_image_width'] = 'Images in your signature must be no wider than %1$d pixels'; +$txt['profile_error_signature_max_image_height'] = 'Images in your signature must be no higher than %1$d pixels'; +$txt['profile_error_signature_max_image_count'] = 'You cannot have more than %1$d images in your signature'; +$txt['profile_error_signature_max_font_size'] = 'Text in your signature must be smaller than %1$s in size'; +$txt['profile_error_signature_allow_smileys'] = 'You are not allowed to use any smileys within your signature'; +$txt['profile_error_signature_max_smileys'] = 'You are not allowed to use more than %1$d smileys within your signature'; +$txt['profile_error_signature_disabled_bbc'] = 'The following BBC is not allowed within your signature: %1$s'; + +$txt['profile_view_warnings'] = 'View Warnings'; +$txt['profile_issue_warning'] = 'Issue a Warning'; +$txt['profile_warning_level'] = 'Warning Level'; +$txt['profile_warning_desc'] = 'From this section you can adjust the user\'s warning level and issue them with a written warning if necessary. You can also track their warning history and view the effects of their current warning level as determined by the administrator.'; +$txt['profile_warning_name'] = 'Member Name'; +$txt['profile_warning_impact'] = 'Result'; +$txt['profile_warning_reason'] = 'Reason for Warning'; +$txt['profile_warning_reason_desc'] = 'This is required and will be logged.'; +$txt['profile_warning_effect_none'] = 'None.'; +$txt['profile_warning_effect_watch'] = 'User will be added to moderator watch list.'; +$txt['profile_warning_effect_own_watched'] = 'You are on the moderator watch list.'; +$txt['profile_warning_is_watch'] = 'being watched'; +$txt['profile_warning_effect_moderation'] = 'All users posts will be moderated.'; +$txt['profile_warning_effect_own_moderated'] = 'All your posts will be moderated.'; +$txt['profile_warning_is_moderation'] = 'posts are moderated'; +$txt['profile_warning_effect_mute'] = 'User will not be able to post.'; +$txt['profile_warning_effect_own_muted'] = 'You will not be able to post.'; +$txt['profile_warning_is_muted'] = 'cannot post'; +$txt['profile_warning_effect_text'] = 'Level >= %1$d: %2$s'; +$txt['profile_warning_notify'] = 'Send a Notification'; +$txt['profile_warning_notify_template'] = 'Select template:'; +$txt['profile_warning_notify_subject'] = 'Notification Subject'; +$txt['profile_warning_notify_body'] = 'Notification Message'; +$txt['profile_warning_notify_template_subject'] = 'You have received a warning'; +// Use numeric entities in below string. +$txt['profile_warning_notify_template_outline'] = '{MEMBER},' . "\n\n" . 'You have received a warning for %1$s. Please cease these activities and abide by the forum rules otherwise we will take further action.' . "\n\n" . '{REGARDS}'; +$txt['profile_warning_notify_template_outline_post'] = '{MEMBER},' . "\n\n" . 'You have received a warning for %1$s in regards to the message:' . "\n" . '{MESSAGE}.' . "\n\n" . 'Please cease these activities and abide by the forum rules otherwise we will take further action.' . "\n\n" . '{REGARDS}'; +$txt['profile_warning_notify_for_spamming'] = 'spamming'; +$txt['profile_warning_notify_title_spamming'] = 'Spamming'; +$txt['profile_warning_notify_for_offence'] = 'posting offensive material'; +$txt['profile_warning_notify_title_offence'] = 'Posting Offensive Material'; +$txt['profile_warning_notify_for_insulting'] = 'insulting other users and/or staff members'; +$txt['profile_warning_notify_title_insulting'] = 'Insulting Users/Staff'; +$txt['profile_warning_issue'] = 'Issue Warning'; +$txt['profile_warning_max'] = '(Max 100)'; +$txt['profile_warning_limit_attribute'] = 'Note you can not adjust this user\'s level by more than %1$d%% in a 24 hour period.'; +$txt['profile_warning_errors_occured'] = 'Warning has not been sent due to following errors'; +$txt['profile_warning_success'] = 'Warning Successfully Issued'; +$txt['profile_warning_new_template'] = 'New Template'; + +$txt['profile_warning_previous'] = 'Previous Warnings'; +$txt['profile_warning_previous_none'] = 'This user has not received any previous warnings.'; +$txt['profile_warning_previous_issued'] = 'Issued By'; +$txt['profile_warning_previous_time'] = 'Time'; +$txt['profile_warning_previous_level'] = 'Points'; +$txt['profile_warning_previous_reason'] = 'Reason'; +$txt['profile_warning_previous_notice'] = 'View Notice Sent to Member'; + +$txt['viewwarning'] = 'View Warnings'; +$txt['profile_viewwarning_for_user'] = 'Warnings for %1$s'; +$txt['profile_viewwarning_no_warnings'] = 'No warnings have yet been issued.'; +$txt['profile_viewwarning_desc'] = 'Below is a summary of all the warnings that have been issued by the forum moderation team.'; +$txt['profile_viewwarning_previous_warnings'] = 'Previous Warnings'; +$txt['profile_viewwarning_impact'] = 'Warning Impact'; + +$txt['subscriptions'] = 'Paid Subscriptions'; + +$txt['pm_settings_desc'] = 'From this page you can change a variety of personal messaging options, including how messages are displayed and who may send them to you.'; +$txt['email_notify'] = 'Notify by email every time you receive a personal message:'; +$txt['email_notify_never'] = 'Never'; +$txt['email_notify_buddies'] = 'From Buddies Only'; +$txt['email_notify_always'] = 'Always'; + +$txt['pm_receive_from'] = 'Receive personal messages from:'; +$txt['pm_receive_from_everyone'] = 'All members'; +$txt['pm_receive_from_ignore'] = 'All members, except those on my ignore list'; +$txt['pm_receive_from_admins'] = 'Administrators only'; +$txt['pm_receive_from_buddies'] = 'Buddies and Administrators only'; + +$txt['copy_to_outbox'] = 'Save a copy of each personal message in my sent items by default.'; +$txt['popup_messages'] = 'Show a popup when I receive new messages.'; +$txt['pm_remove_inbox_label'] = 'Remove the inbox label when applying another label'; +$txt['pm_display_mode'] = 'Display personal messages'; +$txt['pm_display_mode_all'] = 'All at once'; +$txt['pm_display_mode_one'] = 'One at a time'; +$txt['pm_display_mode_linked'] = 'As a conversation'; +// Use entities in the below string. +$txt['pm_recommend_enable_outbox'] = 'To make the most of this setting we suggest you enable "Save a copy of each Personal Message in my sent items by default"\\n\\nThis will help ensure that the conversations flow better as you can see both sides of the conversation.'; + +$txt['tracking'] = 'Tracking'; +$txt['tracking_description'] = 'This section allows you to review certain profile actions performed on this member\'s profile as well as track their IP address.'; + +$txt['trackEdits'] = 'Profile Edits'; +$txt['trackEdit_deleted_member'] = 'Deleted Member'; +$txt['trackEdit_no_edits'] = 'No edits have so far been recorded for this member.'; +$txt['trackEdit_action'] = 'Field'; +$txt['trackEdit_before'] = 'Value Before'; +$txt['trackEdit_after'] = 'Value After'; +$txt['trackEdit_applicator'] = 'Changed By'; + +$txt['trackEdit_action_real_name'] = 'Member Name'; +$txt['trackEdit_action_usertitle'] = 'Custom Title'; +$txt['trackEdit_action_member_name'] = 'Username'; +$txt['trackEdit_action_email_address'] = 'Email Address'; +$txt['trackEdit_action_id_group'] = 'Primary Membergroup'; +$txt['trackEdit_action_additional_groups'] = 'Additional Membergroups'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Reports.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Reports.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,140 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Search.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Search.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,159 @@ +e.g. Orwell "Animal Farm" -movie'; + +$txt['search_engines_description'] = 'From this area you can decide in what detail you wish to track search engines as they index your forum as well as review search engine logs.'; +$txt['spider_mode'] = 'Search Engine Tracking Level
    Note higher level tracking increases server resource requirement.
    '; +$txt['spider_mode_off'] = 'Disabled'; +$txt['spider_mode_standard'] = 'Standard - Logs minimal spider activity.'; +$txt['spider_mode_high'] = 'High - Provides more accurate statistics.'; +$txt['spider_mode_vhigh'] = 'Very High - As for "High" but logs data about each page visited.'; +$txt['spider_settings_desc'] = 'You can change settings for spider tracking from this page. Note, if you wish to enable automatic pruning of the hit logs you can set this up here'; + +$txt['spider_group'] = 'Apply restrictive permissions from group
    To enable you to stop spiders indexing some pages.
    '; +$txt['spider_group_none'] = 'Disabled'; + +$txt['show_spider_online'] = 'Show spiders in the online list'; +$txt['show_spider_online_no'] = 'Not at all'; +$txt['show_spider_online_summary'] = 'Show spider quantity'; +$txt['show_spider_online_detail'] = 'Show spider names'; +$txt['show_spider_online_detail_admin'] = 'Show spider names - admin only'; + +$txt['spider_name'] = 'Spider Name'; +$txt['spider_last_seen'] = 'Last Seen'; +$txt['spider_last_never'] = 'Never'; +$txt['spider_agent'] = 'User Agent'; +$txt['spider_ip_info'] = 'IP Addresses'; +$txt['spiders_add'] = 'Add New Spider'; +$txt['spiders_edit'] = 'Edit Spider'; +$txt['spiders_remove_selected'] = 'Remove Selected Spiders'; +$txt['spider_remove_selected_confirm'] = 'Are you sure you wish to remove these spiders?\\n\\nAll associated statistics will also be deleted!'; +$txt['spiders_no_entries'] = 'There are currently no spiders configured.'; + +$txt['add_spider_desc'] = 'From this page you can edit the parameters against which a spider is categorised. If a guest\'s user agent/IP address matches those entered below it will be detected as a search engine spider and tracked as per the forum preferences.'; +$txt['spider_name_desc'] = 'Name by which the spider will be referred.'; +$txt['spider_agent_desc'] = 'User agent associated with this spider.'; +$txt['spider_ip_info_desc'] = 'Comma separated list of IP addresses associated with this spider.'; + +$txt['spider'] = 'Spider'; +$txt['spider_time'] = 'Time'; +$txt['spider_viewing'] = 'Viewing'; +$txt['spider_logs_empty'] = 'There are currently no spider log entries.'; +$txt['spider_logs_info'] = 'Note that logging of every spider action only occurs if tracking is set to either "high" or "very high". Detail of every spiders action is only logged if tracking is set to "very high".'; +$txt['spider_disabled'] = 'Disabled'; + +$txt['spider_logs_delete'] = 'Delete Entries'; +$txt['spider_logs_delete_older'] = 'Delete all entries older than'; +$txt['spider_logs_delete_day'] = 'days.'; +$txt['spider_logs_delete_submit'] = 'Delete'; +// Don't use entities in the below string. +$txt['spider_logs_delete_confirm'] = 'Are you sure you wish to empty out all log entries?'; + +$txt['spider_stats_select_month'] = 'Jump To Month'; +$txt['spider_stats_page_hits'] = 'Page Hits'; +$txt['spider_stats_no_entries'] = 'There are currently no spider statistics available.'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Settings.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Settings.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ +
    Author: The Simple Machines Team'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Stats.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Stats.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,44 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Themes.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Themes.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,148 @@ +
    Don\'t forget to look at the theme settings for your themes for layout options.'; +$txt['themeadmin_list_desc'] = 'From here, you can view the list of themes you currently have installed, change their paths and settings, and uninstall them.'; +$txt['themeadmin_reset_desc'] = 'Below you will see an interface to change the current theme-specific options for all your members. You will only see those themes that have their own set of settings.'; +$txt['themeadmin_edit_desc'] = 'Modify the stylesheet and source code of your installed themes. Please consult the documentation for more information.'; + +$txt['themeadmin_list_heading'] = 'Theme Settings Overview'; +$txt['themeadmin_list_tip'] = 'Remember, the layout settings may be different between one theme and another. Click on the theme\'s names below to set their options, change their directory or URL settings, or to find other options.'; +$txt['themeadmin_list_theme_dir'] = 'Theme directory (templates)'; +$txt['themeadmin_list_invalid'] = '(warning, this path is not correct!)'; +$txt['themeadmin_list_theme_url'] = 'URL to above directory'; +$txt['themeadmin_list_images_url'] = 'URL to images directory'; +$txt['themeadmin_list_reset'] = 'Reset Theme URLs and Directories'; +$txt['themeadmin_list_reset_dir'] = 'Base path to Themes directory'; +$txt['themeadmin_list_reset_url'] = 'Base URL to the same directory'; +$txt['themeadmin_list_reset_go'] = 'Attempt to reset all themes'; + +$txt['themeadmin_reset_tip'] = 'Each theme may have its own custom options for selection by your members. These include things like "quick reply", avatars and signatures, layout options, and other similar options. Here you can change the defaults or reset everyone\'s options.

    Please note that some themes may use the default options, in which case they will not have their own options.'; +$txt['themeadmin_reset_defaults'] = 'Configure guest and new user options for this theme'; +$txt['themeadmin_reset_defaults_current'] = 'options currently set.'; +$txt['themeadmin_reset_members'] = 'Change current options for all members using this theme'; +$txt['themeadmin_reset_remove'] = 'Remove all members\' options and use the defaults'; +$txt['themeadmin_reset_remove_current'] = 'members currently using their own options.'; +// Don't use entities in the below string. +$txt['themeadmin_reset_remove_confirm'] = 'Are you sure you want to remove all theme options?\\nThis may reset some custom profile fields as well.'; +$txt['themeadmin_reset_options_info'] = 'The options below will reset options for everyone. To change an option, select "change" in the box next to it, and then select a value for it. To use the default, select "remove". Otherwise, use "don\'t change" to keep it as-is.'; +$txt['themeadmin_reset_options_change'] = 'Change'; +$txt['themeadmin_reset_options_none'] = 'Don\'t change'; +$txt['themeadmin_reset_options_remove'] = 'Remove'; + +$txt['themeadmin_edit_browse'] = 'Browse the templates and files in this theme.'; +$txt['themeadmin_edit_style'] = 'Edit this theme\'s stylesheets.'; +$txt['themeadmin_edit_copy_template'] = 'Copy a template from the theme this is based on.'; +$txt['themeadmin_edit_exists'] = 'already exists'; +$txt['themeadmin_edit_do_copy'] = 'copy'; +$txt['themeadmin_edit_copy_warning'] = 'When SMF needs a template or language file which is not in the current theme, it looks in the theme it is based on, or the default theme.
    Unless you need to modify a template, it\'s better not to copy it.'; +$txt['themeadmin_edit_copy_confirm'] = 'Are you sure you want to copy this template?'; +$txt['themeadmin_edit_overwrite_confirm'] = 'Are you sure you want to copy this template over the one that already exists?\nThis will OVERWRITE any changes you\\\'ve made!'; +$txt['themeadmin_edit_no_copy'] = '(can\'t copy)'; +$txt['themeadmin_edit_filename'] = 'Filename'; +$txt['themeadmin_edit_modified'] = 'Last Modified'; +$txt['themeadmin_edit_size'] = 'Size'; +$txt['themeadmin_edit_bytes'] = 'B'; +$txt['themeadmin_edit_kilobytes'] = 'KB'; +$txt['themeadmin_edit_error'] = 'The file you tried to save generated the following error:'; +$txt['themeadmin_edit_on_line'] = 'Beginning on line'; +$txt['themeadmin_edit_preview'] = 'Preview'; +$txt['themeadmin_selectable'] = 'Themes that the user is able to select'; +$txt['themeadmin_themelist_link'] = 'Show the list of themes'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Who.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Who.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,156 @@ +Nothing, or nothing you can see...'; +$txt['who_unknown'] = 'Unknown Action'; +$txt['who_user'] = 'User'; +$txt['who_time'] = 'Time'; +$txt['who_action'] = 'Action'; +$txt['who_show1'] = 'Show '; +$txt['who_show_members_only'] = 'Members Only'; +$txt['who_show_guests_only'] = 'Guests Only'; +$txt['who_show_spiders_only'] = 'Spiders Only'; +$txt['who_show_all'] = 'Everyone'; +$txt['who_no_online_spiders'] = 'There are currently no spiders online.'; +$txt['who_no_online_guests'] = 'There are currently no guests online.'; +$txt['who_no_online_members'] = 'There are currently no members online.'; + +$txt['whospider_login'] = 'Viewing the login page.'; +$txt['whospider_register'] = 'Viewing the registration page.'; +$txt['whospider_reminder'] = 'Viewing the reminder page.'; + +$txt['whoall_activate'] = 'Activating their account.'; +$txt['whoall_buddy'] = 'Modifying their buddy list.'; +$txt['whoall_coppa'] = 'Filling out parent/guardian consent form.'; +$txt['whoall_credits'] = 'Viewing credits page.'; +$txt['whoall_emailuser'] = 'Sending email to another member.'; +$txt['whoall_groups'] = 'Viewing the member groups page.'; +$txt['whoall_help'] = 'Viewing the help page.'; +$txt['whoall_helpadmin'] = 'Viewing a help popup.'; +$txt['whoall_pm'] = 'Viewing their messages.'; +$txt['whoall_login'] = 'Logging into the forum.'; +$txt['whoall_login2'] = 'Logging into the forum.'; +$txt['whoall_logout'] = 'Logging out of the forum.'; +$txt['whoall_markasread'] = 'Marking topics read or unread.'; +$txt['whoall_modifykarma_applaud'] = 'Applauding a member.'; +$txt['whoall_modifykarma_smite'] = 'Smiting a member.'; +$txt['whoall_news'] = 'Viewing the news.'; +$txt['whoall_notify'] = 'Changing their notification settings.'; +$txt['whoall_notifyboard'] = 'Changing their notification settings.'; +$txt['whoall_openidreturn'] = 'Logging in using OpenID.'; +$txt['whoall_quickmod'] = 'Moderating a board.'; +$txt['whoall_recent'] = 'Viewing a list of recent topics.'; +$txt['whoall_register'] = 'Registering for an account on the forum.'; +$txt['whoall_register2'] = 'Registering for an account on the forum.'; +$txt['whoall_reminder'] = 'Requesting a password reminder.'; +$txt['whoall_reporttm'] = 'Reporting a topic to a moderator.'; +$txt['whoall_spellcheck'] = 'Using the spellchecker'; +$txt['whoall_unread'] = 'Viewing unread topics since their last visit.'; +$txt['whoall_unreadreplies'] = 'Viewing unread replies since their last visit.'; +$txt['whoall_who'] = 'Viewing Who\'s Online.'; + +$txt['whoall_collapse_collapse'] = 'Collapsing a category.'; +$txt['whoall_collapse_expand'] = 'Expanding a category.'; +$txt['whoall_pm_removeall'] = 'Removing all their messages.'; +$txt['whoall_pm_send'] = 'Sending a message.'; +$txt['whoall_pm_send2'] = 'Sending a message.'; + +$txt['whotopic_announce'] = 'Announcing the topic "%2$s".'; +$txt['whotopic_attachapprove'] = 'Approving an attachment.'; +$txt['whotopic_dlattach'] = 'Viewing an attachment.'; +$txt['whotopic_deletemsg'] = 'Deleting a message.'; +$txt['whotopic_editpoll'] = 'Editing the poll in "%2$s".'; +$txt['whotopic_editpoll2'] = 'Editing the poll in "%2$s".'; +$txt['whotopic_jsmodify'] = 'Modifying a post in "%2$s".'; +$txt['whotopic_lock'] = 'Locking the topic "%2$s".'; +$txt['whotopic_lockvoting'] = 'Locking the poll in "%2$s".'; +$txt['whotopic_mergetopics'] = 'Merging the topic "%2$s" with another topic.'; +$txt['whotopic_movetopic'] = 'Moving the topic "%2$s" to another board.'; +$txt['whotopic_movetopic2'] = 'Moving the topic "%2$s" to another board.'; +$txt['whotopic_post'] = 'Posting in %2$s.'; +$txt['whotopic_post2'] = 'Posting in %2$s.'; +$txt['whotopic_printpage'] = 'Printing the topic "%2$s".'; +$txt['whotopic_quickmod2'] = 'Moderating the topic %2$s.'; +$txt['whotopic_removepoll'] = 'Removing the poll in "%2$s".'; +$txt['whotopic_removetopic2'] = 'Removing the topic %2$s.'; +$txt['whotopic_sendtopic'] = 'Sending the topic "%2$s" to a friend.'; +$txt['whotopic_splittopics'] = 'Splitting the topic "%2$s" into two topics.'; +$txt['whotopic_sticky'] = 'Setting the topic "%2$s" as sticky.'; +$txt['whotopic_vote'] = 'Voting in %2$s.'; + +$txt['whopost_quotefast'] = 'Quoting a post from "%2$s".'; + +$txt['whoadmin_editagreement'] = 'Editing the registration agreement.'; +$txt['whoadmin_featuresettings'] = 'Editing forum features and options.'; +$txt['whoadmin_modlog'] = 'Viewing the moderator log.'; +$txt['whoadmin_serversettings'] = 'Editing the forum settings.'; +$txt['whoadmin_packageget'] = 'Getting packages.'; +$txt['whoadmin_packages'] = 'Viewing the package manager.'; +$txt['whoadmin_permissions'] = 'Editing the forum permissions.'; +$txt['whoadmin_pgdownload'] = 'Downloading a package.'; +$txt['whoadmin_theme'] = 'Editing the theme settings.'; +$txt['whoadmin_trackip'] = 'Tracking an IP address.'; + +$txt['whoallow_manageboards'] = 'Editing the board and category settings.'; +$txt['whoallow_admin'] = 'Viewing the administration center.'; +$txt['whoallow_ban'] = 'Editing the ban list.'; +$txt['whoallow_boardrecount'] = 'Recounting the forum totals.'; +$txt['whoallow_calendar'] = 'Viewing the calendar.'; +$txt['whoallow_editnews'] = 'Editing the news.'; +$txt['whoallow_mailing'] = 'Sending a forum email.'; +$txt['whoallow_maintain'] = 'Performing routine forum maintenance.'; +$txt['whoallow_manageattachments'] = 'Managing the attachments.'; +$txt['whoallow_moderate'] = 'Viewing the Moderation Center.'; +$txt['whoallow_mlist'] = 'Viewing the memberlist.'; +$txt['whoallow_optimizetables'] = 'Optimizing the database tables.'; +$txt['whoallow_repairboards'] = 'Repairing the database tables.'; +$txt['whoallow_search'] = 'Searching the forum.'; +$txt['whoallow_search2'] = 'Viewing the results of a search.'; +$txt['whoallow_setcensor'] = 'Editing the censor text.'; +$txt['whoallow_setreserve'] = 'Editing the reserved names.'; +$txt['whoallow_stats'] = 'Viewing the forum stats.'; +$txt['whoallow_viewErrorLog'] = 'Viewing the error log.'; +$txt['whoallow_viewmembers'] = 'Viewing a list of members.'; + +$txt['who_topic'] = 'Viewing the topic %2$s.'; +$txt['who_board'] = 'Viewing the board %2$s.'; +$txt['who_index'] = 'Viewing the board index of ' . $context['forum_name'] . '.'; +$txt['who_viewprofile'] = 'Viewing %2$s\'s profile.'; +$txt['who_profile'] = 'Editing the profile of %2$s.'; +$txt['who_post'] = 'Posting a new topic in %2$s.'; +$txt['who_poll'] = 'Posting a new poll in %2$s.'; + +// Credits text +$txt['credits'] = 'Credits'; +$txt['credits_intro'] = 'Simple Machines wants to thank everyone who helped make SMF 2.0 what it is today; shaping and directing our project, all through the thick and the thin. It wouldn\'t have been possible without you. This includes our users and especially Charter Members - thanks for installing and using our software as well as providing valuable feedback, bug reports, and opinions.'; +$txt['credits_team'] = 'The Team'; +$txt['credits_special'] = 'Special Thanks'; +$txt['credits_and'] = 'and'; +$txt['credits_anyone'] = 'And for anyone we may have missed, thank you!'; +$txt['credits_copyright'] = 'Copyrights'; +$txt['credits_forum'] = 'Forum'; +$txt['credits_modifications'] = 'Modifications'; +$txt['credits_groups_ps'] = 'Project Support'; +$txt['credits_groups_dev'] = 'Developers'; +$txt['credits_groups_support'] = 'Support Specialists'; +$txt['credits_groups_customize'] = 'Customizers'; +$txt['credits_groups_docs'] = 'Documentation Writers'; +$txt['credits_groups_marketing'] = 'Marketing'; +$txt['credits_groups_internationalizers'] = 'Localizers'; +$txt['credits_groups_servers'] = 'Servers Administrators'; +$txt['credits_groups_site'] = 'Site Administrators'; +// Replace "English" with the name of this language pack in the string below. +$txt['credits_groups_translation'] = 'English Translation'; +$txt['credits_groups_translators'] = 'Language Translators'; +$txt['credits_translators_message'] = 'Thank you for your efforts which make it possible for people all around the world to use SMF.'; +$txt['credits_groups_consultants'] = 'Consulting Developers'; +$txt['credits_groups_beta'] = 'Beta Testers'; +$txt['credits_beta_message'] = 'The invaluable few who tirelessly find bugs, provide feedback, and drive the developers crazier.'; +$txt['credits_groups_founder'] = 'Founding Father of SMF'; +$txt['credits_groups_orignal_pm'] = 'Original Project Managers'; + +// List of people who have made more than a token contribution to this translation. (blank for English) +$txt['translation_credits'] = array(); +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/Wireless.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/Wireless.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,49 @@ +%1$d new)'; +$txt['wireless_pm_by'] = 'by'; +$txt['wireless_pm_add_buddy'] = 'Add buddy'; +$txt['wireless_pm_select_buddy'] = 'Select a buddy'; +$txt['wireless_pm_search_member'] = 'Search member'; +$txt['wireless_pm_search_name'] = 'Name'; +$txt['wireless_pm_no_recipients'] = 'No recipients (yet)'; +$txt['wireless_pm_reply'] = 'Reply'; +$txt['wireless_pm_reply_all'] = 'Reply All'; +$txt['wireless_pm_reply_to'] = 'Reply to'; + +$txt['wireless_recent_unread_posts'] = 'Unread posts'; +$txt['wireless_recent_unread_replies'] = 'Unread replies'; + +$txt['wireless_display_edit'] = 'Edit'; +$txt['wireless_display_moderate'] = 'Moderate'; +$txt['wireless_display_sticky'] = 'Sticky'; +$txt['wireless_display_unsticky'] = 'Un-Sticky'; +$txt['wireless_display_lock'] = 'Lock'; +$txt['wireless_display_unlock'] = 'Unlock'; + +$txt['wireless_end_code'] = 'End code'; +$txt['wireless_end_quote'] = 'End quote'; + +$txt['wireless_profile_pm'] = 'Send Personal Message'; +$txt['wireless_ban_ip'] = 'Ban on IP'; +$txt['wireless_ban_hostname'] = 'Ban on Hostname'; +$txt['wireless_ban_email'] = 'Ban on Email'; +$txt['wireless_additional_info'] = 'Additional Information'; +$txt['wireless_go_to_full_version'] = 'Go to full version'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/index.english.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/index.english.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,753 @@ + 'January'. (or translated, of course.) +$txt['months'] = array(1 => 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'); +$txt['months_titles'] = array(1 => 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'); +$txt['months_short'] = array(1 => 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); + +$txt['time_am'] = 'am'; +$txt['time_pm'] = 'pm'; + +$txt['newmessages0'] = 'is new'; +$txt['newmessages1'] = 'are new'; +$txt['newmessages3'] = 'New'; +$txt['newmessages4'] = ','; + +$txt['admin'] = 'Admin'; +$txt['moderate'] = 'Moderate'; + +$txt['save'] = 'Save'; + +$txt['modify'] = 'Modify'; +$txt['forum_index'] = '%1$s - Index'; +$txt['members'] = 'Members'; +$txt['board_name'] = 'Board name'; +$txt['posts'] = 'Posts'; + +$txt['member_postcount'] = 'Posts'; +$txt['no_subject'] = '(No subject)'; +$txt['view_profile'] = 'View Profile'; +$txt['guest_title'] = 'Guest'; +$txt['author'] = 'Author'; +$txt['on'] = 'on'; +$txt['remove'] = 'Remove'; +$txt['start_new_topic'] = 'Start new topic'; + +$txt['login'] = 'Login'; +// Use numeric entities in the below string. +$txt['username'] = 'Username'; +$txt['password'] = 'Password'; + +$txt['username_no_exist'] = 'That username does not exist.'; +$txt['no_user_with_email'] = 'There are no usernames associated with that email.'; + +$txt['board_moderator'] = 'Board Moderator'; +$txt['remove_topic'] = 'Remove Topic'; +$txt['topics'] = 'Topics'; +$txt['modify_msg'] = 'Modify message'; +$txt['name'] = 'Name'; +$txt['email'] = 'Email'; +$txt['subject'] = 'Subject'; +$txt['message'] = 'Message'; +$txt['redirects'] = 'Redirects'; +$txt['quick_modify'] = 'Modify Inline'; + +$txt['choose_pass'] = 'Choose password'; +$txt['verify_pass'] = 'Verify password'; +$txt['position'] = 'Position'; + +$txt['profile_of'] = 'View the profile of'; +$txt['total'] = 'Total'; +$txt['posts_made'] = 'Posts'; +$txt['website'] = 'Website'; +$txt['register'] = 'Register'; +$txt['warning_status'] = 'Warning Status'; +$txt['user_warn_watch'] = 'User is on moderator watch list'; +$txt['user_warn_moderate'] = 'User posts join approval queue'; +$txt['user_warn_mute'] = 'User is banned from posting'; +$txt['warn_watch'] = 'Watched'; +$txt['warn_moderate'] = 'Moderated'; +$txt['warn_mute'] = 'Muted'; + +$txt['message_index'] = 'Message Index'; +$txt['news'] = 'News'; +$txt['home'] = 'Home'; + +$txt['lock_unlock'] = 'Lock/Unlock Topic'; +$txt['post'] = 'Post'; +$txt['error_occured'] = 'An Error Has Occurred!'; +$txt['at'] = 'at'; +$txt['logout'] = 'Logout'; +$txt['started_by'] = 'Started by'; +$txt['replies'] = 'Replies'; +$txt['last_post'] = 'Last post'; +$txt['admin_login'] = 'Administration Login'; +// Use numeric entities in the below string. +$txt['topic'] = 'Topic'; +$txt['help'] = 'Help'; +$txt['notify'] = 'Notify'; +$txt['unnotify'] = 'Unnotify'; +$txt['notify_request'] = 'Do you want a notification email if someone replies to this topic?'; +// Use numeric entities in the below string. +$txt['regards_team'] = 'Regards,' . "\n" . 'The ' . $context['forum_name'] . ' Team.'; +$txt['notify_replies'] = 'Notify of replies'; +$txt['move_topic'] = 'Move Topic'; +$txt['move_to'] = 'Move to'; +$txt['pages'] = 'Pages'; +$txt['users_active'] = 'Users active in past %1$d minutes'; +$txt['personal_messages'] = 'Personal Messages'; +$txt['reply_quote'] = 'Reply with quote'; +$txt['reply'] = 'Reply'; +$txt['reply_noun'] = 'Reply'; +$txt['approve'] = 'Approve'; +$txt['approve_all'] = 'approve all'; +$txt['awaiting_approval'] = 'Awaiting Approval'; +$txt['attach_awaiting_approve'] = 'Attachments awaiting approval'; +$txt['post_awaiting_approval'] = 'Note: This message is awaiting approval by a moderator.'; +$txt['there_are_unapproved_topics'] = 'There are %1$s topics and %2$s posts awaiting approval in this board. Click here to view them all.'; + +$txt['msg_alert_none'] = 'No messages...'; +$txt['msg_alert_you_have'] = 'you have'; +$txt['msg_alert_messages'] = 'messages'; +$txt['remove_message'] = 'Remove this message'; + +$txt['online_users'] = 'Users Online'; +$txt['personal_message'] = 'Personal Message'; +$txt['jump_to'] = 'Jump to'; +$txt['go'] = 'go'; +$txt['are_sure_remove_topic'] = 'Are you sure you want to remove this topic?'; +$txt['yes'] = 'Yes'; +$txt['no'] = 'No'; + +$txt['search_end_results'] = 'End of results'; +$txt['search_on'] = 'on'; + +$txt['search'] = 'Search'; +$txt['all'] = 'All'; + +$txt['back'] = 'Back'; +$txt['password_reminder'] = 'Password reminder'; +$txt['topic_started'] = 'Topic started by'; +$txt['title'] = 'Title'; +$txt['post_by'] = 'Post by'; +$txt['memberlist_searchable'] = 'Searchable list of all registered members.'; +$txt['welcome_member'] = 'Please welcome'; +$txt['admin_center'] = 'Administration Center'; +$txt['last_edit'] = 'Last Edit'; +$txt['notify_deactivate'] = 'Would you like to deactivate notification on this topic?'; + +$txt['recent_posts'] = 'Recent Posts'; + +$txt['location'] = 'Location'; +$txt['gender'] = 'Gender'; +$txt['date_registered'] = 'Date Registered'; + +$txt['recent_view'] = 'View the most recent posts on the forum.'; +$txt['recent_updated'] = 'is the most recently updated topic'; + +$txt['male'] = 'Male'; +$txt['female'] = 'Female'; + +$txt['error_invalid_characters_username'] = 'Invalid character used in Username.'; + +$txt['welcome_guest'] = 'Welcome, %1$s. Please login or register.'; +$txt['login_or_register'] = 'Please login or register.'; +$txt['welcome_guest_activate'] = '
    Did you miss your activation email?'; +$txt['hello_member'] = 'Hey,'; +// Use numeric entities in the below string. +$txt['hello_guest'] = 'Welcome,'; +$txt['welmsg_hey'] = 'Hey,'; +$txt['welmsg_welcome'] = 'Welcome,'; +$txt['welmsg_please'] = 'Please'; +$txt['select_destination'] = 'Please select a destination'; + +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['posted_by'] = 'Posted by'; + +$txt['icon_smiley'] = 'Smiley'; +$txt['icon_angry'] = 'Angry'; +$txt['icon_cheesy'] = 'Cheesy'; +$txt['icon_laugh'] = 'Laugh'; +$txt['icon_sad'] = 'Sad'; +$txt['icon_wink'] = 'Wink'; +$txt['icon_grin'] = 'Grin'; +$txt['icon_shocked'] = 'Shocked'; +$txt['icon_cool'] = 'Cool'; +$txt['icon_huh'] = 'Huh'; +$txt['icon_rolleyes'] = 'Roll Eyes'; +$txt['icon_tongue'] = 'Tongue'; +$txt['icon_embarrassed'] = 'Embarrassed'; +$txt['icon_lips'] = 'Lips sealed'; +$txt['icon_undecided'] = 'Undecided'; +$txt['icon_kiss'] = 'Kiss'; +$txt['icon_cry'] = 'Cry'; + +$txt['moderator'] = 'Moderator'; +$txt['moderators'] = 'Moderators'; + +$txt['mark_board_read'] = 'Mark Topics as Read for this Board'; +$txt['views'] = 'Views'; +$txt['new'] = 'New'; + +$txt['view_all_members'] = 'View All Members'; +$txt['view'] = 'View'; + +$txt['viewing_members'] = 'Viewing Members %1$s to %2$s'; +$txt['of_total_members'] = 'of %1$s total members'; + +$txt['forgot_your_password'] = 'Forgot your password?'; + +$txt['date'] = 'Date'; +// Use numeric entities in the below string. +$txt['from'] = 'From'; +$txt['check_new_messages'] = 'Check for new messages'; +$txt['to'] = 'To'; + +$txt['board_topics'] = 'Topics'; +$txt['members_title'] = 'Members'; +$txt['members_list'] = 'Members List'; +$txt['new_posts'] = 'New Posts'; +$txt['old_posts'] = 'No New Posts'; +$txt['redirect_board'] = 'Redirect Board'; + +$txt['sendtopic_send'] = 'Send'; +$txt['report_sent'] = 'Your report has been sent successfully.'; + +$txt['time_offset'] = 'Time Offset'; +$txt['or'] = 'or'; + +$txt['no_matches'] = 'Sorry, no matches were found'; + +$txt['notification'] = 'Notification'; + +$txt['your_ban'] = 'Sorry %1$s, you are banned from using this forum!'; +$txt['your_ban_expires'] = 'This ban is set to expire %1$s.'; +$txt['your_ban_expires_never'] = 'This ban is not set to expire.'; +$txt['ban_continue_browse'] = 'You may continue to browse the forum as a guest.'; + +$txt['mark_as_read'] = 'Mark ALL messages as read'; + +$txt['hot_topics'] = 'Hot Topic (More than %1$d replies)'; +$txt['very_hot_topics'] = 'Very Hot Topic (More than %1$d replies)'; +$txt['locked_topic'] = 'Locked Topic'; +$txt['normal_topic'] = 'Normal Topic'; +$txt['participation_caption'] = 'Topic you have posted in'; + +$txt['go_caps'] = 'GO'; + +$txt['print'] = 'Print'; +$txt['profile'] = 'Profile'; +$txt['topic_summary'] = 'Topic Summary'; +$txt['not_applicable'] = 'N/A'; +$txt['message_lowercase'] = 'message'; +$txt['name_in_use'] = 'This name is already in use by another member.'; + +$txt['total_members'] = 'Total Members'; +$txt['total_posts'] = 'Total Posts'; +$txt['total_topics'] = 'Total Topics'; + +$txt['mins_logged_in'] = 'Minutes to stay logged in'; + +$txt['preview'] = 'Preview'; +$txt['always_logged_in'] = 'Always stay logged in'; + +$txt['logged'] = 'Logged'; +// Use numeric entities in the below string. +$txt['ip'] = 'IP'; + +$txt['www'] = 'WWW'; + +$txt['by'] = 'by'; + +$txt['hours'] = 'hours'; +$txt['days_word'] = 'days'; + +$txt['newest_member'] = ', our newest member.'; + +$txt['search_for'] = 'Search for'; + +$txt['aim'] = 'AIM'; +// In this string, please use +'s for spaces. +$txt['aim_default_message'] = 'Hi.+Are+you+there?'; +$txt['aim_title'] = 'AOL Instant Messenger'; +$txt['icq'] = 'ICQ'; +$txt['icq_title'] = 'ICQ Messenger'; +$txt['msn'] = 'MSN'; +$txt['msn_title'] = 'MSN Messenger'; +$txt['yim'] = 'YIM'; +$txt['yim_title'] = 'Yahoo Instant Messenger'; + +$txt['maintain_mode_on'] = 'Remember, this forum is in \'Maintenance Mode\'.'; + +$txt['read'] = 'Read'; +$txt['times'] = 'times'; + +$txt['forum_stats'] = 'Forum Stats'; +$txt['latest_member'] = 'Latest Member'; +$txt['total_cats'] = 'Total Categories'; +$txt['latest_post'] = 'Latest Post'; + +$txt['you_have'] = 'You\'ve got'; +$txt['click'] = 'Click'; +$txt['here'] = 'here'; +$txt['to_view'] = 'to view them.'; + +$txt['total_boards'] = 'Total Boards'; + +$txt['print_page'] = 'Print Page'; + +$txt['valid_email'] = 'This must be a valid email address.'; + +$txt['geek'] = 'I am a geek!!'; +$txt['info_center_title'] = '%1$s - Info Center'; + +$txt['send_topic'] = 'Send this topic'; + +$txt['sendtopic_title'] = 'Send the topic "%1$s" to a friend.'; +$txt['sendtopic_sender_name'] = 'Your name'; +$txt['sendtopic_sender_email'] = 'Your email address'; +$txt['sendtopic_receiver_name'] = 'Recipient\'s name'; +$txt['sendtopic_receiver_email'] = 'Recipient\'s email address'; +$txt['sendtopic_comment'] = 'Add a comment'; + +$txt['allow_user_email'] = 'Allow users to email me'; + +$txt['check_all'] = 'Check all'; + +// Use numeric entities in the below string. +$txt['database_error'] = 'Database Error'; +$txt['try_again'] = 'Please try again. If you come back to this error screen, report the error to an administrator.'; +$txt['file'] = 'File'; +$txt['line'] = 'Line'; +// Use numeric entities in the below string. +$txt['tried_to_repair'] = 'SMF has detected and automatically tried to repair an error in your database. If you continue to have problems, or continue to receive these emails, please contact your host.'; +$txt['database_error_versions'] = 'Note: It appears that your database may require an upgrade. Your forum\'s files are currently at version %1$s, while your database is at version %2$s. The above error might possibly go away if you execute the latest version of upgrade.php.'; +$txt['template_parse_error'] = 'Template Parse Error!'; +$txt['template_parse_error_message'] = 'It seems something has gone sour on the forum with the template system. This problem should only be temporary, so please come back later and try again. If you continue to see this message, please contact the administrator.

    You can also try refreshing this page.'; +$txt['template_parse_error_details'] = 'There was a problem loading the %1$s template or language file. Please check the syntax and try again - remember, single quotes (\') often have to be escaped with a slash (\\). To see more specific error information from PHP, try accessing the file directly.

    You may want to try to refresh this page or use the default theme.'; + +$txt['today'] = 'Today at '; +$txt['yesterday'] = 'Yesterday at '; +$txt['new_poll'] = 'New poll'; +$txt['poll_question'] = 'Question'; +$txt['poll_vote'] = 'Submit Vote'; +$txt['poll_total_voters'] = 'Total Members Voted'; +$txt['shortcuts'] = 'shortcuts: hit alt+s to submit/post or alt+p to preview'; +$txt['shortcuts_firefox'] = 'shortcuts: hit shift+alt+s to submit/post or shift+alt+p to preview'; +$txt['poll_results'] = 'View results'; +$txt['poll_lock'] = 'Lock Voting'; +$txt['poll_unlock'] = 'Unlock Voting'; +$txt['poll_edit'] = 'Edit Poll'; +$txt['poll'] = 'Poll'; +$txt['one_day'] = '1 Day'; +$txt['one_week'] = '1 Week'; +$txt['one_month'] = '1 Month'; +$txt['forever'] = 'Forever'; +$txt['quick_login_dec'] = 'Login with username, password and session length'; +$txt['one_hour'] = '1 Hour'; +$txt['moved'] = 'MOVED'; +$txt['moved_why'] = 'Please enter a brief description as to
    why this topic is being moved.'; +$txt['board'] = 'Board'; +$txt['in'] = 'in'; +$txt['sticky_topic'] = 'Sticky Topic'; + +$txt['delete'] = 'Delete'; + +$txt['your_pms'] = 'Your Personal Messages'; + +$txt['kilobyte'] = 'kB'; + +$txt['more_stats'] = '[More Stats]'; + +// Use numeric entities in the below three strings. +$txt['code'] = 'Code'; +$txt['code_select'] = '[Select]'; +$txt['quote_from'] = 'Quote from'; +$txt['quote'] = 'Quote'; + +$txt['merge_to_topic_id'] = 'ID of target topic'; +$txt['split'] = 'Split Topic'; +$txt['merge'] = 'Merge Topics'; +$txt['subject_new_topic'] = 'Subject For New Topic'; +$txt['split_this_post'] = 'Only split this post.'; +$txt['split_after_and_this_post'] = 'Split topic after and including this post.'; +$txt['select_split_posts'] = 'Select posts to split.'; +$txt['new_topic'] = 'New Topic'; +$txt['split_successful'] = 'Topic successfully split into two topics.'; +$txt['origin_topic'] = 'Origin Topic'; +$txt['please_select_split'] = 'Please select which posts you wish to split.'; +$txt['merge_successful'] = 'Topics successfully merged.'; +$txt['new_merged_topic'] = 'Newly Merged Topic'; +$txt['topic_to_merge'] = 'Topic to be merged'; +$txt['target_board'] = 'Target board'; +$txt['target_topic'] = 'Target topic'; +$txt['merge_confirm'] = 'Are you sure you want to merge'; +$txt['with'] = 'with'; +$txt['merge_desc'] = 'This function will merge the messages of two topics into one topic. The messages will be sorted according to the time of posting. Therefore the earliest posted message will be the first message of the merged topic.'; + +$txt['set_sticky'] = 'Set topic sticky'; +$txt['set_nonsticky'] = 'Set topic non-sticky'; +$txt['set_lock'] = 'Lock topic'; +$txt['set_unlock'] = 'Unlock topic'; + +$txt['search_advanced'] = 'Advanced search'; + +$txt['security_risk'] = 'MAJOR SECURITY RISK:'; +$txt['not_removed'] = 'You have not removed '; +$txt['not_removed_extra'] ='%1$s is a backup of %2$s that was not generated by SMF. It can be accessed directly and used to gain unauthorised access to your forum. You should delete it immediately.'; + +$txt['cache_writable_head'] = 'Performance Warning'; +$txt['cache_writable'] = 'The cache directory is not writable - this will adversely affect the performance of your forum.'; + +$txt['page_created'] = 'Page created in '; +$txt['seconds_with'] = ' seconds with '; +$txt['queries'] = ' queries.'; + +$txt['report_to_mod_func'] = 'Use this function to inform the moderators and administrators of an abusive or wrongly posted message.
    Please note that your email address will be revealed to the moderators if you use this.'; + +$txt['online'] = 'Online'; +$txt['offline'] = 'Offline'; +$txt['pm_online'] = 'Personal Message (Online)'; +$txt['pm_offline'] = 'Personal Message (Offline)'; +$txt['status'] = 'Status'; + +$txt['go_up'] = 'Go Up'; +$txt['go_down'] = 'Go Down'; + +$forum_copyright = '%1$s | + SMF © 2013, Simple Machines'; + +$txt['birthdays'] = 'Birthdays:'; +$txt['events'] = 'Events:'; +$txt['birthdays_upcoming'] = 'Upcoming Birthdays:'; +$txt['events_upcoming'] = 'Upcoming Events:'; +// Prompt for holidays in the calendar, leave blank to just display the holiday's name. +$txt['calendar_prompt'] = ''; +$txt['calendar_month'] = 'Month:'; +$txt['calendar_year'] = 'Year:'; +$txt['calendar_day'] = 'Day:'; +$txt['calendar_event_title'] = 'Event Title'; +$txt['calendar_event_options'] = 'Event Options'; +$txt['calendar_post_in'] = 'Post In:'; +$txt['calendar_edit'] = 'Edit Event'; +$txt['event_delete_confirm'] = 'Delete this event?'; +$txt['event_delete'] = 'Delete Event'; +$txt['calendar_post_event'] = 'Post Event'; +$txt['calendar'] = 'Calendar'; +$txt['calendar_link'] = 'Link to Calendar'; +$txt['calendar_upcoming'] = 'Upcoming Calendar'; +$txt['calendar_today'] = 'Today\'s Calendar'; +$txt['calendar_week'] = 'Week'; +$txt['calendar_week_title'] = 'Week %1$d of %2$d'; +$txt['calendar_numb_days'] = 'Number of Days:'; +$txt['calendar_how_edit'] = 'how do you edit these events?'; +$txt['calendar_link_event'] = 'Link Event To Post:'; +$txt['calendar_confirm_delete'] = 'Are you sure you want to delete this event?'; +$txt['calendar_linked_events'] = 'Linked Events'; +$txt['calendar_click_all'] = 'click to see all %1$s'; + +$txt['moveTopic1'] = 'Post a redirection topic'; +$txt['moveTopic2'] = 'Change the topic\'s subject'; +$txt['moveTopic3'] = 'New subject'; +$txt['moveTopic4'] = 'Change every message\'s subject'; +$txt['move_topic_unapproved_js'] = 'Warning! This topic has not yet been approved.\\n\\nIt is not recommended that you create a redirection topic unless you intend to approve the post immediately following the move.'; + +$txt['theme_template_error'] = 'Unable to load the \'%1$s\' template.'; +$txt['theme_language_error'] = 'Unable to load the \'%1$s\' language file.'; + +$txt['parent_boards'] = 'Child Boards'; + +$txt['smtp_no_connect'] = 'Could not connect to SMTP host'; +$txt['smtp_port_ssl'] = 'SMTP port setting incorrect; it should be 465 for SSL servers.'; +$txt['smtp_bad_response'] = 'Couldn\'t get mail server response codes'; +$txt['smtp_error'] = 'Ran into problems sending Mail. Error: '; +$txt['mail_send_unable'] = 'Unable to send mail to the email address \'%1$s\''; + +$txt['mlist_search'] = 'Search For Members'; +$txt['mlist_search_again'] = 'Search again'; +$txt['mlist_search_email'] = 'Search by email address'; +$txt['mlist_search_messenger'] = 'Search by messenger nickname'; +$txt['mlist_search_group'] = 'Search by position'; +$txt['mlist_search_name'] = 'Search by name'; +$txt['mlist_search_website'] = 'Search by website'; +$txt['mlist_search_results'] = 'Search results for'; +$txt['mlist_search_by'] = 'Search by %1$s'; +$txt['mlist_menu_view'] = 'View the memberlist'; + +$txt['attach_downloaded'] = 'downloaded'; +$txt['attach_viewed'] = 'viewed'; +$txt['attach_times'] = 'times'; + +$txt['settings'] = 'Settings'; +$txt['never'] = 'Never'; +$txt['more'] = 'more'; + +$txt['hostname'] = 'Hostname'; +$txt['you_are_post_banned'] = 'Sorry %1$s, you are banned from posting and sending personal messages on this forum.'; +$txt['ban_reason'] = 'Reason'; + +$txt['tables_optimized'] = 'Database tables optimized'; + +$txt['add_poll'] = 'Add poll'; +$txt['poll_options6'] = 'You may only select up to %1$s options.'; +$txt['poll_remove'] = 'Remove Poll'; +$txt['poll_remove_warn'] = 'Are you sure you want to remove this poll from the topic?'; +$txt['poll_results_expire'] = 'Results will be shown when voting has closed'; +$txt['poll_expires_on'] = 'Voting closes'; +$txt['poll_expired_on'] = 'Voting closed'; +$txt['poll_change_vote'] = 'Remove Vote'; +$txt['poll_return_vote'] = 'Voting options'; +$txt['poll_cannot_see'] = 'You cannot see the results of this poll at the moment.'; + +$txt['quick_mod_approve'] = 'Approve selected'; +$txt['quick_mod_remove'] = 'Remove selected'; +$txt['quick_mod_lock'] = 'Lock/Unlock selected'; +$txt['quick_mod_sticky'] = 'Sticky/Unsticky selected'; +$txt['quick_mod_move'] = 'Move selected to'; +$txt['quick_mod_merge'] = 'Merge selected'; +$txt['quick_mod_markread'] = 'Mark selected read'; +$txt['quick_mod_go'] = 'Go!'; +$txt['quickmod_confirm'] = 'Are you sure you want to do this?'; + +$txt['spell_check'] = 'Spell Check'; + +$txt['quick_reply'] = 'Quick Reply'; +$txt['quick_reply_desc'] = 'With Quick-Reply you can write a post when viewing a topic without loading a new page. You can still use bulletin board code and smileys as you would in a normal post.'; +$txt['quick_reply_warning'] = 'Warning: this topic is currently locked! Only admins and moderators can reply.'; +$txt['quick_reply_verification'] = 'After submitting your post you will be directed to the regular post page to verify your post %1$s.'; +$txt['quick_reply_verification_guests'] = '(required for all guests)'; +$txt['quick_reply_verification_posts'] = '(required for all users with less than %1$d posts)'; +$txt['wait_for_approval'] = 'Note: this post will not display until it\'s been approved by a moderator.'; + +$txt['notification_enable_board'] = 'Are you sure you wish to enable notification of new topics for this board?'; +$txt['notification_disable_board'] = 'Are you sure you wish to disable notification of new topics for this board?'; +$txt['notification_enable_topic'] = 'Are you sure you wish to enable notification of new replies for this topic?'; +$txt['notification_disable_topic'] = 'Are you sure you wish to disable notification of new replies for this topic?'; + +$txt['report_to_mod'] = 'Report to moderator'; +$txt['issue_warning_post'] = 'Issue a warning because of this message'; + +$txt['unread_topics_visit'] = 'Recent Unread Topics'; +$txt['unread_topics_visit_none'] = 'No unread topics found since your last visit. Click here to try all unread topics.'; +$txt['unread_topics_all'] = 'All Unread Topics'; +$txt['unread_replies'] = 'Updated Topics'; + +$txt['who_title'] = 'Who\'s Online'; +$txt['who_and'] = ' and '; +$txt['who_viewing_topic'] = ' are viewing this topic.'; +$txt['who_viewing_board'] = ' are viewing this board.'; +$txt['who_member'] = 'Member'; + +// No longer used by default theme, but for backwards compat +$txt['powered_by_php'] = 'Powered by PHP'; +$txt['powered_by_mysql'] = 'Powered by MySQL'; +$txt['valid_css'] = 'Valid CSS!'; + +// Current footer strings +$txt['valid_html'] = 'Valid HTML 4.01!'; +$txt['valid_xhtml'] = 'Valid XHTML 1.0!'; +$txt['wap2'] = 'WAP2'; +$txt['rss'] = 'RSS'; +$txt['xhtml'] = 'XHTML'; +$txt['html'] = 'HTML'; + +$txt['guest'] = 'Guest'; +$txt['guests'] = 'Guests'; +$txt['user'] = 'User'; +$txt['users'] = 'Users'; +$txt['hidden'] = 'Hidden'; +$txt['buddy'] = 'Buddy'; +$txt['buddies'] = 'Buddies'; +$txt['most_online_ever'] = 'Most Online Ever'; +$txt['most_online_today'] = 'Most Online Today'; + +$txt['merge_select_target_board'] = 'Select the target board of the merged topic'; +$txt['merge_select_poll'] = 'Select which poll the merged topic should have'; +$txt['merge_topic_list'] = 'Select topics to be merged'; +$txt['merge_select_subject'] = 'Select subject of merged topic'; +$txt['merge_custom_subject'] = 'Custom subject'; +$txt['merge_enforce_subject'] = 'Change the subject of all the messages'; +$txt['merge_include_notifications'] = 'Include notifications?'; +$txt['merge_check'] = 'Merge?'; +$txt['merge_no_poll'] = 'No poll'; + +$txt['response_prefix'] = 'Re: '; +$txt['current_icon'] = 'Current Icon'; +$txt['message_icon'] = 'Message Icon'; + +$txt['smileys_current'] = 'Current Smiley Set'; +$txt['smileys_none'] = 'No Smileys'; +$txt['smileys_forum_board_default'] = 'Forum/Board Default'; + +$txt['search_results'] = 'Search Results'; +$txt['search_no_results'] = 'Sorry, no matches were found'; + +$txt['totalTimeLogged1'] = 'Total time logged in: '; +$txt['totalTimeLogged2'] = ' days, '; +$txt['totalTimeLogged3'] = ' hours and '; +$txt['totalTimeLogged4'] = ' minutes.'; +$txt['totalTimeLogged5'] = 'd '; +$txt['totalTimeLogged6'] = 'h '; +$txt['totalTimeLogged7'] = 'm'; + +$txt['approve_thereis'] = 'There is'; +$txt['approve_thereare'] = 'There are'; +$txt['approve_member'] = 'one member'; +$txt['approve_members'] = 'members'; +$txt['approve_members_waiting'] = 'awaiting approval.'; + +$txt['notifyboard_turnon'] = 'Do you want a notification email when someone posts a new topic in this board?'; +$txt['notifyboard_turnoff'] = 'Are you sure you do not want to receive new topic notifications for this board?'; + +$txt['activate_code'] = 'Your activation code is'; + +$txt['find_members'] = 'Find Members'; +$txt['find_username'] = 'Name, username, or email address'; +$txt['find_buddies'] = 'Show Buddies Only?'; +$txt['find_wildcards'] = 'Allowed Wildcards: *, ?'; +$txt['find_no_results'] = 'No results found'; +$txt['find_results'] = 'Results'; +$txt['find_close'] = 'Close'; + +$txt['unread_since_visit'] = 'Show unread posts since last visit.'; +$txt['show_unread_replies'] = 'Show new replies to your posts.'; + +$txt['change_color'] = 'Change Color'; + +$txt['quickmod_delete_selected'] = 'Remove Selected'; + +// In this string, don't use entities. (&, etc.) +$txt['show_personal_messages'] = 'You have received one or more new personal messages.\\nWould you like to open a new window to view them?'; + +$txt['previous_next_back'] = '« previous'; +$txt['previous_next_forward'] = 'next »'; + +$txt['movetopic_auto_board'] = '[BOARD]'; +$txt['movetopic_auto_topic'] = '[TOPIC LINK]'; +$txt['movetopic_default'] = 'This topic has been moved to ' . $txt['movetopic_auto_board'] . ".\n\n" . $txt['movetopic_auto_topic']; + +$txt['upshrink_description'] = 'Shrink or expand the header.'; + +$txt['mark_unread'] = 'Mark unread'; + +$txt['ssi_not_direct'] = 'Please don\'t access SSI.php by URL directly; you may want to use the path (%1$s) or add ?ssi_function=something.'; +$txt['ssi_session_broken'] = 'SSI.php was unable to load a session! This may cause problems with logout and other functions - please make sure SSI.php is included before *anything* else in all your scripts!'; + +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['preview_title'] = 'Preview post'; +$txt['preview_fetch'] = 'Fetching preview...'; +$txt['preview_new'] = 'New message'; +$txt['error_while_submitting'] = 'The following error or errors occurred while posting this message:'; +$txt['error_old_topic'] = 'Warning: this topic has not been posted in for at least %1$d days.
    Unless you\'re sure you want to reply, please consider starting a new topic.'; + +$txt['split_selected_posts'] = 'Selected posts'; +$txt['split_selected_posts_desc'] = 'The posts below will form a new topic after splitting.'; +$txt['split_reset_selection'] = 'reset selection'; + +$txt['modify_cancel'] = 'Cancel'; +$txt['mark_read_short'] = 'Mark Read'; + +$txt['pm_short'] = 'My Messages'; +$txt['pm_menu_read'] = 'Read your messages'; +$txt['pm_menu_send'] = 'Send a message'; + +$txt['hello_member_ndt'] = 'Hello'; + +$txt['unapproved_posts'] = 'Unapproved Posts (Topics: %1$d, Posts: %2$d)'; + +$txt['ajax_in_progress'] = 'Loading...'; + +$txt['mod_reports_waiting'] = 'There are currently %1$d moderator reports open.'; + +$txt['view_unread_category'] = 'Unread Posts'; +$txt['verification'] = 'Verification'; +$txt['visual_verification_description'] = 'Type the letters shown in the picture'; +$txt['visual_verification_sound'] = 'Listen to the letters'; +$txt['visual_verification_request_new'] = 'Request another image'; + +// Sub menu labels +$txt['summary'] = 'Summary'; +$txt['account'] = 'Account Settings'; +$txt['forumprofile'] = 'Forum Profile'; + +$txt['modSettings_title'] = 'Features and Options'; +$txt['package'] = 'Package Manager'; +$txt['errlog'] = 'Error Log'; +$txt['edit_permissions'] = 'Permissions'; +$txt['mc_unapproved_attachments'] = 'Unapproved Attachments'; +$txt['mc_unapproved_poststopics'] = 'Unapproved Posts and Topics'; +$txt['mc_reported_posts'] = 'Reported Posts'; +$txt['modlog_view'] = 'Moderation Log'; +$txt['calendar_menu'] = 'View Calendar'; + +//!!! Send email strings - should move? +$txt['send_email'] = 'Send Email'; +$txt['send_email_disclosed'] = 'Note this will be visible to the recipient.'; +$txt['send_email_subject'] = 'Email Subject'; + +$txt['ignoring_user'] = 'You are ignoring this user.'; +$txt['show_ignore_user_post'] = 'Show me the post.'; + +$txt['spider'] = 'Spider'; +$txt['spiders'] = 'Spiders'; +$txt['openid'] = 'OpenID'; + +$txt['downloads'] = 'Downloads'; +$txt['filesize'] = 'Filesize'; +$txt['subscribe_webslice'] = 'Subscribe to Webslice'; + +// Restore topic +$txt['restore_topic'] = 'Restore Topic'; +$txt['restore_message'] = 'Restore'; +$txt['quick_mod_restore'] = 'Restore Selected'; + +// Editor prompt. +$txt['prompt_text_email'] = 'Please enter the email address.'; +$txt['prompt_text_ftp'] = 'Please enter the ftp address.'; +$txt['prompt_text_url'] = 'Please enter the URL you wish to link to.'; +$txt['prompt_text_img'] = 'Enter image location'; + +// Escape any single quotes in here twice.. 'it\'s' -> 'it\\\'s'. +$txt['autosuggest_delete_item'] = 'Delete Item'; + +// Debug related - when $db_show_debug is true. +$txt['debug_templates'] = 'Templates: '; +$txt['debug_subtemplates'] = 'Sub templates: '; +$txt['debug_language_files'] = 'Language files: '; +$txt['debug_stylesheets'] = 'Style sheets: '; +$txt['debug_files_included'] = 'Files included: '; +$txt['debug_kb'] = 'KB.'; +$txt['debug_show'] = 'show'; +$txt['debug_cache_hits'] = 'Cache hits: '; +$txt['debug_cache_seconds_bytes'] = '%1$ss - %2$s bytes'; +$txt['debug_cache_seconds_bytes_total'] = '%1$ss for %2$s bytes'; +$txt['debug_queries_used'] = 'Queries used: %1$d.'; +$txt['debug_queries_used_and_warnings'] = 'Queries used: %1$d, %2$d warnings.'; +$txt['debug_query_in_line'] = 'in %1$s line %2$s, '; +$txt['debug_query_which_took'] = 'which took %1$s seconds.'; +$txt['debug_query_which_took_at'] = 'which took %1$s seconds at %2$s into request.'; +$txt['debug_show_queries'] = '[Show Queries]'; +$txt['debug_hide_queries'] = '[Hide Queries]'; + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/languages/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/languages/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/license.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/license.txt Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,27 @@ +Copyright © 2011 Simple Machines. All rights reserved. + +Developed by: Simple Machines Forum Project + Simple Machines + http://www.simplemachines.org + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + 3. Neither the names of Simple Machines Forum, Simple Machines, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior written + permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +WITH THE SOFTWARE. + +This license may be viewed online at http://www.simplemachines.org/about/smf/license.php \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/print.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/print.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ +#headerarea +{ + display: none; +} + +.tborder +{ + border: none; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/script.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/script.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,486 @@ +var smf_formSubmitted = false; + +// Define document.getElementById for Internet Explorer 4. +if (typeof(document.getElementById) == "undefined") + document.getElementById = function (id) + { + // Just return the corresponding index of all. + return document.all[id]; + } +// Define XMLHttpRequest for IE 5 and above. (don't bother for IE 4 :/.... works in Opera 7.6 and Safari 1.2!) +else if (!window.XMLHttpRequest && window.ActiveXObject) + window.XMLHttpRequest = function () + { + return new ActiveXObject(navigator.userAgent.indexOf("MSIE 5") != -1 ? "Microsoft.XMLHTTP" : "MSXML2.XMLHTTP"); + }; + +// Some older versions of Mozilla don't have this, for some reason. +if (typeof(document.forms) == "undefined") + document.forms = document.getElementsByTagName("form"); + +// Load an XML document using XMLHttpRequest. +function getXMLDocument(url, callback) +{ + if (!window.XMLHttpRequest) + return false; + + var myDoc = new XMLHttpRequest(); + if (typeof(callback) != "undefined") + { + myDoc.onreadystatechange = function () + { + if (myDoc.readyState != 4) + return; + + if (myDoc.responseXML != null && myDoc.status == 200) + callback(myDoc.responseXML); + }; + } + myDoc.open('GET', url, true); + myDoc.send(null); + + return true; +} + +// Send a post form to the server using XMLHttpRequest. +function sendXMLDocument(url, content, callback) +{ + if (!window.XMLHttpRequest) + return false; + + var sendDoc = new window.XMLHttpRequest(); + if (typeof(callback) != "undefined") + { + sendDoc.onreadystatechange = function () + { + if (sendDoc.readyState != 4) + return; + + if (sendDoc.responseXML != null && sendDoc.status == 200) + callback(sendDoc.responseXML); + else + callback(false); + }; + } + sendDoc.open('POST', url, true); + if (typeof(sendDoc.setRequestHeader) != "undefined") + sendDoc.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + sendDoc.send(content); + + return true; +} + +function textToEntities(text) +{ + var entities = ""; + for (var i = 0; i < text.length; i++) + { + if (text.charCodeAt(i) > 127) + entities += "&#" + text.charCodeAt(i) + ";"; + else + entities += text.charAt(i); + } + + return entities; +} + +// Open a new window. +function reqWin(desktopURL, alternateWidth, alternateHeight, noScrollbars) +{ + if ((alternateWidth && self.screen.availWidth * 0.8 < alternateWidth) || (alternateHeight && self.screen.availHeight * 0.8 < alternateHeight)) + { + noScrollbars = false; + alternateWidth = Math.min(alternateWidth, self.screen.availWidth * 0.8); + alternateHeight = Math.min(alternateHeight, self.screen.availHeight * 0.8); + } + else + noScrollbars = typeof(noScrollbars) != "undefined" && noScrollbars == true; + + window.open(desktopURL, 'requested_popup', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=' + (noScrollbars ? 'no' : 'yes') + ',width=' + (alternateWidth ? alternateWidth : 480) + ',height=' + (alternateHeight ? alternateHeight : 220) + ',resizable=no'); + + // Return false so the click won't follow the link ;). + return false; +} + +// Remember the current position. +function storeCaret(text) +{ + // Only bother if it will be useful. + if (typeof(text.createTextRange) != "undefined") + text.caretPos = document.selection.createRange().duplicate(); +} + +// Replaces the currently selected text with the passed text. +function replaceText(text, textarea) +{ + // Attempt to create a text range (IE). + if (typeof(textarea.caretPos) != "undefined" && textarea.createTextRange) + { + var caretPos = textarea.caretPos; + + caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text; + caretPos.select(); + } + // Mozilla text range replace. + else if (typeof(textarea.selectionStart) != "undefined") + { + var begin = textarea.value.substr(0, textarea.selectionStart); + var end = textarea.value.substr(textarea.selectionEnd); + var scrollPos = textarea.scrollTop; + + textarea.value = begin + text + end; + + if (textarea.setSelectionRange) + { + textarea.focus(); + textarea.setSelectionRange(begin.length + text.length, begin.length + text.length); + } + textarea.scrollTop = scrollPos; + } + // Just put it on the end. + else + { + textarea.value += text; + textarea.focus(textarea.value.length - 1); + } +} + +// Surrounds the selected text with text1 and text2. +function surroundText(text1, text2, textarea) +{ + // Can a text range be created? + if (typeof(textarea.caretPos) != "undefined" && textarea.createTextRange) + { + var caretPos = textarea.caretPos, temp_length = caretPos.text.length; + + caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text1 + caretPos.text + text2 + ' ' : text1 + caretPos.text + text2; + + if (temp_length == 0) + { + caretPos.moveStart("character", -text2.length); + caretPos.moveEnd("character", -text2.length); + caretPos.select(); + } + else + textarea.focus(caretPos); + } + // Mozilla text range wrap. + else if (typeof(textarea.selectionStart) != "undefined") + { + var begin = textarea.value.substr(0, textarea.selectionStart); + var selection = textarea.value.substr(textarea.selectionStart, textarea.selectionEnd - textarea.selectionStart); + var end = textarea.value.substr(textarea.selectionEnd); + var newCursorPos = textarea.selectionStart; + var scrollPos = textarea.scrollTop; + + textarea.value = begin + text1 + selection + text2 + end; + + if (textarea.setSelectionRange) + { + if (selection.length == 0) + textarea.setSelectionRange(newCursorPos + text1.length, newCursorPos + text1.length); + else + textarea.setSelectionRange(newCursorPos, newCursorPos + text1.length + selection.length + text2.length); + textarea.focus(); + } + textarea.scrollTop = scrollPos; + } + // Just put them on the end, then. + else + { + textarea.value += text1 + text2; + textarea.focus(textarea.value.length - 1); + } +} + +// Checks if the passed input's value is nothing. +function isEmptyText(theField) +{ + // Copy the value so changes can be made.. + var theValue = theField.value; + + // Strip whitespace off the left side. + while (theValue.length > 0 && (theValue.charAt(0) == ' ' || theValue.charAt(0) == '\t')) + theValue = theValue.substring(1, theValue.length); + // Strip whitespace off the right side. + while (theValue.length > 0 && (theValue.charAt(theValue.length - 1) == ' ' || theValue.charAt(theValue.length - 1) == '\t')) + theValue = theValue.substring(0, theValue.length - 1); + + if (theValue == '') + return true; + else + return false; +} + +// Only allow form submission ONCE. +function submitonce(theform) +{ + smf_formSubmitted = true; +} +function submitThisOnce(form) +{ + // Hateful, hateful fix for Safari 1.3 beta. + if (navigator.userAgent.indexOf('AppleWebKit') != -1) + return !smf_formSubmitted; + + if (typeof(form.form) != "undefined") + form = form.form; + + for (var i = 0; i < form.length; i++) + if (typeof(form[i]) != "undefined" && form[i].tagName.toLowerCase() == "textarea") + form[i].readOnly = true; + + return !smf_formSubmitted; +} + +// Set the "inside" HTML of an element. +function setInnerHTML(element, toValue) +{ + // IE has this built in... + if (typeof(element.innerHTML) != 'undefined') + element.innerHTML = toValue; + // Otherwise, try createContextualFragment(). + else + { + var range = document.createRange(); + range.selectNodeContents(element); + range.deleteContents(); + element.appendChild(range.createContextualFragment(toValue)); + } +} + +// Set the "outer" HTML of an element. +function setOuterHTML(element, toValue) +{ + if (typeof(element.outerHTML) != 'undefined') + element.outerHTML = toValue; + else + { + var range = document.createRange(); + range.setStartBefore(element); + element.parentNode.replaceChild(range.createContextualFragment(toValue), element); + } +} + +// Get the inner HTML of an element. +function getInnerHTML(element) +{ + if (typeof(element.innerHTML) != 'undefined') + return element.innerHTML; + else + { + var returnStr = ''; + for (var i = 0; i < element.childNodes.length; i++) + returnStr += getOuterHTML(element.childNodes[i]); + + return returnStr; + } +} + +function getOuterHTML(node) +{ + if (typeof(node.outerHTML) != 'undefined') + return node.outerHTML; + + var str = ''; + + switch (node.nodeType) + { + // An element. + case 1: + str += '<' + node.nodeName; + + for (var i = 0; i < node.attributes.length; i++) + { + if (node.attributes[i].nodeValue != null) + str += ' ' + node.attributes[i].nodeName + '="' + node.attributes[i].nodeValue + '"'; + } + + if (node.childNodes.length == 0 && in_array(node.nodeName.toLowerCase(), ['hr', 'input', 'img', 'link', 'meta', 'br'])) + str += ' />'; + else + str += '>' + getInnerHTML(node) + ''; + break; + + // 2 is an attribute. + + // Just some text.. + case 3: + str += node.nodeValue; + break; + + // A CDATA section. + case 4: + str += ''; + break; + + // Entity reference.. + case 5: + str += '&' + node.nodeName + ';'; + break; + + // 6 is an actual entity, 7 is a PI. + + // Comment. + case 8: + str += ''; + break; + } + + return str; +} + +// Checks for variable in theArray. +function in_array(variable, theArray) +{ + for (var i = 0; i < theArray.length; i++) + { + if (theArray[i] == variable) + return true; + } + return false; +} + +// Find a specific radio button in its group and select it. +function selectRadioByName(radioGroup, name) +{ + if (typeof(radioGroup.length) == "undefined") + return radioGroup.checked = true; + + for (var i = 0; i < radioGroup.length; i++) + { + if (radioGroup[i].value == name) + return radioGroup[i].checked = true; + } + + return false; +} + +// Invert all checkboxes at once by clicking a single checkbox. +function invertAll(headerfield, checkform, mask) +{ + for (var i = 0; i < checkform.length; i++) + { + if (typeof(checkform[i].name) == "undefined" || (typeof(mask) != "undefined" && checkform[i].name.substr(0, mask.length) != mask)) + continue; + + if (!checkform[i].disabled) + checkform[i].checked = headerfield.checked; + } +} + +// Keep the session alive - always! +var lastKeepAliveCheck = new Date().getTime(); +function smf_sessionKeepAlive() +{ + var curTime = new Date().getTime(); + + // Prevent a Firefox bug from hammering the server. + if (smf_scripturl && curTime - lastKeepAliveCheck > 900000) + { + var tempImage = new Image(); + tempImage.src = smf_scripturl + (smf_scripturl.indexOf("?") == -1 ? "?" : "&") + "action=keepalive;" + curTime; + lastKeepAliveCheck = curTime; + } + + window.setTimeout("smf_sessionKeepAlive();", 1200000); +} +window.setTimeout("smf_sessionKeepAlive();", 1200000); + +// Set a theme option through javascript. +function smf_setThemeOption(option, value, theme, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + var tempImage = new Image(); + tempImage.src = smf_scripturl + (smf_scripturl.indexOf("?") == -1 ? "?" : "&") + "action=jsoption;var=" + option + ";val=" + value + ";sesc=" + cur_session_id + (theme == null ? "" : "&id=" + theme) + ";" + (new Date().getTime()); +} + +function smf_avatarResize() +{ + var possibleAvatars = document.getElementsByTagName ? document.getElementsByTagName("img") : document.all.tags("img"); + + for (var i = 0; i < possibleAvatars.length; i++) + { + if (possibleAvatars[i].className != "avatar") + continue; + + var tempAvatar = new Image(); + tempAvatar.src = possibleAvatars[i].src; + + if (smf_avatarMaxWidth != 0 && tempAvatar.width > smf_avatarMaxWidth) + { + possibleAvatars[i].height = (smf_avatarMaxWidth * tempAvatar.height) / tempAvatar.width; + possibleAvatars[i].width = smf_avatarMaxWidth; + } + else if (smf_avatarMaxHeight != 0 && tempAvatar.height > smf_avatarMaxHeight) + { + possibleAvatars[i].width = (smf_avatarMaxHeight * tempAvatar.width) / tempAvatar.height; + possibleAvatars[i].height = smf_avatarMaxHeight; + } + else + { + possibleAvatars[i].width = tempAvatar.width; + possibleAvatars[i].height = tempAvatar.height; + } + } + + if (typeof(window_oldAvatarOnload) != "undefined" && window_oldAvatarOnload) + { + window_oldAvatarOnload(); + window_oldAvatarOnload = null; + } +} + +function hashLoginPassword(doForm, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + if (typeof(hex_sha1) == "undefined") + return; + // Are they using an email address? + if (doForm.user.value.indexOf("@") != -1) + return; + + // Unless the browser is Opera, the password will not save properly. + if (typeof(window.opera) == "undefined") + doForm.passwrd.autocomplete = "off"; + + doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id); + + // It looks nicer to fill it with asterisks, but Firefox will try to save that. + if (navigator.userAgent.indexOf("Firefox/") != -1) + doForm.passwrd.value = ""; + else + doForm.passwrd.value = doForm.passwrd.value.replace(/./g, "*"); +} + +function hashAdminPassword(doForm, username, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + if (typeof(hex_sha1) == "undefined") + return; + + doForm.admin_hash_pass.value = hex_sha1(hex_sha1(username.toLowerCase() + doForm.admin_pass.value) + cur_session_id); + doForm.admin_pass.value = doForm.admin_pass.value.replace(/./g, "*"); +} + +function ajax_indicator(turn_on) +{ + var indicator = document.getElementById("ajax_in_progress"); + if (indicator != null) + { + if (navigator.appName == "Microsoft Internet Explorer" && navigator.userAgent.indexOf("MSIE 7") == -1) + { + indicator.style.top = document.documentElement.scrollTop; + } + indicator.style.display = turn_on ? "block" : "none"; + } +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/PersonalMessage.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/PersonalMessage.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,88 @@ + +// Handle the JavaScript surrounding personal messages send form. +function smf_PersonalMessageSend(oOptions) +{ + this.opt = oOptions; + this.oBccDiv = null; + this.oBccDiv2 = null; + this.oToAutoSuggest = null; + this.oBccAutoSuggest = null; + this.oToListContainer = null; + this.init(); +} + +smf_PersonalMessageSend.prototype.init = function() +{ + if (!this.opt.bBccShowByDefault) + { + // Hide the BCC control. + this.oBccDiv = document.getElementById(this.opt.sBccDivId); + this.oBccDiv.style.display = 'none'; + this.oBccDiv2 = document.getElementById(this.opt.sBccDivId2); + this.oBccDiv2.style.display = 'none'; + + // Show the link to bet the BCC control back. + var oBccLinkContainer = document.getElementById(this.opt.sBccLinkContainerId); + oBccLinkContainer.style.display = ''; + setInnerHTML(oBccLinkContainer, this.opt.sShowBccLinkTemplate); + + // Make the link show the BCC control. + var oBccLink = document.getElementById(this.opt.sBccLinkId); + oBccLink.instanceRef = this; + oBccLink.onclick = function () { + this.instanceRef.showBcc(); + return false; + }; + } + + var oToControl = document.getElementById(this.opt.sToControlId); + this.oToAutoSuggest = new smc_AutoSuggest({ + sSelf: this.opt.sSelf + '.oToAutoSuggest', + sSessionId: this.opt.sSessionId, + sSessionVar: this.opt.sSessionVar, + sSuggestId: 'to_suggest', + sControlId: this.opt.sToControlId, + sSearchType: 'member', + sPostName: 'recipient_to', + sURLMask: 'action=profile;u=%item_id%', + sTextDeleteItem: this.opt.sTextDeleteItem, + bItemList: true, + sItemListContainerId: 'to_item_list_container', + aListItems: this.opt.aToRecipients + }); + this.oToAutoSuggest.registerCallback('onBeforeAddItem', this.opt.sSelf + '.callbackAddItem'); + + this.oBccAutoSuggest = new smc_AutoSuggest({ + sSelf: this.opt.sSelf + '.oBccAutoSuggest', + sSessionId: this.opt.sSessionId, + sSessionVar: this.opt.sSessionVar, + sSuggestId: 'bcc_suggest', + sControlId: this.opt.sBccControlId, + sSearchType: 'member', + sPostName: 'recipient_bcc', + sURLMask: 'action=profile;u=%item_id%', + sTextDeleteItem: this.opt.sTextDeleteItem, + bItemList: true, + sItemListContainerId: 'bcc_item_list_container', + aListItems: this.opt.aBccRecipients + }); + this.oBccAutoSuggest.registerCallback('onBeforeAddItem', this.opt.sSelf + '.callbackAddItem'); + +} + +smf_PersonalMessageSend.prototype.showBcc = function() +{ + // No longer hide it, show it to the world! + this.oBccDiv.style.display = ''; + this.oBccDiv2.style.display = ''; +} + + +// Prevent items to be added twice or to both the 'To' and 'Bcc'. +smf_PersonalMessageSend.prototype.callbackAddItem = function(oAutoSuggestInstance, sSuggestId) +{ + this.oToAutoSuggest.deleteAddedItem(sSuggestId); + this.oBccAutoSuggest.deleteAddedItem(sSuggestId); + + return true; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/admin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/admin.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,330 @@ +/* + smf_AdminIndex(oOptions) + { + public init() + public loadAdminIndex() + public setAnnouncements() + public showCurrentVersion() + public checkUpdateAvailable() + } + + smf_ViewVersions(oOptions) + { + public init() + public loadViewVersions + public swapOption(oSendingElement, sName) + public compareVersions(sCurrent, sTarget) + public determineVersions() + } +*/ + + + +// Handle the JavaScript surrounding the admin and moderation center. +function smf_AdminIndex(oOptions) +{ + this.opt = oOptions; + this.init(); +} + +smf_AdminIndex.prototype.init = function () +{ + window.adminIndexInstanceRef = this; + var fHandlePageLoaded = function () { + window.adminIndexInstanceRef.loadAdminIndex(); + } + addLoadEvent(fHandlePageLoaded); +} + +smf_AdminIndex.prototype.loadAdminIndex = function () +{ + // Load the text box containing the latest news items. + if (this.opt.bLoadAnnouncements) + this.setAnnouncements(); + + // Load the current SMF and your SMF version numbers. + if (this.opt.bLoadVersions) + this.showCurrentVersion(); + + // Load the text box that sais there's a new version available. + if (this.opt.bLoadUpdateNotification) + this.checkUpdateAvailable(); +} + + +smf_AdminIndex.prototype.setAnnouncements = function () +{ + if (!('smfAnnouncements' in window) || !('length' in window.smfAnnouncements)) + return; + + var sMessages = ''; + for (var i = 0; i < window.smfAnnouncements.length; i++) + sMessages += this.opt.sAnnouncementMessageTemplate.replace('%href%', window.smfAnnouncements[i].href).replace('%subject%', window.smfAnnouncements[i].subject).replace('%time%', window.smfAnnouncements[i].time).replace('%message%', window.smfAnnouncements[i].message); + + setInnerHTML(document.getElementById(this.opt.sAnnouncementContainerId), this.opt.sAnnouncementTemplate.replace('%content%', sMessages)); +} + +smf_AdminIndex.prototype.showCurrentVersion = function () +{ + if (!('smfVersion' in window)) + return; + + var oSmfVersionContainer = document.getElementById(this.opt.sSmfVersionContainerId); + var oYourVersionContainer = document.getElementById(this.opt.sYourVersionContainerId); + + setInnerHTML(oSmfVersionContainer, window.smfVersion); + + var sCurrentVersion = getInnerHTML(oYourVersionContainer); + if (sCurrentVersion != window.smfVersion) + setInnerHTML(oYourVersionContainer, this.opt.sVersionOutdatedTemplate.replace('%currentVersion%', sCurrentVersion)); +} + +smf_AdminIndex.prototype.checkUpdateAvailable = function () +{ + if (!('smfUpdatePackage' in window)) + return; + + var oContainer = document.getElementById(this.opt.sUpdateNotificationContainerId); + + // Are we setting a custom title and message? + var sTitle = 'smfUpdateTitle' in window ? window.smfUpdateTitle : this.opt.sUpdateNotificationDefaultTitle; + var sMessage = 'smfUpdateNotice' in window ? window.smfUpdateNotice : this.opt.sUpdateNotificationDefaultMessage; + + setInnerHTML(oContainer, this.opt.sUpdateNotificationTemplate.replace('%title%', sTitle).replace('%message%', sMessage)); + + // Parse in the package download URL if it exists in the string. + document.getElementById('update-link').href = this.opt.sUpdateNotificationLink.replace('%package%', window.smfUpdatePackage); + + // If we decide to override life into "red" mode, do it. + if ('smfUpdateCritical' in window) + { + document.getElementById('update_table').style.backgroundColor = '#aa2222'; + document.getElementById('update_title').style.backgroundColor = '#dd2222'; + document.getElementById('update_title').style.color = 'white'; + document.getElementById('update_message').style.backgroundColor = '#eebbbb'; + document.getElementById('update_message').style.color = 'black'; + } +} + + + +function smf_ViewVersions (oOptions) +{ + this.opt = oOptions; + this.oSwaps = {}; + this.init(); +} + +smf_ViewVersions.prototype.init = function () +{ + // Load this on loading of the page. + window.viewVersionsInstanceRef = this; + var fHandlePageLoaded = function () { + window.viewVersionsInstanceRef.loadViewVersions(); + } + addLoadEvent(fHandlePageLoaded); +} + +smf_ViewVersions.prototype.loadViewVersions = function () +{ + this.determineVersions(); +} + +smf_ViewVersions.prototype.swapOption = function (oSendingElement, sName) +{ + // If it is undefined, or currently off, turn it on - otherwise off. + this.oSwaps[sName] = !(sName in this.oSwaps) || !this.oSwaps[sName]; + document.getElementById(sName).style.display = this.oSwaps[sName] ? '' : 'none'; + + // Unselect the link and return false. + oSendingElement.blur(); + return false; +} + +smf_ViewVersions.prototype.compareVersions = function (sCurrent, sTarget) +{ + var aVersions = aParts = new Array(); + var aCompare = new Array(sCurrent, sTarget); + + for (var i = 0; i < 2; i++) + { + // Clean the version and extract the version parts. + var sClean = aCompare[i].toLowerCase().replace(/ /g, '').replace(/2.0rc1-1/, '2.0rc1.1'); + aParts = sClean.match(/(\d+)(?:\.(\d+|))?(?:\.)?(\d+|)(?:(alpha|beta|rc)(\d+|)(?:\.)?(\d+|))?(?:(dev))?(\d+|)/); + + // No matches? + if (aParts == null) + return false; + + // Build an array of parts. + aVersions[i] = [ + aParts[1] > 0 ? parseInt(aParts[1]) : 0, + aParts[2] > 0 ? parseInt(aParts[2]) : 0, + aParts[3] > 0 ? parseInt(aParts[3]) : 0, + typeof(aParts[4]) == 'undefined' ? 'stable' : aParts[4], + aParts[5] > 0 ? parseInt(aParts[5]) : 0, + aParts[6] > 0 ? parseInt(aParts[6]) : 0, + typeof(aParts[7]) != 'undefined', + ]; + } + + // Loop through each category. + for (i = 0; i < 7; i++) + { + // Is there something for us to calculate? + if (aVersions[0][i] != aVersions[1][i]) + { + // Dev builds are a problematic exception. + // (stable) dev < (stable) but (unstable) dev = (unstable) + if (i == 3) + return aVersions[0][i] < aVersions[1][i] ? !aVersions[1][6] : aVersions[0][6]; + else if (i == 6) + return aVersions[0][6] ? aVersions[1][3] == 'stable' : false; + // Otherwise a simple comparison. + else + return aVersions[0][i] < aVersions[1][i]; + } + } + + // They are the same! + return false; +} + +smf_ViewVersions.prototype.determineVersions = function () +{ + var oHighYour = { + Sources: '??', + Default: '??', + Languages: '??', + Templates: '??' + }; + var oHighCurrent = { + Sources: '??', + Default: '??', + Languages: '??', + Templates: '??' + }; + var oLowVersion = { + Sources: false, + Default: false, + Languages: false, + Templates: false + }; + + var sSections = [ + 'Sources', + 'Default', + 'Languages', + 'Templates' + ]; + + for (var i = 0, n = sSections.length; i < n; i++) + { + // Collapse all sections. + var oSection = document.getElementById(sSections[i]); + if (typeof(oSection) == 'object' && oSection != null) + oSection.style.display = 'none'; + + // Make all section links clickable. + var oSectionLink = document.getElementById(sSections[i] + '-link'); + if (typeof(oSectionLink) == 'object' && oSectionLink != null) + { + oSectionLink.instanceRef = this; + oSectionLink.sSection = sSections[i]; + oSectionLink.onclick = function () { + this.instanceRef.swapOption(this, this.sSection); + return false; + }; + } + } + + if (!('smfVersions' in window)) + window.smfVersions = {}; + + for (var sFilename in window.smfVersions) + { + if (!document.getElementById('current' + sFilename)) + continue; + + var sYourVersion = getInnerHTML(document.getElementById('your' + sFilename)); + + var sCurVersionType; + for (var sVersionType in oLowVersion) + if (sFilename.substr(0, sVersionType.length) == sVersionType) + { + sCurVersionType = sVersionType; + break; + } + + if (typeof(sCurVersionType) != 'undefined') + { + if ((this.compareVersions(oHighYour[sCurVersionType], sYourVersion) || oHighYour[sCurVersionType] == '??') && !oLowVersion[sCurVersionType]) + oHighYour[sCurVersionType] = sYourVersion; + if (this.compareVersions(oHighCurrent[sCurVersionType], smfVersions[sFilename]) || oHighCurrent[sCurVersionType] == '??') + oHighCurrent[sCurVersionType] = smfVersions[sFilename]; + + if (this.compareVersions(sYourVersion, smfVersions[sFilename])) + { + oLowVersion[sCurVersionType] = sYourVersion; + document.getElementById('your' + sFilename).style.color = 'red'; + } + } + else if (this.compareVersions(sYourVersion, smfVersions[sFilename])) + oLowVersion[sCurVersionType] = sYourVersion; + + setInnerHTML(document.getElementById('current' + sFilename), smfVersions[sFilename]); + setInnerHTML(document.getElementById('your' + sFilename), sYourVersion); + } + + if (!('smfLanguageVersions' in window)) + window.smfLanguageVersions = {}; + + for (sFilename in window.smfLanguageVersions) + { + for (var i = 0; i < this.opt.aKnownLanguages.length; i++) + { + if (!document.getElementById('current' + sFilename + this.opt.aKnownLanguages[i])) + continue; + + setInnerHTML(document.getElementById('current' + sFilename + this.opt.aKnownLanguages[i]), smfLanguageVersions[sFilename]); + + sYourVersion = getInnerHTML(document.getElementById('your' + sFilename + this.opt.aKnownLanguages[i])); + setInnerHTML(document.getElementById('your' + sFilename + this.opt.aKnownLanguages[i]), sYourVersion); + + if ((this.compareVersions(oHighYour.Languages, sYourVersion) || oHighYour.Languages == '??') && !oLowVersion.Languages) + oHighYour.Languages = sYourVersion; + if (this.compareVersions(oHighCurrent.Languages, smfLanguageVersions[sFilename]) || oHighCurrent.Languages == '??') + oHighCurrent.Languages = smfLanguageVersions[sFilename]; + + if (this.compareVersions(sYourVersion, smfLanguageVersions[sFilename])) + { + oLowVersion.Languages = sYourVersion; + document.getElementById('your' + sFilename + this.opt.aKnownLanguages[i]).style.color = 'red'; + } + } + } + + setInnerHTML(document.getElementById('yourSources'), oLowVersion.Sources ? oLowVersion.Sources : oHighYour.Sources); + setInnerHTML(document.getElementById('currentSources'), oHighCurrent.Sources); + if (oLowVersion.Sources) + document.getElementById('yourSources').style.color = 'red'; + + setInnerHTML(document.getElementById('yourDefault'), oLowVersion.Default ? oLowVersion.Default : oHighYour.Default); + setInnerHTML(document.getElementById('currentDefault'), oHighCurrent.Default); + if (oLowVersion.Default) + document.getElementById('yourDefault').style.color = 'red'; + + if (document.getElementById('Templates')) + { + setInnerHTML(document.getElementById('yourTemplates'), oLowVersion.Templates ? oLowVersion.Templates : oHighYour.Templates); + setInnerHTML(document.getElementById('currentTemplates'), oHighCurrent.Templates); + + if (oLowVersion.Templates) + document.getElementById('yourTemplates').style.color = 'red'; + } + + setInnerHTML(document.getElementById('yourLanguages'), oLowVersion.Languages ? oLowVersion.Languages : oHighYour.Languages); + setInnerHTML(document.getElementById('currentLanguages'), oHighCurrent.Languages); + if (oLowVersion.Languages) + document.getElementById('yourLanguages').style.color = 'red'; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/captcha.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/captcha.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,79 @@ +// This file contains javascript associated with the captcha visual verification stuffs. + +function smfCaptcha(imageURL, uniqueID, useLibrary, letterCount) +{ + // By default the letter count is five. + if (!letterCount) + letterCount = 5; + + uniqueID = uniqueID ? '_' + uniqueID : ''; + autoCreate(); + + // Automatically get the captcha event handlers in place and the like. + function autoCreate() + { + // Is there anything to cycle images with - if so attach the refresh image function? + var cycleHandle = document.getElementById('visual_verification' + uniqueID + '_refresh'); + if (cycleHandle) + { + createEventListener(cycleHandle); + cycleHandle.addEventListener('click', refreshImages, false); + } + + // Maybe a voice is here to spread light? + var soundHandle = document.getElementById('visual_verification' + uniqueID + '_sound'); + if (soundHandle) + { + createEventListener(soundHandle); + soundHandle.addEventListener('click', playSound, false); + } + } + + // Change the images. + function refreshImages() + { + // Make sure we are using a new rand code. + var new_url = new String(imageURL); + new_url = new_url.substr(0, new_url.indexOf("rand=") + 5); + + // Quick and dirty way of converting decimal to hex + var hexstr = "0123456789abcdef"; + for(var i=0; i < 32; i++) + new_url = new_url + hexstr.substr(Math.floor(Math.random() * 16), 1); + + if (useLibrary && document.getElementById("verification_image" + uniqueID)) + { + document.getElementById("verification_image" + uniqueID).src = new_url; + } + else if (document.getElementById("verification_image" + uniqueID)) + { + for (i = 1; i <= letterCount; i++) + if (document.getElementById("verification_image" + uniqueID + "_" + i)) + document.getElementById("verification_image" + uniqueID + "_" + i).src = new_url + ";letter=" + i; + } + + return false; + } + + // Request a sound... play it Mr Soundman... + function playSound(ev) + { + if (!ev) + ev = window.event; + + popupFailed = reqWin(imageURL + ";sound", 400, 120); + // Don't follow the link if the popup worked, which it would have done! + if (!popupFailed) + { + if (is_ie && ev.cancelBubble) + ev.cancelBubble = true; + else if (ev.stopPropagation) + { + ev.stopPropagation(); + ev.preventDefault(); + } + } + + return popupFailed; + } +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/editor.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/editor.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1744 @@ +// *** smc_Editor class. +function smc_Editor(oOptions) +{ + this.opt = oOptions; + + // Create some links to the editor object. + this.oTextHandle = null; + this.sCurrentText = 'sText' in this.opt ? this.opt.sText : ''; + + // How big? + this.sEditWidth = 'sEditWidth' in this.opt ? this.opt.sEditWidth : '70%'; + this.sEditHeight = 'sEditHeight' in this.opt ? this.opt.sEditHeight : '150px'; + + this.showDebug = false; + this.bRichTextEnabled = 'bWysiwyg' in this.opt && this.opt.bWysiwyg; + // This doesn't work on Opera as they cannot restore focus after clicking a BBC button. + this.bRichTextPossible = !this.opt.bRichEditOff && ((is_ie5up && !is_ie50) || is_ff || is_opera95up || is_safari || is_chrome) && !(is_iphone || is_android); + + this.oFrameHandle = null; + this.oFrameDocument = null; + this.oFrameWindow = null; + + // These hold the breadcrumb. + this.oBreadHandle = null; + this.oResizerElement = null; + + // Kinda holds all the useful stuff. + this.aKeyboardShortcuts = new Array(); + + // This tracks the cursor position on IE to avoid refocus problems. + this.cursorX = 0; + this.cursorY = 0; + + // This is all the elements that can have a simple execCommand. + this.oSimpleExec = { + b: 'bold', + u: 'underline', + i: 'italic', + s: 'strikethrough', + left: 'justifyleft', + center: 'justifycenter', + right: 'justifyright', + hr: 'inserthorizontalrule', + list: 'insertunorderedlist', + orderlist: 'insertorderedlist', + sub: 'subscript', + sup: 'superscript', + indent: 'indent', + outdent: 'outdent' + } + + // Codes to call a private function + this.oSmfExec = { + unformat: 'removeFormatting', + toggle: 'toggleView' + } + + // Any special breadcrumb mappings to ensure we show a consistant tag name. + this.breadCrumbNameTags = { + strike: 's', + strong: 'b', + em: 'i' + } + + this.aBreadCrumbNameStyles = [ + { + sStyleType: 'text-decoration', + sStyleValue: 'underline', + sBbcTag: 'u' + }, + { + sStyleType: 'text-decoration', + sStyleValue: 'line-through', + sBbcTag: 's' + }, + { + sStyleType: 'text-align', + sStyleValue: 'left', + sBbcTag: 'left' + }, + { + sStyleType: 'text-align', + sStyleValue: 'center', + sBbcTag: 'center' + }, + { + sStyleType: 'text-align', + sStyleValue: 'right', + sBbcTag: 'right' + }, + { + sStyleType: 'font-weight', + sStyleValue: 'bold', + sBbcTag: 'b' + }, + { + sStyleType: 'font-style', + sStyleValue: 'italic', + sBbcTag: 'i' + } + ]; + + // All the fonts in the world. + this.aFontFaces = [ + 'Arial', + 'Arial Black', + 'Impact', + 'Verdana', + 'Times New Roman', + 'Georgia', + 'Andale Mono', + 'Trebuchet MS', + 'Comic Sans MS' + ]; + // Font maps (HTML => CSS size) + this.aFontSizes = [ + 0, + 8, + 10, + 12, + 14, + 18, + 24, + 36 + ]; + // Color maps! (hex => name) + this.oFontColors = { + black: '#000000', + red: '#ff0000', + yellow: '#ffff00', + pink: '#ffc0cb', + green: '#008000', + orange: '#ffa500', + purple: '#800080', + blue: '#0000ff', + beige: '#f5f5dc', + brown: '#a52a2a', + teal: '#008080', + navy: '#000080', + maroon: '#800000', + limegreen: '#32cd32' + } + + this.sFormId = 'sFormId' in this.opt ? this.opt.sFormId : 'postmodify'; + this.iArrayPosition = smf_editorArray.length; + + // Current resize state. + this.osmc_EditorCurrentResize = {}; + + this.init(); +} + +smc_Editor.prototype.init = function() +{ + // Define the event wrapper functions. + var oCaller = this; + this.aEventWrappers = { + editorKeyUp: function(oEvent) {return oCaller.editorKeyUp(oEvent);}, + shortcutCheck: function(oEvent) {return oCaller.shortcutCheck(oEvent);}, + editorBlur: function(oEvent) {return oCaller.editorBlur(oEvent);}, + editorFocus: function(oEvent) {return oCaller.editorFocus(oEvent);}, + startResize: function(oEvent) {return oCaller.startResize(oEvent);}, + resizeOverDocument: function(oEvent) {return oCaller.resizeOverDocument(oEvent);}, + endResize: function(oEvent) {return oCaller.endResize(oEvent);}, + resizeOverIframe: function(oEvent) {return oCaller.resizeOverIframe(oEvent);} + }; + + // Set the textHandle. + this.oTextHandle = document.getElementById(this.opt.sUniqueId); + + // Ensure the currentText is set correctly depending on the mode. + if (this.sCurrentText == '' && !this.bRichTextEnabled) + this.sCurrentText = getInnerHTML(this.oTextHandle).php_unhtmlspecialchars(); + + // Only try to do this if rich text is supported. + if (this.bRichTextPossible) + { + // Make the iframe itself, stick it next to the current text area, and give it an ID. + this.oFrameHandle = document.createElement('iframe'); + this.oFrameHandle.src = 'about:blank'; + this.oFrameHandle.id = 'html_' + this.opt.sUniqueId; + this.oFrameHandle.className = 'rich_editor_frame'; + this.oFrameHandle.style.display = 'none'; + this.oFrameHandle.style.margin = '0px'; + this.oFrameHandle.tabIndex = this.oTextHandle.tabIndex; + this.oTextHandle.parentNode.appendChild(this.oFrameHandle); + + // Create some handy shortcuts. + this.oFrameDocument = this.oFrameHandle.contentDocument ? this.oFrameHandle.contentDocument : ('contentWindow' in this.oFrameHandle ? this.oFrameHandle.contentWindow.document : this.oFrameHandle.document); + this.oFrameWindow = 'contentWindow' in this.oFrameHandle ? this.oFrameHandle.contentWindow : this.oFrameHandle.document.parentWindow; + + // Create the debug window... and stick this under the main frame - make it invisible by default. + this.oBreadHandle = document.createElement('div'); + this.oBreadHandle.id = 'bread_' . uid; + this.oBreadHandle.style.visibility = 'visible'; + this.oBreadHandle.style.display = 'none'; + this.oFrameHandle.parentNode.appendChild(this.oBreadHandle); + + // Size the iframe dimensions to something sensible. + this.oFrameHandle.style.width = this.sEditWidth; + this.oFrameHandle.style.height = this.sEditHeight; + this.oFrameHandle.style.visibility = 'visible'; + + // Only bother formatting the debug window if debug is enabled. + if (this.showDebug) + { + this.oBreadHandle.style.width = this.sEditWidth; + this.oBreadHandle.style.height = '20px'; + this.oBreadHandle.className = 'windowbg2'; + this.oBreadHandle.style.border = '1px black solid'; + this.oBreadHandle.style.display = ''; + } + + // Populate the editor with nothing by default. + if (!is_opera95up) + { + this.oFrameDocument.open(); + this.oFrameDocument.write(''); + this.oFrameDocument.close(); + } + + // Right to left mode? + if (this.opt.bRTL) + { + this.oFrameDocument.dir = "rtl"; + this.oFrameDocument.body.dir = "rtl"; + } + + // Mark it as editable... + if (this.oFrameDocument.body.contentEditable) + this.oFrameDocument.body.contentEditable = true; + else + { + this.oFrameHandle.style.display = ''; + this.oFrameDocument.designMode = 'on'; + this.oFrameHandle.style.display = 'none'; + } + + // Now we need to try and style the editor - internet explorer allows us to do the whole lot. + if (document.styleSheets['editor_css'] || document.styleSheets['editor_ie_css']) + { + var oMyStyle = this.oFrameDocument.createElement('style'); + this.oFrameDocument.documentElement.firstChild.appendChild(oMyStyle); + oMyStyle.styleSheet.cssText = document.styleSheets['editor_ie_css'] ? document.styleSheets['editor_ie_css'].cssText : document.styleSheets['editor_css'].cssText; + } + // Otherwise we seem to have to try to rip out each of the styles one by one! + else if (document.styleSheets.length) + { + var bFoundSomething = false; + // First we need to find the right style sheet. + for (var i = 0, iNumStyleSheets = document.styleSheets.length; i < iNumStyleSheets; i++) + { + // Start off looking for the right style sheet. + if (!document.styleSheets[i].href || document.styleSheets[i].href.indexOf('editor') < 1) + continue; + + // Firefox won't allow us to get a CSS file which ain't in the right URL. + try + { + if (document.styleSheets[i].cssRules.length < 1) + continue; + } + catch (e) + { + continue; + } + + // Manually try to find the rich_editor class. + for (var r = 0, iNumRules = document.styleSheets[i].cssRules.length; r < iNumRules; r++) + { + // Got the main editor? + if (document.styleSheets[i].cssRules[r].selectorText == '.rich_editor') + { + // Set some possible styles. + if (document.styleSheets[i].cssRules[r].style.color) + this.oFrameDocument.body.style.color = document.styleSheets[i].cssRules[r].style.color; + if (document.styleSheets[i].cssRules[r].style.backgroundColor) + this.oFrameDocument.body.style.backgroundColor = document.styleSheets[i].cssRules[r].style.backgroundColor; + if (document.styleSheets[i].cssRules[r].style.fontSize) + this.oFrameDocument.body.style.fontSize = document.styleSheets[i].cssRules[r].style.fontSize; + if (document.styleSheets[i].cssRules[r].style.fontFamily) + this.oFrameDocument.body.style.fontFamily = document.styleSheets[i].cssRules[r].style.fontFamily; + if (document.styleSheets[i].cssRules[r].style.border) + this.oFrameDocument.body.style.border = document.styleSheets[i].cssRules[r].style.border; + bFoundSomething = true; + } + // The frame? + else if (document.styleSheets[i].cssRules[r].selectorText == '.rich_editor_frame') + { + if (document.styleSheets[i].cssRules[r].style.border) + this.oFrameHandle.style.border = document.styleSheets[i].cssRules[r].style.border; + } + } + } + + // Didn't find it? + if (!bFoundSomething) + { + // Do something that is better than nothing. + this.oFrameDocument.body.style.color = 'black'; + this.oFrameDocument.body.style.backgroundColor = 'white'; + this.oFrameDocument.body.style.fontSize = '78%'; + this.oFrameDocument.body.style.fontFamily = '"Verdana", "Arial", "Helvetica", "sans-serif"'; + this.oFrameDocument.body.style.border = 'none'; + this.oFrameHandle.style.border = '1px solid #808080'; + if (is_opera) + this.oFrameDocument.body.style.height = '99%'; + } + } + + // Apply the class... + this.oFrameDocument.body.className = 'rich_editor'; + + // Set the frame padding/margin inside the editor. + this.oFrameDocument.body.style.padding = '1px'; + this.oFrameDocument.body.style.margin = '0'; + + // Listen for input. + this.oFrameDocument.instanceRef = this; + this.oFrameHandle.instanceRef = this; + this.oTextHandle.instanceRef = this; + + // Attach addEventListener for those browsers that don't support it. + createEventListener(this.oFrameHandle); + createEventListener(this.oFrameDocument); + createEventListener(this.oTextHandle); + createEventListener(window); + createEventListener(document); + + // Attach functions to the key and mouse events. + this.oFrameDocument.addEventListener('keyup', this.aEventWrappers.editorKeyUp, true); + this.oFrameDocument.addEventListener('mouseup', this.aEventWrappers.editorKeyUp, true); + this.oFrameDocument.addEventListener('keydown', this.aEventWrappers.shortcutCheck, true); + this.oTextHandle.addEventListener('keydown', this.aEventWrappers.shortcutCheck, true); + + if (is_ie) + { + this.oFrameDocument.addEventListener('blur', this.aEventWrappers.editorBlur, true); + this.oFrameDocument.addEventListener('focus', this.aEventWrappers.editorFocus, true); + } + + // Show the iframe only if wysiwyrg is on - and hide the text area. + this.oTextHandle.style.display = this.bRichTextEnabled ? 'none' : ''; + this.oFrameHandle.style.display = this.bRichTextEnabled ? '' : 'none'; + this.oBreadHandle.style.display = this.bRichTextEnabled ? '' : 'none'; + } + // If we can't do advanced stuff then just do the basics. + else + { + // Cannot have WYSIWYG anyway! + this.bRichTextEnabled = false; + + // We need some of the event handlers. + createEventListener(this.oTextHandle); + createEventListener(window); + createEventListener(document); + } + + // Make sure we set the message mode correctly. + document.getElementById(this.opt.sUniqueId + '_mode').value = this.bRichTextEnabled ? 1 : 0; + + // Show the resizer. + if (document.getElementById(this.opt.sUniqueId + '_resizer') && (!is_opera || is_opera95up) && !(is_chrome && !this.bRichTextEnabled)) + { + // Currently nothing is being resized...I assume! + window.smf_oCurrentResizeEditor = null; + + this.oResizerElement = document.getElementById(this.opt.sUniqueId + '_resizer'); + this.oResizerElement.style.display = ''; + + createEventListener(this.oResizerElement); + this.oResizerElement.addEventListener('mousedown', this.aEventWrappers.startResize, false); + } + + // Set the text - if WYSIWYG is enabled that is. + if (this.bRichTextEnabled) + { + this.insertText(this.sCurrentText, true); + + // Better make us the focus! + this.setFocus(); + } + + // Finally, register shortcuts. + this.registerDefaultShortcuts(); + this.updateEditorControls(); +} + +// Return the current text. +smc_Editor.prototype.getText = function(bPrepareEntities, bModeOverride) +{ + var bCurMode = typeof(bModeOverride) != 'undefined' ? bModeOverride : this.bRichTextEnabled; + + if (!bCurMode || this.oFrameDocument == null) + { + var sText = this.oTextHandle.value; + if (bPrepareEntities) + sText = sText.replace(//g, '#smgt#').replace(/&/g, '#smamp#'); + } + else + { + var sText = this.oFrameDocument.body.innerHTML; + if (bPrepareEntities) + sText = sText.replace(/</g, '#smlt#').replace(/>/g, '#smgt#').replace(/&/g, '#smamp#'); + } + + // Clean it up - including removing semi-colons. + if (bPrepareEntities) + sText = sText.replace(/ /g, ' ').replace(/;/g, '#smcol#'); + + // Return it. + return sText; +} + +// Return the current text. +smc_Editor.prototype.unprotectText = function(sText) +{ + var bCurMode = typeof(bModeOverride) != 'undefined' ? bModeOverride : this.bRichTextEnabled; + + // This restores smlt, smgt and smamp into boring entities, to unprotect against XML'd information like quotes. + sText = sText.replace(/#smlt#/g, '<').replace(/#smgt#/g, '>').replace(/#smamp#/g, '&'); + + // Return it. + return sText; +} + +smc_Editor.prototype.editorKeyUp = function() +{ + // Rebuild the breadcrumb. + this.updateEditorControls(); +} + +smc_Editor.prototype.editorBlur = function() +{ + if (!is_ie) + return; + + // Need to do something here. +} + +smc_Editor.prototype.editorFocus = function() +{ + if (!is_ie) + return; + + // Need to do something here. +} + +// Rebuild the breadcrumb etc - and set things to the correct context. +smc_Editor.prototype.updateEditorControls = function() +{ + // Everything else is specific to HTML mode. + if (!this.bRichTextEnabled) + { + // Set none of the buttons active. + if (this.opt.oBBCBox) + this.opt.oBBCBox.setActive([]); + return; + } + + var aCrumb = new Array(); + var aAllCrumbs = new Array(); + var iMaxLength = 6; + + // What is the current element? + var oCurTag = this.getCurElement(); + + var i = 0; + while (typeof(oCurTag) == 'object' && oCurTag != null && oCurTag.nodeName.toLowerCase() != 'body' && i < iMaxLength) + { + aCrumb[i++] = oCurTag; + oCurTag = oCurTag.parentNode; + } + + // Now print out the tree. + var sTree = ''; + var sCurFontName = ''; + var sCurFontSize = ''; + var sCurFontColor = ''; + for (var i = 0, iNumCrumbs = aCrumb.length; i < iNumCrumbs; i++) + { + var sCrumbName = aCrumb[i].nodeName.toLowerCase(); + + // Does it have an alternative name? + if (sCrumbName in this.breadCrumbNameTags) + sCrumbName = this.breadCrumbNameTags[sCrumbName]; + // Don't bother with this... + else if (sCrumbName == 'p') + continue; + // A link? + else if (sCrumbName == 'a') + { + var sUrlInfo = aCrumb[i].getAttribute('href'); + sCrumbName = 'url'; + if (typeof(sUrlInfo) == 'string') + { + if (sUrlInfo.substr(0, 3) == 'ftp') + sCrumbName = 'ftp'; + else if (sUrlInfo.substr(0, 6) == 'mailto') + sCrumbName = 'email'; + } + } + else if (sCrumbName == 'span' || sCrumbName == 'div') + { + if (aCrumb[i].style) + { + for (var j = 0, iNumStyles = this.aBreadCrumbNameStyles.length; j < iNumStyles; j++) + { + // Do we have a font? + if (aCrumb[i].style.fontFamily && aCrumb[i].style.fontFamily != '' && sCurFontName == '') + { + sCurFontName = aCrumb[i].style.fontFamily; + sCrumbName = 'face'; + } + // ... or a font size? + if (aCrumb[i].style.fontSize && aCrumb[i].style.fontSize != '' && sCurFontSize == '') + { + sCurFontSize = aCrumb[i].style.fontSize; + sCrumbName = 'size'; + } + // ... even color? + if (aCrumb[i].style.color && aCrumb[i].style.color != '' && sCurFontColor == '') + { + sCurFontColor = aCrumb[i].style.color; + if (in_array(sCurFontColor, this.oFontColors)) + sCurFontColor = array_search(sCurFontColor, this.oFontColors); + sCrumbName = 'color'; + } + + if (this.aBreadCrumbNameStyles[j].sStyleType == 'text-align' && aCrumb[i].style.textAlign && aCrumb[i].style.textAlign == this.aBreadCrumbNameStyles[j].sStyleValue) + sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag; + else if (this.aBreadCrumbNameStyles[j].sStyleType == 'text-decoration' && aCrumb[i].style.textDecoration && aCrumb[i].style.textDecoration == this.aBreadCrumbNameStyles[j].sStyleValue) + sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag; + else if (this.aBreadCrumbNameStyles[j].sStyleType == 'font-weight' && aCrumb[i].style.fontWeight && aCrumb[i].style.fontWeight == this.aBreadCrumbNameStyles[j].sStyleValue) + sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag; + else if (this.aBreadCrumbNameStyles[j].sStyleType == 'font-style' && aCrumb[i].style.fontStyle && aCrumb[i].style.fontStyle == this.aBreadCrumbNameStyles[j].sStyleValue) + sCrumbName = this.aBreadCrumbNameStyles[j].sBbcTag; + } + } + } + // Do we have a font? + else if (sCrumbName == 'font') + { + if (aCrumb[i].getAttribute('face') && sCurFontName == '') + { + sCurFontName = aCrumb[i].getAttribute('face').toLowerCase(); + sCrumbName = 'face'; + } + if (aCrumb[i].getAttribute('size') && sCurFontSize == '') + { + sCurFontSize = aCrumb[i].getAttribute('size'); + sCrumbName = 'size'; + } + if (aCrumb[i].getAttribute('color') && sCurFontColor == '') + { + sCurFontColor = aCrumb[i].getAttribute('color'); + if (in_array(sCurFontColor, this.oFontColors)) + sCurFontColor = array_search(sCurFontColor, this.oFontColors); + sCrumbName = 'color'; + } + // Something else - ignore. + if (sCrumbName == 'font') + continue; + } + + sTree += (i != 0 ? ' >' : '') + ' ' + sCrumbName; + aAllCrumbs[aAllCrumbs.length] = sCrumbName; + } + + // Since we're in WYSIWYG state, show the toggle button as active. + aAllCrumbs[aAllCrumbs.length] = 'toggle'; + + this.opt.oBBCBox.setActive(aAllCrumbs); + + // Try set the font boxes correct. + this.opt.oBBCBox.setSelect('sel_face', sCurFontName); + this.opt.oBBCBox.setSelect('sel_size', sCurFontSize); + this.opt.oBBCBox.setSelect('sel_color', sCurFontColor); + + if (this.showDebug) + setInnerHTML(this.oBreadHandle, sTree); +} + +// Set the HTML content to be that of the text box - if we are in wysiwyg mode. +smc_Editor.prototype.doSubmit = function() +{ + if (this.bRichTextEnabled) + this.oTextHandle.value = this.oFrameDocument.body.innerHTML; +} + +// Populate the box with text. +smc_Editor.prototype.insertText = function(sText, bClear, bForceEntityReverse, iMoveCursorBack) +{ + if (bForceEntityReverse) + sText = this.unprotectText(sText); + + // Erase it all? + if (bClear) + { + if (this.bRichTextEnabled) + { + // This includes a work around for FF to get the cursor to show! + this.oFrameDocument.body.innerHTML = sText; + + // If FF trick the cursor into coming back! + if (is_ff || is_opera) + { + // For some entirely unknown reason FF3 Beta 2 and some Opera versions + // require this. + this.oFrameDocument.body.contentEditable = false; + + this.oFrameDocument.designMode = 'off'; + this.oFrameDocument.designMode = 'on'; + } + } + else + this.oTextHandle.value = sText; + } + else + { + this.setFocus(); + if (this.bRichTextEnabled) + { + // IE croaks if you have an image selected and try to insert! + if ('selection' in this.oFrameDocument && this.oFrameDocument.selection.type != 'Text' && this.oFrameDocument.selection.type != 'None' && this.oFrameDocument.selection.clear) + this.oFrameDocument.selection.clear(); + + var oRange = this.getRange(); + + if (oRange.pasteHTML) + { + oRange.pasteHTML(sText); + + // Do we want to move the cursor back at all? + if (iMoveCursorBack) + oRange.moveEnd('character', -iMoveCursorBack); + + oRange.select(); + } + else + { + // If the cursor needs to be positioned, insert the last fragment first. + if (typeof(iMoveCursorBack) != 'undefined' && iMoveCursorBack > 0 && sText.length > iMoveCursorBack) + { + var oSelection = this.getSelect(false, false); + var oRange = oSelection.getRangeAt(0); + oRange.insertNode(this.oFrameDocument.createTextNode(sText.substr(sText.length - iMoveCursorBack))); + } + + this.smf_execCommand('inserthtml', false, typeof(iMoveCursorBack) == 'undefined' ? sText : sText.substr(0, sText.length - iMoveCursorBack)); + } + } + else + { + replaceText(sText, this.oTextHandle); + } + } +} + + +// Special handler for WYSIWYG. +smc_Editor.prototype.smf_execCommand = function(sCommand, bUi, sValue) +{ + return this.oFrameDocument.execCommand(sCommand, bUi, sValue); +} + +smc_Editor.prototype.insertSmiley = function(oSmileyProperties) +{ + // In text mode we just add it in as we always did. + if (!this.bRichTextEnabled) + this.insertText(' ' + oSmileyProperties.sCode); + + // Otherwise we need to do a whole image... + else + { + var iUniqueSmileyId = 1000 + Math.floor(Math.random() * 100000); + this.insertText(''); + } +} + +smc_Editor.prototype.handleButtonClick = function (oButtonProperties) +{ + this.setFocus(); + + // A special SMF function? + if (oButtonProperties.sCode in this.oSmfExec) + this[this.oSmfExec[oButtonProperties.sCode]](); + + else + { + // In text this is easy... + if (!this.bRichTextEnabled) + { + // Replace? + if (!('sAfter' in oButtonProperties) || oButtonProperties.sAfter == null) + replaceText(oButtonProperties.sBefore.replace(/\\n/g, '\n'), this.oTextHandle) + + // Surround! + else + surroundText(oButtonProperties.sBefore.replace(/\\n/g, '\n'), oButtonProperties.sAfter.replace(/\\n/g, '\n'), this.oTextHandle) + } + else + { + // Is it easy? + if (oButtonProperties.sCode in this.oSimpleExec) + this.smf_execCommand(this.oSimpleExec[oButtonProperties.sCode], false, null); + + // A link? + else if (oButtonProperties.sCode == 'url' || oButtonProperties.sCode == 'email' || oButtonProperties.sCode == 'ftp') + this.insertLink(oButtonProperties.sCode); + + // Maybe an image? + else if (oButtonProperties.sCode == 'img') + this.insertImage(); + + // Everything else means doing something ourselves. + else if ('sBefore' in oButtonProperties) + this.insertCustomHTML(oButtonProperties.sBefore.replace(/\\n/g, '\n'), oButtonProperties.sAfter.replace(/\\n/g, '\n')); + + } + } + + this.updateEditorControls(); + + // Finally set the focus. + this.setFocus(); +} + +// Changing a select box? +smc_Editor.prototype.handleSelectChange = function (oSelectProperties) +{ + this.setFocus(); + + var sValue = oSelectProperties.oSelect.value; + if (sValue == '') + return true; + + // Changing font face? + if (oSelectProperties.sName == 'sel_face') + { + // Not in HTML mode? + if (!this.bRichTextEnabled) + { + sValue = sValue.replace(/"/, ''); + surroundText('[font=' + sValue + ']', '[/font]', this.oTextHandle); + oSelectProperties.oSelect.selectedIndex = 0; + } + else + { + if (is_webkit) + this.smf_execCommand('styleWithCSS', false, true); + this.smf_execCommand('fontname', false, sValue); + } + } + + // Font size? + else if (oSelectProperties.sName == 'sel_size') + { + // Are we in boring mode? + if (!this.bRichTextEnabled) + { + surroundText('[size=' + this.aFontSizes[sValue] + 'pt]', '[/size]', this.oTextHandle); + oSelectProperties.oSelect.selectedIndex = 0; + } + + else + this.smf_execCommand('fontsize', false, sValue); + } + // Or color even? + else if (oSelectProperties.sName == 'sel_color') + { + // Are we in boring mode? + if (!this.bRichTextEnabled) + { + surroundText('[color=' + sValue + ']', '[/color]', this.oTextHandle); + oSelectProperties.oSelect.selectedIndex = 0; + } + + else + this.smf_execCommand('forecolor', false, sValue); + } + + this.updateEditorControls(); + + return true; +} + +// Put in some custom HTML. +smc_Editor.prototype.insertCustomHTML = function(sLeftTag, sRightTag) +{ + var sSelection = this.getSelect(true, true); + if (sSelection.length == 0) + sSelection = ''; + + // Are we overwriting? + if (sRightTag == '') + this.insertText(sLeftTag); + // If something was selected, replace and position cursor at the end of it. + else if (sSelection.length > 0) + this.insertText(sLeftTag + sSelection + sRightTag, false, false, 0); + // Wrap the tags around the cursor position. + else + this.insertText(sLeftTag + sRightTag, false, false, sRightTag.length); + +} + +// Insert a URL link. +smc_Editor.prototype.insertLink = function(sType) +{ + if (sType == 'email') + var sPromptText = oEditorStrings['prompt_text_email']; + else if (sType == 'ftp') + var sPromptText = oEditorStrings['prompt_text_ftp']; + else + var sPromptText = oEditorStrings['prompt_text_url']; + + // IE has a nice prompt for this - others don't. + if (sType != 'email' && sType != 'ftp' && is_ie) + this.smf_execCommand('createlink', true, 'http://'); + + else + { + // Ask them where to link to. + var sText = prompt(sPromptText, sType == 'email' ? '' : (sType == 'ftp' ? 'ftp://' : 'http://')); + if (!sText) + return; + + if (sType == 'email' && sText.indexOf('mailto:') != 0) + sText = 'mailto:' + sText; + + // Check if we have text selected and if not force us to have some. + var oCurText = this.getSelect(true, true); + + if (oCurText.toString().length != 0) + { + this.smf_execCommand('unlink'); + this.smf_execCommand('createlink', false, sText); + } + else + this.insertText('' + sText + ''); + } +} + +smc_Editor.prototype.insertImage = function(sSrc) +{ + if (!sSrc) + { + sSrc = prompt(oEditorStrings['prompt_text_img'], 'http://'); + if (!sSrc || sSrc.length < 10) + return; + } + this.smf_execCommand('insertimage', false, sSrc); +} + +smc_Editor.prototype.getSelect = function(bWantText, bWantHTMLText) +{ + if (is_ie && 'selection' in this.oFrameDocument) + { + // Just want plain text? + if (bWantText && !bWantHTMLText) + return this.oFrameDocument.selection.createRange().text; + // We want the HTML flavoured variety? + else if (bWantHTMLText) + return this.oFrameDocument.selection.createRange().htmlText; + + return this.oFrameDocument.selection; + } + + // This is mainly Firefox. + if ('getSelection' in this.oFrameWindow) + { + // Plain text? + if (bWantText && !bWantHTMLText) + return this.oFrameWindow.getSelection().toString(); + + // HTML is harder - currently using: http://www.faqts.com/knowledge_base/view.phtml/aid/32427 + else if (bWantHTMLText) + { + var oSelection = this.oFrameWindow.getSelection(); + if (oSelection.rangeCount > 0) + { + var oRange = oSelection.getRangeAt(0); + var oClonedSelection = oRange.cloneContents(); + var oDiv = this.oFrameDocument.createElement('div'); + oDiv.appendChild(oClonedSelection); + return oDiv.innerHTML; + } + else + return ''; + } + + // Want the whole object then. + return this.oFrameWindow.getSelection(); + } + + // If we're here it's not good. + return this.oFrameDocument.getSelection(); +} + +smc_Editor.prototype.getRange = function() +{ + // Get the current selection. + var oSelection = this.getSelect(); + + if (!oSelection) + return null; + + if (is_ie && oSelection.createRange) + return oSelection.createRange(); + + return oSelection.rangeCount == 0 ? null : oSelection.getRangeAt(0); +} + +// Get the current element. +smc_Editor.prototype.getCurElement = function() +{ + var oRange = this.getRange(); + + if (!oRange) + return null; + + if (is_ie) + { + if (oRange.item) + return oRange.item(0); + else + return oRange.parentElement(); + } + else + { + var oElement = oRange.commonAncestorContainer; + return this.getParentElement(oElement); + } +} + +smc_Editor.prototype.getParentElement = function(oNode) +{ + if (oNode.nodeType == 1) + return oNode; + + for (var i = 0; i < 50; i++) + { + if (!oNode.parentNode) + break; + + oNode = oNode.parentNode; + if (oNode.nodeType == 1) + return oNode; + } + return null; +} + +// Remove formatting for the selected text. +smc_Editor.prototype.removeFormatting = function() +{ + // Do both at once. + if (this.bRichTextEnabled) + { + this.smf_execCommand('removeformat'); + this.smf_execCommand('unlink'); + } + // Otherwise do a crude move indeed. + else + { + // Get the current selection first. + if (this.oTextHandle.caretPos) + var sCurrentText = this.oTextHandle.caretPos.text; + + else if ('selectionStart' in this.oTextHandle) + var sCurrentText = this.oTextHandle.value.substr(this.oTextHandle.selectionStart, (this.oTextHandle.selectionEnd - this.oTextHandle.selectionStart)); + + else + return; + + // Do bits that are likely to have attributes. + sCurrentText = sCurrentText.replace(RegExp("\\[/?(url|img|iurl|ftp|email|img|color|font|size|list|bdo).*?\\]", "g"), ''); + // Then just anything that looks like BBC. + sCurrentText = sCurrentText.replace(RegExp("\\[/?[A-Za-z]+\\]", "g"), ''); + + replaceText(sCurrentText, this.oTextHandle); + } +} + +// Toggle wysiwyg/normal mode. +smc_Editor.prototype.toggleView = function(bView) +{ + if (!this.bRichTextPossible) + { + alert(oEditorStrings['wont_work']); + return false; + } + + // Overriding or alternating? + if (typeof(bView) == 'undefined') + bView = !this.bRichTextEnabled; + + this.requestParsedMessage(bView); + + return true; +} + +// Request the message in a different form. +smc_Editor.prototype.requestParsedMessage = function(bView) +{ + // Replace with a force reload. + if (!window.XMLHttpRequest) + { + alert(oEditorStrings['func_disabled']); + return; + } + + // Get the text. + var sText = this.getText(true, !bView).replace(/&#/g, "&#").php_to8bit().php_urlencode(); + + this.tmpMethod = sendXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=' + (bView ? 1 : 0) + ';' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, this.onToggleDataReceived); + delete tmpMethod; +} + +smc_Editor.prototype.onToggleDataReceived = function(oXMLDoc) +{ + var sText = ''; + for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++) + sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue; + + // What is this new view we have? + this.bRichTextEnabled = oXMLDoc.getElementsByTagName('message')[0].getAttribute('view') != '0'; + + if (this.bRichTextEnabled) + { + this.oFrameHandle.style.display = ''; + if (this.showDebug) + this.oBreadHandle.style.display = ''; + this.oTextHandle.style.display = 'none'; + } + else + { + sText = sText.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); + this.oFrameHandle.style.display = 'none'; + this.oBreadHandle.style.display = 'none'; + this.oTextHandle.style.display = ''; + } + + // First we focus. + this.setFocus(); + + this.insertText(sText, true); + + // Record the new status. + document.getElementById(this.opt.sUniqueId + '_mode').value = this.bRichTextEnabled ? '1' : '0'; + + // Rebuild the bread crumb! + this.updateEditorControls(); +} + +// Set the focus for the editing window. +smc_Editor.prototype.setFocus = function(force_both) +{ + if (!this.bRichTextEnabled) + this.oTextHandle.focus(); + else if (is_ff || is_opera) + this.oFrameHandle.focus(); + else + this.oFrameWindow.focus(); +} + +// Start up the spellchecker! +smc_Editor.prototype.spellCheckStart = function() +{ + if (!spellCheck) + return false; + + // If we're in HTML mode we need to get the non-HTML text. + if (this.bRichTextEnabled) + { + var sText = escape(this.getText(true, 1).php_to8bit()); + + this.tmpMethod = sendXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=0;' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, this.onSpellCheckDataReceived); + delete tmpMethod; + } + // Otherwise start spellchecking right away. + else + spellCheck(this.sFormId, this.opt.sUniqueId); + + return true; +} + +// This contains the spellcheckable text. +smc_Editor.prototype.onSpellCheckDataReceived = function(oXMLDoc) +{ + var sText = ''; + for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++) + sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue; + + sText = sText.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); + + this.oTextHandle.value = sText; + spellCheck(this.sFormId, this.opt.sUniqueId); +} + +// Function called when the Spellchecker is finished and ready to pass back. +smc_Editor.prototype.spellCheckEnd = function() +{ + // If HTML edit put the text back! + if (this.bRichTextEnabled) + { + var sText = escape(this.getText(true, 0).php_to8bit()); + + this.tmpMethod = sendXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jseditor;view=1;' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';xml', 'message=' + sText, smf_editorArray[this.iArrayPosition].onSpellCheckCompleteDataReceived); + delete tmpMethod; + } + else + this.setFocus(); +} + +// The corrected text. +smc_Editor.prototype.onSpellCheckCompleteDataReceived = function(oXMLDoc) +{ + var sText = ''; + for (var i = 0; i < oXMLDoc.getElementsByTagName('message')[0].childNodes.length; i++) + sText += oXMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue; + + this.insertText(sText, true); + this.setFocus(); +} + +smc_Editor.prototype.resizeTextArea = function(newHeight, newWidth, is_change) +{ + // Work out what the new height is. + if (is_change) + { + // We'll assume pixels but may not be. + newHeight = this._calculateNewDimension(this.oTextHandle.style.height, newHeight); + if (newWidth) + newWidth = this._calculateNewDimension(this.oTextHandle.style.width, newWidth); + } + + // Do the HTML editor - but only if it's enabled! + if (this.bRichTextPossible) + { + this.oFrameHandle.style.height = newHeight; + if (newWidth) + this.oFrameHandle.style.width = newWidth; + } + // Do the text box regardless! + this.oTextHandle.style.height = newHeight; + if (newWidth) + this.oTextHandle.style.width = newWidth; +} + +// A utility instruction to save repetition when trying to work out what to change on a height/width. +smc_Editor.prototype._calculateNewDimension = function(old_size, change_size) +{ + // We'll assume pixels but may not be. + changeReg = change_size.toString().match(/(-)?(\d+)(\D*)/); + curReg = old_size.toString().match(/(\d+)(\D*)/); + + if (!changeReg[3]) + changeReg[3] = 'px'; + + if (changeReg[1] == '-') + changeReg[2] = 0 - changeReg[2]; + + // Both the same type? + if (changeReg[3] == curReg[2]) + { + new_size = parseInt(changeReg[2]) + parseInt(curReg[1]); + if (new_size < 50) + new_size = 50; + new_size = new_size.toString() + changeReg[3]; + } + // Is the change a percentage? + else if (changeReg[3] == '%') + new_size = (parseInt(curReg[1]) + parseInt((parseInt(changeReg[2]) * parseInt(curReg[1])) / 100)).toString() + 'px'; + // Otherwise just guess! + else + new_size = (parseInt(curReg[1]) + (parseInt(changeReg[2]) / 10)).toString() + '%'; + + return new_size; +} + +// Register default keyboard shortcuts. +smc_Editor.prototype.registerDefaultShortcuts = function() +{ + if (is_ff) + { + this.registerShortcut('b', 'ctrl', 'b'); + this.registerShortcut('u', 'ctrl', 'u'); + this.registerShortcut('i', 'ctrl', 'i'); + this.registerShortcut('p', 'alt', 'preview'); + this.registerShortcut('s', 'alt', 'submit'); + } +} + +// Register a keyboard shortcut. +smc_Editor.prototype.registerShortcut = function(sLetter, sModifiers, sCodeName) +{ + if (!sCodeName) + return; + + var oNewShortcut = { + code : sCodeName, + key: sLetter.toUpperCase().charCodeAt(0), + alt : false, + ctrl : false + }; + + var aSplitModifiers = sModifiers.split(','); + for(var i = 0, n = aSplitModifiers.length; i < n; i++) + if (aSplitModifiers[i] in oNewShortcut) + oNewShortcut[aSplitModifiers[i]] = true; + + this.aKeyboardShortcuts[this.aKeyboardShortcuts.length] = oNewShortcut; +} + +// Check whether the key has triggered a shortcut? +smc_Editor.prototype.checkShortcut = function(oEvent) +{ + // To be a shortcut it needs to be one of these, duh! + if (!oEvent.altKey && !oEvent.ctrlKey) + return false; + + var sReturnCode = false; + + // Let's take a look at each of our shortcuts shall we? + for (var i = 0, n = this.aKeyboardShortcuts.length; i < n; i++) + { + // Found something? + if (oEvent.altKey == this.aKeyboardShortcuts[i].alt && oEvent.ctrlKey == this.aKeyboardShortcuts[i].ctrl && oEvent.keyCode == this.aKeyboardShortcuts[i].key) + sReturnCode = this.aKeyboardShortcuts[i].code; + } + + return sReturnCode; +} + +// The actual event check for the above! +smc_Editor.prototype.shortcutCheck = function(oEvent) +{ + var sFoundCode = this.checkShortcut(oEvent); + + // Run it and exit. + if (typeof(sFoundCode) == 'string' && sFoundCode != '') + { + var bCancelEvent = false; + if (sFoundCode == 'submit') + { + // So much to do! + var oForm = document.getElementById(this.sFormId); + submitThisOnce(oForm); + submitonce(oForm); + smc_saveEntities(oForm.name, ['subject', this.opt.sUniqueId, 'guestname', 'evtitle', 'question']); + oForm.submit(); + + bCancelEvent = true; + } + else if (sFoundCode == 'preview') + { + previewPost(); + bCancelEvent = true; + } + else + bCancelEvent = this.opt.oBBCBox.emulateClick(sFoundCode); + + if (bCancelEvent) + { + if (is_ie && oEvent.cancelBubble) + oEvent.cancelBubble = true; + + else if (oEvent.stopPropagation) + { + oEvent.stopPropagation(); + oEvent.preventDefault(); + } + + return false; + } + } + + return true; +} + +// This is the method called after clicking the resize bar. +smc_Editor.prototype.startResize = function(oEvent) +{ + if ('event' in window) + oEvent = window.event; + + if (!oEvent || window.smf_oCurrentResizeEditor != null) + return true; + + window.smf_oCurrentResizeEditor = this.iArrayPosition; + + var aCurCoordinates = smf_mousePose(oEvent); + this.osmc_EditorCurrentResize.old_y = aCurCoordinates[1]; + this.osmc_EditorCurrentResize.old_rel_y = null; + this.osmc_EditorCurrentResize.cur_height = parseInt(this.oTextHandle.style.height); + + // Set the necessary events for resizing. + var oResizeEntity = is_ie ? document : window; + oResizeEntity.addEventListener('mousemove', this.aEventWrappers.resizeOverDocument, false); + + if (this.bRichTextPossible) + this.oFrameDocument.addEventListener('mousemove', this.aEventWrappers.resizeOverIframe, false); + + document.addEventListener('mouseup', this.aEventWrappers.endResize, true); + + if (this.bRichTextPossible) + this.oFrameDocument.addEventListener('mouseup', this.aEventWrappers.endResize, true); + + return false; +} + +// This is kind of a cheat, as it only works over the IFRAME. +smc_Editor.prototype.resizeOverIframe = function(oEvent) +{ + if ('event' in window) + oEvent = window.event; + + if (!oEvent || window.smf_oCurrentResizeEditor == null) + return true; + + var newCords = smf_mousePose(oEvent); + + if (this.osmc_EditorCurrentResize.old_rel_y == null) + this.osmc_EditorCurrentResize.old_rel_y = newCords[1]; + else + { + var iNewHeight = newCords[1] - this.osmc_EditorCurrentResize.old_rel_y + this.osmc_EditorCurrentResize.cur_height; + if (iNewHeight < 0) + this.endResize(); + else + this.resizeTextArea(iNewHeight + 'px', 0, false); + } + + return false; +} + +// This resizes an editor. +smc_Editor.prototype.resizeOverDocument = function (oEvent) +{ + if ('event' in window) + oEvent = window.event; + + if (!oEvent || window.smf_oCurrentResizeEditor == null) + return true; + + var newCords = smf_mousePose(oEvent); + + var iNewHeight = newCords[1] - this.osmc_EditorCurrentResize.old_y + this.osmc_EditorCurrentResize.cur_height; + if (iNewHeight < 0) + this.endResize(); + else + this.resizeTextArea(iNewHeight + 'px', 0, false); + + return false; +} + +smc_Editor.prototype.endResize = function (oEvent) +{ + if ('event' in window) + oEvent = window.event; + + if (window.smf_oCurrentResizeEditor == null) + return true; + + window.smf_oCurrentResizeEditor = null; + + // Remove the event... + var oResizeEntity = is_ie ? document : window; + oResizeEntity.removeEventListener('mousemove', this.aEventWrappers.resizeOverDocument, false); + + if (this.bRichTextPossible) + this.oFrameDocument.removeEventListener('mousemove', this.aEventWrappers.resizeOverIframe, false); + + document.removeEventListener('mouseup', this.aEventWrappers.endResize, true); + + if (this.bRichTextPossible) + this.oFrameDocument.removeEventListener('mouseup', this.aEventWrappers.endResize, true); + + return false; +} + +// *** smc_SmileyBox class. +function smc_SmileyBox(oOptions) +{ + this.opt = oOptions; + this.oSmileyRowsContent = {}; + this.oSmileyPopupWindow = null; + this.init(); +} + +smc_SmileyBox.prototype.init = function () +{ + // Get the HTML content of the smileys visible on the post screen. + this.getSmileyRowsContent('postform'); + + // Inject the HTML. + setInnerHTML(document.getElementById(this.opt.sContainerDiv), this.opt.sSmileyBoxTemplate.easyReplace({ + smileyRows: this.oSmileyRowsContent.postform, + moreSmileys: this.opt.oSmileyLocations.popup.length == 0 ? '' : this.opt.sMoreSmileysTemplate.easyReplace({ + moreSmileysId: this.opt.sUniqueId + '_addMoreSmileys' + }) + })); + + // Initialize the smileys. + this.initSmileys('postform', document); + + // Initialize the [more] button. + if (this.opt.oSmileyLocations.popup.length > 0) + { + var oMoreLink = document.getElementById(this.opt.sUniqueId + '_addMoreSmileys'); + oMoreLink.instanceRef = this; + oMoreLink.onclick = function () { + this.instanceRef.handleShowMoreSmileys(); + return false; + } + } +} + +// Loop through the smileys to setup the HTML. +smc_SmileyBox.prototype.getSmileyRowsContent = function (sLocation) +{ + // If it's already defined, don't bother. + if (sLocation in this.oSmileyRowsContent) + return; + + this.oSmileyRowsContent[sLocation] = ''; + + for (var iSmileyRowIndex = 0, iSmileyRowCount = this.opt.oSmileyLocations[sLocation].length; iSmileyRowIndex < iSmileyRowCount; iSmileyRowIndex++) + { + var sSmileyRowContent = ''; + for (var iSmileyIndex = 0, iSmileyCount = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex].length; iSmileyIndex < iSmileyCount; iSmileyIndex++) + sSmileyRowContent += this.opt.sSmileyTemplate.easyReplace({ + smileySource: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sSrc.php_htmlspecialchars(), + smileyDescription: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sDescription.php_htmlspecialchars(), + smileyCode: this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex].sCode.php_htmlspecialchars(), + smileyId: this.opt.sUniqueId + '_' + sLocation + '_' + iSmileyRowIndex.toString() + '_' + iSmileyIndex.toString() + }); + + this.oSmileyRowsContent[sLocation] += this.opt.sSmileyRowTemplate.easyReplace({ + smileyRow: sSmileyRowContent + }); + } +} + +smc_SmileyBox.prototype.initSmileys = function (sLocation, oDocument) +{ + for (var iSmileyRowIndex = 0, iSmileyRowCount = this.opt.oSmileyLocations[sLocation].length; iSmileyRowIndex < iSmileyRowCount; iSmileyRowIndex++) + { + for (var iSmileyIndex = 0, iSmileyCount = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex].length; iSmileyIndex < iSmileyCount; iSmileyIndex++) + { + var oSmiley = oDocument.getElementById(this.opt.sUniqueId + '_' + sLocation + '_' + iSmileyRowIndex.toString() + '_' + iSmileyIndex.toString()); + oSmiley.instanceRef = this; + oSmiley.style.cursor = 'pointer'; + oSmiley.onclick = function () { + this.instanceRef.clickHandler(this); + return false; + } + } + } +} + +smc_SmileyBox.prototype.clickHandler = function (oSmileyImg) +{ + // Dissect the id... + var aMatches = oSmileyImg.id.match(/([^_]+)_(\d+)_(\d+)$/); + if (aMatches.length != 4) + return false; + + // ...to determine its exact smiley properties. + var sLocation = aMatches[1]; + var iSmileyRowIndex = aMatches[2]; + var iSmileyIndex = aMatches[3]; + var oProperties = this.opt.oSmileyLocations[sLocation][iSmileyRowIndex][iSmileyIndex]; + + if ('sClickHandler' in this.opt) + eval(this.opt.sClickHandler + '(oProperties)'); + + return false; +} + +smc_SmileyBox.prototype.handleShowMoreSmileys = function () +{ + // Focus the window if it's already opened. + if (this.oSmileyPopupWindow != null && 'closed' in this.oSmileyPopupWindow && !this.oSmileyPopupWindow.closed) + { + this.oSmileyPopupWindow.focus(); + return; + } + + // Get the smiley HTML. + this.getSmileyRowsContent('popup'); + + // Open the popup. + this.oSmileyPopupWindow = window.open('about:blank', this.opt.sUniqueId + '_addMoreSmileysPopup', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,width=480,height=220,resizable=yes'); + + // Paste the template in the popup. + this.oSmileyPopupWindow.document.open('text/html', 'replace'); + this.oSmileyPopupWindow.document.write(this.opt.sMoreSmileysPopupTemplate.easyReplace({ + smileyRows: this.oSmileyRowsContent.popup, + moreSmileysCloseLinkId: this.opt.sUniqueId + '_closeMoreSmileys' + })); + this.oSmileyPopupWindow.document.close(); + + // Initialize the smileys that are in the popup window. + this.initSmileys('popup', this.oSmileyPopupWindow.document); + + // Add a function to the close window button. + var aCloseLink = this.oSmileyPopupWindow.document.getElementById(this.opt.sUniqueId + '_closeMoreSmileys'); + aCloseLink.instanceRef = this; + aCloseLink.onclick = function () { + this.instanceRef.oSmileyPopupWindow.close(); + return false; + } +} + + +// *** smc_BBCButtonBox class. +function smc_BBCButtonBox(oOptions) +{ + this.opt = oOptions; + this.init(); +} + +smc_BBCButtonBox.prototype.init = function () +{ + var sBbcContent = ''; + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + var sRowContent = ''; + var bPreviousWasDivider = false; + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurButton = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + switch (oCurButton.sType) + { + case 'button': + if (oCurButton.bEnabled) + { + sRowContent += this.opt.sButtonTemplate.easyReplace({ + buttonId: this.opt.sUniqueId.php_htmlspecialchars() + '_button_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString(), + buttonSrc: oCurButton.sImage.php_htmlspecialchars(), + buttonDescription: oCurButton.sDescription.php_htmlspecialchars() + }); + + bPreviousWasDivider = false; + } + break; + + case 'divider': + if (!bPreviousWasDivider) + sRowContent += this.opt.sDividerTemplate; + + bPreviousWasDivider = true; + break; + + case 'select': + var sOptions = ''; + + // Fighting javascript's idea of order in a for loop... :P + if ('' in oCurButton.oOptions) + sOptions = ''; + for (var sSelectValue in oCurButton.oOptions) + // we've been through this before + if (sSelectValue != '') + sOptions += ''; + + sRowContent += this.opt.sSelectTemplate.easyReplace({ + selectName: oCurButton.sName, + selectId: this.opt.sUniqueId.php_htmlspecialchars() + '_select_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString(), + selectOptions: sOptions + }); + + bPreviousWasDivider = false; + break; + } + } + sBbcContent += this.opt.sButtonRowTemplate.easyReplace({ + buttonRow: sRowContent + }); + } + + var oBbcContainer = document.getElementById(this.opt.sContainerDiv); + setInnerHTML(oBbcContainer, sBbcContent); + + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + switch (oCurControl.sType) + { + case 'button': + if (!oCurControl.bEnabled) + break; + + oCurControl.oImg = document.getElementById(this.opt.sUniqueId.php_htmlspecialchars() + '_button_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString()); + oCurControl.oImg.style.cursor = 'pointer'; + if ('sButtonBackgroundImage' in this.opt) + oCurControl.oImg.style.backgroundImage = 'url(' + this.opt.sButtonBackgroundImage + ')'; + + oCurControl.oImg.instanceRef = this; + oCurControl.oImg.onmouseover = function () { + this.instanceRef.handleButtonMouseOver(this); + }; + oCurControl.oImg.onmouseout = function () { + this.instanceRef.handleButtonMouseOut(this); + }; + oCurControl.oImg.onclick = function () { + this.instanceRef.handleButtonClick(this); + }; + + oCurControl.oImg.bIsActive = false; + oCurControl.oImg.bHover = false; + break; + + case 'select': + oCurControl.oSelect = document.getElementById(this.opt.sUniqueId.php_htmlspecialchars() + '_select_' + iButtonRowIndex.toString() + '_' + iButtonIndex.toString()); + + oCurControl.oSelect.instanceRef = this; + oCurControl.oSelect.onchange = oCurControl.onchange = function () { + this.instanceRef.handleSelectChange(this); + } + break; + } + } + } +} + +smc_BBCButtonBox.prototype.handleButtonMouseOver = function (oButtonImg) +{ + oButtonImg.bHover = true; + this.updateButtonStatus(oButtonImg); +} + +smc_BBCButtonBox.prototype.handleButtonMouseOut = function (oButtonImg) +{ + oButtonImg.bHover = false; + this.updateButtonStatus(oButtonImg); +} + +smc_BBCButtonBox.prototype.updateButtonStatus = function (oButtonImg) +{ + var sNewURL = ''; + if (oButtonImg.bHover && oButtonImg.bIsActive && 'sActiveButtonBackgroundImageHover' in this.opt) + sNewURL = 'url(' + this.opt.sActiveButtonBackgroundImageHover + ')'; + else if (!oButtonImg.bHover && oButtonImg.bIsActive && 'sActiveButtonBackgroundImage' in this.opt) + sNewURL = 'url(' + this.opt.sActiveButtonBackgroundImage + ')'; + else if (oButtonImg.bHover && 'sButtonBackgroundImageHover' in this.opt) + sNewURL = 'url(' + this.opt.sButtonBackgroundImageHover + ')'; + else if ('sButtonBackgroundImage' in this.opt) + sNewURL = 'url(' + this.opt.sButtonBackgroundImage + ')'; + + if (oButtonImg.style.backgroundImage != sNewURL) + oButtonImg.style.backgroundImage = sNewURL; +} + +smc_BBCButtonBox.prototype.handleButtonClick = function (oButtonImg) +{ + // Dissect the id attribute... + var aMatches = oButtonImg.id.match(/(\d+)_(\d+)$/); + if (aMatches.length != 3) + return false; + + // ...so that we can point to the exact button. + var iButtonRowIndex = aMatches[1]; + var iButtonIndex = aMatches[2]; + var oProperties = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + oProperties.bIsActive = oButtonImg.bIsActive; + + if ('sButtonClickHandler' in this.opt) + eval(this.opt.sButtonClickHandler + '(oProperties)'); + + return false; +} + +smc_BBCButtonBox.prototype.handleSelectChange = function (oSelectControl) +{ + // Dissect the id attribute... + var aMatches = oSelectControl.id.match(/(\d+)_(\d+)$/); + if (aMatches.length != 3) + return false; + + // ...so that we can point to the exact button. + var iButtonRowIndex = aMatches[1]; + var iButtonIndex = aMatches[2]; + var oProperties = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + + if ('sSelectChangeHandler' in this.opt) + eval(this.opt.sSelectChangeHandler + '(oProperties)'); + + return true; +} + +smc_BBCButtonBox.prototype.setActive = function (aButtons) +{ + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + if (oCurControl.sType == 'button' && oCurControl.bEnabled) + { + oCurControl.oImg.bIsActive = in_array(oCurControl.sCode, aButtons); + this.updateButtonStatus(oCurControl.oImg); + } + } + } +} + +smc_BBCButtonBox.prototype.emulateClick = function (sCode) +{ + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + if (oCurControl.sType == 'button' && oCurControl.sCode == sCode) + { + eval(this.opt.sButtonClickHandler + '(oCurControl)'); + return true; + } + } + } + return false; +} + +smc_BBCButtonBox.prototype.setSelect = function (sSelectName, sValue) +{ + if (!('sButtonClickHandler' in this.opt)) + return; + + for (var iButtonRowIndex = 0, iRowCount = this.opt.aButtonRows.length; iButtonRowIndex < iRowCount; iButtonRowIndex++) + { + for (var iButtonIndex = 0, iButtonCount = this.opt.aButtonRows[iButtonRowIndex].length; iButtonIndex < iButtonCount; iButtonIndex++) + { + var oCurControl = this.opt.aButtonRows[iButtonRowIndex][iButtonIndex]; + if (oCurControl.sType == 'select' && oCurControl.sName == sSelectName) + oCurControl.oSelect.value = sValue; + } + } +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/fader.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/fader.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,217 @@ +function smf_NewsFader(oOptions) +{ + this.opt = oOptions; + + this.oFaderHandle = document.getElementById(this.opt.sFaderControlId); + + // Fade from... what text color? Default to black. + this.oFadeFrom = 'oFadeFrom' in this.opt ? this.opt.oFadeFrom : { + r: 0, + g: 0, + b: 0 + }; + + // To which background color? Default to white. + this.oFadeTo = 'oFadeTo' in this.opt ? this.opt.oFadeTo : { + r: 255, + g: 255, + b: 255 + }; + + // Surround each item with... anything special? + this.sItemTemplate = 'sItemTemplate' in this.opt ? this.opt.sItemTemplate : '%1$s'; + + // Fade delay (in milliseconds). + this.iFadeDelay = 'iFadeDelay' in this.opt ? this.opt.iFadeDelay : 5000; + + // The array that contains all the lines of the news for display. + this.aFaderItems = 'aFaderItems' in this.opt ? this.opt.aFaderItems : []; + + // Should we look for fader data, still? + this.bReceivedItemsOnConstruction = 'aFaderItems' in this.opt; + + // The current item in smfFadeContent. + this.iFadeIndex = -1; + + // Percent of fade (-64 to 510). + this.iFadePercent = 510 + + // Direction (in or out). + this.bFadeSwitch = false; + + // Just make sure the page is loaded before calling the init. + setTimeout(this.opt.sSelf + '.init();', 1); +} + +smf_NewsFader.prototype.init = function init() +{ + var oForeEl, oForeColor, oBackEl, oBackColor; + + // Try to find the fore- and background colors. + if ('currentStyle' in this.oFaderHandle) + { + oForeColor = this.oFaderHandle.currentStyle.color.match(/#([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/); + this.oFadeFrom = { + r: parseInt(oForeColor[1]), + g: parseInt(oForeColor[2]), + b: parseInt(oForeColor[3]) + }; + + oBackEl = this.oFaderHandle; + while (oBackEl.currentStyle.backgroundColor == 'transparent' && 'parentNode' in oBackEl) + oBackEl = oBackEl.parentNode; + + oBackColor = oBackEl.currentStyle.backgroundColor.match(/#([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/); + this.oFadeTo = { + r: eval('0x' + oBackColor[1]), + g: eval('0x' + oBackColor[2]), + b: eval('0x' + oBackColor[3]) + }; + } + else if (!('opera' in window) && 'defaultView' in document) + { + oForeEl = this.oFaderHandle; + while (document.defaultView.getComputedStyle(oForeEl, null).getPropertyCSSValue('color') == null && 'parentNode' in oForeEl && 'tagName' in oForeEl.parentNode) + oForeEl = oForeEl.parentNode; + + oForeColor = document.defaultView.getComputedStyle(oForeEl, null).getPropertyValue('color').match(/rgb\((\d+), (\d+), (\d+)\)/); + this.oFadeFrom = { + r: parseInt(oForeColor[1]), + g: parseInt(oForeColor[2]), + b: parseInt(oForeColor[3]) + }; + + oBackEl = this.oFaderHandle; + while (document.defaultView.getComputedStyle(oBackEl, null).getPropertyCSSValue('background-color') == null && 'parentNode' in oBackEl && 'tagName' in oBackEl.parentNode) + oBackEl = oBackEl.parentNode; + + oBackColor = document.defaultView.getComputedStyle(oBackEl, null).getPropertyValue('background-color'); + this.oFadeTo = { + r: parseInt(oBackColor[1]), + g: parseInt(oBackColor[2]), + b: parseInt(oBackColor[3]) + }; + } + + // Did we get our fader items on construction, or should we be gathering them instead? + if (!this.bReceivedItemsOnConstruction) + { + // Get the news from the list in boardindex + var oNewsItems = this.oFaderHandle.getElementsByTagName('li'); + + // Fill the array that has previously been created + for (var i = 0, n = oNewsItems.length; i < n; i ++) + this.aFaderItems[i] = oNewsItems[i].innerHTML; + } + + // The ranges to fade from for R, G, and B. (how far apart they are.) + this.oFadeRange = { + 'r': this.oFadeFrom.r - this.oFadeTo.r, + 'g': this.oFadeFrom.g - this.oFadeTo.g, + 'b': this.oFadeFrom.b - this.oFadeTo.b + }; + + // Divide by 20 because we are doing it 20 times per one ms. + this.iFadeDelay /= 20; + + // Start the fader! + window.setTimeout(this.opt.sSelf + '.fade();', 20); +} + +// Main fading function... called 50 times every second. +smf_NewsFader.prototype.fade = function fade() +{ + if (this.aFaderItems.length <= 1) + return; + + // A fix for Internet Explorer 4: wait until the document is loaded so we can use setInnerHTML(). + if ('readyState' in document && document.readyState != 'complete') + { + window.setTimeout(this.opt.sSelf + '.fade();', 20); + return; + } + + // Starting out? Set up the first item. + if (this.iFadeIndex == -1) + { + setInnerHTML(this.oFaderHandle, this.sItemTemplate.replace('%1$s', this.aFaderItems[0])); + this.iFadeIndex = 1; + + // In Mozilla, text jumps around from this when 1 or 0.5, etc... + if ('MozOpacity' in this.oFaderHandle.style) + this.oFaderHandle.style.MozOpacity = '0.90'; + else if ('opacity' in this.oFaderHandle.style) + this.oFaderHandle.style.opacity = '0.90'; + // In Internet Explorer, we have to define this to use it. + else if ('filter' in this.oFaderHandle.style) + this.oFaderHandle.style.filter = 'alpha(opacity=100)'; + } + + // Are we already done fading in? If so, fade out. + if (this.iFadePercent >= 510) + this.bFadeSwitch = !this.bFadeSwitch; + + // All the way faded out? + else if (this.iFadePercent <= -64) + { + this.bFadeSwitch = !this.bFadeSwitch; + + // Go to the next item, or first if we're out of items. + setInnerHTML(this.oFaderHandle, this.sItemTemplate.replace('%1$s', this.aFaderItems[this.iFadeIndex ++])); + if (this.iFadeIndex >= this.aFaderItems.length) + this.iFadeIndex = 0; + } + + // Increment or decrement the fade percentage. + if (this.bFadeSwitch) + this.iFadePercent -= 255 / this.iFadeDelay * 2; + else + this.iFadePercent += 255 / this.iFadeDelay * 2; + + // If it's not outside 0 and 256... (otherwise it's just delay time.) + if (this.iFadePercent < 256 && this.iFadePercent > 0) + { + // Easier... also faster... + var tempPercent = this.iFadePercent / 255, rounded; + + if ('MozOpacity' in this.oFaderHandle.style) + { + rounded = Math.round(tempPercent * 100) / 100; + this.oFaderHandle.style.MozOpacity = rounded == 1 ? '0.99' : rounded; + } + else if ('opacity' in this.oFaderHandle.style) + { + rounded = Math.round(tempPercent * 100) / 100; + this.oFaderHandle.style.opacity = rounded == 1 ? '0.99' : rounded; + } + else + { + var done = false; + if ('alpha' in this.oFaderHandle.filters) + { + try + { + this.oFaderHandle.filters.alpha.opacity = Math.round(tempPercent * 100); + done = true; + } + catch (err) + { + } + } + + if (!done) + { + // Get the new R, G, and B. (it should be bottom + (range of color * percent)...) + var r = Math.ceil(this.oFadeTo.r + this.oFadeRange.r * tempPercent); + var g = Math.ceil(this.oFadeTo.g + this.oFadeRange.g * tempPercent); + var b = Math.ceil(this.oFadeTo.b + this.oFadeRange.b * tempPercent); + + // Set the color in the style, thereby fading it. + this.oFaderHandle.style.color = 'rgb(' + r + ', ' + g + ', ' + b + ')'; + } + } + } + + // Keep going. + window.setTimeout(this.opt.sSelf + '.fade();', 20); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/profile.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/profile.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,40 @@ +var localTime = new Date(); +function autoDetectTimeOffset(currentTime) +{ + if (typeof(currentTime) != 'string') + var serverTime = currentTime; + else + var serverTime = new Date(currentTime); + + // Something wrong? + if (!localTime.getTime() || !serverTime.getTime()) + return 0; + + // Get the difference between the two, set it up so that the sign will tell us who is ahead of who. + var diff = Math.round((localTime.getTime() - serverTime.getTime())/3600000); + + // Make sure we are limiting this to one day's difference. + diff %= 24; + + return diff; +} + +// Prevent Chrome from auto completing fields when viewing/editing other members profiles +function disableAutoComplete() +{ + if (is_chrome && document.addEventListener) + document.addEventListener("DOMContentLoaded", disableAutoCompleteNow, false); +} + +// Once DOMContentLoaded is triggered, call the function +function disableAutoCompleteNow() +{ + for (var i = 0, n = document.forms.length; i < n; i++) + { + var die = document.forms[i].elements; + for (var j = 0, m = die.length; j < m; j++) + // Only bother with text/password fields? + if (die[j].type == "text" || die[j].type == "password") + die[j].setAttribute("autocomplete", "off"); + } +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/register.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/register.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,266 @@ +function smfRegister(formID, passwordDifficultyLevel, regTextStrings) +{ + this.addVerify = addVerificationField; + this.autoSetup = autoSetup; + this.refreshMainPassword = refreshMainPassword; + this.refreshVerifyPassword = refreshVerifyPassword; + + var verificationFields = new Array(); + var verificationFieldLength = 0; + var textStrings = regTextStrings ? regTextStrings : new Array(); + var passwordLevel = passwordDifficultyLevel ? passwordDifficultyLevel : 0; + + // Setup all the fields! + autoSetup(formID); + + // This is a field which requires some form of verification check. + function addVerificationField(fieldType, fieldID) + { + // Check the field exists. + if (!document.getElementById(fieldID)) + return; + + // Get the handles. + var inputHandle = document.getElementById(fieldID); + var imageHandle = document.getElementById(fieldID + '_img') ? document.getElementById(fieldID + '_img') : false; + var divHandle = document.getElementById(fieldID + '_div') ? document.getElementById(fieldID + '_div') : false; + + // What is the event handler? + var eventHandler = false; + if (fieldType == 'pwmain') + eventHandler = refreshMainPassword; + else if (fieldType == 'pwverify') + eventHandler = refreshVerifyPassword; + else if (fieldType == 'username') + eventHandler = refreshUsername; + else if (fieldType == 'reserved') + eventHandler = refreshMainPassword; + + // Store this field. + var vFieldIndex = fieldType == 'reserved' ? fieldType + verificationFieldLength : fieldType; + verificationFields[vFieldIndex] = Array(6); + verificationFields[vFieldIndex][0] = fieldID; + verificationFields[vFieldIndex][1] = inputHandle; + verificationFields[vFieldIndex][2] = imageHandle; + verificationFields[vFieldIndex][3] = divHandle; + verificationFields[vFieldIndex][4] = fieldType; + verificationFields[vFieldIndex][5] = inputHandle.className; + + // Keep a count to it! + verificationFieldLength++; + + // Step to it! + if (eventHandler) + { + createEventListener(inputHandle); + inputHandle.addEventListener('keyup', eventHandler, false); + eventHandler(); + + // Username will auto check on blur! + inputHandle.addEventListener('blur', autoCheckUsername, false); + } + + // Make the div visible! + if (divHandle) + divHandle.style.display = ''; + } + + // A button to trigger a username search? + function addUsernameSearchTrigger(elementID) + { + var buttonHandle = document.getElementById(elementID); + + // Attach the event to this element. + createEventListener(buttonHandle); + buttonHandle.addEventListener('click', checkUsername, false); + } + + // This function will automatically pick up all the necessary verification fields and initialise their visual status. + function autoSetup(formID) + { + if (!document.getElementById(formID)) + return false; + + var curElement, curType; + for (var i = 0, n = document.getElementById(formID).elements.length; i < n; i++) + { + curElement = document.getElementById(formID).elements[i]; + + // Does the ID contain the keyword 'autov'? + if (curElement.id.indexOf('autov') != -1 && (curElement.type == 'text' || curElement.type == 'password')) + { + // This is probably it - but does it contain a field type? + curType = 0; + // Username can only be done with XML. + if (curElement.id.indexOf('username') != -1 && window.XMLHttpRequest) + curType = 'username'; + else if (curElement.id.indexOf('pwmain') != -1) + curType = 'pwmain'; + else if (curElement.id.indexOf('pwverify') != -1) + curType = 'pwverify'; + // This means this field is reserved and cannot be contained in the password! + else if (curElement.id.indexOf('reserve') != -1) + curType = 'reserved'; + + // If we're happy let's add this element! + if (curType) + addVerificationField(curType, curElement.id); + + // If this is the username do we also have a button to find the user? + if (curType == 'username' && document.getElementById(curElement.id + '_link')) + { + addUsernameSearchTrigger(curElement.id + '_link'); + } + } + } + + return true; + } + + // What is the password state? + function refreshMainPassword(called_from_verify) + { + if (!verificationFields['pwmain']) + return false; + + var curPass = verificationFields['pwmain'][1].value; + var stringIndex = ''; + + // Is it a valid length? + if ((curPass.length < 8 && passwordLevel >= 1) || curPass.length < 4) + stringIndex = 'password_short'; + + // More than basic? + if (passwordLevel >= 1) + { + // If there is a username check it's not in the password! + if (verificationFields['username'] && verificationFields['username'][1].value && curPass.indexOf(verificationFields['username'][1].value) != -1) + stringIndex = 'password_reserved'; + + // Any reserved fields? + for (var i in verificationFields) + { + if (verificationFields[i][4] == 'reserved' && verificationFields[i][1].value && curPass.indexOf(verificationFields[i][1].value) != -1) + stringIndex = 'password_reserved'; + } + + // Finally - is it hard and as such requiring mixed cases and numbers? + if (passwordLevel > 1) + { + if (curPass == curPass.toLowerCase()) + stringIndex = 'password_numbercase'; + if (!curPass.match(/(\D\d|\d\D)/)) + stringIndex = 'password_numbercase'; + } + } + + var isValid = stringIndex == '' ? true : false; + if (stringIndex == '') + stringIndex = 'password_valid'; + + // Set the image. + setVerificationImage(verificationFields['pwmain'][2], isValid, textStrings[stringIndex] ? textStrings[stringIndex] : ''); + verificationFields['pwmain'][1].className = verificationFields['pwmain'][5] + ' ' + (isValid ? 'valid_input' : 'invalid_input'); + + // As this has changed the verification one may have too! + if (verificationFields['pwverify'] && !called_from_verify) + refreshVerifyPassword(); + + return isValid; + } + + // Check that the verification password matches the main one! + function refreshVerifyPassword() + { + // Can't do anything without something to check again! + if (!verificationFields['pwmain']) + return false; + + // Check and set valid status! + var isValid = verificationFields['pwmain'][1].value == verificationFields['pwverify'][1].value && refreshMainPassword(true); + var alt = textStrings[isValid == 1 ? 'password_valid' : 'password_no_match'] ? textStrings[isValid == 1 ? 'password_valid' : 'password_no_match'] : ''; + setVerificationImage(verificationFields['pwverify'][2], isValid, alt); + verificationFields['pwverify'][1].className = verificationFields['pwverify'][5] + ' ' + (isValid ? 'valid_input' : 'invalid_input'); + + return true; + } + + // If the username is changed just revert the status of whether it's valid! + function refreshUsername() + { + if (!verificationFields['username']) + return false; + + // Restore the class name. + if (verificationFields['username'][1].className) + verificationFields['username'][1].className = verificationFields['username'][5]; + // Check the image is correct. + var alt = textStrings['username_check'] ? textStrings['username_check'] : ''; + setVerificationImage(verificationFields['username'][2], 'check', alt); + + // Check the password is still OK. + refreshMainPassword(); + + return true; + } + + // This is a pass through function that ensures we don't do any of the AJAX notification stuff. + function autoCheckUsername() + { + checkUsername(true); + } + + // Check whether the username exists? + function checkUsername(is_auto) + { + if (!verificationFields['username']) + return false; + + // Get the username and do nothing without one! + var curUsername = verificationFields['username'][1].value; + if (!curUsername) + return false; + + if (!is_auto) + ajax_indicator(true); + + // Request a search on that username. + checkName = curUsername.php_to8bit().php_urlencode(); + getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=register;sa=usernamecheck;xml;username=' + checkName, checkUsernameCallback); + + return true; + } + + // Callback for getting the username data. + function checkUsernameCallback(XMLDoc) + { + if (XMLDoc.getElementsByTagName("username")) + isValid = XMLDoc.getElementsByTagName("username")[0].getAttribute("valid"); + else + isValid = true; + + // What to alt? + var alt = textStrings[isValid == 1 ? 'username_valid' : 'username_invalid'] ? textStrings[isValid == 1 ? 'username_valid' : 'username_invalid'] : ''; + + verificationFields['username'][1].className = verificationFields['username'][5] + ' ' + (isValid == 1 ? 'valid_input' : 'invalid_input'); + setVerificationImage(verificationFields['username'][2], isValid == 1, alt); + + ajax_indicator(false); + } + + // Set the image to be the correct type. + function setVerificationImage(imageHandle, imageIcon, alt) + { + if (!imageHandle) + return false; + if (!alt) + alt = '*'; + + var curImage = imageIcon ? (imageIcon == 'check' ? 'field_check.gif' : 'field_valid.gif') : 'field_invalid.gif'; + imageHandle.src = smf_images_url + '/icons/' + curImage; + imageHandle.alt = alt; + imageHandle.title = alt; + + return true; + } +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/script.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/script.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1412 @@ +var smf_formSubmitted = false; +var lastKeepAliveCheck = new Date().getTime(); +var smf_editorArray = new Array(); + +// Some very basic browser detection - from Mozilla's sniffer page. +var ua = navigator.userAgent.toLowerCase(); + +var is_opera = ua.indexOf('opera') != -1; +var is_opera5 = ua.indexOf('opera/5') != -1 || ua.indexOf('opera 5') != -1; +var is_opera6 = ua.indexOf('opera/6') != -1 || ua.indexOf('opera 6') != -1; +var is_opera7 = ua.indexOf('opera/7') != -1 || ua.indexOf('opera 7') != -1; +var is_opera8 = ua.indexOf('opera/8') != -1 || ua.indexOf('opera 8') != -1; +var is_opera9 = ua.indexOf('opera/9') != -1 || ua.indexOf('opera 9') != -1; +var is_opera95 = ua.indexOf('opera/9.5') != -1 || ua.indexOf('opera 9.5') != -1; +var is_opera96 = ua.indexOf('opera/9.6') != -1 || ua.indexOf('opera 9.6') != -1; +var is_opera10 = (ua.indexOf('opera/9.8') != -1 || ua.indexOf('opera 9.8') != -1 || ua.indexOf('opera/10.') != -1 || ua.indexOf('opera 10.') != -1) || ua.indexOf('version/10.') != -1; +var is_opera95up = is_opera95 || is_opera96 || is_opera10; + +var is_ff = (ua.indexOf('firefox') != -1 || ua.indexOf('iceweasel') != -1 || ua.indexOf('icecat') != -1 || ua.indexOf('shiretoko') != -1 || ua.indexOf('minefield') != -1) && !is_opera; +var is_gecko = ua.indexOf('gecko') != -1 && !is_opera; + +var is_chrome = ua.indexOf('chrome') != -1; +var is_safari = ua.indexOf('applewebkit') != -1 && !is_chrome; +var is_webkit = ua.indexOf('applewebkit') != -1; + +var is_ie = ua.indexOf('msie') != -1 && !is_opera; +var is_ie4 = is_ie && ua.indexOf('msie 4') != -1; +var is_ie5 = is_ie && ua.indexOf('msie 5') != -1; +var is_ie50 = is_ie && ua.indexOf('msie 5.0') != -1; +var is_ie55 = is_ie && ua.indexOf('msie 5.5') != -1; +var is_ie5up = is_ie && !is_ie4; +var is_ie6 = is_ie && ua.indexOf('msie 6') != -1; +var is_ie6up = is_ie5up && !is_ie55 && !is_ie5; +var is_ie6down = is_ie6 || is_ie5 || is_ie4; +var is_ie7 = is_ie && ua.indexOf('msie 7') != -1; +var is_ie7up = is_ie6up && !is_ie6; +var is_ie7down = is_ie7 || is_ie6 || is_ie5 || is_ie4; + +var is_ie8 = is_ie && ua.indexOf('msie 8') != -1; +var is_ie8up = is_ie8 && !is_ie7down; + +var is_iphone = ua.indexOf('iphone') != -1 || ua.indexOf('ipod') != -1; +var is_android = ua.indexOf('android') != -1; + +var ajax_indicator_ele = null; + +// Define document.getElementById for Internet Explorer 4. +if (!('getElementById' in document) && 'all' in document) + document.getElementById = function (sId) { + return document.all[sId]; + } + +// Define XMLHttpRequest for IE 5 and above. (don't bother for IE 4 :/.... works in Opera 7.6 and Safari 1.2!) +else if (!('XMLHttpRequest' in window) && 'ActiveXObject' in window) + window.XMLHttpRequest = function () { + return new ActiveXObject(is_ie5 ? 'Microsoft.XMLHTTP' : 'MSXML2.XMLHTTP'); + }; + +// Ensure the getElementsByTagName exists. +if (!'getElementsByTagName' in document && 'all' in document) + document.getElementsByTagName = function (sName) { + return document.all.tags[sName]; + } + +// Some older versions of Mozilla don't have this, for some reason. +if (!('forms' in document)) + document.forms = document.getElementsByTagName('form'); + +// Load an XML document using XMLHttpRequest. +function getXMLDocument(sUrl, funcCallback) +{ + if (!window.XMLHttpRequest) + return null; + + var oMyDoc = new XMLHttpRequest(); + var bAsync = typeof(funcCallback) != 'undefined'; + var oCaller = this; + if (bAsync) + { + oMyDoc.onreadystatechange = function () { + if (oMyDoc.readyState != 4) + return; + + if (oMyDoc.responseXML != null && oMyDoc.status == 200) + { + if (funcCallback.call) + { + funcCallback.call(oCaller, oMyDoc.responseXML); + } + // A primitive substitute for the call method to support IE 5.0. + else + { + oCaller.tmpMethod = funcCallback; + oCaller.tmpMethod(oMyDoc.responseXML); + delete oCaller.tmpMethod; + } + } + }; + } + oMyDoc.open('GET', sUrl, bAsync); + oMyDoc.send(null); + + return oMyDoc; +} + +// Send a post form to the server using XMLHttpRequest. +function sendXMLDocument(sUrl, sContent, funcCallback) +{ + if (!window.XMLHttpRequest) + return false; + + var oSendDoc = new window.XMLHttpRequest(); + var oCaller = this; + if (typeof(funcCallback) != 'undefined') + { + oSendDoc.onreadystatechange = function () { + if (oSendDoc.readyState != 4) + return; + + if (oSendDoc.responseXML != null && oSendDoc.status == 200) + funcCallback.call(oCaller, oSendDoc.responseXML); + else + funcCallback.call(oCaller, false); + }; + } + oSendDoc.open('POST', sUrl, true); + if ('setRequestHeader' in oSendDoc) + oSendDoc.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + oSendDoc.send(sContent); + + return true; +} + +// A property we'll be needing for php_to8bit. +String.prototype.oCharsetConversion = { + from: '', + to: '' +}; + +// Convert a string to an 8 bit representation (like in PHP). +String.prototype.php_to8bit = function () +{ + if (smf_charset == 'UTF-8') + { + var n, sReturn = ''; + + for (var i = 0, iTextLen = this.length; i < iTextLen; i++) + { + n = this.charCodeAt(i); + if (n < 128) + sReturn += String.fromCharCode(n) + else if (n < 2048) + sReturn += String.fromCharCode(192 | n >> 6) + String.fromCharCode(128 | n & 63); + else if (n < 65536) + sReturn += String.fromCharCode(224 | n >> 12) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); + else + sReturn += String.fromCharCode(240 | n >> 18) + String.fromCharCode(128 | n >> 12 & 63) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); + } + + return sReturn; + } + + else if (this.oCharsetConversion.from.length == 0) + { + switch (smf_charset) + { + case 'ISO-8859-1': + this.oCharsetConversion = { + from: '\xa0-\xff', + to: '\xa0-\xff' + }; + break; + + case 'ISO-8859-2': + this.oCharsetConversion = { + from: '\xa0\u0104\u02d8\u0141\xa4\u013d\u015a\xa7\xa8\u0160\u015e\u0164\u0179\xad\u017d\u017b\xb0\u0105\u02db\u0142\xb4\u013e\u015b\u02c7\xb8\u0161\u015f\u0165\u017a\u02dd\u017e\u017c\u0154\xc1\xc2\u0102\xc4\u0139\u0106\xc7\u010c\xc9\u0118\xcb\u011a\xcd\xce\u010e\u0110\u0143\u0147\xd3\xd4\u0150\xd6\xd7\u0158\u016e\xda\u0170\xdc\xdd\u0162\xdf\u0155\xe1\xe2\u0103\xe4\u013a\u0107\xe7\u010d\xe9\u0119\xeb\u011b\xed\xee\u010f\u0111\u0144\u0148\xf3\xf4\u0151\xf6\xf7\u0159\u016f\xfa\u0171\xfc\xfd\u0163\u02d9', + to: '\xa0-\xff' + }; + break; + + case 'ISO-8859-5': + this.oCharsetConversion = { + from: '\xa0\u0401-\u040c\xad\u040e-\u044f\u2116\u0451-\u045c\xa7\u045e\u045f', + to: '\xa0-\xff' + }; + break; + + case 'ISO-8859-9': + this.oCharsetConversion = { + from: '\xa0-\xcf\u011e\xd1-\xdc\u0130\u015e\xdf-\xef\u011f\xf1-\xfc\u0131\u015f\xff', + to: '\xa0-\xff' + }; + break; + + case 'ISO-8859-15': + this.oCharsetConversion = { + from: '\xa0-\xa3\u20ac\xa5\u0160\xa7\u0161\xa9-\xb3\u017d\xb5-\xb7\u017e\xb9-\xbb\u0152\u0153\u0178\xbf-\xff', + to: '\xa0-\xff' + }; + break; + + case 'tis-620': + this.oCharsetConversion = { + from: '\u20ac\u2026\u2018\u2019\u201c\u201d\u2022\u2013\u2014\xa0\u0e01-\u0e3a\u0e3f-\u0e5b', + to: '\x80\x85\x91-\x97\xa0-\xda\xdf-\xfb' + }; + break; + + case 'windows-1251': + this.oCharsetConversion = { + from: '\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\u040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u0459\u203a\u045a\u045c\u045b\u045f\xa0\u040e\u045e\u0408\xa4\u0490\xa6\xa7\u0401\xa9\u0404\xab-\xae\u0407\xb0\xb1\u0406\u0456\u0491\xb5-\xb7\u0451\u2116\u0454\xbb\u0458\u0405\u0455\u0457\u0410-\u044f', + to: '\x80-\x97\x99-\xff' + }; + break; + + case 'windows-1253': + this.oCharsetConversion = { + from: '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u203a\xa0\u0385\u0386\xa3-\xa9\xab-\xae\u2015\xb0-\xb3\u0384\xb5-\xb7\u0388-\u038a\xbb\u038c\xbd\u038e-\u03a1\u03a3-\u03ce', + to: '\x80\x82-\x87\x89\x8b\x91-\x97\x99\x9b\xa0-\xa9\xab-\xd1\xd3-\xfe' + }; + break; + + case 'windows-1255': + this.oCharsetConversion = { + from: '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u203a\xa0-\xa3\u20aa\xa5-\xa9\xd7\xab-\xb9\xf7\xbb-\xbf\u05b0-\u05b9\u05bb-\u05c3\u05f0-\u05f4\u05d0-\u05ea\u200e\u200f', + to: '\x80\x82-\x89\x8b\x91-\x99\x9b\xa0-\xc9\xcb-\xd8\xe0-\xfa\xfd\xfe' + }; + break; + + case 'windows-1256': + this.oCharsetConversion = { + from: '\u20ac\u067e\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0679\u2039\u0152\u0686\u0698\u0688\u06af\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u06a9\u2122\u0691\u203a\u0153\u200c\u200d\u06ba\xa0\u060c\xa2-\xa9\u06be\xab-\xb9\u061b\xbb-\xbe\u061f\u06c1\u0621-\u0636\xd7\u0637-\u063a\u0640-\u0643\xe0\u0644\xe2\u0645-\u0648\xe7-\xeb\u0649\u064a\xee\xef\u064b-\u064e\xf4\u064f\u0650\xf7\u0651\xf9\u0652\xfb\xfc\u200e\u200f\u06d2', + to: '\x80-\xff' + }; + break; + + default: + this.oCharsetConversion = { + from: '', + to: '' + }; + break; + } + var funcExpandString = function (sSearch) { + var sInsert = ''; + for (var i = sSearch.charCodeAt(0), n = sSearch.charCodeAt(2); i <= n; i++) + sInsert += String.fromCharCode(i); + return sInsert; + }; + this.oCharsetConversion.from = this.oCharsetConversion.from.replace(/.\-./g, funcExpandString); + this.oCharsetConversion.to = this.oCharsetConversion.to.replace(/.\-./g, funcExpandString); + } + + var sReturn = '', iOffsetFrom = 0; + for (var i = 0, n = this.length; i < n; i++) + { + iOffsetFrom = this.oCharsetConversion.from.indexOf(this.charAt(i)); + sReturn += iOffsetFrom > -1 ? this.oCharsetConversion.to.charAt(iOffsetFrom) : (this.charCodeAt(i) > 127 ? '&#' + this.charCodeAt(i) + ';' : this.charAt(i)); + } + + return sReturn +} + +// Character-level replacement function. +String.prototype.php_strtr = function (sFrom, sTo) +{ + return this.replace(new RegExp('[' + sFrom + ']', 'g'), function (sMatch) { + return sTo.charAt(sFrom.indexOf(sMatch)); + }); +} + +// Simulate PHP's strtolower (in SOME cases PHP uses ISO-8859-1 case folding). +String.prototype.php_strtolower = function () +{ + return typeof(smf_iso_case_folding) == 'boolean' && smf_iso_case_folding == true ? this.php_strtr( + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ\x8a\x8c\x8e\x9f\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde', + 'abcdefghijklmnopqrstuvwxyz\x9a\x9c\x9e\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe' + ) : this.php_strtr('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); +} + +String.prototype.php_urlencode = function() +{ + return escape(this).replace(/\+/g, '%2b').replace('*', '%2a').replace('/', '%2f').replace('@', '%40'); +} + +String.prototype.php_htmlspecialchars = function() +{ + return this.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +} + +String.prototype.php_unhtmlspecialchars = function() +{ + return this.replace(/"/g, '"').replace(/>/g, '>').replace(/</g, '<').replace(/&/g, '&'); +} + +String.prototype.php_addslashes = function() +{ + return this.replace(/\\/g, '\\\\').replace(/'/g, '\\\''); +} + +String.prototype._replaceEntities = function(sInput, sDummy, sNum) +{ + return String.fromCharCode(parseInt(sNum)); +} + +String.prototype.removeEntities = function() +{ + return this.replace(/&(amp;)?#(\d+);/g, this._replaceEntities); +} + +String.prototype.easyReplace = function (oReplacements) +{ + var sResult = this; + for (var sSearch in oReplacements) + sResult = sResult.replace(new RegExp('%' + sSearch + '%', 'g'), oReplacements[sSearch]); + + return sResult; +} + + +// Open a new window. +function reqWin(desktopURL, alternateWidth, alternateHeight, noScrollbars) +{ + if ((alternateWidth && self.screen.availWidth * 0.8 < alternateWidth) || (alternateHeight && self.screen.availHeight * 0.8 < alternateHeight)) + { + noScrollbars = false; + alternateWidth = Math.min(alternateWidth, self.screen.availWidth * 0.8); + alternateHeight = Math.min(alternateHeight, self.screen.availHeight * 0.8); + } + else + noScrollbars = typeof(noScrollbars) == 'boolean' && noScrollbars == true; + + window.open(desktopURL, 'requested_popup', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=' + (noScrollbars ? 'no' : 'yes') + ',width=' + (alternateWidth ? alternateWidth : 480) + ',height=' + (alternateHeight ? alternateHeight : 220) + ',resizable=no'); + + // Return false so the click won't follow the link ;). + return false; +} + +// Remember the current position. +function storeCaret(oTextHandle) +{ + // Only bother if it will be useful. + if ('createTextRange' in oTextHandle) + oTextHandle.caretPos = document.selection.createRange().duplicate(); +} + +// Replaces the currently selected text with the passed text. +function replaceText(text, oTextHandle) +{ + // Attempt to create a text range (IE). + if ('caretPos' in oTextHandle && 'createTextRange' in oTextHandle) + { + var caretPos = oTextHandle.caretPos; + + caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text; + caretPos.select(); + } + // Mozilla text range replace. + else if ('selectionStart' in oTextHandle) + { + var begin = oTextHandle.value.substr(0, oTextHandle.selectionStart); + var end = oTextHandle.value.substr(oTextHandle.selectionEnd); + var scrollPos = oTextHandle.scrollTop; + + oTextHandle.value = begin + text + end; + + if (oTextHandle.setSelectionRange) + { + oTextHandle.focus(); + var goForward = is_opera ? text.match(/\n/g).length : 0; + oTextHandle.setSelectionRange(begin.length + text.length + goForward, begin.length + text.length + goForward); + } + oTextHandle.scrollTop = scrollPos; + } + // Just put it on the end. + else + { + oTextHandle.value += text; + oTextHandle.focus(oTextHandle.value.length - 1); + } +} + +// Surrounds the selected text with text1 and text2. +function surroundText(text1, text2, oTextHandle) +{ + // Can a text range be created? + if ('caretPos' in oTextHandle && 'createTextRange' in oTextHandle) + { + var caretPos = oTextHandle.caretPos, temp_length = caretPos.text.length; + + caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text1 + caretPos.text + text2 + ' ' : text1 + caretPos.text + text2; + + if (temp_length == 0) + { + caretPos.moveStart('character', -text2.length); + caretPos.moveEnd('character', -text2.length); + caretPos.select(); + } + else + oTextHandle.focus(caretPos); + } + // Mozilla text range wrap. + else if ('selectionStart' in oTextHandle) + { + var begin = oTextHandle.value.substr(0, oTextHandle.selectionStart); + var selection = oTextHandle.value.substr(oTextHandle.selectionStart, oTextHandle.selectionEnd - oTextHandle.selectionStart); + var end = oTextHandle.value.substr(oTextHandle.selectionEnd); + var newCursorPos = oTextHandle.selectionStart; + var scrollPos = oTextHandle.scrollTop; + + oTextHandle.value = begin + text1 + selection + text2 + end; + + if (oTextHandle.setSelectionRange) + { + var goForward = is_opera ? text1.match(/\n/g).length : 0, goForwardAll = is_opera ? (text1 + text2).match(/\n/g).length : 0; + if (selection.length == 0) + oTextHandle.setSelectionRange(newCursorPos + text1.length + goForward, newCursorPos + text1.length + goForward); + else + oTextHandle.setSelectionRange(newCursorPos, newCursorPos + text1.length + selection.length + text2.length + goForwardAll); + oTextHandle.focus(); + } + oTextHandle.scrollTop = scrollPos; + } + // Just put them on the end, then. + else + { + oTextHandle.value += text1 + text2; + oTextHandle.focus(oTextHandle.value.length - 1); + } +} + +// Checks if the passed input's value is nothing. +function isEmptyText(theField) +{ + // Copy the value so changes can be made.. + var theValue = theField.value; + + // Strip whitespace off the left side. + while (theValue.length > 0 && (theValue.charAt(0) == ' ' || theValue.charAt(0) == '\t')) + theValue = theValue.substring(1, theValue.length); + // Strip whitespace off the right side. + while (theValue.length > 0 && (theValue.charAt(theValue.length - 1) == ' ' || theValue.charAt(theValue.length - 1) == '\t')) + theValue = theValue.substring(0, theValue.length - 1); + + if (theValue == '') + return true; + else + return false; +} + +// Only allow form submission ONCE. +function submitonce(theform) +{ + smf_formSubmitted = true; + + // If there are any editors warn them submit is coming! + for (var i = 0; i < smf_editorArray.length; i++) + smf_editorArray[i].doSubmit(); +} +function submitThisOnce(oControl) +{ + // Hateful, hateful fix for Safari 1.3 beta. + if (is_safari) + return !smf_formSubmitted; + + // oControl might also be a form. + var oForm = 'form' in oControl ? oControl.form : oControl; + + var aTextareas = oForm.getElementsByTagName('textarea'); + for (var i = 0, n = aTextareas.length; i < n; i++) + aTextareas[i].readOnly = true; + + return !smf_formSubmitted; +} + +// Deprecated, as innerHTML is supported everywhere. +function setInnerHTML(oElement, sToValue) +{ + oElement.innerHTML = sToValue; +} + +function getInnerHTML(oElement) +{ + return oElement.innerHTML; +} + +// Set the "outer" HTML of an element. +function setOuterHTML(oElement, sToValue) +{ + if ('outerHTML' in oElement) + oElement.outerHTML = sToValue; + else + { + var range = document.createRange(); + range.setStartBefore(oElement); + oElement.parentNode.replaceChild(range.createContextualFragment(sToValue), oElement); + } +} + +// Checks for variable in theArray. +function in_array(variable, theArray) +{ + for (var i in theArray) + if (theArray[i] == variable) + return true; + + return false; +} + +// Checks for variable in theArray. +function array_search(variable, theArray) +{ + for (var i in theArray) + if (theArray[i] == variable) + return i; + + return null; +} + +// Find a specific radio button in its group and select it. +function selectRadioByName(oRadioGroup, sName) +{ + if (!('length' in oRadioGroup)) + return oRadioGroup.checked = true; + + for (var i = 0, n = oRadioGroup.length; i < n; i++) + if (oRadioGroup[i].value == sName) + return oRadioGroup[i].checked = true; + + return false; +} + +// Invert all checkboxes at once by clicking a single checkbox. +function invertAll(oInvertCheckbox, oForm, sMask, bIgnoreDisabled) +{ + for (var i = 0; i < oForm.length; i++) + { + if (!('name' in oForm[i]) || (typeof(sMask) == 'string' && oForm[i].name.substr(0, sMask.length) != sMask && oForm[i].id.substr(0, sMask.length) != sMask)) + continue; + + if (!oForm[i].disabled || (typeof(bIgnoreDisabled) == 'boolean' && bIgnoreDisabled)) + oForm[i].checked = oInvertCheckbox.checked; + } +} + +// Keep the session alive - always! +var lastKeepAliveCheck = new Date().getTime(); +function smf_sessionKeepAlive() +{ + var curTime = new Date().getTime(); + + // Prevent a Firefox bug from hammering the server. + if (smf_scripturl && curTime - lastKeepAliveCheck > 900000) + { + var tempImage = new Image(); + tempImage.src = smf_prepareScriptUrl(smf_scripturl) + 'action=keepalive;time=' + curTime; + lastKeepAliveCheck = curTime; + } + + window.setTimeout('smf_sessionKeepAlive();', 1200000); +} +window.setTimeout('smf_sessionKeepAlive();', 1200000); + +// Set a theme option through javascript. +function smf_setThemeOption(option, value, theme, cur_session_id, cur_session_var, additional_vars) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + if (typeof(cur_session_var) == 'undefined') + cur_session_var = 'sesc'; + + if (additional_vars == null) + additional_vars = ''; + + var tempImage = new Image(); + tempImage.src = smf_prepareScriptUrl(smf_scripturl) + 'action=jsoption;var=' + option + ';val=' + value + ';' + cur_session_var + '=' + cur_session_id + additional_vars + (theme == null ? '' : '&th=' + theme) + ';time=' + (new Date().getTime()); +} + +function smf_avatarResize() +{ + var possibleAvatars = document.getElementsByTagName('img'); + + for (var i = 0; i < possibleAvatars.length; i++) + { + var tempAvatars = []; j = 0; + if (possibleAvatars[i].className != 'avatar') + continue; + + // Image.prototype.avatar = possibleAvatars[i]; + tempAvatars[j] = new Image(); + tempAvatars[j].avatar = possibleAvatars[i]; + + tempAvatars[j].onload = function() + { + this.avatar.width = this.width; + this.avatar.height = this.height; + if (smf_avatarMaxWidth != 0 && this.width > smf_avatarMaxWidth) + { + this.avatar.height = (smf_avatarMaxWidth * this.height) / this.width; + this.avatar.width = smf_avatarMaxWidth; + } + if (smf_avatarMaxHeight != 0 && this.avatar.height > smf_avatarMaxHeight) + { + this.avatar.width = (smf_avatarMaxHeight * this.avatar.width) / this.avatar.height; + this.avatar.height = smf_avatarMaxHeight; + } + } + tempAvatars[j].src = possibleAvatars[i].src; + j++; + } + + if (typeof(window_oldAvatarOnload) != 'undefined' && window_oldAvatarOnload) + { + window_oldAvatarOnload(); + window_oldAvatarOnload = null; + } +} + + +function hashLoginPassword(doForm, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + if (typeof(hex_sha1) == 'undefined') + return; + // Are they using an email address? + if (doForm.user.value.indexOf('@') != -1) + return; + + // Unless the browser is Opera, the password will not save properly. + if (!('opera' in window)) + doForm.passwrd.autocomplete = 'off'; + + doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id); + + // It looks nicer to fill it with asterisks, but Firefox will try to save that. + if (is_ff != -1) + doForm.passwrd.value = ''; + else + doForm.passwrd.value = doForm.passwrd.value.replace(/./g, '*'); +} + +function hashAdminPassword(doForm, username, cur_session_id) +{ + // Compatibility. + if (cur_session_id == null) + cur_session_id = smf_session_id; + + if (typeof(hex_sha1) == 'undefined') + return; + + doForm.admin_hash_pass.value = hex_sha1(hex_sha1(username.php_to8bit().php_strtolower() + doForm.admin_pass.value.php_to8bit()) + cur_session_id); + doForm.admin_pass.value = doForm.admin_pass.value.replace(/./g, '*'); +} + +// Shows the page numbers by clicking the dots (in compact view). +function expandPages(spanNode, baseURL, firstPage, lastPage, perPage) +{ + var replacement = '', i, oldLastPage = 0; + var perPageLimit = 50; + + // The dots were bold, the page numbers are not (in most cases). + spanNode.style.fontWeight = 'normal'; + spanNode.onclick = ''; + + // Prevent too many pages to be loaded at once. + if ((lastPage - firstPage) / perPage > perPageLimit) + { + oldLastPage = lastPage; + lastPage = firstPage + perPageLimit * perPage; + } + + // Calculate the new pages. + for (i = firstPage; i < lastPage; i += perPage) + replacement += '' + (1 + i / perPage) + ' '; + + if (oldLastPage > 0) + replacement += ' ... '; + + // Replace the dots by the new page links. + setInnerHTML(spanNode, replacement); +} + +function smc_preCacheImage(sSrc) +{ + if (!('smc_aCachedImages' in window)) + window.smc_aCachedImages = []; + + if (!in_array(sSrc, window.smc_aCachedImages)) + { + var oImage = new Image(); + oImage.src = sSrc; + } +} + + +// *** smc_Cookie class. +function smc_Cookie(oOptions) +{ + this.opt = oOptions; + this.oCookies = {}; + this.init(); +} + +smc_Cookie.prototype.init = function() +{ + if ('cookie' in document && document.cookie != '') + { + var aCookieList = document.cookie.split(';'); + for (var i = 0, n = aCookieList.length; i < n; i++) + { + var aNameValuePair = aCookieList[i].split('='); + this.oCookies[aNameValuePair[0].replace(/^\s+|\s+$/g, '')] = decodeURIComponent(aNameValuePair[1]); + } + } +} + +smc_Cookie.prototype.get = function(sKey) +{ + return sKey in this.oCookies ? this.oCookies[sKey] : null; +} + +smc_Cookie.prototype.set = function(sKey, sValue) +{ + document.cookie = sKey + '=' + encodeURIComponent(sValue); +} + + +// *** smc_Toggle class. +function smc_Toggle(oOptions) +{ + this.opt = oOptions; + this.bCollapsed = false; + this.oCookie = null; + this.init(); +} + +smc_Toggle.prototype.init = function () +{ + // The master switch can disable this toggle fully. + if ('bToggleEnabled' in this.opt && !this.opt.bToggleEnabled) + return; + + // If cookies are enabled and they were set, override the initial state. + if ('oCookieOptions' in this.opt && this.opt.oCookieOptions.bUseCookie) + { + // Initialize the cookie handler. + this.oCookie = new smc_Cookie({}); + + // Check if the cookie is set. + var cookieValue = this.oCookie.get(this.opt.oCookieOptions.sCookieName) + if (cookieValue != null) + this.opt.bCurrentlyCollapsed = cookieValue == '1'; + } + + // If the init state is set to be collapsed, collapse it. + if (this.opt.bCurrentlyCollapsed) + this.changeState(true, true); + + // Initialize the images to be clickable. + if ('aSwapImages' in this.opt) + { + for (var i = 0, n = this.opt.aSwapImages.length; i < n; i++) + { + var oImage = document.getElementById(this.opt.aSwapImages[i].sId); + if (typeof(oImage) == 'object' && oImage != null) + { + // Display the image in case it was hidden. + if (oImage.style.display == 'none') + oImage.style.display = ''; + + oImage.instanceRef = this; + oImage.onclick = function () { + this.instanceRef.toggle(); + this.blur(); + } + oImage.style.cursor = 'pointer'; + + // Preload the collapsed image. + smc_preCacheImage(this.opt.aSwapImages[i].srcCollapsed); + } + } + } + + // Initialize links. + if ('aSwapLinks' in this.opt) + { + for (var i = 0, n = this.opt.aSwapLinks.length; i < n; i++) + { + var oLink = document.getElementById(this.opt.aSwapLinks[i].sId); + if (typeof(oLink) == 'object' && oLink != null) + { + // Display the link in case it was hidden. + if (oLink.style.display == 'none') + oLink.style.display = ''; + + oLink.instanceRef = this; + oLink.onclick = function () { + this.instanceRef.toggle(); + this.blur(); + return false; + } + } + } + } +} + +// Collapse or expand the section. +smc_Toggle.prototype.changeState = function(bCollapse, bInit) +{ + // Default bInit to false. + bInit = typeof(bInit) == 'undefined' ? false : true; + + // Handle custom function hook before collapse. + if (!bInit && bCollapse && 'funcOnBeforeCollapse' in this.opt) + { + this.tmpMethod = this.opt.funcOnBeforeCollapse; + this.tmpMethod(); + delete this.tmpMethod; + } + + // Handle custom function hook before expand. + else if (!bInit && !bCollapse && 'funcOnBeforeExpand' in this.opt) + { + this.tmpMethod = this.opt.funcOnBeforeExpand; + this.tmpMethod(); + delete this.tmpMethod; + } + + // Loop through all the images that need to be toggled. + if ('aSwapImages' in this.opt) + { + for (var i = 0, n = this.opt.aSwapImages.length; i < n; i++) + { + var oImage = document.getElementById(this.opt.aSwapImages[i].sId); + if (typeof(oImage) == 'object' && oImage != null) + { + // Only (re)load the image if it's changed. + var sTargetSource = bCollapse ? this.opt.aSwapImages[i].srcCollapsed : this.opt.aSwapImages[i].srcExpanded; + if (oImage.src != sTargetSource) + oImage.src = sTargetSource; + + oImage.alt = oImage.title = bCollapse ? this.opt.aSwapImages[i].altCollapsed : this.opt.aSwapImages[i].altExpanded; + } + } + } + + // Loop through all the links that need to be toggled. + if ('aSwapLinks' in this.opt) + { + for (var i = 0, n = this.opt.aSwapLinks.length; i < n; i++) + { + var oLink = document.getElementById(this.opt.aSwapLinks[i].sId); + if (typeof(oLink) == 'object' && oLink != null) + setInnerHTML(oLink, bCollapse ? this.opt.aSwapLinks[i].msgCollapsed : this.opt.aSwapLinks[i].msgExpanded); + } + } + + // Now go through all the sections to be collapsed. + for (var i = 0, n = this.opt.aSwappableContainers.length; i < n; i++) + { + if (this.opt.aSwappableContainers[i] == null) + continue; + + var oContainer = document.getElementById(this.opt.aSwappableContainers[i]); + if (typeof(oContainer) == 'object' && oContainer != null) + oContainer.style.display = bCollapse ? 'none' : ''; + } + + // Update the new state. + this.bCollapsed = bCollapse; + + // Update the cookie, if desired. + if ('oCookieOptions' in this.opt && this.opt.oCookieOptions.bUseCookie) + this.oCookie.set(this.opt.oCookieOptions.sCookieName, this.bCollapsed ? '1' : '0'); + + if ('oThemeOptions' in this.opt && this.opt.oThemeOptions.bUseThemeSettings) + smf_setThemeOption(this.opt.oThemeOptions.sOptionName, this.bCollapsed ? '1' : '0', 'sThemeId' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sThemeId : null, this.opt.oThemeOptions.sSessionId, this.opt.oThemeOptions.sSessionVar, 'sAdditionalVars' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sAdditionalVars : null); +} + +smc_Toggle.prototype.toggle = function() +{ + // Change the state by reversing the current state. + this.changeState(!this.bCollapsed); +} + + +function ajax_indicator(turn_on) +{ + if (ajax_indicator_ele == null) + { + ajax_indicator_ele = document.getElementById('ajax_in_progress'); + + if (ajax_indicator_ele == null && typeof(ajax_notification_text) != null) + { + create_ajax_indicator_ele(); + } + } + + if (ajax_indicator_ele != null) + { + if (navigator.appName == 'Microsoft Internet Explorer' && !is_ie7up) + { + ajax_indicator_ele.style.position = 'absolute'; + ajax_indicator_ele.style.top = document.documentElement.scrollTop; + } + + ajax_indicator_ele.style.display = turn_on ? 'block' : 'none'; + } +} + +function create_ajax_indicator_ele() +{ + // Create the div for the indicator. + ajax_indicator_ele = document.createElement('div'); + + // Set the id so it'll load the style properly. + ajax_indicator_ele.id = 'ajax_in_progress'; + + // Add the image in and link to turn it off. + var cancel_link = document.createElement('a'); + cancel_link.href = 'javascript:ajax_indicator(false)'; + var cancel_img = document.createElement('img'); + cancel_img.src = smf_images_url + '/icons/quick_remove.gif'; + + if (typeof(ajax_notification_cancel_text) != 'undefined') + { + cancel_img.alt = ajax_notification_cancel_text; + cancel_img.title = ajax_notification_cancel_text; + } + + // Add the cancel link and image to the indicator. + cancel_link.appendChild(cancel_img); + ajax_indicator_ele.appendChild(cancel_link); + + // Set the text. (Note: You MUST append here and not overwrite.) + ajax_indicator_ele.innerHTML += ajax_notification_text; + + // Finally attach the element to the body. + document.body.appendChild(ajax_indicator_ele); +} + +function createEventListener(oTarget) +{ + if (!('addEventListener' in oTarget)) + { + if (oTarget.attachEvent) + { + oTarget.addEventListener = function (sEvent, funcHandler, bCapture) { + oTarget.attachEvent('on' + sEvent, funcHandler); + } + oTarget.removeEventListener = function (sEvent, funcHandler, bCapture) { + oTarget.detachEvent('on' + sEvent, funcHandler); + } + } + else + { + oTarget.addEventListener = function (sEvent, funcHandler, bCapture) { + oTarget['on' + sEvent] = funcHandler; + } + oTarget.removeEventListener = function (sEvent, funcHandler, bCapture) { + oTarget['on' + sEvent] = null; + } + } + } +} + +// This function will retrieve the contents needed for the jump to boxes. +function grabJumpToContent() +{ + var oXMLDoc = getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=xmlhttp;sa=jumpto;xml'); + var aBoardsAndCategories = new Array(); + + ajax_indicator(true); + + if (oXMLDoc.responseXML) + { + var items = oXMLDoc.responseXML.getElementsByTagName('smf')[0].getElementsByTagName('item'); + for (var i = 0, n = items.length; i < n; i++) + { + aBoardsAndCategories[aBoardsAndCategories.length] = { + id: parseInt(items[i].getAttribute('id')), + isCategory: items[i].getAttribute('type') == 'category', + name: items[i].firstChild.nodeValue.removeEntities(), + is_current: false, + childLevel: parseInt(items[i].getAttribute('childlevel')) + } + } + } + + ajax_indicator(false); + + for (var i = 0, n = aJumpTo.length; i < n; i++) + aJumpTo[i].fillSelect(aBoardsAndCategories); +} + +// This'll contain all JumpTo objects on the page. +var aJumpTo = new Array(); + +// *** JumpTo class. +function JumpTo(oJumpToOptions) +{ + this.opt = oJumpToOptions; + this.dropdownList = null; + this.showSelect(); +} + +// Show the initial select box (onload). Method of the JumpTo class. +JumpTo.prototype.showSelect = function () +{ + var sChildLevelPrefix = ''; + for (var i = this.opt.iCurBoardChildLevel; i > 0; i--) + sChildLevelPrefix += this.opt.sBoardChildLevelIndicator; + setInnerHTML(document.getElementById(this.opt.sContainerId), this.opt.sJumpToTemplate.replace(/%select_id%/, this.opt.sContainerId + '_select').replace(/%dropdown_list%/, ' ')); + this.dropdownList = document.getElementById(this.opt.sContainerId + '_select'); +} + +// Fill the jump to box with entries. Method of the JumpTo class. +JumpTo.prototype.fillSelect = function (aBoardsAndCategories) +{ + var bIE5x = !('implementation' in document); + var iIndexPointer = 0; + + // Create an option that'll be above and below the category. + var oDashOption = document.createElement('option'); + oDashOption.appendChild(document.createTextNode(this.opt.sCatSeparator)); + oDashOption.disabled = 'disabled'; + oDashOption.value = ''; + + // Reset the events and clear the list (IE5.x only). + if (bIE5x) + { + this.dropdownList.onmouseover = null; + this.dropdownList.remove(0); + } + if ('onbeforeactivate' in document) + this.dropdownList.onbeforeactivate = null; + else + this.dropdownList.onfocus = null; + + // Create a document fragment that'll allowing inserting big parts at once. + var oListFragment = bIE5x ? this.dropdownList : document.createDocumentFragment(); + + // Loop through all items to be added. + for (var i = 0, n = aBoardsAndCategories.length; i < n; i++) + { + var j, sChildLevelPrefix, oOption; + + // If we've reached the currently selected board add all items so far. + if (!aBoardsAndCategories[i].isCategory && aBoardsAndCategories[i].id == this.opt.iCurBoardId) + { + if (bIE5x) + iIndexPointer = this.dropdownList.options.length; + else + { + this.dropdownList.insertBefore(oListFragment, this.dropdownList.options[0]); + oListFragment = document.createDocumentFragment(); + continue; + } + } + + if (aBoardsAndCategories[i].isCategory) + oListFragment.appendChild(oDashOption.cloneNode(true)); + else + for (j = aBoardsAndCategories[i].childLevel, sChildLevelPrefix = ''; j > 0; j--) + sChildLevelPrefix += this.opt.sBoardChildLevelIndicator; + + oOption = document.createElement('option'); + oOption.appendChild(document.createTextNode((aBoardsAndCategories[i].isCategory ? this.opt.sCatPrefix : sChildLevelPrefix + this.opt.sBoardPrefix) + aBoardsAndCategories[i].name)); + oOption.value = aBoardsAndCategories[i].isCategory ? '#c' + aBoardsAndCategories[i].id : '?board=' + aBoardsAndCategories[i].id + '.0'; + oListFragment.appendChild(oOption); + + if (aBoardsAndCategories[i].isCategory) + oListFragment.appendChild(oDashOption.cloneNode(true)); + } + + // Add the remaining items after the currently selected item. + this.dropdownList.appendChild(oListFragment); + + if (bIE5x) + this.dropdownList.options[iIndexPointer].selected = true; + + // Internet Explorer needs this to keep the box dropped down. + this.dropdownList.style.width = 'auto'; + this.dropdownList.focus(); + + // Add an onchange action + this.dropdownList.onchange = function() { + if (this.selectedIndex > 0 && this.options[this.selectedIndex].value) + window.location.href = smf_scripturl + this.options[this.selectedIndex].value.substr(smf_scripturl.indexOf('?') == -1 || this.options[this.selectedIndex].value.substr(0, 1) != '?' ? 0 : 1); + } +} + +// A global array containing all IconList objects. +var aIconLists = new Array(); + +// *** IconList object. +function IconList(oOptions) +{ + if (!window.XMLHttpRequest) + return; + + this.opt = oOptions; + this.bListLoaded = false; + this.oContainerDiv = null; + this.funcMousedownHandler = null; + this.funcParent = this; + this.iCurMessageId = 0; + this.iCurTimeout = 0; + + // Add backwards compatibility with old themes. + if (!('sSessionVar' in this.opt)) + this.opt.sSessionVar = 'sesc'; + + this.initIcons(); +} + +// Replace all message icons by icons with hoverable and clickable div's. +IconList.prototype.initIcons = function () +{ + for (var i = document.images.length - 1, iPrefixLength = this.opt.sIconIdPrefix.length; i >= 0; i--) + if (document.images[i].id.substr(0, iPrefixLength) == this.opt.sIconIdPrefix) + setOuterHTML(document.images[i], '
    ' + document.images[i].alt + '
    '); +} + +// Event for the mouse hovering over the original icon. +IconList.prototype.onBoxHover = function (oDiv, bMouseOver) +{ + oDiv.style.border = bMouseOver ? this.opt.iBoxBorderWidthHover + 'px solid ' + this.opt.sBoxBorderColorHover : ''; + oDiv.style.background = bMouseOver ? this.opt.sBoxBackgroundHover : this.opt.sBoxBackground; + oDiv.style.padding = bMouseOver ? (3 - this.opt.iBoxBorderWidthHover) + 'px' : '3px' +} + +// Show the list of icons after the user clicked the original icon. +IconList.prototype.openPopup = function (oDiv, iMessageId) +{ + this.iCurMessageId = iMessageId; + + if (!this.bListLoaded && this.oContainerDiv == null) + { + // Create a container div. + this.oContainerDiv = document.createElement('div'); + this.oContainerDiv.id = 'iconList'; + this.oContainerDiv.style.display = 'none'; + this.oContainerDiv.style.cursor = is_ie && !is_ie6up ? 'hand' : 'pointer'; + this.oContainerDiv.style.position = 'absolute'; + this.oContainerDiv.style.width = oDiv.offsetWidth + 'px'; + this.oContainerDiv.style.background = this.opt.sContainerBackground; + this.oContainerDiv.style.border = this.opt.sContainerBorder; + this.oContainerDiv.style.padding = '1px'; + this.oContainerDiv.style.textAlign = 'center'; + document.body.appendChild(this.oContainerDiv); + + // Start to fetch its contents. + ajax_indicator(true); + this.tmpMethod = getXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=xmlhttp;sa=messageicons;board=' + this.opt.iBoardId + ';xml', this.onIconsReceived); + delete this.tmpMethod; + + createEventListener(document.body); + } + + // Set the position of the container. + var aPos = smf_itemPos(oDiv); + if (is_ie50) + aPos[1] += 4; + + this.oContainerDiv.style.top = (aPos[1] + oDiv.offsetHeight) + 'px'; + this.oContainerDiv.style.left = (aPos[0] - 1) + 'px'; + this.oClickedIcon = oDiv; + + if (this.bListLoaded) + this.oContainerDiv.style.display = 'block'; + + document.body.addEventListener('mousedown', this.onWindowMouseDown, false); +} + +// Setup the list of icons once it is received through xmlHTTP. +IconList.prototype.onIconsReceived = function (oXMLDoc) +{ + var icons = oXMLDoc.getElementsByTagName('smf')[0].getElementsByTagName('icon'); + var sItems = ''; + + for (var i = 0, n = icons.length; i < n; i++) + sItems += '
    ' + icons[i].getAttribute('name') + '
    '; + + setInnerHTML(this.oContainerDiv, sItems); + this.oContainerDiv.style.display = 'block'; + this.bListLoaded = true; + + if (is_ie) + this.oContainerDiv.style.width = this.oContainerDiv.clientWidth + 'px'; + + ajax_indicator(false); +} + +// Event handler for hovering over the icons. +IconList.prototype.onItemHover = function (oDiv, bMouseOver) +{ + oDiv.style.background = bMouseOver ? this.opt.sItemBackgroundHover : this.opt.sItemBackground; + oDiv.style.border = bMouseOver ? this.opt.sItemBorderHover : this.opt.sItemBorder; + if (this.iCurTimeout != 0) + window.clearTimeout(this.iCurTimeout); + if (bMouseOver) + this.onBoxHover(this.oClickedIcon, true); + else + this.iCurTimeout = window.setTimeout(this.opt.sBackReference + '.collapseList();', 500); +} + +// Event handler for clicking on one of the icons. +IconList.prototype.onItemMouseDown = function (oDiv, sNewIcon) +{ + if (this.iCurMessageId != 0) + { + ajax_indicator(true); + this.tmpMethod = getXMLDocument; + var oXMLDoc = this.tmpMethod(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=jsmodify;topic=' + this.opt.iTopicId + ';msg=' + this.iCurMessageId + ';' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';icon=' + sNewIcon + ';xml'); + delete this.tmpMethod; + ajax_indicator(false); + + var oMessage = oXMLDoc.responseXML.getElementsByTagName('smf')[0].getElementsByTagName('message')[0]; + if (oMessage.getElementsByTagName('error').length == 0) + { + if (this.opt.bShowModify && oMessage.getElementsByTagName('modified').length != 0) + setInnerHTML(document.getElementById('modified_' + this.iCurMessageId), oMessage.getElementsByTagName('modified')[0].childNodes[0].nodeValue); + this.oClickedIcon.getElementsByTagName('img')[0].src = oDiv.getElementsByTagName('img')[0].src; + } + } +} + +// Event handler for clicking outside the list (will make the list disappear). +IconList.prototype.onWindowMouseDown = function () +{ + for (var i = aIconLists.length - 1; i >= 0; i--) + { + aIconLists[i].funcParent.tmpMethod = aIconLists[i].collapseList; + aIconLists[i].funcParent.tmpMethod(); + delete aIconLists[i].funcParent.tmpMethod; + } +} + +// Collapse the list of icons. +IconList.prototype.collapseList = function() +{ + this.onBoxHover(this.oClickedIcon, false); + this.oContainerDiv.style.display = 'none'; + this.iCurMessageId = 0; + document.body.removeEventListener('mousedown', this.onWindowMouseDown, false); +} + +// Handy shortcuts for getting the mouse position on the screen - only used for IE at the moment. +function smf_mousePose(oEvent) +{ + var x = 0; + var y = 0; + + if (oEvent.pageX) + { + y = oEvent.pageY; + x = oEvent.pageX; + } + else if (oEvent.clientX) + { + x = oEvent.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft); + y = oEvent.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop); + } + + return [x, y]; +} + +// Short function for finding the actual position of an item. +function smf_itemPos(itemHandle) +{ + var itemX = 0; + var itemY = 0; + + if ('offsetParent' in itemHandle) + { + itemX = itemHandle.offsetLeft; + itemY = itemHandle.offsetTop; + while (itemHandle.offsetParent && typeof(itemHandle.offsetParent) == 'object') + { + itemHandle = itemHandle.offsetParent; + itemX += itemHandle.offsetLeft; + itemY += itemHandle.offsetTop; + } + } + else if ('x' in itemHandle) + { + itemX = itemHandle.x; + itemY = itemHandle.y; + } + + return [itemX, itemY]; +} + +// This function takes the script URL and prepares it to allow the query string to be appended to it. +function smf_prepareScriptUrl(sUrl) +{ + return sUrl.indexOf('?') == -1 ? sUrl + '?' : sUrl + (sUrl.charAt(sUrl.length - 1) == '?' || sUrl.charAt(sUrl.length - 1) == '&' || sUrl.charAt(sUrl.length - 1) == ';' ? '' : ';'); +} + +var aOnloadEvents = new Array(); +function addLoadEvent(fNewOnload) +{ + // If there's no event set, just set this one + if (typeof(fNewOnload) == 'function' && (!('onload' in window) || typeof(window.onload) != 'function')) + window.onload = fNewOnload; + + // If there's just one event, setup the array. + else if (aOnloadEvents.length == 0) + { + aOnloadEvents[0] = window.onload; + aOnloadEvents[1] = fNewOnload; + window.onload = function() { + for (var i = 0, n = aOnloadEvents.length; i < n; i++) + { + if (typeof(aOnloadEvents[i]) == 'function') + aOnloadEvents[i](); + else if (typeof(aOnloadEvents[i]) == 'string') + eval(aOnloadEvents[i]); + } + } + } + + // This isn't the first event function, add it to the list. + else + aOnloadEvents[aOnloadEvents.length] = fNewOnload; +} + +function smfFooterHighlight(element, value) +{ + element.src = smf_images_url + '/' + (value ? 'h_' : '') + element.id + '.gif'; +} + +// Get the text in a code tag. +function smfSelectText(oCurElement, bActOnElement) +{ + // The place we're looking for is one div up, and next door - if it's auto detect. + if (typeof(bActOnElement) == 'boolean' && bActOnElement) + var oCodeArea = document.getElementById(oCurElement); + else + var oCodeArea = oCurElement.parentNode.nextSibling; + + if (typeof(oCodeArea) != 'object' || oCodeArea == null) + return false; + + // Start off with my favourite, internet explorer. + if ('createTextRange' in document.body) + { + var oCurRange = document.body.createTextRange(); + oCurRange.moveToElementText(oCodeArea); + oCurRange.select(); + } + // Firefox at el. + else if (window.getSelection) + { + var oCurSelection = window.getSelection(); + // Safari is special! + if (oCurSelection.setBaseAndExtent) + { + var oLastChild = oCodeArea.lastChild; + oCurSelection.setBaseAndExtent(oCodeArea, 0, oLastChild, 'innerText' in oLastChild ? oLastChild.innerText.length : oLastChild.textContent.length); + } + else + { + var curRange = document.createRange(); + curRange.selectNodeContents(oCodeArea); + + oCurSelection.removeAllRanges(); + oCurSelection.addRange(curRange); + } + } + + return false; +} + +// A function needed to discern HTML entities from non-western characters. +function smc_saveEntities(sFormName, aElementNames, sMask) +{ + if (typeof(sMask) == 'string') + { + for (var i = 0, n = document.forms[sFormName].elements.length; i < n; i++) + if (document.forms[sFormName].elements[i].id.substr(0, sMask.length) == sMask) + aElementNames[aElementNames.length] = document.forms[sFormName].elements[i].name; + } + + for (var i = 0, n = aElementNames.length; i < n; i++) + { + if (aElementNames[i] in document.forms[sFormName]) + document.forms[sFormName][aElementNames[i]].value = document.forms[sFormName][aElementNames[i]].value.replace(/&#/g, '&#'); + } +} + +// A function used to clean the attachments on post page +function cleanFileInput(idElement) +{ + // Simpler solutions work in Opera, IE, Safari and Chrome. + if (is_opera || is_ie || is_safari || is_chrome) + { + document.getElementById(idElement).outerHTML = document.getElementById(idElement).outerHTML; + } + // What else can we do? By the way, this doesn't work in Chrome and Mac's Safari. + else + { + document.getElementById(idElement).type = 'input'; + document.getElementById(idElement).type = 'file'; + } +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/sha1.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/sha1.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,205 @@ +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1 Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} + +/* + * Perform a simple self-test to see if the VM is working + */ +function sha1_vm_test() +{ + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; +} + +/* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ +function core_sha1(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for (var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for (var j = 0; j < 80; j++) + { + if (j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); +} + +/* + * Perform the appropriate triplet combination function for the current + * iteration + */ +function sha1_ft(t, b, c, d) +{ + if (t < 20) return (b & c) | ((~b) & d); + if (t < 40) return b ^ c ^ d; + if (t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +/* + * Determine the appropriate additive constant for the current iteration + */ +function sha1_kt(t) +{ + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; +} + +/* + * Calculate the HMAC-SHA1 of a key and some data + */ +function core_hmac_sha1(key, data) +{ + var bkey = str2binb(key); + if (bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for (var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ +function str2binb(str) +{ + var bin = Array(); + + for (var i = 0, n = 1 + ((str.length * chrsz) >> 5); i < n; i++) + bin[i] = 0; + + var mask = (1 << chrsz) - 1; + for (var i = 0; i < str.length * chrsz; i += chrsz) + bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i % 32); + return bin; +} + +/* + * Convert an array of big-endian words to a string + */ +function binb2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for (var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask); + return str; +} + +/* + * Convert an array of big-endian words to a hex string. + */ +function binb2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of big-endian words to a base-64 string + */ +function binb2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for (var j = 0; j < 4; j++) + { + if (i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/spellcheck.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/spellcheck.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,297 @@ +// These are variables the popup is going to want to access... +var spell_formname, spell_fieldname; + +// Spell check the specified field in the specified form. +function spellCheck(formName, fieldName) +{ + // Grab the (hidden) spell checking form. + var spellform = document.forms.spell_form; + + // Register the name of the editing form for future reference. + spell_formname = formName; + spell_fieldname = fieldName; + + // This should match a word (most of the time). + var regexpWordMatch = /(?:<[^>]+>)|(?:\[[^ ][^\]]*\])|(?:&[^; ]+;)|(?:[^0-9\s\]\[{};:"\\|,<.>\/?`~!@#$%^&*()_+=]+)/g; + + // These characters can appear within words. + var aWordCharacters = ['-', '\'']; + + var aWords = new Array(), aResult = new Array(); + var sText = document.forms[formName][fieldName].value; + var bInCode = false; + var iOffset1, iOffset2; + + // Loop through all words. + while ((aResult = regexpWordMatch.exec(sText)) && typeof(aResult) != 'undefined') + { + iOffset1 = 0; + iOffset2 = aResult[0].length - 1; + + // Strip the dashes and hyphens from the begin of the word. + while (in_array(aResult[0].charAt(iOffset1), aWordCharacters) && iOffset1 < iOffset2) + iOffset1++; + + // Strip the dashes and hyphens from the end of the word. + while (in_array(aResult[0].charAt(iOffset2), aWordCharacters) && iOffset1 < iOffset2) + iOffset2--; + + // I guess there's only dashes and hyphens in this word... + if (iOffset1 == iOffset2) + continue; + + // Ignore code blocks. + if (aResult[0].substr(0, 5).toLowerCase() == '[code') + bInCode = true; + + // Glad we're out of that code block! + else if (bInCode && aResult[0].substr(0, 7).toLowerCase() == '[/code]') + bInCode = false; + + // Now let's get to business. + else if (!bInCode && !in_array(aResult[0].charAt(0), ['[', '<']) && aResult[0].toUpperCase() != aResult[0]) + aWords[aWords.length] = aResult[0].substr(iOffset1, iOffset2 - iOffset1 + 1) + '|' + (iOffset1 + sText.substr(0, aResult.index).length) + '|' + (iOffset2 + sText.substr(0, aResult.index).length); + } + + // Open the window... + openSpellWin(640, 480); + + // Pass the data to a form... + spellform.spellstring.value = aWords.join('\n'); + + // and go! + spellform.submit(); + + return true; +} + +// Private functions ------------------------------- + +// Globals... +var wordindex = -1, offsetindex = 0; +var ignoredWords = []; + +// A "misspelled word" object. +function misp(word, start, end, suggestions) +{ + // The word, start index, end index, and array of suggestions. + this.word = word; + this.start = start; + this.end = end; + this.suggestions = suggestions; +} + +// Replace the word in the misps array at the "wordindex" index. The +// misps array is generated by a PHP script after the string to be spell +// checked is evaluated with pspell. +function replaceWord() +{ + var strstart = ""; + var strend; + + // If this isn't the beginning of the string then get all of the string + // that is before the word we are replacing. + if (misps[wordindex].start != 0) + strstart = mispstr.slice(0, misps[wordindex].start + offsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[wordindex].end + 1 + offsetindex); + + // Rebuild the string with the new word. + mispstr = strstart + document.forms.spellingForm.changeto.value + strend; + + // Update offsetindex to compensate for replacing a word with a word + // of a different length. + offsetindex += document.forms.spellingForm.changeto.value.length - misps[wordindex].word.length; + + // Update the word so future replaceAll calls don't change it. + misps[wordindex].word = document.forms.spellingForm.changeto.value; + + nextWord(false); +} + +// Replaces all instances of currently selected word with contents chosen by user. +// Note: currently only replaces words after highlighted word. I think we can re-index +// all words at replacement or ignore time to have it wrap to the beginning if we want +// to. +function replaceAll() +{ + var strend; + var idx; + var origword; + var localoffsetindex = offsetindex; + + origword = misps[wordindex].word; + + // Re-index everything past the current word. + for (idx = wordindex; idx < misps.length; idx++) + { + misps[idx].start += localoffsetindex; + misps[idx].end += localoffsetindex; + } + + localoffsetindex = 0; + + for (idx = 0; idx < misps.length; idx++) + { + if (misps[idx].word == origword) + { + var strstart = ""; + if (misps[idx].start != 0) + strstart = mispstr.slice(0, misps[idx].start + localoffsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[idx].end + 1 + localoffsetindex); + + // Rebuild the string with the new word. + mispstr = strstart + document.forms.spellingForm.changeto.value + strend; + + // Update offsetindex to compensate for replacing a word with a word + // of a different length. + localoffsetindex += document.forms.spellingForm.changeto.value.length - misps[idx].word.length; + } + + // We have to re-index everything after replacements. + misps[idx].start += localoffsetindex; + misps[idx].end += localoffsetindex; + } + + // Add the word to the ignore array. + ignoredWords[origword] = true; + + // Reset offsetindex since we re-indexed. + offsetindex = 0; + + nextWord(false); +} + +// Highlight the word that was selected using the nextWord function. +function highlightWord() +{ + var strstart = ""; + var strend; + + // If this isn't the beginning of the string then get all of the string + // that is before the word we are replacing. + if (misps[wordindex].start != 0) + strstart = mispstr.slice(0, misps[wordindex].start + offsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[wordindex].end + 1 + offsetindex); + + // Rebuild the string with a span wrapped around the misspelled word + // so we can highlight it in the div the user is viewing the string in. + var divptr, newValue; + divptr = document.getElementById("spellview"); + + newValue = htmlspecialchars(strstart) + '' + misps[wordindex].word + '' + htmlspecialchars(strend); + setInnerHTML(divptr, newValue.replace(/_\|_/g, '
    ')); + + // We could use scrollIntoView, but it's just not that great anyway. + var spellview_height = typeof(document.getElementById("spellview").currentStyle) != "undefined" ? parseInt(document.getElementById("spellview").currentStyle.height) : document.getElementById("spellview").offsetHeight; + var word_position = document.getElementById("h1").offsetTop; + var current_position = document.getElementById("spellview").scrollTop; + + // The spellview is not tall enough! Scroll down! + if (spellview_height <= (word_position + current_position)) + document.getElementById("spellview").scrollTop = word_position + current_position - spellview_height + 32; +} + +// Display the next misspelled word to the user and populate the suggested spellings box. +function nextWord(ignoreall) +{ + // Push ignored word onto ignoredWords array. + if (ignoreall) + ignoredWords[misps[wordindex].word] = true; + + // Update the index of all words we have processed... + // This must be done to accomodate the replaceAll function. + if (wordindex >= 0) + { + misps[wordindex].start += offsetindex; + misps[wordindex].end += offsetindex; + } + + // Increment the counter for the array of misspelled words. + wordindex++; + + // Draw it and quit if there are no more misspelled words to evaluate. + if (misps.length <= wordindex) + { + var divptr; + divptr = document.getElementById("spellview"); + setInnerHTML(divptr, htmlspecialchars(mispstr).replace(/_\|_/g, "
    ")); + + while (document.forms.spellingForm.suggestions.options.length > 0) + document.forms.spellingForm.suggestions.options[0] = null; + + alert(txt['done']); + document.forms.spellingForm.change.disabled = true; + document.forms.spellingForm.changeall.disabled = true; + document.forms.spellingForm.ignore.disabled = true; + document.forms.spellingForm.ignoreall.disabled = true; + + // Put line feeds back... + mispstr = mispstr.replace(/_\|_/g, "\n"); + + // Get a handle to the field we need to re-populate. + window.opener.document.forms[spell_formname][spell_fieldname].value = mispstr; + if (!window.opener.spellCheckDone) + window.opener.document.forms[spell_formname][spell_fieldname].focus(); + else + window.opener.spellCheckDone(); + + window.close(); + return true; + } + + // Check to see if word is supposed to be ignored. + if (typeof(ignoredWords[misps[wordindex].word]) != "undefined") + { + nextWord(false); + return false; + } + + // Clear out the suggestions box! + while (document.forms.spellingForm.suggestions.options.length > 0) + document.forms.spellingForm.suggestions.options[0] = null; + + // Re-populate the suggestions box if there are any suggested spellings for the word. + if (misps[wordindex].suggestions.length) + { + for (var sugidx = 0; sugidx < misps[wordindex].suggestions.length; sugidx++) + { + var newopt = new Option(misps[wordindex].suggestions[sugidx], misps[wordindex].suggestions[sugidx]); + document.forms.spellingForm.suggestions.options[sugidx] = newopt; + + if (sugidx == 0) + { + newopt.selected = true; + document.forms.spellingForm.changeto.value = newopt.value; + document.forms.spellingForm.changeto.select(); + } + } + } + + if (document.forms.spellingForm.suggestions.options.length == 0) + document.forms.spellingForm.changeto.value = ""; + + highlightWord(); + + return false; +} + +function htmlspecialchars(thetext) +{ + thetext = thetext.replace(/\/g, ">"); + thetext = thetext.replace(/\n/g, "
    "); + thetext = thetext.replace(/\ \ /g, "  "); + + return thetext; +} + +function openSpellWin(width, height) +{ + window.open("", "spellWindow", "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=no,width=" + width + ",height=" + height); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/stats.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/stats.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,239 @@ +function smf_StatsCenter(oOptions) +{ + this.opt = oOptions; + + this.oTable = null; + this.oYears = {}; + + this.bIsLoading = false; + + this.init(); +} + +smf_StatsCenter.prototype.init = function () +{ + this.oTable = document.getElementById(this.opt.sTableId); + + // Is the table actually present? + if (typeof(this.oTable) != 'object') + return; + + // Find all months and years defined in the table. + var aRows = this.oTable.getElementsByTagName('tr'); + var aResults = []; + + var sYearId = null; + var oCurYear = null; + + var sMonthId = null; + var oCurMonth = null; + for (var i = 0, n = aRows.length; i < n; i++) + { + // Check if the current row represents a year. + if ((aResults = this.opt.reYearPattern.exec(aRows[i].id)) != null) + { + // The id is part of the pattern match. + sYearId = aResults[1]; + + // Setup the object that'll have the state information of the year. + this.oYears[sYearId] = { + oCollapseImage: document.getElementById(this.opt.sYearImageIdPrefix + sYearId), + oMonths: {} + }; + + // Create a shortcut, makes things more readable. + oCurYear = this.oYears[sYearId]; + + // Use the collapse image to determine the current state. + oCurYear.bIsCollapsed = oCurYear.oCollapseImage.src.indexOf(this.opt.sYearImageCollapsed) >= 0; + + // Setup the toggle element for the year. + oCurYear.oToggle = new smc_Toggle({ + bToggleEnabled: true, + bCurrentlyCollapsed: oCurYear.bIsCollapsed, + instanceRef: this, + sYearId: sYearId, + funcOnBeforeCollapse: function () { + this.opt.instanceRef.onBeforeCollapseYear(this); + }, + aSwappableContainers: [ + ], + aSwapImages: [ + { + sId: this.opt.sYearImageIdPrefix + sYearId, + srcExpanded: smf_images_url + '/' + this.opt.sYearImageExpanded, + altExpanded: '-', + srcCollapsed: smf_images_url + '/' + this.opt.sYearImageCollapsed, + altCollapsed: '+' + } + ], + aSwapLinks: [ + { + sId: this.opt.sYearLinkIdPrefix + sYearId, + msgExpanded: sYearId, + msgCollapsed: sYearId + } + ] + }); + } + + // Or maybe the current row represents a month. + else if ((aResults = this.opt.reMonthPattern.exec(aRows[i].id)) != null) + { + // Set the id to the matched pattern. + sMonthId = aResults[1]; + + // Initialize the month as a child object of the year. + oCurYear.oMonths[sMonthId] = { + oCollapseImage: document.getElementById(this.opt.sMonthImageIdPrefix + sMonthId) + }; + + // Create a shortcut to the current month. + oCurMonth = oCurYear.oMonths[sMonthId]; + + // Determine whether the month is currently collapsed or expanded.. + oCurMonth.bIsCollapsed = oCurMonth.oCollapseImage.src.indexOf(this.opt.sMonthImageCollapsed) >= 0; + + var sLinkText = getInnerHTML(document.getElementById(this.opt.sMonthLinkIdPrefix + sMonthId)); + + // Setup the toggle element for the month. + oCurMonth.oToggle = new smc_Toggle({ + bToggleEnabled: true, + bCurrentlyCollapsed: oCurMonth.bIsCollapsed, + instanceRef: this, + sMonthId: sMonthId, + funcOnBeforeCollapse: function () { + this.opt.instanceRef.onBeforeCollapseMonth(this); + }, + funcOnBeforeExpand: function () { + this.opt.instanceRef.onBeforeExpandMonth(this); + }, + aSwappableContainers: [ + ], + aSwapImages: [ + { + sId: this.opt.sMonthImageIdPrefix + sMonthId, + srcExpanded: smf_images_url + '/' + this.opt.sMonthImageExpanded, + altExpanded: '-', + srcCollapsed: smf_images_url + '/' + this.opt.sMonthImageCollapsed, + altCollapsed: '+' + } + ], + aSwapLinks: [ + { + sId: this.opt.sMonthLinkIdPrefix + sMonthId, + msgExpanded: sLinkText, + msgCollapsed: sLinkText + } + ] + }); + + oCurYear.oToggle.opt.aSwappableContainers[oCurYear.oToggle.opt.aSwappableContainers.length] = aRows[i].id; + } + + else if((aResults = this.opt.reDayPattern.exec(aRows[i].id)) != null) + { + oCurMonth.oToggle.opt.aSwappableContainers[oCurMonth.oToggle.opt.aSwappableContainers.length] = aRows[i].id; + oCurYear.oToggle.opt.aSwappableContainers[oCurYear.oToggle.opt.aSwappableContainers.length] = aRows[i].id; + } + } + + // Collapse all collapsed years! + for (i = 0; i < this.opt.aCollapsedYears.length; i++) + this.oYears[this.opt.aCollapsedYears[i]].oToggle.toggle(); +} + +smf_StatsCenter.prototype.onBeforeCollapseYear = function (oToggle) +{ + // Tell SMF that all underlying months have disappeared. + for (var sMonth in this.oYears[oToggle.opt.sYearId].oMonths) + if (this.oYears[oToggle.opt.sYearId].oMonths[sMonth].oToggle.opt.aSwappableContainers.length > 0) + this.oYears[oToggle.opt.sYearId].oMonths[sMonth].oToggle.changeState(true); +} + + +smf_StatsCenter.prototype.onBeforeCollapseMonth = function (oToggle) +{ + if (!oToggle.bCollapsed) + { + // Tell SMF that it the state has changed. + getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=stats;collapse=' + oToggle.opt.sMonthId + ';xml'); + + // Remove the month rows from the year toggle. + var aNewContainers = []; + var oYearToggle = this.oYears[oToggle.opt.sMonthId.substr(0, 4)].oToggle; + + for (var i = 0, n = oYearToggle.opt.aSwappableContainers.length; i < n; i++) + if (!in_array(oYearToggle.opt.aSwappableContainers[i], oToggle.opt.aSwappableContainers)) + aNewContainers[aNewContainers.length] = oYearToggle.opt.aSwappableContainers[i]; + + oYearToggle.opt.aSwappableContainers = aNewContainers; + } +} + + +smf_StatsCenter.prototype.onBeforeExpandMonth = function (oToggle) +{ + // Ignore if we're still loading the previous batch. + if (this.bIsLoading) + return; + + if (oToggle.opt.aSwappableContainers.length == 0) + { + // A complicated way to call getXMLDocument, but stay in scope. + this.tmpMethod = getXMLDocument; + this.oXmlRequestHandle = this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=stats;expand=' + oToggle.opt.sMonthId + ';xml', this.onDocReceived); + delete this.tmpMethod; + + if ('ajax_indicator' in window) + ajax_indicator(true); + + this.bIsLoading = true; + } + + // Silently let SMF know this one is expanded. + else + getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=stats;expand=' + oToggle.opt.sMonthId + ';xml'); +} + +smf_StatsCenter.prototype.onDocReceived = function (oXMLDoc) +{ + // Loop through all the months we got from the XML. + var aMonthNodes = oXMLDoc.getElementsByTagName('month'); + for (var iMonthIndex = 0, iNumMonths = aMonthNodes.length; iMonthIndex < iNumMonths; iMonthIndex++) + { + var sMonthId = aMonthNodes[iMonthIndex].getAttribute('id'); + var iStart = document.getElementById('tr_month_' + sMonthId).rowIndex + 1; + var sYearId = sMonthId.substr(0, 4); + + // Within the current months, check out all the days. + var aDayNodes = aMonthNodes[iMonthIndex].getElementsByTagName('day'); + for (var iDayIndex = 0, iNumDays = aDayNodes.length; iDayIndex < iNumDays; iDayIndex++) + { + var oCurRow = this.oTable.insertRow(iStart + iDayIndex); + oCurRow.className = this.opt.sDayRowClassname; + oCurRow.id = this.opt.sDayRowIdPrefix + aDayNodes[iDayIndex].getAttribute('date'); + + for (var iCellIndex = 0, iNumCells = this.opt.aDataCells.length; iCellIndex < iNumCells; iCellIndex++) + { + var oCurCell = oCurRow.insertCell(-1); + + if (this.opt.aDataCells[iCellIndex] == 'date') + oCurCell.style.paddingLeft = '6ex'; + else + oCurCell.style.textAlign = 'center'; + + var sCurData = aDayNodes[iDayIndex].getAttribute(this.opt.aDataCells[iCellIndex]); + oCurCell.appendChild(document.createTextNode(sCurData)); + } + + // Add these day rows to the toggle objects in case of collapse. + this.oYears[sYearId].oMonths[sMonthId].oToggle.opt.aSwappableContainers[this.oYears[sYearId].oMonths[sMonthId].oToggle.opt.aSwappableContainers.length] = oCurRow.id; + this.oYears[sYearId].oToggle.opt.aSwappableContainers[this.oYears[sYearId].oToggle.opt.aSwappableContainers.length] = oCurRow.id; + } + } + + this.bIsLoading = false; + if (typeof(window.ajax_indicator) == 'function') + ajax_indicator(false); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/suggest.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/suggest.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,630 @@ +// This file contains javascript associated with a autosuggest control +function smc_AutoSuggest(oOptions) +{ + this.opt = oOptions; + + // Store the handle to the text box. + this.oTextHandle = document.getElementById(this.opt.sControlId); + this.oRealTextHandle = null; + + this.oSuggestDivHandle = null; + this.sLastSearch = ''; + this.sLastDirtySearch = ''; + this.oSelectedDiv = null; + this.aCache = []; + this.aDisplayData = []; + + this.sRetrieveURL = 'sRetrieveURL' in this.opt ? this.opt.sRetrieveURL : '%scripturl%action=suggest;suggest_type=%suggest_type%;search=%search%;%sessionVar%=%sessionID%;xml;time=%time%'; + + // How many objects can we show at once? + this.iMaxDisplayQuantity = 'iMaxDisplayQuantity' in this.opt ? this.opt.iMaxDisplayQuantity : 15; + + // How many characters shall we start searching on? + this.iMinimumSearchChars = 'iMinimumSearchChars' in this.opt ? this.opt.iMinimumSearchChars : 3; + + // Should selected items be added to a list? + this.bItemList = 'bItemList' in this.opt ? this.opt.bItemList : false; + + // Are there any items that should be added in advance? + this.aListItems = 'aListItems' in this.opt ? this.opt.aListItems : []; + + this.sItemTemplate = 'sItemTemplate' in this.opt ? this.opt.sItemTemplate : '%item_name% %delete_text%'; + + this.sTextDeleteItem = 'sTextDeleteItem' in this.opt ? this.opt.sTextDeleteItem : ''; + + this.oCallback = {}; + this.bDoAutoAdd = false; + this.iItemCount = 0; + + this.oHideTimer = null; + this.bPositionComplete = false; + + this.oXmlRequestHandle = null; + + // Just make sure the page is loaded before calling the init. + addLoadEvent(this.opt.sSelf + '.init();'); +} + +smc_AutoSuggest.prototype.init = function() +{ + if (!window.XMLHttpRequest) + return false; + + // Create a div that'll contain the results later on. + this.oSuggestDivHandle = document.createElement('div'); + this.oSuggestDivHandle.className = 'auto_suggest_div'; + document.body.appendChild(this.oSuggestDivHandle); + + // Create a backup text input. + this.oRealTextHandle = document.createElement('input'); + this.oRealTextHandle.type = 'hidden'; + this.oRealTextHandle.name = this.oTextHandle.name; + this.oRealTextHandle.value = this.oTextHandle.value; + this.oTextHandle.form.appendChild(this.oRealTextHandle); + + // Disable autocomplete in any browser by obfuscating the name. + this.oTextHandle.name = 'dummy_' + Math.floor(Math.random() * 1000000); + this.oTextHandle.autocomplete = 'off'; + + this.oTextHandle.instanceRef = this; + + var fOnKeyDown = function (oEvent) { + return this.instanceRef.handleKey(oEvent); + }; + is_opera ? this.oTextHandle.onkeypress = fOnKeyDown : this.oTextHandle.onkeydown = fOnKeyDown; + + this.oTextHandle.onkeyup = function (oEvent) { + return this.instanceRef.autoSuggestUpdate(oEvent); + }; + + this.oTextHandle.onchange = function (oEvent) { + return this.instanceRef.autoSuggestUpdate(oEvent); + }; + + this.oTextHandle.onblur = function (oEvent) { + return this.instanceRef.autoSuggestHide(oEvent); + }; + + this.oTextHandle.onfocus = function (oEvent) { + return this.instanceRef.autoSuggestUpdate(oEvent); + }; + + if (this.bItemList) + { + if ('sItemListContainerId' in this.opt) + this.oItemList = document.getElementById(this.opt.sItemListContainerId); + else + { + this.oItemList = document.createElement('div'); + this.oTextHandle.parentNode.insertBefore(this.oItemList, this.oTextHandle.nextSibling); + } + } + + if (this.aListItems.length > 0) + for (var i = 0, n = this.aListItems.length; i < n; i++) + this.addItemLink(this.aListItems[i].sItemId, this.aListItems[i].sItemName); + + return true; +} + +// Was it an enter key - if so assume they are trying to select something. +smc_AutoSuggest.prototype.handleKey = function(oEvent) +{ + // Grab the event object, one way or the other + if (!oEvent) + oEvent = window.event; + + // Get the keycode of the key that was pressed. + var iKeyPress = 0; + if ('keyCode' in oEvent) + iKeyPress = oEvent.keyCode; + else if ('which' in oEvent) + iKeyPress = oEvent.which; + + switch (iKeyPress) + { + // Tab. + case 9: + if (this.aDisplayData.length > 0) + { + if (this.oSelectedDiv != null) + this.itemClicked(this.oSelectedDiv); + else + this.handleSubmit(); + } + + // Continue to the next control. + return true; + break; + + // Enter. + case 13: + if (this.aDisplayData.length > 0 && this.oSelectedDiv != null) + { + this.itemClicked(this.oSelectedDiv); + + // Do our best to stop it submitting the form! + return false; + } + else + return true; + + break; + + // Up/Down arrow? + case 38: + case 40: + if (this.aDisplayData.length && this.oSuggestDivHandle.style.visibility != 'hidden') + { + // Loop through the display data trying to find our entry. + var bPrevHandle = false; + var oToHighlight = null; + for (var i = 0; i < this.aDisplayData.length; i++) + { + // If we're going up and yet the top one was already selected don't go around. + if (this.oSelectedDiv != null && this.oSelectedDiv == this.aDisplayData[i] && i == 0 && iKeyPress == 38) + { + oToHighlight = this.oSelectedDiv; + break; + } + // If nothing is selected and we are going down then we select the first one. + if (this.oSelectedDiv == null && iKeyPress == 40) + { + oToHighlight = this.aDisplayData[i]; + break; + } + + // If the previous handle was the actual previously selected one and we're hitting down then this is the one we want. + if (bPrevHandle != false && bPrevHandle == this.oSelectedDiv && iKeyPress == 40) + { + oToHighlight = this.aDisplayData[i]; + break; + } + // If we're going up and this is the previously selected one then we want the one before, if there was one. + if (bPrevHandle != false && this.aDisplayData[i] == this.oSelectedDiv && iKeyPress == 38) + { + oToHighlight = bPrevHandle; + break; + } + // Make the previous handle this! + bPrevHandle = this.aDisplayData[i]; + } + + // If we don't have one to highlight by now then it must be the last one that we're after. + if (oToHighlight == null) + oToHighlight = bPrevHandle; + + // Remove any old highlighting. + if (this.oSelectedDiv != null) + this.itemMouseOut(this.oSelectedDiv); + // Mark what the selected div now is. + this.oSelectedDiv = oToHighlight; + this.itemMouseOver(this.oSelectedDiv); + } + break; + } + return true; +} + +// Functions for integration. +smc_AutoSuggest.prototype.registerCallback = function(sCallbackType, sCallback) +{ + switch (sCallbackType) + { + case 'onBeforeAddItem': + this.oCallback.onBeforeAddItem = sCallback; + break; + + case 'onAfterAddItem': + this.oCallback.onAfterAddItem = sCallback; + break; + + case 'onAfterDeleteItem': + this.oCallback.onAfterDeleteItem = sCallback; + break; + + case 'onBeforeUpdate': + this.oCallback.onBeforeUpdate = sCallback; + break; + } +} + +// User hit submit? +smc_AutoSuggest.prototype.handleSubmit = function() +{ + var bReturnValue = true; + var oFoundEntry = null; + + // Do we have something that matches the current text? + for (var i = 0; i < this.aCache.length; i++) + { + if (this.sLastSearch.toLowerCase() == this.aCache[i].sItemName.toLowerCase().substr(0, this.sLastSearch.length)) + { + // Exact match? + if (this.sLastSearch.length == this.aCache[i].sItemName.length) + { + // This is the one! + oFoundEntry = { + sItemId: this.aCache[i].sItemId, + sItemName: this.aCache[i].sItemName + }; + break; + } + + // Not an exact match, but it'll do for now. + else + { + // If we have two matches don't find anything. + if (oFoundEntry != null) + bReturnValue = false; + else + oFoundEntry = { + sItemId: this.aCache[i].sItemId, + sItemName: this.aCache[i].sItemName + }; + } + } + } + + if (oFoundEntry == null || bReturnValue == false) + return bReturnValue; + else + { + this.addItemLink(oFoundEntry.sItemId, oFoundEntry.sItemName, true); + return false; + } +} + +// Positions the box correctly on the window. +smc_AutoSuggest.prototype.positionDiv = function() +{ + // Only do it once. + if (this.bPositionComplete) + return true; + + this.bPositionComplete = true; + + // Put the div under the text box. + var aParentPos = smf_itemPos(this.oTextHandle); + + this.oSuggestDivHandle.style.left = aParentPos[0] + 'px'; + this.oSuggestDivHandle.style.top = (aParentPos[1] + this.oTextHandle.offsetHeight) + 'px'; + this.oSuggestDivHandle.style.width = this.oTextHandle.style.width; + + return true; +} + +// Do something after clicking an item. +smc_AutoSuggest.prototype.itemClicked = function(oCurElement) +{ + // Is there a div that we are populating? + if (this.bItemList) + this.addItemLink(oCurElement.sItemId, oCurElement.innerHTML); + + // Otherwise clear things down. + else + this.oTextHandle.value = oCurElement.innerHTML.php_unhtmlspecialchars(); + + this.oRealTextHandle.value = this.oTextHandle.value; + this.autoSuggestActualHide(); + this.oSelectedDiv = null; +} + +// Remove the last searched for name from the search box. +smc_AutoSuggest.prototype.removeLastSearchString = function () +{ + // Remove the text we searched for from the div. + var sTempText = this.oTextHandle.value.toLowerCase(); + var iStartString = sTempText.indexOf(this.sLastSearch.toLowerCase()); + // Just attempt to remove the bits we just searched for. + if (iStartString != -1) + { + while (iStartString > 0) + { + if (sTempText.charAt(iStartString - 1) == '"' || sTempText.charAt(iStartString - 1) == ',' || sTempText.charAt(iStartString - 1) == ' ') + { + iStartString--; + if (sTempText.charAt(iStartString - 1) == ',') + break; + } + else + break; + } + + // Now remove anything from iStartString upwards. + this.oTextHandle.value = this.oTextHandle.value.substr(0, iStartString); + } + // Just take it all. + else + this.oTextHandle.value = ''; +} + +// Add a result if not already done. +smc_AutoSuggest.prototype.addItemLink = function (sItemId, sItemName, bFromSubmit) +{ + // Increase the internal item count. + this.iItemCount ++; + + // If there's a callback then call it. + if ('oCallback' in this && 'onBeforeAddItem' in this.oCallback && typeof(this.oCallback.onBeforeAddItem) == 'string') + { + // If it returns false the item must not be added. + if (!eval(this.oCallback.onBeforeAddItem + '(' + this.opt.sSelf + ', \'' + sItemId + '\');')) + return; + } + + var oNewDiv = document.createElement('div'); + oNewDiv.id = 'suggest_' + this.opt.sSuggestId + '_' + sItemId; + setInnerHTML(oNewDiv, this.sItemTemplate.replace(/%post_name%/g, this.opt.sPostName).replace(/%item_id%/g, sItemId).replace(/%item_href%/g, smf_prepareScriptUrl(smf_scripturl) + this.opt.sURLMask.replace(/%item_id%/g, sItemId)).replace(/%item_name%/g, sItemName).replace(/%images_url%/g, smf_images_url).replace(/%self%/g, this.opt.sSelf).replace(/%delete_text%/g, this.sTextDeleteItem)); + this.oItemList.appendChild(oNewDiv); + + // If there's a registered callback, call it. + if ('oCallback' in this && 'onAfterAddItem' in this.oCallback && typeof(this.oCallback.onAfterAddItem) == 'string') + eval(this.oCallback.onAfterAddItem + '(' + this.opt.sSelf + ', \'' + oNewDiv.id + '\', ' + this.iItemCount + ');'); + + // Clear the div a bit. + this.removeLastSearchString(); + + // If we came from a submit, and there's still more to go, turn on auto add for all the other things. + this.bDoAutoAdd = this.oTextHandle.value != '' && bFromSubmit; + + // Update the fellow.. + this.autoSuggestUpdate(); +} + +// Delete an item that has been added, if at all? +smc_AutoSuggest.prototype.deleteAddedItem = function (sItemId) +{ + var oDiv = document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId); + + // Remove the div if it exists. + if (typeof(oDiv) == 'object' && oDiv != null) + { + oDiv.parentNode.removeChild(document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId)); + + // Decrease the internal item count. + this.iItemCount --; + + // If there's a registered callback, call it. + if ('oCallback' in this && 'onAfterDeleteItem' in this.oCallback && typeof(this.oCallback.onAfterDeleteItem) == 'string') + eval(this.oCallback.onAfterDeleteItem + '(' + this.opt.sSelf + ', ' + this.iItemCount + ');'); + } + + return false; +} + +// Hide the box. +smc_AutoSuggest.prototype.autoSuggestHide = function () +{ + // Delay to allow events to propogate through.... + this.oHideTimer = setTimeout(this.opt.sSelf + '.autoSuggestActualHide();', 250); +} + +// Do the actual hiding after a timeout. +smc_AutoSuggest.prototype.autoSuggestActualHide = function() +{ + this.oSuggestDivHandle.style.display = 'none'; + this.oSuggestDivHandle.style.visibility = 'hidden'; + this.oSelectedDiv = null; +} + +// Show the box. +smc_AutoSuggest.prototype.autoSuggestShow = function() +{ + if (this.oHideTimer) + { + clearTimeout(this.oHideTimer); + this.oHideTimer = false; + } + + this.positionDiv(); + + this.oSuggestDivHandle.style.visibility = 'visible'; + this.oSuggestDivHandle.style.display = ''; +} + +// Populate the actual div. +smc_AutoSuggest.prototype.populateDiv = function(aResults) +{ + // Cannot have any children yet. + while (this.oSuggestDivHandle.childNodes.length > 0) + { + // Tidy up the events etc too. + this.oSuggestDivHandle.childNodes[0].onmouseover = null; + this.oSuggestDivHandle.childNodes[0].onmouseout = null; + this.oSuggestDivHandle.childNodes[0].onclick = null; + + this.oSuggestDivHandle.removeChild(this.oSuggestDivHandle.childNodes[0]); + } + + // Something to display? + if (typeof(aResults) == 'undefined') + { + this.aDisplayData = []; + return false; + } + + var aNewDisplayData = []; + for (var i = 0; i < (aResults.length > this.iMaxDisplayQuantity ? this.iMaxDisplayQuantity : aResults.length); i++) + { + // Create the sub element + var oNewDivHandle = document.createElement('div'); + oNewDivHandle.sItemId = aResults[i].sItemId; + oNewDivHandle.className = 'auto_suggest_item'; + oNewDivHandle.innerHTML = aResults[i].sItemName; + //oNewDivHandle.style.width = this.oTextHandle.style.width; + + this.oSuggestDivHandle.appendChild(oNewDivHandle); + + // Attach some events to it so we can do stuff. + oNewDivHandle.instanceRef = this; + oNewDivHandle.onmouseover = function (oEvent) + { + this.instanceRef.itemMouseOver(this); + } + oNewDivHandle.onmouseout = function (oEvent) + { + this.instanceRef.itemMouseOut(this); + } + oNewDivHandle.onclick = function (oEvent) + { + this.instanceRef.itemClicked(this); + } + + + aNewDisplayData[i] = oNewDivHandle; + } + + this.aDisplayData = aNewDisplayData; + + return true; +} + +// Refocus the element. +smc_AutoSuggest.prototype.itemMouseOver = function (oCurElement) +{ + this.oSelectedDiv = oCurElement; + oCurElement.className = 'auto_suggest_item_hover'; +} + +// Onfocus the element +smc_AutoSuggest.prototype.itemMouseOut = function (oCurElement) +{ + oCurElement.className = 'auto_suggest_item'; +} + +smc_AutoSuggest.prototype.onSuggestionReceived = function (oXMLDoc) +{ + var sQuoteText = ''; + var aItems = oXMLDoc.getElementsByTagName('item'); + this.aCache = []; + for (var i = 0; i < aItems.length; i++) + { + this.aCache[i] = { + sItemId: aItems[i].getAttribute('id'), + sItemName: aItems[i].childNodes[0].nodeValue + }; + + // If we're doing auto add and we find the exact person, then add them! + if (this.bDoAutoAdd && this.sLastSearch == this.aCache[i].sItemName) + { + var oReturnValue = { + sItemId: this.aCache[i].sItemId, + sItemName: this.aCache[i].sItemName + }; + this.aCache = []; + return this.addItemLink(oReturnValue.sItemId, oReturnValue.sItemName, true); + } + } + + // Check we don't try to keep auto updating! + this.bDoAutoAdd = false; + + // Populate the div. + this.populateDiv(this.aCache); + + // Make sure we can see it - if we can. + if (aItems.length == 0) + this.autoSuggestHide(); + else + this.autoSuggestShow(); + + return true; +} + +// Get a new suggestion. +smc_AutoSuggest.prototype.autoSuggestUpdate = function () +{ + // If there's a callback then call it. + if ('onBeforeUpdate' in this.oCallback && typeof(this.oCallback.onBeforeUpdate) == 'string') + { + // If it returns false the item must not be added. + if (!eval(this.oCallback.onBeforeUpdate + '(' + this.opt.sSelf + ');')) + return false; + } + + this.oRealTextHandle.value = this.oTextHandle.value; + + if (isEmptyText(this.oTextHandle)) + { + this.aCache = []; + + this.populateDiv(); + + this.autoSuggestHide(); + + return true; + } + + // Nothing changed? + if (this.oTextHandle.value == this.sLastDirtySearch) + return true; + this.sLastDirtySearch = this.oTextHandle.value; + + // We're only actually interested in the last string. + var sSearchString = this.oTextHandle.value.replace(/^("[^"]+",[ ]*)+/, '').replace(/^([^,]+,[ ]*)+/, ''); + if (sSearchString.substr(0, 1) == '"') + sSearchString = sSearchString.substr(1); + + // Stop replication ASAP. + var sRealLastSearch = this.sLastSearch; + this.sLastSearch = sSearchString; + + // Either nothing or we've completed a sentance. + if (sSearchString == '' || sSearchString.substr(sSearchString.length - 1) == '"') + { + this.populateDiv(); + return true; + } + + // Nothing? + if (sRealLastSearch == sSearchString) + return true; + + // Too small? + else if (sSearchString.length < this.iMinimumSearchChars) + { + this.aCache = []; + this.autoSuggestHide(); + return true; + } + else if (sSearchString.substr(0, sRealLastSearch.length) == sRealLastSearch) + { + // Instead of hitting the server again, just narrow down the results... + var aNewCache = []; + var j = 0; + var sLowercaseSearch = sSearchString.toLowerCase(); + for (var k = 0; k < this.aCache.length; k++) + { + if (this.aCache[k].sItemName.substr(0, sSearchString.length).toLowerCase() == sLowercaseSearch) + aNewCache[j++] = this.aCache[k]; + } + + this.aCache = []; + if (aNewCache.length != 0) + { + this.aCache = aNewCache; + // Repopulate. + this.populateDiv(this.aCache); + + // Check it can be seen. + this.autoSuggestShow(); + + return true; + } + } + + // In progress means destroy! + if (typeof(this.oXmlRequestHandle) == 'object' && this.oXmlRequestHandle != null) + this.oXmlRequestHandle.abort(); + + // Clean the text handle. + sSearchString = sSearchString.php_to8bit().php_urlencode(); + + // Get the document. + this.tmpMethod = getXMLDocument; + this.oXmlRequestHandle = this.tmpMethod(this.sRetrieveURL.replace(/%scripturl%/g, smf_prepareScriptUrl(smf_scripturl)).replace(/%suggest_type%/g, this.opt.sSearchType).replace(/%search%/g, sSearchString).replace(/%sessionVar%/g, this.opt.sSessionVar).replace(/%sessionID%/g, this.opt.sSessionId).replace(/%time%/g, new Date().getTime()), this.onSuggestionReceived); + delete this.tmpMethod; + + return true; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/theme.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/theme.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,96 @@ +// The purpose of this code is to fix the height of overflow: auto blocks, because some browsers can't figure it out for themselves. +function smf_codeBoxFix() +{ + var codeFix = document.getElementsByTagName('code'); + for (var i = codeFix.length - 1; i >= 0; i--) + { + if (is_webkit && codeFix[i].offsetHeight < 20) + codeFix[i].style.height = (codeFix[i].offsetHeight + 20) + 'px'; + + else if (is_ff && (codeFix[i].scrollWidth > codeFix[i].clientWidth || codeFix[i].clientWidth == 0)) + codeFix[i].style.overflow = 'scroll'; + + else if ('currentStyle' in codeFix[i] && codeFix[i].currentStyle.overflow == 'auto' && (codeFix[i].currentStyle.height == '' || codeFix[i].currentStyle.height == 'auto') && (codeFix[i].scrollWidth > codeFix[i].clientWidth || codeFix[i].clientWidth == 0) && (codeFix[i].offsetHeight != 0)) + codeFix[i].style.height = (codeFix[i].offsetHeight + 24) + 'px'; + } +} + +// Add a fix for code stuff? +if ((is_ie && !is_ie4) || is_webkit || is_ff) + addLoadEvent(smf_codeBoxFix); + +// Toggles the element height and width styles of an image. +function smc_toggleImageDimensions() +{ + var oImages = document.getElementsByTagName('IMG'); + for (oImage in oImages) + { + // Not a resized image? Skip it. + if (oImages[oImage].className == undefined || oImages[oImage].className.indexOf('bbc_img resized') == -1) + continue; + + oImages[oImage].style.cursor = 'pointer'; + oImages[oImage].onclick = function() { + this.style.width = this.style.height = this.style.width == 'auto' ? null : 'auto'; + }; + } +} + +// Add a load event for the function above. +addLoadEvent(smc_toggleImageDimensions); + +// Adds a button to a certain button strip. +function smf_addButton(sButtonStripId, bUseImage, oOptions) +{ + var oButtonStrip = document.getElementById(sButtonStripId); + var aItems = oButtonStrip.getElementsByTagName('span'); + + // Remove the 'last' class from the last item. + if (aItems.length > 0) + { + var oLastSpan = aItems[aItems.length - 1]; + oLastSpan.className = oLastSpan.className.replace(/\s*last/, 'position_holder'); + } + + // Add the button. + var oButtonStripList = oButtonStrip.getElementsByTagName('ul')[0]; + var oNewButton = document.createElement('li'); + setInnerHTML(oNewButton, '' + oOptions.sText + ''); + + oButtonStripList.appendChild(oNewButton); +} + +// Adds hover events to list items. Used for a versions of IE that don't support this by default. +var smf_addListItemHoverEvents = function() +{ + var cssRule, newSelector; + + // Add a rule for the list item hover event to every stylesheet. + for (var iStyleSheet = 0; iStyleSheet < document.styleSheets.length; iStyleSheet ++) + for (var iRule = 0; iRule < document.styleSheets[iStyleSheet].rules.length; iRule ++) + { + oCssRule = document.styleSheets[iStyleSheet].rules[iRule]; + if (oCssRule.selectorText.indexOf('LI:hover') != -1) + { + sNewSelector = oCssRule.selectorText.replace(/LI:hover/gi, 'LI.iehover'); + document.styleSheets[iStyleSheet].addRule(sNewSelector, oCssRule.style.cssText); + } + } + + // Now add handling for these hover events. + var oListItems = document.getElementsByTagName('LI'); + for (oListItem in oListItems) + { + oListItems[oListItem].onmouseover = function() { + this.className += ' iehover'; + }; + + oListItems[oListItem].onmouseout = function() { + this.className = this.className.replace(new RegExp(' iehover\\b'), ''); + }; + } +} + +// Add hover events to list items if the browser requires it. +if (is_ie7down && 'attachEvent' in window) + window.attachEvent('onload', smf_addListItemHoverEvents); diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/scripts/topic.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/scripts/topic.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,541 @@ +var cur_topic_id, cur_msg_id, buff_subject, cur_subject_div, in_edit_mode = 0; +var hide_prefixes = Array(); + +function modify_topic(topic_id, first_msg_id) +{ + if (!('XMLHttpRequest' in window)) + return; + + if ('opera' in window) + { + var oTest = new XMLHttpRequest(); + if (!('setRequestHeader' in oTest)) + return; + } + + // Add backwards compatibility with old themes. + if (typeof(cur_session_var) == 'undefined') + cur_session_var = 'sesc'; + + if (in_edit_mode == 1) + { + if (cur_topic_id == topic_id) + return; + else + modify_topic_cancel(); + } + + in_edit_mode = 1; + mouse_on_div = 1; + cur_topic_id = topic_id; + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + "action=quotefast;quote=" + first_msg_id + ";modify;xml", onDocReceived_modify_topic); +} + +function onDocReceived_modify_topic(XMLDoc) +{ + cur_msg_id = XMLDoc.getElementsByTagName("message")[0].getAttribute("id"); + + cur_subject_div = document.getElementById('msg_' + cur_msg_id.substr(4)); + buff_subject = getInnerHTML(cur_subject_div); + + // Here we hide any other things they want hiding on edit. + set_hidden_topic_areas('none'); + + modify_topic_show_edit(XMLDoc.getElementsByTagName("subject")[0].childNodes[0].nodeValue); + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + +function modify_topic_cancel() +{ + setInnerHTML(cur_subject_div, buff_subject); + set_hidden_topic_areas(''); + + in_edit_mode = 0; + return false; +} + +function modify_topic_save(cur_session_id, cur_session_var) +{ + if (!in_edit_mode) + return true; + + // Add backwards compatibility with old themes. + if (typeof(cur_session_var) == 'undefined') + cur_session_var = 'sesc'; + + var i, x = new Array(); + x[x.length] = 'subject=' + document.forms.quickModForm['subject'].value.replace(/&#/g, "&#").php_to8bit().php_urlencode(); + x[x.length] = 'topic=' + parseInt(document.forms.quickModForm.elements['topic'].value); + x[x.length] = 'msg=' + parseInt(document.forms.quickModForm.elements['msg'].value); + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + sendXMLDocument(smf_prepareScriptUrl(smf_scripturl) + "action=jsmodify;topic=" + parseInt(document.forms.quickModForm.elements['topic'].value) + ";" + cur_session_var + "=" + cur_session_id + ";xml", x.join("&"), modify_topic_done); + + return false; +} + +function modify_topic_done(XMLDoc) +{ + if (!XMLDoc) + { + modify_topic_cancel(); + return true; + } + + var message = XMLDoc.getElementsByTagName("smf")[0].getElementsByTagName("message")[0]; + var subject = message.getElementsByTagName("subject")[0]; + var error = message.getElementsByTagName("error")[0]; + + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); + + if (!subject || error) + return false; + + subjectText = subject.childNodes[0].nodeValue; + + modify_topic_hide_edit(subjectText); + + set_hidden_topic_areas(''); + + in_edit_mode = 0; + + return false; +} + +// Simply restore any hidden bits during topic editing. +function set_hidden_topic_areas(set_style) +{ + for (var i = 0; i < hide_prefixes.length; i++) + { + if (document.getElementById(hide_prefixes[i] + cur_msg_id.substr(4)) != null) + document.getElementById(hide_prefixes[i] + cur_msg_id.substr(4)).style.display = set_style; + } +} + +// *** QuickReply object. +function QuickReply(oOptions) +{ + this.opt = oOptions; + this.bCollapsed = this.opt.bDefaultCollapsed; +} + +// When a user presses quote, put it in the quick reply box (if expanded). +QuickReply.prototype.quote = function (iMessageId, xDeprecated) +{ + // Compatibility with older templates. + if (typeof(xDeprecated) != 'undefined') + return true; + + if (this.bCollapsed) + { + window.location.href = smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=post;quote=' + iMessageId + ';topic=' + this.opt.iTopicId + '.' + this.opt.iStart; + return false; + } + else + { + // Doing it the XMLhttp way? + if (window.XMLHttpRequest) + { + ajax_indicator(true); + getXMLDocument(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=quotefast;quote=' + iMessageId + ';xml', this.onQuoteReceived); + } + // Or with a smart popup! + else + reqWin(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=quotefast;quote=' + iMessageId, 240, 90); + + // Move the view to the quick reply box. + if (navigator.appName == 'Microsoft Internet Explorer') + window.location.hash = this.opt.sJumpAnchor; + else + window.location.hash = '#' + this.opt.sJumpAnchor; + + return false; + } +} + +// This is the callback function used after the XMLhttp request. +QuickReply.prototype.onQuoteReceived = function (oXMLDoc) +{ + var sQuoteText = ''; + + for (var i = 0; i < oXMLDoc.getElementsByTagName('quote')[0].childNodes.length; i++) + sQuoteText += oXMLDoc.getElementsByTagName('quote')[0].childNodes[i].nodeValue; + + replaceText(sQuoteText, document.forms.postmodify.message); + + ajax_indicator(false); +} + +// The function handling the swapping of the quick reply. +QuickReply.prototype.swap = function () +{ + document.getElementById(this.opt.sImageId).src = this.opt.sImagesUrl + "/" + (this.bCollapsed ? this.opt.sImageCollapsed : this.opt.sImageExpanded); + document.getElementById(this.opt.sContainerId).style.display = this.bCollapsed ? '' : 'none'; + + this.bCollapsed = !this.bCollapsed; +} + +// *** QuickModify object. +function QuickModify(oOptions) +{ + this.opt = oOptions; + this.bInEditMode = false; + this.sCurMessageId = ''; + this.oCurMessageDiv = null; + this.oCurSubjectDiv = null; + this.sMessageBuffer = ''; + this.sSubjectBuffer = ''; + this.bXmlHttpCapable = this.isXmlHttpCapable(); + + // Show the edit buttons + if (this.bXmlHttpCapable) + { + for (var i = document.images.length - 1; i >= 0; i--) + if (document.images[i].id.substr(0, 14) == 'modify_button_') + document.images[i].style.display = ''; + } +} + +// Determine whether the quick modify can actually be used. +QuickModify.prototype.isXmlHttpCapable = function () +{ + if (typeof(window.XMLHttpRequest) == 'undefined') + return false; + + // Opera didn't always support POST requests. So test it first. + if ('opera' in window) + { + var oTest = new XMLHttpRequest(); + if (!('setRequestHeader' in oTest)) + return false; + } + + return true; +} + +// Function called when a user presses the edit button. +QuickModify.prototype.modifyMsg = function (iMessageId) +{ + if (!this.bXmlHttpCapable) + return; + + // Add backwards compatibility with old themes. + if (typeof(sSessionVar) == 'undefined') + sSessionVar = 'sesc'; + + // First cancel if there's another message still being edited. + if (this.bInEditMode) + this.modifyCancel(); + + // At least NOW we're in edit mode + this.bInEditMode = true; + + // Send out the XMLhttp request to get more info + ajax_indicator(true); + + // For IE 5.0 support, 'call' is not yet used. + this.tmpMethod = getXMLDocument; + this.tmpMethod(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=quotefast;quote=' + iMessageId + ';modify;xml', this.onMessageReceived); + delete this.tmpMethod; +} + +// The callback function used for the XMLhttp request retrieving the message. +QuickModify.prototype.onMessageReceived = function (XMLDoc) +{ + var sBodyText = '', sSubjectText = ''; + + // No longer show the 'loading...' sign. + ajax_indicator(false); + + // Grab the message ID. + this.sCurMessageId = XMLDoc.getElementsByTagName('message')[0].getAttribute('id'); + + // If this is not valid then simply give up. + if (!document.getElementById(this.sCurMessageId)) + return this.modifyCancel(); + + // Replace the body part. + for (var i = 0; i < XMLDoc.getElementsByTagName("message")[0].childNodes.length; i++) + sBodyText += XMLDoc.getElementsByTagName("message")[0].childNodes[i].nodeValue; + this.oCurMessageDiv = document.getElementById(this.sCurMessageId); + this.sMessageBuffer = getInnerHTML(this.oCurMessageDiv); + + // We have to force the body to lose its dollar signs thanks to IE. + sBodyText = sBodyText.replace(/\$/g, '{&dollarfix;$}'); + + // Actually create the content, with a bodge for disappearing dollar signs. + setInnerHTML(this.oCurMessageDiv, this.opt.sTemplateBodyEdit.replace(/%msg_id%/g, this.sCurMessageId.substr(4)).replace(/%body%/, sBodyText).replace(/\{&dollarfix;\$\}/g, '$')); + + // Replace the subject part. + this.oCurSubjectDiv = document.getElementById('subject_' + this.sCurMessageId.substr(4)); + this.sSubjectBuffer = getInnerHTML(this.oCurSubjectDiv); + + sSubjectText = XMLDoc.getElementsByTagName('subject')[0].childNodes[0].nodeValue.replace(/\$/g, '{&dollarfix;$}'); + setInnerHTML(this.oCurSubjectDiv, this.opt.sTemplateSubjectEdit.replace(/%subject%/, sSubjectText).replace(/\{&dollarfix;\$\}/g, '$')); + + return true; +} + +// Function in case the user presses cancel (or other circumstances cause it). +QuickModify.prototype.modifyCancel = function () +{ + // Roll back the HTML to its original state. + if (this.oCurMessageDiv) + { + setInnerHTML(this.oCurMessageDiv, this.sMessageBuffer); + setInnerHTML(this.oCurSubjectDiv, this.sSubjectBuffer); + } + + // No longer in edit mode, that's right. + this.bInEditMode = false; + + return false; +} + +// The function called after a user wants to save his precious message. +QuickModify.prototype.modifySave = function (sSessionId, sSessionVar) +{ + // We cannot save if we weren't in edit mode. + if (!this.bInEditMode) + return true; + + // Add backwards compatibility with old themes. + if (typeof(sSessionVar) == 'undefined') + sSessionVar = 'sesc'; + + var i, x = new Array(); + x[x.length] = 'subject=' + escape(document.forms.quickModForm['subject'].value.replace(/&#/g, "&#").php_to8bit()).replace(/\+/g, "%2B"); + x[x.length] = 'message=' + escape(document.forms.quickModForm['message'].value.replace(/&#/g, "&#").php_to8bit()).replace(/\+/g, "%2B"); + x[x.length] = 'topic=' + parseInt(document.forms.quickModForm.elements['topic'].value); + x[x.length] = 'msg=' + parseInt(document.forms.quickModForm.elements['msg'].value); + + // Send in the XMLhttp request and let's hope for the best. + ajax_indicator(true); + sendXMLDocument.call(this, smf_prepareScriptUrl(this.opt.sScriptUrl) + "action=jsmodify;topic=" + this.opt.iTopicId + ";" + sSessionVar + "=" + sSessionId + ";xml", x.join("&"), this.onModifyDone); + + return false; +} + +// Callback function of the XMLhttp request sending the modified message. +QuickModify.prototype.onModifyDone = function (XMLDoc) +{ + // We've finished the loading stuff. + ajax_indicator(false); + + // If we didn't get a valid document, just cancel. + if (!XMLDoc || !XMLDoc.getElementsByTagName('smf')[0]) + { + // Mozilla will nicely tell us what's wrong. + if (XMLDoc.childNodes.length > 0 && XMLDoc.firstChild.nodeName == 'parsererror') + setInnerHTML(document.getElementById('error_box'), XMLDoc.firstChild.textContent); + else + this.modifyCancel(); + return; + } + + var message = XMLDoc.getElementsByTagName('smf')[0].getElementsByTagName('message')[0]; + var body = message.getElementsByTagName('body')[0]; + var error = message.getElementsByTagName('error')[0]; + + if (body) + { + // Show new body. + var bodyText = ''; + for (var i = 0; i < body.childNodes.length; i++) + bodyText += body.childNodes[i].nodeValue; + + this.sMessageBuffer = this.opt.sTemplateBodyNormal.replace(/%body%/, bodyText.replace(/\$/g, '{&dollarfix;$}')).replace(/\{&dollarfix;\$\}/g,'$'); + setInnerHTML(this.oCurMessageDiv, this.sMessageBuffer); + + // Show new subject. + var oSubject = message.getElementsByTagName('subject')[0]; + var sSubjectText = oSubject.childNodes[0].nodeValue.replace(/\$/g, '{&dollarfix;$}'); + this.sSubjectBuffer = this.opt.sTemplateSubjectNormal.replace(/%msg_id%/g, this.sCurMessageId.substr(4)).replace(/%subject%/, sSubjectText).replace(/\{&dollarfix;\$\}/g,'$'); + setInnerHTML(this.oCurSubjectDiv, this.sSubjectBuffer); + + // If this is the first message, also update the topic subject. + if (oSubject.getAttribute('is_first') == '1') + setInnerHTML(document.getElementById('top_subject'), this.opt.sTemplateTopSubject.replace(/%subject%/, sSubjectText).replace(/\{&dollarfix;\$\}/g, '$')); + + // Show this message as 'modified on x by y'. + if (this.opt.bShowModify) + setInnerHTML(document.getElementById('modified_' + this.sCurMessageId.substr(4)), message.getElementsByTagName('modified')[0].childNodes[0].nodeValue); + } + else if (error) + { + setInnerHTML(document.getElementById('error_box'), error.childNodes[0].nodeValue); + document.forms.quickModForm.message.style.border = error.getAttribute('in_body') == '1' ? this.opt.sErrorBorderStyle : ''; + document.forms.quickModForm.subject.style.border = error.getAttribute('in_subject') == '1' ? this.opt.sErrorBorderStyle : ''; + } +} + +function InTopicModeration(oOptions) +{ + this.opt = oOptions; + this.bButtonsShown = false; + this.iNumSelected = 0; + + // Add backwards compatibility with old themes. + if (typeof(this.opt.sSessionVar) == 'undefined') + this.opt.sSessionVar = 'sesc'; + + this.init(); +} + +InTopicModeration.prototype.init = function() +{ + // Add checkboxes to all the messages. + for (var i = 0, n = this.opt.aMessageIds.length; i < n; i++) + { + // Create the checkbox. + var oCheckbox = document.createElement('input'); + oCheckbox.type = 'checkbox'; + oCheckbox.className = 'input_check'; + oCheckbox.name = 'msgs[]'; + oCheckbox.value = this.opt.aMessageIds[i]; + oCheckbox.instanceRef = this; + oCheckbox.onclick = function () { + this.instanceRef.handleClick(this); + } + + // Append it to the container + var oCheckboxContainer = document.getElementById(this.opt.sCheckboxContainerMask + this.opt.aMessageIds[i]); + oCheckboxContainer.appendChild(oCheckbox); + oCheckboxContainer.style.display = ''; + } +} + +InTopicModeration.prototype.handleClick = function(oCheckbox) +{ + if (!this.bButtonsShown && this.opt.sButtonStripDisplay) + { + var oButtonStrip = document.getElementById(this.opt.sButtonStrip); + var oButtonStripDisplay = document.getElementById(this.opt.sButtonStripDisplay); + + // Make sure it can go somewhere. + if (typeof(oButtonStripDisplay) == 'object' && oButtonStripDisplay != null) + oButtonStripDisplay.style.display = ""; + else + { + var oNewDiv = document.createElement('div'); + var oNewList = document.createElement('ul'); + + oNewDiv.id = this.opt.sButtonStripDisplay; + oNewDiv.className = this.opt.sButtonStripClass ? this.opt.sButtonStripClass : 'buttonlist floatbottom'; + + oNewDiv.appendChild(oNewList); + oButtonStrip.appendChild(oNewDiv); + } + + // Add the 'remove selected items' button. + if (this.opt.bCanRemove) + smf_addButton(this.opt.sButtonStrip, this.opt.bUseImageButton, { + sId: this.opt.sSelf + '_remove_button', + sText: this.opt.sRemoveButtonLabel, + sImage: this.opt.sRemoveButtonImage, + sUrl: '#', + sCustom: ' onclick="return ' + this.opt.sSelf + '.handleSubmit(\'remove\')"' + }); + + // Add the 'restore selected items' button. + if (this.opt.bCanRestore) + smf_addButton(this.opt.sButtonStrip, this.opt.bUseImageButton, { + sId: this.opt.sSelf + '_restore_button', + sText: this.opt.sRestoreButtonLabel, + sImage: this.opt.sRestoreButtonImage, + sUrl: '#', + sCustom: ' onclick="return ' + this.opt.sSelf + '.handleSubmit(\'restore\')"' + }); + + // Adding these buttons once should be enough. + this.bButtonsShown = true; + } + + // Keep stats on how many items were selected. + this.iNumSelected += oCheckbox.checked ? 1 : -1; + + // Show the number of messages selected in the button. + if (this.opt.bCanRemove && !this.opt.bUseImageButton) + { + setInnerHTML(document.getElementById(this.opt.sSelf + '_remove_button'), this.opt.sRemoveButtonLabel + ' [' + this.iNumSelected + ']'); + document.getElementById(this.opt.sSelf + '_remove_button').style.display = this.iNumSelected < 1 ? "none" : ""; + } + + if (this.opt.bCanRestore && !this.opt.bUseImageButton) + { + setInnerHTML(document.getElementById(this.opt.sSelf + '_restore_button'), this.opt.sRestoreButtonLabel + ' [' + this.iNumSelected + ']'); + document.getElementById(this.opt.sSelf + '_restore_button').style.display = this.iNumSelected < 1 ? "none" : ""; + } + + // Try to restore the correct position. + var aItems = document.getElementById(this.opt.sButtonStrip).getElementsByTagName('span'); + if (aItems.length > 3) + { + if (this.iNumSelected < 1) + { + aItems[aItems.length - 3].className = aItems[aItems.length - 3].className.replace(/\s*position_holder/, 'last'); + aItems[aItems.length - 2].className = aItems[aItems.length - 2].className.replace(/\s*position_holder/, 'last'); + } + else + { + aItems[aItems.length - 2].className = aItems[aItems.length - 2].className.replace(/\s*last/, 'position_holder'); + aItems[aItems.length - 3].className = aItems[aItems.length - 3].className.replace(/\s*last/, 'position_holder'); + } + } +} + +InTopicModeration.prototype.handleSubmit = function (sSubmitType) +{ + var oForm = document.getElementById(this.opt.sFormId); + + // Make sure this form isn't submitted in another way than this function. + var oInput = document.createElement('input'); + oInput.type = 'hidden'; + oInput.name = this.opt.sSessionVar; + oInput.value = this.opt.sSessionId; + oForm.appendChild(oInput); + + switch (sSubmitType) + { + case 'remove': + if (!confirm(this.opt.sRemoveButtonConfirm)) + return false; + + oForm.action = oForm.action.replace(/;restore_selected=1/, ''); + break; + + case 'restore': + if (!confirm(this.opt.sRestoreButtonConfirm)) + return false; + + oForm.action = oForm.action + ';restore_selected=1'; + break; + + default: + return false; + break; + } + + oForm.submit(); + return true; +} + + +// *** Other functions... +function expandThumb(thumbID) +{ + var img = document.getElementById('thumb_' + thumbID); + var link = document.getElementById('link_' + thumbID); + var tmp = img.src; + img.src = link.href; + link.href = tmp; + img.style.width = ''; + img.style.height = ''; + return false; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/sha1.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/sha1.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,289 @@ +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1 Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} + +/* + * Perform a simple self-test to see if the VM is working + */ +function sha1_vm_test() +{ + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; +} + +/* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ +function core_sha1(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for (var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for (var j = 0; j < 80; j++) + { + if (j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); +} + +/* + * Perform the appropriate triplet combination function for the current + * iteration + */ +function sha1_ft(t, b, c, d) +{ + if (t < 20) return (b & c) | ((~b) & d); + if (t < 40) return b ^ c ^ d; + if (t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +/* + * Determine the appropriate additive constant for the current iteration + */ +function sha1_kt(t) +{ + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; +} + +/* + * Calculate the HMAC-SHA1 of a key and some data + */ +function core_hmac_sha1(key, data) +{ + var bkey = str2binb(key); + if (bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for (var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ +function str2binb(str) +{ + var bin = Array(); + var mask = (1 << chrsz) - 1; + for (var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32); + return bin; +} + +/* + * Convert an array of big-endian words to a string + */ +function binb2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for (var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask); + return str; +} + +/* + * Convert an array of big-endian words to a hex string. + */ +function binb2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of big-endian words to a base-64 string + */ +function binb2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for (var j = 0; j < 4; j++) + { + if (i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + +// Character-level replacement function. +String.prototype.php_strtr = function (sFrom, sTo) { + return this.replace(new RegExp('[' + sFrom + ']', 'g'), function (sMatch) { + return sTo.charAt(sFrom.indexOf(sMatch)); + }); +} + +// Simulate PHP's strtolower (in SOME cases PHP uses ISO-8859-1 case folding). +String.prototype.php_strtolower = function () { + return typeof(smf_iso_case_folding) != "undefined" && smf_iso_case_folding == true ? this.php_strtr( + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ\x8a\x8c\x8e\x9f\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde', + 'abcdefghijklmnopqrstuvwxyz\x9a\x9c\x9e\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe' + ) : this.php_strtr('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); +} + +// Convert a string to an 8 bit representation (like in PHP). +String.prototype.php_to8bit = function () { + if (smf_charset == 'UTF-8') + { + var n, sReturn = ''; + + for (var i = 0, iTextLen = this.length; i < iTextLen; i++) + { + n = this.charCodeAt(i); + if (n < 128) + sReturn += String.fromCharCode(n) + else if (n < 2048) + sReturn += String.fromCharCode(192 | n >> 6) + String.fromCharCode(128 | n & 63); + else if (n < 65536) + sReturn += String.fromCharCode(224 | n >> 12) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); + else + sReturn += String.fromCharCode(240 | n >> 18) + String.fromCharCode(128 | n >> 12 & 63) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); + } + + return sReturn; + } + else if (smf_charset == 'ISO-8859-2') + { + return this.php_strtr( + '\u0104\u02d8\u0141\u013d\u026a\u0160\u015e\u0164\u0179\u017d\u017b\u0105\u02db\u0142\u013e\u015b\u02c7\u0161\u015f\u0165\u017a\u02dd\u017e\u017c\u0154\u0102\u0139\u0106\u010c\u0118\u011a\u010e\u0110\u0143\u0147\u0150\u0158\u016e\u0170\u0162\u0155\u0103\u013a\u0107\u010d\u0119\u011b\u010f\u0111\u0144\u0148\u0151\u0159\u016f\u0171\u0163\u02d9', + '\xa1\xa2\xa3\xa5\xa6\xa9\xaa\xab\xac\xae\xaf\xb1\xb2\xb3\xb5\xb6\xb7\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc3\xc5\xc6\xc8\xca\xcc\xcf\xd0\xd1\xd2\xd5\xd8\xd9\xdc\xde\xe0\xe3\xe5\xe6\xe8\xea\xec\xef\xf0\xf1\xf2\xf5\xf8\xf9\xfb\xfe\xff' + ); + } + else if (smf_charset == 'ISO-8859-9') + { + return this.php_strtr( + '\u011e\u0130\u015e\u011f\u0131\u015f', + '\xd0\xdd\xde\xf0\xfd\xfe' + ); + } + else if (smf_charset == 'tis-620') + { + return this.php_strtr( + '\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e31\u0e32\u0e33\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\u0e3f\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0e4f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0e5a\u0e5b', + '\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb' + ); + } + else if (smf_charset == 'windows-1251') + { + return this.php_strtr( + '\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\u040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u0459\u203a\u045a\u045c\u045b\u045f\u040e\u045e\u0408\u0490\u0401\u0404\u0407\u0406\u0456\u0491\u0451\u2116\u0454\u0458\u0405\u0455\u0457\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f', + '\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa1\xa2\xa3\xa5\xa8\xaa\xaf\xb2\xb3\xb4\xb8\xb9\xba\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' + ); + } + else if (smf_charset == 'windows-1253') + { + return this.php_strtr( + '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u203a\u0385\u0386\u2015\u0384\u0388\u0389\u038a\u038c\u038e\u038f\u0390\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce', + '\x80\x82\x83\x84\x85\x86\x87\x89\x8b\x91\x92\x93\x94\x95\x96\x97\x99\x9b\xa1\xa2\xaf\xb4\xb8\xb9\xba\xbc\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe' + ); + } + else if (smf_charset == 'windows-1255') + { + return this.php_strtr( + '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u203a\u20aa\u00d7\u00f7\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2\u05c3\u05f0\u05f1\u05f2\u05f3\u05f4\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u200e\u200f', + '\x80\x82\x83\x84\x85\x86\x87\x88\x89\x8b\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9b\xa4\xaa\xba\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfd\xfe' + ); + } + else if (smf_charset == 'windows-1256') + { + return this.php_strtr( + '\u20ac\u067e\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0679\u2039\u0152\u0686\u0698\u0688\u06af\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u06a9\u2122\u0691\u203a\u0153\u200c\u200d\u06ba\u060c\u06be\u061b\u061f\u06c1\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0640\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u200e\u200f\u06d2', + '\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa1\xaa\xba\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe1\xe3\xe4\xe5\xe6\xec\xed\xf0\xf1\xf2\xf3\xf5\xf6\xf8\xfa\xfd\xfe\xff' + ); + } + else + return this; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/spellcheck.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/spellcheck.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,293 @@ +// These are variables the popup is going to want to access... +var spell_formname, spell_fieldname; + +// Spell check the specified field in the specified form. +function spellCheck(formName, fieldName) +{ + // Grab the (hidden) spell checking form. + var spellform = document.forms.spell_form; + + // Register the name of the editing form for future reference. + spell_formname = formName; + spell_fieldname = fieldName; + + // This should match a word (most of the time). + var regexpWordMatch = /(?:<[^>]+>)|(?:\[[^ ][^\]]*\])|(?:&[^; ]+;)|(?:[^0-9\s\]\[{};:"\\|,<.>\/?`~!@#$%^&*()_+=]+)/g; + + // These characters can appear within words. + var aWordCharacters = ['-', '\'']; + + var aWords = new Array(), aResult = new Array(); + var sText = document.forms[formName][fieldName].value + var bInCode = false; + var iOffset1, iOffset2; + + // Loop through all words. + while ((aResult = regexpWordMatch.exec(sText)) && typeof(aResult) != 'undefined') + { + iOffset1 = 0; + iOffset2 = aResult[0].length - 1; + + // Strip the dashes and hyphens from the begin of the word. + while (in_array(aResult[0].charAt(iOffset1), aWordCharacters) && iOffset1 < iOffset2) + iOffset1++; + + // Strip the dashes and hyphens from the end of the word. + while (in_array(aResult[0].charAt(iOffset2), aWordCharacters) && iOffset1 < iOffset2) + iOffset2--; + + // I guess there's only dashes and hyphens in this word... + if (iOffset1 == iOffset2) + continue; + + // Ignore code blocks. + if (aResult[0].substr(0, 5).toLowerCase() == '[code') + bInCode = true; + + // Glad we're out of that code block! + else if (bInCode && aResult[0].substr(0, 7).toLowerCase() == '[/code]') + bInCode = false; + + // Now let's get to business. + else if (!bInCode && !in_array(aResult[0].charAt(0), ['[', '<']) && aResult[0].toUpperCase() != aResult[0]) + aWords[aWords.length] = aResult[0].substr(iOffset1, iOffset2 - iOffset1 + 1) + '|' + (iOffset1 + sText.substr(0, aResult.index).length) + '|' + (iOffset2 + sText.substr(0, aResult.index).length); + } + + // Open the window... + openSpellWin(640, 480); + + // Pass the data to a form... + spellform.spellstring.value = aWords.join('\n'); + + // and go! + spellform.submit(); + + return true; +} + +// Private functions ------------------------------- + +// Globals... +var wordindex = -1, offsetindex = 0; +var ignoredWords = []; + +// A "misspelled word" object. +function misp(word, start, end, suggestions) +{ + // The word, start index, end index, and array of suggestions. + this.word = word; + this.start = start; + this.end = end; + this.suggestions = suggestions; +} + +// Replace the word in the misps array at the "wordindex" index. The +// misps array is generated by a PHP script after the string to be spell +// checked is evaluated with pspell. +function replaceWord() +{ + var strstart = ""; + var strend; + + // If this isn't the beginning of the string then get all of the string + // that is before the word we are replacing. + if (misps[wordindex].start != 0) + strstart = mispstr.slice(0, misps[wordindex].start + offsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[wordindex].end + 1 + offsetindex); + + // Rebuild the string with the new word. + mispstr = strstart + document.forms.spellingForm.changeto.value + strend; + + // Update offsetindex to compensate for replacing a word with a word + // of a different length. + offsetindex += document.forms.spellingForm.changeto.value.length - misps[wordindex].word.length; + + // Update the word so future replaceAll calls don't change it. + misps[wordindex].word = document.forms.spellingForm.changeto.value; + + nextWord(false); +} + +// Replaces all instances of currently selected word with contents chosen by user. +// Note: currently only replaces words after highlighted word. I think we can re-index +// all words at replacement or ignore time to have it wrap to the beginning if we want +// to. +function replaceAll() +{ + var strend; + var idx; + var origword; + var localoffsetindex = offsetindex; + + origword = misps[wordindex].word; + + // Re-index everything past the current word. + for (idx = wordindex; idx < misps.length; idx++) + { + misps[idx].start += localoffsetindex; + misps[idx].end += localoffsetindex; + } + + localoffsetindex = 0; + + for (idx = 0; idx < misps.length; idx++) + { + if (misps[idx].word == origword) + { + var strstart = ""; + if (misps[idx].start != 0) + strstart = mispstr.slice(0, misps[idx].start + localoffsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[idx].end + 1 + localoffsetindex); + + // Rebuild the string with the new word. + mispstr = strstart + document.forms.spellingForm.changeto.value + strend; + + // Update offsetindex to compensate for replacing a word with a word + // of a different length. + localoffsetindex += document.forms.spellingForm.changeto.value.length - misps[idx].word.length; + } + + // We have to re-index everything after replacements. + misps[idx].start += localoffsetindex; + misps[idx].end += localoffsetindex; + } + + // Add the word to the ignore array. + ignoredWords[origword] = true; + + // Reset offsetindex since we re-indexed. + offsetindex = 0; + + nextWord(false); +} + +// Highlight the word that was selected using the nextWord function. +function highlightWord() +{ + var strstart = ""; + var strend; + + // If this isn't the beginning of the string then get all of the string + // that is before the word we are replacing. + if (misps[wordindex].start != 0) + strstart = mispstr.slice(0, misps[wordindex].start + offsetindex); + + // Get the end of the string after the word we are replacing. + strend = mispstr.slice(misps[wordindex].end + 1 + offsetindex); + + // Rebuild the string with a span wrapped around the misspelled word + // so we can highlight it in the div the user is viewing the string in. + var divptr, newValue; + divptr = document.getElementById("spellview"); + + newValue = htmlspecialchars(strstart) + '' + misps[wordindex].word + '' + htmlspecialchars(strend); + setInnerHTML(divptr, newValue.replace(/_\|_/g, '
    ')); + + // We could use scrollIntoView, but it's just not that great anyway. + var spellview_height = typeof(document.getElementById("spellview").currentStyle) != "undefined" ? parseInt(document.getElementById("spellview").currentStyle.height) : document.getElementById("spellview").offsetHeight; + var word_position = document.getElementById("h1").offsetTop; + var current_position = document.getElementById("spellview").scrollTop; + + // The spellview is not tall enough! Scroll down! + if (spellview_height <= (word_position + current_position)) + document.getElementById("spellview").scrollTop = word_position + current_position - spellview_height + 32; +} + +// Display the next misspelled word to the user and populate the suggested spellings box. +function nextWord(ignoreall) +{ + // Push ignored word onto ignoredWords array. + if (ignoreall) + ignoredWords[misps[wordindex].word] = true; + + // Update the index of all words we have processed... + // This must be done to accomodate the replaceAll function. + if (wordindex >= 0) + { + misps[wordindex].start += offsetindex; + misps[wordindex].end += offsetindex; + } + + // Increment the counter for the array of misspelled words. + wordindex++; + + // Draw it and quit if there are no more misspelled words to evaluate. + if (misps.length <= wordindex) + { + var divptr; + divptr = document.getElementById("spellview"); + setInnerHTML(divptr, htmlspecialchars(mispstr).replace(/_\|_/g, "
    ")); + + while (document.forms.spellingForm.suggestions.options.length > 0) + document.forms.spellingForm.suggestions.options[0] = null; + + alert(txt['done']); + document.forms.spellingForm.change.disabled = true; + document.forms.spellingForm.changeall.disabled = true; + document.forms.spellingForm.ignore.disabled = true; + document.forms.spellingForm.ignoreall.disabled = true; + + // Put line feeds back... + mispstr = mispstr.replace(/_\|_/g, "\n"); + + // Get a handle to the field we need to re-populate. + window.opener.document.forms[spell_formname][spell_fieldname].value = mispstr; + window.opener.document.forms[spell_formname][spell_fieldname].focus(); + window.close(); + return true; + } + + // Check to see if word is supposed to be ignored. + if (typeof(ignoredWords[misps[wordindex].word]) != "undefined") + { + nextWord(false); + return false; + } + + // Clear out the suggestions box! + while (document.forms.spellingForm.suggestions.options.length > 0) + document.forms.spellingForm.suggestions.options[0] = null; + + // Re-populate the suggestions box if there are any suggested spellings for the word. + if (misps[wordindex].suggestions.length) + { + for (var sugidx = 0; sugidx < misps[wordindex].suggestions.length; sugidx++) + { + var newopt = new Option(misps[wordindex].suggestions[sugidx], misps[wordindex].suggestions[sugidx]); + document.forms.spellingForm.suggestions.options[sugidx] = newopt; + + if (sugidx == 0) + { + newopt.selected = true; + document.forms.spellingForm.changeto.value = newopt.value; + document.forms.spellingForm.changeto.select(); + } + } + } + + if (document.forms.spellingForm.suggestions.options.length == 0) + document.forms.spellingForm.changeto.value = ""; + + highlightWord(); + + return false; +} + +function htmlspecialchars(thetext) +{ + thetext = thetext.replace(/\/g, ">"); + thetext = thetext.replace(/\n/g, "
    "); + thetext = thetext.replace(/\ \ /g, "  "); + + return thetext; +} + +function openSpellWin(width, height) +{ + window.open("", "spellWindow", "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=no,width=" + width + ",height=" + height); +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/style.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/style.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,451 @@ +/* Normal, standard links. */ +a:link +{ + color: #476C8E; + text-decoration: none; +} +a:visited +{ + color: #476C8E; + text-decoration: none; +} +a:hover +{ + text-decoration: underline; +} + +/* Navigation links - for the link tree. */ +.nav, .nav:link, .nav:visited +{ + color: #000000; + text-decoration: none; +} +a.nav:hover +{ + color: #cc3333; + text-decoration: underline; +} + +/* Tables should show empty cells. */ +table +{ + empty-cells: show; +} + +/* By default (td, body..) use verdana in black. */ +body, td, th , tr +{ + color: #000000; + font-size: small; + font-family: verdana, sans-serif; +} + +/* The main body of the entire forum. */ +body +{ + background-color: #E5E5E8; + margin: 0px; + padding: 12px 30px 4px 30px; +} + +/* Input boxes - just a bit smaller than normal so they align well. */ +input, textarea, button +{ + color: #000000; + font-family: verdana, sans-serif; +} +input, button +{ + font-size: 90%; +} + +textarea +{ + font-size: 100%; + color: #000000; + font-family: verdana, sans-serif; +} + +/* All input elements that are checkboxes or radio buttons. */ +input.check +{ +} + +/* Selects are a bit smaller, because it makes them look even better 8). */ +select +{ + font-size: 90%; + font-weight: normal; + color: #000000; + font-family: verdana, sans-serif; +} + +/* Standard horizontal rule.. ([hr], etc.) */ +hr, .hrcolor +{ + height: 1px; + border: 0; + color: #666666; + background-color: #666666; +} + +/* No image should have a border when linked */ +a img +{ + border: 0; +} +/* A quote, perhaps from another post. */ +.quote +{ + color: #000000; + background-color: #D7DAEC; + border: 1px solid #000000; + margin: 1px; + padding: 1px; + font-size: x-small; + line-height: 1.4em; +} + +/* A code block - maybe even PHP ;). */ +.code +{ + color: #000000; + background-color: #dddddd; + font-family: "courier new", "times new roman", monospace; + font-size: x-small; + line-height: 1.3em; + /* Put a nice border around it. */ + border: 1px solid #000000; + margin: 1px auto 1px auto; + padding: 1px; + width: 99%; + /* Don't wrap its contents, and show scrollbars. */ + white-space: nowrap; + overflow: auto; + /* Stop after about 24 lines, and just show a scrollbar. */ + max-height: 24em; +} + +/* The "Quote:" and "Code:" header parts... */ +.quoteheader, .codeheader +{ + color: #000000; + text-decoration: none; + font-style: normal; + font-weight: bold; + font-size: x-small; + line-height: 1.2em; +} + +/* Generally, those [?] icons. This makes your cursor a help icon. */ +.help +{ + cursor: help; +} + +/* /me uses this a lot. (emote, try typing /me in a post.) */ +.meaction +{ + color: red; +} + +/* The main post box - this makes it as wide as possible. */ +.editor +{ + width: 96%; +} + +/* Highlighted text - such as search results. */ +.highlight +{ + background-color: yellow; + font-weight: bold; + color: black; +} + +/* Alternating backgrounds for posts, and several other sections of the forum. */ +.windowbg, #preview_body +{ + color: #000000; + background-color: #ECEDF3; +} +.windowbg2 +{ + color: #000000; + background-color: #F6F6F6; +} +.windowbg3 +{ + color: #000000; + background-color: #E0E1E8; +} +/* the today container in calendar */ +.calendar_today +{ + background-color: #FFFFFF; +} + +/* These are used primarily for titles, but also for headers (the row that says what everything in the table is.) */ +.titlebg, tr.titlebg th, tr.titlebg td, .titlebg2, tr.titlebg2 th, tr.titlebg2 td +{ + color: black; + font-style: normal; + background: url(images/titlebg.jpg) #E9F0F6 repeat-x; + border-bottom: solid 1px #9BAEBF; + border-top: solid 1px #FFFFFF; + padding-left: 10px; + padding-right: 10px; +} +.titlebg, .titlebg a:link, .titlebg a:visited +{ + font-weight: bold; + color: black; + font-style: normal; +} + +.titlebg a:hover +{ + color: #404040; +} +/* same as titlebg, but used where bold text is not needed */ +.titlebg2 a:link, .titlebg2 a:visited +{ + color: black; + font-style: normal; + text-decoration: underline; +} + +.titlebg2 a:hover +{ + text-decoration: underline; +} + +/* This is used for categories, page indexes, and several other areas in the forum. +.catbg and .catbg2 is for boardindex, while .catbg3 is for messageindex and display headers*/ +.catbg , tr.catbg td , .catbg3 , tr.catbg3 td +{ + background: url(images/catbg.jpg) #88A6C0 repeat-x; + color: #ffffff; + padding-left: 10px; + padding-right: 10px; +} +.catbg2 , tr.catbg2 td +{ + background: url(images/catbg2.jpg) #A1BFD9 repeat-x; + color: #ffffff; + padding-left: 10px; + padding-right: 10px; +} +.catbg, .catbg2, .catbg3 +{ + border-bottom: solid 1px #375576; +} +.catbg, .catbg2 +{ + font-weight: bold; +} +.catbg3, tr.catbg3 td, .catbg3 a:link, .catbg3 a:visited +{ + font-size: 95%; + color: white; + text-decoration: none; +} +.catbg a:link, .catbg a:visited , .catbg2 a:link, .catbg2 a:visited +{ + color: white; + text-decoration: none; +} +.catbg a:hover, .catbg2 a:hover, .catbg3 a:hover +{ + color: #e0e0ff; +} +/* This is used for tables that have a grid/border background color (such as the topic listing.) */ +.bordercolor +{ + background-color: #ADADAD; + padding: 0px; +} + +/* This is used on tables that should just have a border around them. */ +.tborder +{ + padding: 1px; + border: 1px solid #696969; + background-color: #FFFFFF; +} + +/* Default font sizes: small (8pt), normal (10pt), and large (14pt). */ +.smalltext +{ + font-size: x-small; + font-family: verdana, sans-serif; +} +.middletext +{ + font-size: 90%; +} +.normaltext +{ + font-size: small; +} +.largetext +{ + font-size: large; +} + + +/* Posts and personal messages displayed throughout the forum. */ +.post, .personalmessage +{ + width: 100%; + overflow: auto; + line-height: 1.3em; +} + +/* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ +.signature +{ + width: 100%; + overflow: auto; + padding-bottom: 3px; + line-height: 1.3em; +} + +/* Sometimes there will be an error when you post */ +.error +{ + color: red; +} + + +/* definitions for the main tab, active means the tab reflects which page is displayed */ +.maintab_first, .maintab_back, .maintab_last, .maintab_active_first, .maintab_active_back, .maintab_active_last +{ + color: white; + text-transform: uppercase; + vertical-align: top; +} +.maintab_back, .maintab_active_back +{ + color: white; + text-decoration: none; + font-size: 9px; + vertical-align: top; + padding: 2px 6px 6px 6px; + font-family: tahoma, sans-serif; +} + +.maintab_first +{ + background: url(images/maintab_first.gif) left bottom no-repeat; + width: 10px; +} +.maintab_back +{ + background: url(images/maintab_back.gif) left bottom repeat-x; +} +.maintab_last +{ + background: url(images/maintab_last.gif) left bottom no-repeat; + width: 8px; +} +.maintab_active_first +{ + background: url(images/maintab_active_first.gif) left bottom no-repeat; + width: 6px; +} +.maintab_active_back +{ + background: url(images/maintab_active_back.gif) left bottom repeat-x; +} +.maintab_active_last +{ + background: url(images/maintab_active_last.gif) left bottom no-repeat; + width: 8px; +} + +/* how links behave in main tab. */ +.maintab_back a:link , .maintab_back a:visited, .maintab_active_back a:link , .maintab_active_back a:visited +{ + color: white; + text-decoration: none; +} + +.maintab_back a:hover, .maintab_active_back a:hover +{ + color: #e0e0ff; + text-decoration: none; +} +/* definitions for the mirror tab */ +.mirrortab_first, .mirrortab_back, .mirrortab_last, .mirrortab_active_first, .mirrortab_active_back, .mirrortab_active_last +{ + color: white; + text-transform: uppercase; + vertical-align: top; +} +.mirrortab_back, .mirrortab_active_back +{ + color: white; + text-decoration: none; + font-size: 9px; + vertical-align: bottom; + padding: 6px 6px 2px 6px; + font-family: tahoma, sans-serif; +} + +.mirrortab_first +{ + background: url(images/mirrortab_first.gif) no-repeat; + width: 10px; +} +.mirrortab_back +{ + background: url(images/mirrortab_back.gif) repeat-x; +} +.mirrortab_last +{ + background: url(images/mirrortab_last.gif) no-repeat; + width: 6px; +} +.mirrortab_active_first +{ + background: url(images/mirrortab_active_first.gif) no-repeat; + width: 6px; +} +.mirrortab_active_back +{ + background: url(images/mirrortab_active_back.gif) repeat-x; +} +.mirrortab_active_last +{ + background: url(images/mirrortab_active_last.gif) no-repeat; + width: 8px; +} + +/* how links behave in mirror tab. */ +.mirrortab_back a:link , .mirrortab_back a:visited, .mirrortab_active_back a:link , .mirrortab_active_back a:visited +{ + color: white; + text-decoration: none; +} + +.mirrortab_back a:hover, .mirrortab_active_back a:hover +{ + color: #e0e0ff; + text-decoration: none; +} + +/* The AJAX notifier */ +#ajax_in_progress +{ + background: #32CD32; + color: white; + text-align: center; + font-weight: bold; + font-size: 18pt; + padding: 3px; + width: 100%; + position: fixed; + top: 0; + left: 0; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/wireless.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/wireless.css Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,30 @@ +.catbg, tr.catbg td +{ + background-color: #6d92aa; + color: #ffffff; +} +.titlebg, .titlebg a, .titlebg a:link, .titlebg a:visited +{ + background-color: #b6dbff; + color: #000000; + text-decoration: none; +} +.windowbg, tr.windowbg td +{ + background-color: #ffffff; + color: #000000; +} +.windowbg2, tr.windowbg2 td +{ + background-color: #c0c0c0; + color: #000000; +} +.new, a:link.new, a:visited.new +{ + background-color: #2f2fc0; + color: #ffffff; +} +.updated +{ + color: #ff0000; +} \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/xml_board.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/xml_board.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,113 @@ +var cur_topic_id, cur_msg_id, buff_subject, cur_subject_div, in_edit_mode = 0; +var hide_prefixes = Array(); + +function modify_topic(topic_id, first_msg_id, cur_session_id) +{ + if (!window.XMLHttpRequest) + return; + if (typeof(window.opera) != "undefined") + { + var test = new XMLHttpRequest(); + if (typeof(test.setRequestHeader) != "function") + return; + } + + if (in_edit_mode == 1) + { + if (cur_topic_id == topic_id) + return; + else + modify_topic_cancel(); + } + + in_edit_mode = 1; + mouse_on_div = 1; + cur_topic_id = topic_id; + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + + getXMLDocument(smf_scripturl + "?action=quotefast;quote=" + first_msg_id + ";sesc=" + cur_session_id + ";modify;xml", onDocReceived_modify_topic); +} + +function onDocReceived_modify_topic(XMLDoc) +{ + cur_msg_id = XMLDoc.getElementsByTagName("message")[0].getAttribute("id"); + + cur_subject_div = document.getElementById('msg_' + cur_msg_id.substr(4)); + buff_subject = getInnerHTML(cur_subject_div); + + // Here we hide any other things they want hiding on edit. + set_hidden_topic_areas('none'); + + modify_topic_show_edit(XMLDoc.getElementsByTagName("subject")[0].childNodes[0].nodeValue); + + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + +function modify_topic_cancel() +{ + setInnerHTML(cur_subject_div, buff_subject); + set_hidden_topic_areas(''); + + in_edit_mode = 0; + return false; +} + +function modify_topic_save(cur_session_id) +{ + if (!in_edit_mode) + return true; + + var i, x = new Array(); + x[x.length] = 'subject=' + escape(textToEntities(document.forms.quickModForm['subject'].value)).replace(/\+/g, "%2B"); + x[x.length] = 'topic=' + parseInt(document.forms.quickModForm.elements['topic'].value); + x[x.length] = 'msg=' + parseInt(document.forms.quickModForm.elements['msg'].value); + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + + sendXMLDocument(smf_scripturl + "?action=jsmodify;topic=" + parseInt(document.forms.quickModForm.elements['topic'].value) + ";sesc=" + cur_session_id + ";xml", x.join("&"), modify_topic_done); + + return false; +} + +function modify_topic_done(XMLDoc) +{ + if (!XMLDoc) + { + modify_topic_cancel(); + return true; + } + + var message = XMLDoc.getElementsByTagName("smf")[0].getElementsByTagName("message")[0]; + var subject = message.getElementsByTagName("subject")[0]; + var error = message.getElementsByTagName("error")[0]; + + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); + + if (!subject || error) + return false; + + subjectText = subject.childNodes[0].nodeValue; + + modify_topic_hide_edit(subjectText); + + set_hidden_topic_areas(''); + + in_edit_mode = 0; + + return false; +} + +// Simply restore any hidden bits during topic editing. +function set_hidden_topic_areas(set_style) +{ + for (var i = 0; i < hide_prefixes.length; i++) + { + if (document.getElementById(hide_prefixes[i] + cur_msg_id.substr(4)) != null) + document.getElementById(hide_prefixes[i] + cur_msg_id.substr(4)).style.display = set_style; + } +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/default/xml_topic.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/default/xml_topic.js Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,206 @@ +var smf_topic, smf_start, smf_show_modify, quickReplyCollapsed, buff_message; +var cur_msg_id, cur_msg_div, buff_subject, cur_subject_div, in_edit_mode = 0; + +function doQuote(messageid, cur_session_id) +{ + if (quickReplyCollapsed) + window.location.href = smf_scripturl + "?action=post;quote=" + messageid + ";topic=" + smf_topic + "." + smf_start + ";sesc=" + cur_session_id; + else + { + if (window.XMLHttpRequest) + { + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + getXMLDocument(smf_scripturl + "?action=quotefast;quote=" + messageid + ";sesc=" + cur_session_id + ";xml", onDocReceived); + } + else + reqWin(smf_scripturl + "?action=quotefast;quote=" + messageid + ";sesc=" + cur_session_id, 240, 90); + + if (navigator.appName == "Microsoft Internet Explorer") + window.location.hash = "quickreply"; + else + window.location.hash = "#quickreply"; + } +} + +function onDocReceived(XMLDoc) +{ + var text = ""; + for (var i = 0; i < XMLDoc.getElementsByTagName("quote")[0].childNodes.length; i++) + text += XMLDoc.getElementsByTagName("quote")[0].childNodes[i].nodeValue; + + replaceText(text, document.forms.postmodify.message); + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + + +function modify_msg(msg_id, cur_session_id) +{ + if (!window.XMLHttpRequest) + return; + if (typeof(window.opera) != "undefined") + { + var test = new XMLHttpRequest(); + if (typeof(test.setRequestHeader) != "function") + return; + } + if (in_edit_mode == 1) + modify_cancel(); + in_edit_mode = 1; + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + getXMLDocument(smf_scripturl + '?action=quotefast;quote=' + msg_id + ';sesc=' + cur_session_id + ';modify;xml', onDocReceived_modify); +} + +function onDocReceived_modify(XMLDoc) +{ + var text = ""; + var subject = ""; + + // Grab the message ID. + cur_msg_id = XMLDoc.getElementsByTagName("message")[0].getAttribute("id"); + + // Replace the body part. + for (var i = 0; i < XMLDoc.getElementsByTagName("message")[0].childNodes.length; i++) + text += XMLDoc.getElementsByTagName("message")[0].childNodes[i].nodeValue; + cur_msg_div = document.getElementById(cur_msg_id); + buff_message = getInnerHTML(cur_msg_div); + + // Actually create the content, with a bodge for dissapearing dollar signs. + text = text.replace(/\$/g,"{&dollarfix;$}"); + text = smf_template_body_edit.replace(/%body%/, text).replace(/%msg_id%/g, cur_msg_id.substr(4)); + text = text.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(cur_msg_div, text); + + // Replace the subject part. + cur_subject_div = document.getElementById('subject_' + cur_msg_id.substr(4)); + buff_subject = getInnerHTML(cur_subject_div); + + subject = XMLDoc.getElementsByTagName("subject")[0].childNodes[0].nodeValue; + subject = subject.replace(/\$/g,"{&dollarfix;$}"); + subject = smf_template_subject_edit.replace(/%subject%/, subject); + subject = subject.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(cur_subject_div, subject); + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + +function modify_cancel() +{ + // Roll back the HTML to its original state. + setInnerHTML(cur_msg_div, buff_message); + setInnerHTML(cur_subject_div, buff_subject); + + // No longer in edit mode, that's right. + in_edit_mode = 0; + + return false; +} + +function modify_save(cur_session_id) +{ + if (!in_edit_mode) + return true; + + var i, x = new Array(); + x[x.length] = 'subject=' + escape(textToEntities(document.forms.quickModForm['subject'].value.replace(/&#/g, "&#"))).replace(/\+/g, "%2B"); + x[x.length] = 'message=' + escape(textToEntities(document.forms.quickModForm['message'].value.replace(/&#/g, "&#"))).replace(/\+/g, "%2B"); + x[x.length] = 'topic=' + parseInt(document.forms.quickModForm.elements['topic'].value); + x[x.length] = 'msg=' + parseInt(document.forms.quickModForm.elements['msg'].value); + + if (typeof window.ajax_indicator == "function") + ajax_indicator(true); + + sendXMLDocument(smf_scripturl + "?action=jsmodify;topic=" + smf_topic + ";sesc=" + cur_session_id + ";xml", x.join("&"), modify_done); + + return false; +} + +function modify_done(XMLDoc) +{ + if (!XMLDoc) + { + modify_cancel(); + return; + } + + var message = XMLDoc.getElementsByTagName("smf")[0].getElementsByTagName("message")[0]; + var body = message.getElementsByTagName("body")[0]; + var error = message.getElementsByTagName("error")[0]; + + if (body) + { + // Show new body. + var bodyText = ""; + for (i = 0; i < body.childNodes.length; i++) + bodyText += body.childNodes[i].nodeValue; + + bodyText = bodyText.replace(/\$/g,"{&dollarfix;$}"); + bodyText = smf_template_body_normal.replace(/%body%/, bodyText); + bodyText = bodyText.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(cur_msg_div, bodyText); + buff_message = bodyText; + + // Show new subject. + var subject = message.getElementsByTagName("subject")[0]; + var subject_text = subject.childNodes[0].nodeValue; + subject_text = subject_text.replace(/\$/g,"{&dollarfix;$}"); + var subject_html = smf_template_subject_normal.replace(/%msg_id%/g, cur_msg_id.substr(4)).replace(/%subject%/, subject_text); + subject_html = subject_html.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(cur_subject_div, subject_html); + buff_subject = subject_html; + + // If this is the first message, also update the topic subject. + if (subject.getAttribute("is_first") == 1) + { + var subject_top = smf_template_top_subject.replace(/%subject%/, subject_text); + subject_top = subject_top.replace(/\{&dollarfix;\$\}/g,"$"); + setInnerHTML(document.getElementById("top_subject"), subject_top); + } + + // Show this message as "modified on x by y". + if (smf_show_modify) + { + var cur_modify_div = document.getElementById('modified_' + cur_msg_id.substr(4)); + setInnerHTML(cur_modify_div, message.getElementsByTagName("modified")[0].childNodes[0].nodeValue); + } + } + else if (error) + { + setInnerHTML(document.getElementById("error_box"), error.childNodes[0].nodeValue); + document.forms.quickModForm.message.style.border = error.getAttribute("in_body") == "1" ? "1px solid red" : ""; + document.forms.quickModForm.subject.style.border = error.getAttribute("in_subject") == "1" ? "1px solid red" : ""; + } + + if (typeof window.ajax_indicator == "function") + ajax_indicator(false); +} + +function showModifyButtons() +{ + var numImages = document.images.length; + for (var i = 0; i < numImages; i++) + if (document.images[i].id.substr(0, 14) == 'modify_button_') + document.images[i].style.display = ''; +} + +function expandThumb(thumbID) +{ + var img = document.getElementById('thumb_' + thumbID); + var link = document.getElementById('link_' + thumbID); + var tmp = img.src; + img.src = link.href; + link.href = tmp; + img.style.width = ''; + img.style.height = ''; + return false; +} + +function swapQuickReply() +{ + document.getElementById("quickReplyExpand").src = smf_images_url + "/" + (quickReplyCollapsed ? "collapse.gif" : "expand.gif"); + document.getElementById("quickReplyOptions").style.display = quickReplyCollapsed ? "" : "none"; + + quickReplyCollapsed = !quickReplyCollapsed; +} diff -r 72f59aa7e503 -r e3e11437ecea forum/Themes/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Themes/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,16 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/agreement.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/agreement.txt Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,13 @@ +You agree, through your use of this forum, that you will not post any material which is false, defamatory, inaccurate, abusive, vulgar, hateful, harassing, obscene, profane, sexually oriented, threatening, invasive of a person's privacy, adult material, or otherwise in violation of any International or United States Federal law. You also agree not to post any copyrighted material unless you own the copyright or you have written consent from the owner of the copyrighted material. Spam, flooding, advertisements, chain letters, pyramid schemes, and solicitations are also forbidden on this forum. + +Note that it is impossible for the staff or the owners of this forum to confirm the validity of posts. Please remember that we do not actively monitor the posted messages, and as such, are not responsible for the content contained within. We do not warrant the accuracy, completeness, or usefulness of any information presented. The posted messages express the views of the author, and not necessarily the views of this forum, its staff, its subsidiaries, or this forum's owner. Anyone who feels that a posted message is objectionable is encouraged to notify an administrator or moderator of this forum immediately. The staff and the owner of this forum reserve the right to remove objectionable content, within a reasonable time frame, if they determine that removal is necessary. This is a manual process, however, please realize that they may not be able to remove or edit particular messages immediately. This policy applies to member profile information as well. + +You remain solely responsible for the content of your posted messages. Furthermore, you agree to indemnify and hold harmless the owners of this forum, any related websites to this forum, its staff, and its subsidiaries. The owners of this forum also reserve the right to reveal your identity (or any other related information collected on this service) in the event of a formal complaint or legal action arising from any situation caused by your use of this forum. + +You have the ability, as you register, to choose your username. We advise that you keep the name appropriate. With this user account you are about to register, you agree to never give your password out to another person except an administrator, for your protection and for validity reasons. You also agree to NEVER use another person's account for any reason. We also HIGHLY recommend you use a complex and unique password for your account, to prevent account theft. + +After you register and login to this forum, you will be able to fill out a detailed profile. It is your responsibility to present clean and accurate information. Any information the forum owner or staff determines to be inaccurate or vulgar in nature will be removed, with or without prior notice. Appropriate sanctions may be applicable. + +Please note that with each post, your IP address is recorded, in the event that you need to be banned from this forum or your ISP contacted. This will only happen in the event of a major violation of this agreement. + +Also note that the software places a cookie, a text file containing bits of information (such as your username and password), in your browser's cache. This is ONLY used to keep you logged in/out. The software does not collect or send any other form of information to your computer. \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/attachments/.htaccess --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/attachments/.htaccess Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,7 @@ + + Order Deny,Allow + Deny from all + Allow from localhost + + +RemoveHandler .php .php3 .phtml .cgi .fcgi .pl .fpl .shtml \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/attachments/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/attachments/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,16 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Brad_Pitt.jpg Binary file forum/avatars/Actors/Brad_Pitt.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Bruce_Campbell.jpg Binary file forum/avatars/Actors/Bruce_Campbell.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Bruce_Willis.jpg Binary file forum/avatars/Actors/Bruce_Willis.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Cameron_Diaz.jpg Binary file forum/avatars/Actors/Cameron_Diaz.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Charisma_Carpenter.jpg Binary file forum/avatars/Actors/Charisma_Carpenter.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Christopher_Lambert.jpg Binary file forum/avatars/Actors/Christopher_Lambert.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Claudia_Schiffer.jpg Binary file forum/avatars/Actors/Claudia_Schiffer.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/David_Boreanaz.jpg Binary file forum/avatars/Actors/David_Boreanaz.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/David_Spade.jpg Binary file forum/avatars/Actors/David_Spade.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Denise_Richards.jpg Binary file forum/avatars/Actors/Denise_Richards.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Emilio_Estevez.jpg Binary file forum/avatars/Actors/Emilio_Estevez.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Eva_Habermann.jpg Binary file forum/avatars/Actors/Eva_Habermann.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Freddie_Prinze_Jr.jpg Binary file forum/avatars/Actors/Freddie_Prinze_Jr.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/Gwyneth_Paltrow.jpg Binary file forum/avatars/Actors/Gwyneth_Paltrow.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Actors/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/avatars/Actors/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Alanis_Morissette.jpg Binary file forum/avatars/Musicians/Alanis_Morissette.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Avril_Lavigne.jpg Binary file forum/avatars/Musicians/Avril_Lavigne.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Bob_Marley.jpg Binary file forum/avatars/Musicians/Bob_Marley.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Britney_Spears.jpg Binary file forum/avatars/Musicians/Britney_Spears.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Cardigans.jpg Binary file forum/avatars/Musicians/Cardigans.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Christina_Aguilera.jpg Binary file forum/avatars/Musicians/Christina_Aguilera.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Dido.jpg Binary file forum/avatars/Musicians/Dido.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Eminem.jpg Binary file forum/avatars/Musicians/Eminem.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Jewel.jpg Binary file forum/avatars/Musicians/Jewel.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Jon_Bon_Jovi.jpg Binary file forum/avatars/Musicians/Jon_Bon_Jovi.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Korn.jpg Binary file forum/avatars/Musicians/Korn.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Limp_Bizkit.jpg Binary file forum/avatars/Musicians/Limp_Bizkit.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Linkin_Park.jpg Binary file forum/avatars/Musicians/Linkin_Park.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Nirvana.jpg Binary file forum/avatars/Musicians/Nirvana.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/No_Doubt.jpg Binary file forum/avatars/Musicians/No_Doubt.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Queen.jpg Binary file forum/avatars/Musicians/Queen.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/Shakira.jpg Binary file forum/avatars/Musicians/Shakira.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/U2.jpg Binary file forum/avatars/Musicians/U2.jpg has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/Musicians/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/avatars/Musicians/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,9 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/blank.gif Binary file forum/avatars/blank.gif has changed diff -r 72f59aa7e503 -r e3e11437ecea forum/avatars/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/avatars/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,16 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/cache/.htaccess --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/cache/.htaccess Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,4 @@ + + Order Deny,Allow + Deny from all + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/cache/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/cache/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,16 @@ + \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/changelog.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/changelog.txt Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,5104 @@ +Changes go downward, months go upward. Please scroll to the bottom for a legend. +Changelog started January 1, 2004; Currently at $Revision: 1.2111.2.470 $. + +SMF 1.1.4 ??? +================================================================================ +September 2007 +-------------------------------------------------------------------------------- + ! Some Mambo installations don't handle objects the same as others. (Mambo bridge/smf.php) + ! Set up the bridge config panel to handle input from third party tabs. (Mambo bridge/admin.smf.php) + + Use the integrate_verify_user hook for authentication in Xoops. (Xoops bridge/index.php) + ! Some input values didn't get escaped properly - reported by Michael Brooks. (ManageMembergroups.php, PersonalMessages.php, Post.php, Profile.php, QueryString.php, Search.php, Subs-Boards.php, Subs.php) + ! Some attachment filenames didn't get escaped properly. (Display.php, ManageAttachments.php) + ! Function for fixing list and table tags wasn't working as expected. (Subs-Post.php) + ! Wireless devices running Internet Explorer don't like refresh header. (Subs.php) + +August 2007 +-------------------------------------------------------------------------------- + ! Make sure it's hard to force an error with a silly URL. (Post.php, Subs-Post.php) + ! If using an old version of host which doesn't support the -W command don't call it. (Subs.php) + ! Allow users to log out in maintenance mode. (index.php) + ! Change the email date format to use -0000 to better comply with the RFC. (Subs-Post.php) + ! Check GET and POST instead of REQUEST for numeric keys. (QueryString.php) + ! Fixed editing grace period inconsistancies within the post script. (Post.php) + + +July 2007 +-------------------------------------------------------------------------------- + ! Inline modification of a board which had moderators was not working correctly. (Post.php) + ! The ComposeMailing flag wasn't always being set right when sending out news letters. (ManageNews.php) + ! Take out the inline style of the login module so that admins can use CSS to format (Mambo bridge/mod_smf_login.php) + ! Some non-SEF URLs were not generated with entities (Mambo bridge/smf.php) + ! Stop CDATA blocks breaking preview/quick modify. (Post.php) + + +SMF 1.1.3 ??? +================================================================================ +July 2007 +-------------------------------------------------------------------------------- + ! Mambo bridge was hanging on login with bridge login module. (Mambo bridge/mod_smf_login.php) + ! Missing global in bridge integration arrays. (Mambo bridge/smf_integration_arrays.php, smf.php) + ! Mambo doesn't pass the password check variable on to mambots. (Mambo bridge/SMF_register.php) + + +June 2007 +-------------------------------------------------------------------------------- + ! Reporting a personal message needed some more sanitation. (PersonalMessage.php) + ! Sending newsletters could cause a timeout on large boards. (ManageNews.php) + ! Session fixation could in rare cases be applied - reported by David Vieira-Kurz. (Subs-Auth.php) + ! Prevent recursive functions causing a stack overflow in PHP. (QueryString.php) + * Quick edit wasn't updating the subject correctly if it was the first message. (xml_topic.js) + ! MySQL's utf8 collation mapped some characters as equal causing a database insert on posting a topic to go wrong in rare cases. (Subs.php) + ! The HTML tag wasn't being properly escaped in the signature. (Subs-Post.php) + ! Ensure locked topics aren't being editted by those without permission. (Post.php, xml_topic.js) + ! Ensure bday3 is also set when checking birthdates. (Profile.php) + ! Cookies were in some cases not stored on localhost. (Subs-Auth.php) + + +May 2007 +-------------------------------------------------------------------------------- + ! ManageBans.php is required for changing ban status. (Mambo bridge/SMF_user_ban.php, SMF_user_unban.php) + + Changed Mambo bridge to use new configuration object instead of $mosConfig variables. (Mambo bridge/several files) + ! The login authenticator bot in Mambo was not logging in users. (Mambo bridge/SMF_login.php) + ! Make sure the bridge doesn't rewrite the password in case of reserved names. (Mambo bridge/SMF_Register.php) + + Have the bridge rewrite XML feeds to SEF URLs. (Mambo/Joomla bridges/smf.php) + ! Update bridge version info. (Mambo/Joomla bridge/several files) + + Added integrate_change_member_data, integrate_reset_pass, and integrate_delete_member hooks to Xoops bridge. (Xoops bridge/index.php) + ! Updated Xoops bridge version. (Xoops bridge/xoops_version.php) + + Added ability for admin to set specific URLs for login and logout redirection in Joomla bridge. (Mambo/Joomla bridge/several files) + + Added Users Extended registration option to Mambo/Joomla bridge. (Mambo/Joomla bridge/several files) + ! Cleaned up code that creates registration rewrites. (Mambo/Joomla bridge/smf.php, mod_smf_login.php, smf_integration_arrays.php) + + +April 2007 +-------------------------------------------------------------------------------- + ! Allow the brd search paramter to be used in the url as a non array. (Search.php) + ! mktree could potentially get itself into a loop. (Subs-Package.php) + ! Attempt to stop loops on BBC parsing. (Subs.php) + ! Sanitize input from the Joomla bridge registration, as the javascript check doesn't necessarily take care of it all. (Mambo/Joomla bridge/smf_registration.php) + + Added AEC compatibility to Joomla bridge. (several files) + ! Some HTML header information was missing from Joomla headers. (Mambo/Joomla bridges/smf.php, SMF_header_include.php) + ! Fixed an undefined constant in e107 bridge language files. (e107 bridge/admin_smf_config.php, English.php, French.php) + + Added mambots for several Mambo integration events. (Mambo 4.6 bridge/several files) + ! Make sure the Mambo admin panel can populate the group arrays (Mambo 4.6 bridge/admin.smf.php) + + +March 2007 +-------------------------------------------------------------------------------- + ! Joomla bridge was removing some semi-colons in URLs instead of converting them to SEF. (Joomla bridge/smf.php) + ! Made bridged outgoing emails smarter when converting URLs. (Mambo/Joomla bridges/smf.php) + ! Bridge wasn't using UTF-8 language files appropriately in certain circumstances (Joomla bridge/mod_smf_login.php, smf.php) + ! Mambo 4.6 sometimes loads arrays as objects, invalidating output. (Mambo 4.6 bridge/admin.smf.php) + ! Updated bridge version information (Joomla bridge/smf.xml) + ! PHP 5.2.0 breaks the package manager. (Subs-Package.php) + ! Searching for the end position in the package manager could fail. (Subs-Package.php) + + +SMF 1.1.2 10 February 2007 +================================================================================ +February 2007 +-------------------------------------------------------------------------------- + ! Fixed a remaining, if not extremely difficult to exploit, issue with downloads on IE - reported by Jessica Hope. (Display.php) + ! Fixed to/bcc fields in Personal Messages not being htmlspecialchar'd - reported by Aria-Security team. (PersonalMessage.php) + ! People with the manage_permissions could maybe abuse it. (ManagePermissions.php, ManageMembergroups.php) + ! The path was sometimes wrong when installing a package. (Subs-Package.php) + ! Display name can now be no longer than 60 characters as people were taking it too far! (Profile.php, Profile template) + & New error string for the above. (Errors language files) + ! Quick edit would lose the old modified time if you made no changes. (Post.php) + ! Password reminder was not respecting password strength. (Reminder.php) + ! Retain all current theme settings when editing a different theme. (Themes.php) + ! Try to ensure ID_MSG_LAST_VISIT is always set. (Load.php) + ! Always send an alternative plain us-ascii text version of the body along with each mail for basic support of older mail clients. (Subs-Post.php) + ! Convert non-ascii characters to entities for mails sent to yahoo addresses to assure characters are being displayed properly in yahoo's client. (Subs-Post.php) + ! People upgrading from non-sha1 supporting PHP to new sha1 supporting PHP were having problems. (Subs-Compat.php, LogInOut.php) + & Fixed minor typos (Manual language files, Login language files) + + +January 2007 +-------------------------------------------------------------------------------- + ! An error sometimes got generated around visual verification. (Register.php) + ! Fixed undefined index error in search. (Search.php) + ! Fixed error when deleting boards in some instances. (ManageBoards.php) + ! Merging a topic wouldn't always work if using $_GET. (SplitTopics.php) + * Javascript wasn't working right when adding a poll. (Poll template) + & Updated copyright dates. (index language files) + + Added a setting for toggling the complexity of the visual verification image used on registration (etc). (ManageRegistration.php, Register.php, Subs-Graphics.php, Register template) + & Added language entries for above. (Login language files) + ! Quick modifying HTML posts would mess up linebreaks. (Post.php) + ! The package manager wasn't uninstalling "end" searches correctly. (Subs-Package.php) + ! Package manager was wrongly labelling redirects. (Packages.php) + ! Inside of html tags convert to a real newline. (Subs.php) + ! Resend activation after email change did not work with Joomla bridge (Joomla bridge/smf.php) + ! Fixed language synch for login module on non-forum pages (Joomla bridge/mod_smf_login.php) + ! It was not possible to remove a partial custom index. (ManageSearch.php) + ! Fixed a bug in PCRE causing regular expression compilation failures in UTF-8 mode. (Load.php, Search.php, Subs.php, Subs-Members.php, Subs-Post.php) + ! If an email had a quote in it sending a newsletter would fail. (ManageNews.php) + + +December 2006 +-------------------------------------------------------------------------------- + ! Wrong capitalization on a couple of function calls. (PersonalMessage.php, Profile.php) + ! Some bridge registration functions were still using deprecated constants. (Mambo 4.6 bridge/smf_registration.php) + ! Users should never be redirected by to registration and activation pages on login (Mambo/Joomla bridge/smf.php) + ! Do not allow 0, 0px, or 0pt to be used in size tags. (Subs.php) + + Added e107 bridge. (Several files) + ! Fixed a bug with the to and bcc fields for PMs that could cause some problems when displaying an invalid name. (PersonalMessage.php) + + Added delete member and change member data integration hooks to e107 bridge. (e107 bridge/smf.php) + + Added an "Upgrade" tab to the Mambo/Joomla bridge config page. (several files) + + +SMF 1.1.1 December 17, 2006 +================================================================================ +December 2006 +-------------------------------------------------------------------------------- + ! Fixed multi-language support for Mambo/Joomla bridge login module on non-forum pages. (Mambo/Joomla bridge/mod_smf_login.php) + + Added register mambot to Mambo 4.6 bridge so that users who register in Mambo are auto-created in SMF. (Mambo 4.6 bridge/SMF_register.php, SMF_register.xml) + ! Fixed bridge registration so that it uses Mambo 4.6's new language method. (Mambo 4.6 bridge/smf_registration.html.php) + ! The regular expression used for parsing '/me'-lines didn't work properly in older PHP versions. (Subs-Post.php) + ! Fixed usage of array before it was declared (Load.php) + ! Mambo/Joomla bridge config should go to main admin page if "Cancel" is clicked (Mambo/Joomla bridge/admin.smf.php) + ! Fixed a missing global (Mambo 4.6 bridge/smf.php) + + Changed the SEF handling in the bridge for use with third party Joomla SEF components (Mambo/Joomla bridge/smf.php) + + Added Kindred's enhancements to the bridged whos online (Mambo/Joomla bridge/smf.php, smf_integration_arrays.php) + ! Deleting a member in SMF deleted in Joomla, but not in Joomla's ACL (Mambo/Joomla bridge/smf.php) + ! Bridge login module didn't handle language variables correctly. (Mambo/Joomla bridge/mod_smf_login.php) + ! Some dates in the installation script were not MySQL STRICT compatible. (install-1.1.sql) + ! Log the ban check IP in messages. (Subs-Post.php) + ! It was possible for files to be sent with the wrong mime type - reported by Jessica Hope and rotwang. (Display.php) + * Search template looked a little odd on IE. (Search template) + * In prune boards the checkbox for stickies would toggle with the board selection. (Admin template) + ! Send annoucements in slightly smaller chunks. (Post.php) + ! Make it impossible to make guests too much like administrators. (ManagePermissions.php) + ! Aposthophes in smiley directories would cause a SQL error. (ManageSmileys.php) + ! Fixed labelling personal messages not being restricted to the current member. (PersonalMessage.php) + ! In rare cases search didn't return any results, while it should have. (Search.php) + + +SMF 1.1 December 02, 2006 +================================================================================ +December 2006 +-------------------------------------------------------------------------------- + ! Optimized a query in recent posts. (Recent.php) + ! Image verification sound wouldn't always work in Firefox. (Subs-Sound.php) + ! Caching user permissions was not always respecting deny permissions. (Load.php) + ! Put in a fix for DST issues cropping up due to spanned events. (Calendar.php) + ! PM labels were not being properly truncated. (PersonalMessage.php) + * Don't allow people to report their own Personal Message. (PersonalMessage template) + + +November 2006 +-------------------------------------------------------------------------------- + ! Previewing a post wasn't respecting the disable smiley option. (Post template) + ! Changed the transfer-encoding method to base64 as it appears to be better supported by webmail clients. (Subs-Post.php) + ! Fixed the spelling checker not work properly for non-western and UTF character sets. (spellcheck.js, Subs-Post.php) + ! The JavaScript function php_strtolower wasn't working properly on all servers. (sha1.js) + ! Made the recognition of the character set for the UTF8 conversion function case insensitive. (Admin.php) + * The inline subject editor on the message index now allows 'enter' as save-key. (MessageIndex template) + * The inline subject editor on the message index in some cases cancelled on selecting the input box. (xml_board.js, MessageIndex template) + ! A missing email address in a package could cause two undefined indexes. (PackageGet.php, Packages template) + ! Redirect to the theme list after deleting a theme. (Themes.php) + ! Applied a different method to trim spaces for UTF-8 strings as the previous one didn't appear to be working on all servers. (Load.php) + * Firefox doesn't display horizontal scrollbars when needed in code tags, so use some javascript to make it happen. (index templates) + * Added a class to the containing div for the help window and for the audio verification. (Help and Register template) + * Cleaned up the HTML a little for the audio verification. (Register template) + ! Fixed a bug causing the age weight of a search to be zero. (Search.php) + * The spell check function for inline editing didn't work properly in some cases. (Display template) + ! Some columns containing message ID's were not formated as unsigned integers. (Search.php, ManageSearch.php, upgrade_1-1.sql, install_1-1.sql) + ! Old PHP-versions didn't compile some regular expressions properly in UTF-8 mode. (Load.php, Search.php, Subs-Members.php, Subs-Post.php, Subs.php) + ! The recent posts on the board index could use a different timezone in rare cases. (BoardIndex.php) + ! Make sure fulltext indexes are dropped before they are created, in case they weren't detected properly. (ManageSearch.php) + ! The avatar settings weren't getting reshown if you uploaded an invalid avatar. (Profile.php) + ! Searching for Personal Messages always resulted in no more than one page. (PersonalMessages.php) + ! PM labels selected for searching Personal messages were not carried over in consecutive pages. (PersonalMessages.php) + ! Selecting multiple (but not all) labels for searching resulted in less results than expected. (PersonalMessages.php) + * Clicking 'Advanced search' on searching Personal Messages redirected to the forum search. (PersonalMessages template) + * The 'Advanced search' link in both PM and forum search didn't carry over the search term properly if it contained UTF-8 characters. (PersonalMessages template, Search template) + * The id attribute used for labels had an invalid format in some templates. (Admin template, ManageNews template, PersonalMessages template, Poll template, Post template) + * The PM folder HTML form was improperly referred to by JavaScript. (PersonalMessages template) + ! Fixed several package manager bugs. (Packages.php, Subs-Package.php) + & Added a language string for the above change. (Packages language files) + ! Fixed the htmltrim function trimming too many characters. (Load.php) + ! Prevent prefetching bots to clutter the error log when trying to access non-public boards. (Load.php) + ! The quick login setting was reversed on the board index. (BoardIndex.php) + ! The move-file directive used in package-info.xml was not working properly. (Subs-Package.php) + ! Using the root path as FTP path for executing modifications in some cases returned a 'Path could not be found' error. (Subs-Package.php) + ! Prevent search engine crawlers from indexing duplicate pages. (Display.php, index template) + ! Selecting members for banning from a popup wasn't working properly. (Subs-Auth.php, ManageBans template) + ! Small typo in Admin.php could throw an error during table optimization. (Admin.php) + * Trying to set a topic to have no subject was causing a javascript error. (xml_board.js) + ! An error could be triggered in rare cases on installing modifications. (Subs-Package.php) + * Some pages of the help section didn't validate. (Help template) + & Changed a language string for the above change. (Manual language file) + + +October 2006 +-------------------------------------------------------------------------------- + ! Fixed the page title on the user account deletion confirmation page. (Profile.php) + ! The modSettings variable wasn't in the global list for the reset password function, making the integration call worthless. (Subs-Auth.php) + ! The "Find Members" box loses track of whether it's buddies only on pagination. (Subs-Auth.php) + ! Fixed various times where some necessary globals were missing. (ManageBoards.php, ManageSmileys.php, Reminder.php, RemoveTopic.php) + * Only show dividers between BBC buttons if there are some within the dividers. (Post templates) + ! Searching for member details by date was often a fruitless experience. (ManageMembers.php) + * Try to avoid the "register button won't work" scenario on register. (Register template) + ! When fixing BBC tags make sure to always assign a closing tag. (Subs-Post.php) + ! Make sure that Mambo/Joomla bridges do not use the $database object for accessing the SMF database. (several files) + ! When saving the config, Joomla was adding in new rows when it should have been updating existing rows. (Mambo/Joomla bridge/admin.smf.php) + & Fixed small typos in admin panel. (Admin.english.php, Themes.english.php) + * Linked membergroup name in ManageMembergroups to the list of members. (ManageMembergroups.php, ManageMembergroups.template) + + Added browser detection for Firefox 1 and Firefox 2 specifically. (Load.php) + ! CAPTCHA now uses truetype fonts wherever possible to give slightly clearer images. (Subs-Graphics.php, several fonts) + ! Fixed bug in showLetterImage that caused the function to often fail. (Subs-Graphics.php) + ! Changed Xoops login integration hook to use Xoops native authorization objects. (Xoops bridge/index.php) + ! Some search parameters weren't being sanatized when accessing a previous search, leading to a potential XSS vulnerability. (PersonalMessage.php, Search.php) + ! Minor typo in PlushSearch2 prevented "user" search parameter from being remembered when trying to access a previous search. (Search.php) + ! Encrypted login failed for some character sets when the username or password contained non-western characters. (sha1.js, script.js, Load.php, index template) + + +September 2006 +-------------------------------------------------------------------------------- + + Added option for showing visual verification on Personal Message page. (PersonalMessage.php, PersonalMessage template, upgrade_1-1.sql, install_1-1.sql, ModSettings.php) + + Added option to limit number of personal messages to be sent in one go. (PersonalMessage.php, upgrade_1-1.sql, install_1-1.sql, ModSettings.php) + & Added language entries for above changes. (Help, ModSettings, PersonalMessage language files) + ! Updated versions numbers to 1.1 (Final). (Several Files) + ! The hotmail fix didn't work properly. (Subs-Post.php) + ! The URL for deleting the install and upgrade files was using an invalid request variable. (upgrade.php, install.php) + ! When checking how many personal messages a user has sent the last hour - count the recipients not the messages. (PersonalMessage.php) + ! If the user has some special characters in their password, check to see if they still have an old hash when logging in. (LogInOut.php) + ! Bridged attached images wouldn't open in a new window. (Mambo/Joomla bridge/smf.php) + ! Some types of outgoing emails were not rewritten properly. (Mambo/Joomla bridge/smf.php) + ! Javascript doesn't like HTML entities. (Mambo/Joomla bridge/smf.php) + ! Fixed a problem when there are no message icons. (Post.php) + + Add login bot to Mambo 4.6 bridge (Mambo 4.6 bridge/SMF_login.php, SMF_login.xml) + ! Fixed a problem with the cur_cat parameter when adding a new board. (ManageBoards.php) + ! The PM visual verification was not respecting the settings. (PersonalMessage.php) + + Add logout functionality to Mambo 4.6 login bot. (Mambo 4.6 bridge/SMF_login.php) + ! Prevent users without access to any boards from causing a query error when using the unread posts search. (Recent.php) + * In the default theme, don't show the mark all as read button if the user can not see any boards. (BoardIndex template) + ! Previewing a PM should no longer cause an undefined variable error. (PersonalMessage.php) + & Fixed typos in some admin language files (Admin language files) + ! Allow the use of uppercase letters when specifying the color for the glow and shadow tags. (Subs.php) + ! Make the php version of SSI examples use the forum character set. (ssi_examples.php) + ! If the user deletes the recycling board, turn off the setting to prevent problems. (Subs-Boards.php) + ! Automatically include the know IPs when tracking a user and looking for other members with the same IP. (Profile.php) + * The IPs used in error messages was checking the wrong context value. (Profile template) + ! Make sure that there are no HTML entities in bridged unwrapped javascript. (Mambo/Joomla bridge/smf.php) + ! Add some missing languages to the synch array. (Mambo/Joomla bridge/smf_integration_arrays.php) + ! Try every possibility for language synching to avoid template errors. (Mambo/Joomla bridge/smf.php) + ! Missing globals in bridge resend password. (Mambo/Joomla bridge/smf_registration.php) + ! Try to change some of the settings in case users don't do it themselves. (Mambo/Joomla bridge/smf.php) + ! Get rid of the extra slash When going from the Xoops admin panel to the SMF admin panel. (Xoops bridge/admin/index.php) + ! Recurse changes from Mambo/Joomla bridge to Mambo 4.6 bridge, and remove Joomla-specific code (several files) + ! When adding a new message icon, make sure to escape the titles of the current icons. (ManageSmileys.php) + ! Don't log password strength errors into the forum error log. (Subs-Members.php) + ! The php tag wasn't acting like a proper block level tag. (Subs.php) + ! Make sure bridge registration is compatible with Mambo 4.6. (Mambo 4.6 bridge/smf_registration.php) + ! Bridge registration component doesn't need an installation script for Mambo 4.6. (Mambo 4.6 bridge/install.smf_registration.php) + ! Search results could in some cases get a relevance above 100%. (Search.php) + ! Check for additional problematic values in the starting value for the memberlist. (Memberlist.php) + ! When synching users from Mambo/Joomla to SMF, check to see if the admin wants usernames or real names as display in SMF. (Mambo/Joomla bridge/admin.smf.php) + ! SEF URLs work differently in Mambo 4.6 (Mambo 4.6 bridge/smf.php) + ! When posting a new calendar event automatically select the default board. (Calendar.php) + * When posting a new calendar event carry over the board selection to the post page. (Calendar template) + ! Correctly close url tags that are changed to iurl. (Subs-Post.php) + ! Change all URLs in RSS feeds to bridged URLs. (Mambo/Joomla bridge/smf.php) + ! Not all special characters were converted correctly in bridged outgoing emails (Mambo/Joomla bridge/smf.php) + ! Upgrade was sometimes losing changes to the Settings.php file. (upgrade.php) + ! Ensure adding a member to a group is case insensitive. (ManageMembergroups.php) + ! Ensure the log_online timestamp is backed up correctly. (DumpDatabase.php) + ! Added a page title to the page that requests ftp details. (Subs-Package.php) + ! Search wasn't always including the subject matches properly. (Search.php) + ! Don't include administrators in the permission reports, they get to do everything anyways. (Reports.php) + ! Show local moderators as being able to access all boards since in reality a moderator can always access their board. (Reports.php) + ! If a guest tries to post with a reserved name, handle it as a post error instead of causing a fatal error. (Post.php, Subs-Members.php) + ! Some configurable number format options were not showing the decimal separator. (Load.php) + ! The 'most popular boards by activity' statistics were not always showing the proper boards. (Profile.php) + ! Check the session when downloading a package. (PackageGet.php) + ! Show the search section in the linktree in the personal message area. (PersonalMessage.php) + ! Make the package options title like the other package page titles. (Packages.php) + ! Check to make sure the limit is a positive value when showing a feed. (News.php) + +August 2006 +-------------------------------------------------------------------------------- + * Change the random code before getting a new visual verification image. (Register template) + ! Keep Joomla from eating Personal Message anchors. (PersonalMessage.php, PersonalMessage.template.php) + ! Some bridged emails have \n\r replaced with underscores. (Mambo/Joomla bridge/smf.php) + ! Let Joomla clean its own variables. (Mambo/Joomla bridge/SMF_header_inlcude.php) + * Fixed a javascript error occuring on posting in the saveEntities() function. (Post template) + ! Tweaked the CAPTCHA image to be a little less obstructed by the dots. (Subs-Graphics.php) + ! Don't allow people to keep guessing at the same image. (Register.php) + ! Updated iGamingCMS bridge for latest version of iGaming. (several files) + ! Get the Mambo mainframe instance before trying to assign values to it. (Mambo 4.6 bridge/smf.php, SMF_header_include.php) + + Added installation instructions for iGaming bridge. (iGamingCMS bridge/readme.txt) + & Added Hungarian language files for Mambo and Joomla bridges. (several files) + ! Uninstalling XML-packages in which the original text string contained a dollar sign wasn't working properly. (Subs-Packages.php) + ! Fixed boardurl without a hostname showing a notice on session check. (Security.php) + ! Redirect to the admin panel home page if user cancels configuration (Mambo/Joomla bridge/admin.smf.php) + ! Restructure Mambo 4.6 bridge for new Universal multi-plugin installation (several files) + ! Using symbols such as an apostrophe in the password should now hash it correctly allowing login from the front page. (Profile.php, LogInOut.php) + * Submitting a quick moderation form could result in a Javascript error. (MessageIndex template) + ! Multiple consecutive /me statements didn't get parsed properly. (Subs-Post.php) + ! Prevent strict notices appearing in PHP 5.1 caused by not setting a default timezone. (Load.php, install.php, upgrade_1-1.sql) + * The auto detect time offset function was returning the wrong sign. (Profile template) + ! No longer block requests with cookies containing numeric keys. (QueryString.php) + ! Changed the column type to allow for a larger buddy list. (upgrade_1-1.sql, install_1-1.sql) + ! Fixed the line break being used in non-windows systems in mails sent by sendmail. (Load.php, Subs-Post.php) + + +SMF 1.1 RC3 August 21, 2006 +================================================================================ +August 2006 +-------------------------------------------------------------------------------- + ! Fixed an undefined index notice in the 1.0 upgrade script. (upgrade_1-0.sql) + ! Use a modified key for APC to counteract an APC bug retrieving the wrong keys. (Load.php) + ! The linked events in the topic display was using the same context variable as SSI's ssi_todaysEvents(). (Display.php, Display template) + + Added option for synching languages to the Mambo/Joomla bridge. (Mambo/Joomla bridge/install.smf.php, admin.smf.php, smf.php) + ! Searching inside topics sorted by topic size didn't yield results. (Search.php) + ! Adjusted the relevancy rating for searching within topics, making the theoretical maximum rating 100%. (Search.php) + ! Make sure CAPTCHA images work in Mambo/Joomla as well. (Mambo/Joomla bridge/smf.php) + ! Fix (fast) quoting/modifying messages that no longer exist showing an 'undefined index' notice. (Post.php) + ! Messages could be sent with a body containing only bulletin board code. (Post.php) + ! The check on the maximum length of a Personal Message didn't respect UTF-8 characters. (PersonalMessages.php) + ! The installer didn't show the error properly in case it was unable to disable mod_security. (install.php) + * Generated reports were still showing a button instead of a tab for the print layout link. (Reports template) + ! The package manager wasn't showing the last two packages from the server's packages.xml correctly. (PackageGet.php) + ! Use the member ID instead of the topic ID to search through the log_topics table for the 'Find and repair any errors' function. (RepairBoards.php) + ! Let the cache (if enabled) help remembering the last visit time when viewing 'unread posts since last visit'. (Load.php) + ! The entityconvert function was declared twice. (Subs-Post.php) + ! The Personal Message search function did not respect the deletion flag. (PersonalMessages.php) + ! Make sure the user themselves are shown in the list of 'who's viewing this board/topic'. (Display.php, MessageIndex.php) + ! Finalize Joom!Fish integration (Mambo/Joomla bridge/admin.smf.php, smf.php) + ! To keep it consistent with the other admin functions, check the permissions for manage boards prior to calling the admin index. (ManageBoards.php) + ! Fixed notifications not being property reset when viewing a topic in some cases. (Display.php) + ! Maintaining attachments was not showing the "Complete" message - ever. (ManageAttachments.php) + * Added the ability to request a new visual verification image and to link directly to the verification sound file. (Register template, Login language files) + * Firefox doesn't play well with the move BBC tag and AJAX previewing, so make it use regular preview if the move tag is present. (Post template) + ! Store a different version of the users IP to get around highly unlikely spoofing issues. (upgrade_1-1.sql, install_1-1.sql, install.php, Subs-Members.php, Security.php, QueryString.php, Load.php) + ! Don't allow users to get a new visual verification code simply by refreshing. (Register.php) + * Changed several definitons into shorter versions. (style.css) + ! Don't send the newsletter to members who are unable to turn off their notification due to a banning. (ManageNews.php) + * The auto detect time offset script should now work regardless of day boundries. (Profile template) + * Help.template changed to better use current theme colors. Updates on PM help section. (Help.template, help.css, languages/Manual.english, languages/Manual.dutch, languages/Manual.german, languages/Manual.spanish) + ! Added a caching mechanism to the memberlist to prevent a query that relies heavily on filesort to put too much pressure on the database. (Memberlist.php, Subs.php, Profile.php) + * The WAP2 template didn't allow expanding of categories while the I-mode template didn't allow both collapsing and expanding of categories. (Wireless template) + ! Converted all occurences of the ucwords function to an internationalized version. (Load.php, ManageServer.php, Profile.php, Register.php, Subs-Charset.php, Subs.php, Packages template) + ! Show languages without the -utf8 suffix. (Profile.php, Register.php, Load.php) + ! Prevent some member/board/topic statistics to become negative. (RemoveTopic, MoveTopic.php, SplitTopics.php) + ! The ;topicseen paramater didn't always work properly. (Display.php) + ! Make sure the upgrade script respects the current database character set setting. (upgrade.php) + ! Fixed undefined index error in Register template. (Register template) + * Tweaked the way the visual verification box looks on the register form. (Register.php) + ! The second IP wasn't properly checked for IP bans. (Load.php, Security.php, QueryString.php) + ! The internationalized version of the ucwords function didn't work properly. (Load.php) + ! Some emails were getting sent twice. (Subs-Post.php) + ! Fixed undefined index in RepairBoards. (RepairBoards.php) + ! Fixed bridged admin user creation (Mambo/Joomla Bridge/smf.php) + ! Language array was backwards (Mambo/Joomla Bridge/smf.php) + ! Posting just an image will again count as posting something. (Post.php) + ! Fixed an undefined index occuring in some cases when sending emails. (Subs-Post.php) + * The sticky topic weight percentage wasn't updated properly. (ManageSearch template) + ! Split utf8_strto into utf8_strtoupper and utf8_strtolower for speed optimization. (Subs-Charset.php, Load.php, Subs.php) + ! The php BBC tag was incorrectly setting up the validate function when the tag was disabled. (Subs.php) + + Added a "who's online" integration hook (Who.php) + + Split off the large integration arrays in the Mambo/Joomla bridge into their own file (Mambo/Joomla bridge/smf.php, smf_integration_arrays.php, smf.xml) + + Added "who's online" hook functionality to Mambo/Joomla bridge (Mambo/Joomla bridge/smf.php, smf_integration_arrays.php) + ! Fixed unset not working properly in all cases due to a vulnerability in PHP. (QueryString.php, Profile.php, Post.php, ManageNews.php) + ! Make sure that variable names in Joomla header bot don't interfere with non-com_frontpage front pages (Mambo/Joomla bridge/SMF_Header_include.php) + + Added bridge for Mambo 4.6 (several files) + ! Don't allow the admin to register a member with the same name as another member. (ManageRegistration.php, Subs-Members.php) + + +July 2006 +-------------------------------------------------------------------------------- + ! Block page requests attempting to modify the $GLOBALS variable. (QueryString.php) + ! Changed some references to the $GLOBALS variable into global scope variables, since the $GLOBALS variable can be rather unpredictable. (several files) + ! Fixed some bugs occurring on the 'Show Posts' section of the Profile screen. (Profile.php) + ! Added an AJAX in progress indicator (script.js, index.template, index language files, style.css) + ! The AJAX indicator will now be used when quoting, quick editting, changing the topic's subject on the message index, and when expanding stats (xml_board.js, xml_topic.js, Stats.template) + ! Added the AJAX indicator to the Babylon and Classic themes (index.template, style.css) + + Added "Guest" as a group for Group Synch option (Mambo/Joomla bridge/admin.smf.php) + ! Function mambo_smf_exit was broken (Mambo/Joomla bridge/smf.php) + ! Added a UTF-8 version of strtolower in order to make search indexing case insensitive. (Load.php, Subs-Charset.php) + ! Fixed an bug causing the repair forum function to generate a lot of notices. (RepairBoards.php) + ! Visual verification didn't work properly when guest access was disabled. (index.php) + ! Fixed some bugs that were causing entities to be shown incorrectly in (nofication) mails. (Subs-Post.php) + ! HTML-entities in a non-UTF-8 environment were not properly parsed in Personal Messages. (Subs-Post.php) + ! Make sure the response prefix is always taken from the forum's default language. (MoveTopics.php, Post.php, PersonalMessages.php, SplitTopics.php, Display.php, Display template) + ! Update version info for Mambo/Joomla bridge. (several files) + ! Only install the header bot in Joomla. (install.smf.php) + ! Make sure that the login conforms to mysqli. (smf.php) + ! Added an error message to the installer in case UTF-8 is not supported by the current MySQL version. (install.php, install language files) + ! No longer do an explicit count on columns that cannot be NULL as MySQL doesn't optimize this properly. (several files) + ! Include the table definition in the temporary table creation to assure the proper formatting of the columns. (Recent.php) + ! Some regular expressions were causing internal code overflow errors when combined with the UTF8 modifier. (Load.php, Subs-Members.php, Subs-Post.php, Subs.php) + ! A few input controls on the install page were wrongly labeled. (install.php) + ! Make sure all time bound processes don't modify the $time_start variable. (ManageAttachments.php, Admin.php) + ! The subject link wasn't adjusted properly after quick editing a message. (xml_topic.js) + ! Don't count requesting an XML/RSS-feed as being online (in most cases these kind of pages are retrieved automatically). (index.php) + ! Make sure $context['utf8'] is set when parse_bbc() is called. (Subs.php) + ! Fixed htmlspecialchars__recursive() and htmltrim__recursive() not working without $func being defined. (QueryString.php) + ! Updated credits. (Admin.php) + ! A few characters were not encoded in mimespecialchars() causing improper headers in rare cases. (Subs-Post.php) + ! Fixed undefined indexes on including certain language files in the installer. (install.php) + ! Return to the previous step if either the email address or the username is invalid. (install.php) + * Added an indicator for new child board posts in the board and message index to the Imode & WAP2 template. (Wireless template) + ! Updated the version for style.css and script.js so browsers will invalidate them upon update. (index.template) + ! The response prefix wasn't loaded into the context variable on posting messages. (Post.php) + ! Cleaned up Mambo/Joomla group synch queries, and display group name instead of group id. (Mambo/Joomla Bridge/admin.smf.php) + + Added support for language components like Joom!fish. (Mambo/Joomla Bridge/smf.php) + ! Deny direct access to the bridge file. (Mambo/Joomla Bridge/smf.php) + ! Prevent search from using regular expression matchin if HTML entities are being used in the query. (Search.php) + ! Ignore some characters in search queries that might prevent the search index from working properly. (Search.php) + ! Don't use regular expressions in the WHERE part of the query when using BOOLEAN MODE in fulltext searches. (Search.php) + ! Use the phrase search feature for BOOLEAN MODE fulltext search. (Search.php) + ! Changed the behavior of the 'force index' option from searching only in combination with an index to searching exclusively using an index. (Search.php) + ! Fixed excluded words in some cases not being checked for in the subject and the body. (Search.php) + ! Make sure the matching body lines in the search results respect UTF-8 character boundaries. (Search.php) + ! Search didn't always return results if only the subject matched. (Search.php) + ! Fix outgoing email integration function for Mambo/Joomla and new SMF UTF-8 requirements (Mambo/Joomla Bridge/smf.php) + ! Remove VirtueMart compatibility code -- fixed by VirtueMart (Mambo/Joomla Bridge/mod_smf_login.php) + ! Added a (hidden) option 'search_simple_fulltext' to search the forum using a single non-binary fulltext query. (Search.php, Search templates) + ! UTF-8 and entity conversion in some cases corrupted serialized strings stored in the database. (Admin.php, Subs-Charset.php) + ! Searching messages with the 'topic subjects only' option selected and specifying a user only yielded a result if the user was the topic starter. (Search.php) + * Updated the login and password reminder screens in the manual to be in sync with the 'real' screens. (Help templates) + ! Recount statistics was not timing out correctly. (Admin.php) + ! Updated version numbers to RC3. (All files) + ! Too many periods in Apache seemed to cause it to segfault. (Subs-Post.php) + ! Try do a better job of protecting against upgrade timeouts for index changes. (upgrade_1-1.sql) + ! Fixed # signs in bridged outgoing emails (Mambo/Joomla Bridge/smf.php) + - Removed obsolete bridge files (Mambo/Joomla Bridge/config.smf.php, config.smf_registration.php) + ! Updated bridge version info (several files) + ! Added a Javascript'ed fix in order to be able to discern numeric HTML-entities from upper-ASCII characters when posting messages. (Calendar template, PersonalMessages template, Poll template, Post template, xml_topic.js, Calendar.php, Poll.php, Post.php) + ! Only run the integrate_exit call if the template layers have been setup. (Subs.php) + ! Make sure transfer-encoding 'quoted-printable' is only used in cases '7bit' is not allowed. (Subs-Post.php) + ! The global variable $db_cache didn't always get initialized, causing a warning. (Subs.php) + ! Integrate outgoing emails before altering messages for mime special characters. (Subs-Post.php, Mambo/Joomla bridge/smf.php) + ! Fix URLs for Joomla 1.0.10 SEF updates. (Mambo/Joomla bridge/smf.php) + ! Fix adding new membergroups to synch in Joomla bridge config. (Mambo/Joomla bridge/admin.smf.php) + ! Fix "Missed Activation" functionality in Mambo/Joomla bridge. (Mambo/Joomla bridge/smf_registration.html.php, smf_registration.php) + & Added 'Search in...' to $txt['search_subject_only'] for clarity sake. (Search language files) + ! Attempted to stop timeout issues with upgrade. (upgrade.php, upgrade_1-1.sql) + ! Fixed 'You have already submitted this post' error showing in some cases it shouldn't. (Security.php, Post.php) + + Added selection for Mambo/Joomla bridge to toggle SMF CSS (Mambo/Joomla Bridge/install.smf.php, admin.smf.php, smf.php, SMF_Header_inlcude.php) + ! Added a fix for Hotmail not showing UTF-8 mails properly. (Subs-Post.php) + ! A query in updateMemberData was not passing the file and line constants to db_query. (Subs.php) + ! Make links in notifications sent to hotmail addresses clickable. (Subs-Post.php) + ! Allow partial word matches of the subject in case it's enabled in the search settings. (Search.php) + ! Limit the amount of table joins when searching for extremely long phrases. (Search.php) + ! Prevent the search function from looking for a suggestion if the search term is an integer as that can for unknown reason crash PHP. (Search.php) + ! Added the log_search_topics table in the 'empty unimportant logs' process. (Admin.php) + ! Some error log filters were shown with too many slashes. (ManageErrors.php) + ! Prevent undefined indexes as result of an error in 'Attachments and Avatars', e.g. session error. (ManageAttachments.php) + + + June 2006: +-------------------------------------------------------------------------------- + + ! Make sure that bridged login is compatible with Joomla 1.0.9. (Mambo/Joomla bridge/smf.php) + ! Alter bridge output for Xoops 2.0.14 compatibilty. (Xoops Bridge/index.php,smf_index.html) + + Added Mambo/Joomla pathway code. (Mambo/Joomla bridge/smf.php) + ! Made it impossible to erase admin status for the original Mambo/Joomla admin. (Mambo/Joomla bridge/admin.smf.php) + ! All numbers should now respect the format specified by the admin. (Profile.php, Subs.php, ManageSearch.php) + ! Added an optional parameter to override the number of decimal places used in a number. (Subs.php) + ! No longer check for an absolute string length before inserting subject and body into the database. (Post.php) + ! Added the visual verification disable option to the interface. (ManageRegistration.php, Register.template, Login language files) + ! Login integration hook was inserting email as username in some cases (LoginOut.php) + ! Show a more generic error when searching doesn't yield a result. (Errors language files) + + + May 2006: +-------------------------------------------------------------------------------- + + ! Make sure that bridge logins are not case sensitive. (Mambo/Joomla bridge/smf.php) + ! When synching users from Joomla to SMF, emails were written into passwords. (Mambo/Joomla bridge/admin.smf.php) + ! Make sure SSI doesn't chop off messy Gallery2 component URLs. (Mambo/Joomla bridge/SMF_Header_include.php) + + Added bridge for Joomla 1.5. (several files) + ! Fixed integrated login for Joomla 1.5. (Joomla 1.5 bridge/smf.php) + ! Fixed integrated logout for Joomla 1.5. (Joomla 1.5 bridge/smf.php) + ! Emptying unimportant logs was accidently erasing the subject cache. (Admin.php) + ! Try to chmod the mambots/system folder in case people don't follow instructions. (Mambo/Joomla bridge/install.smf.php) + * Added a (manual) method by which the auto-news from SMF can be disabled. (Admin, ManageSmileys, Packages, Themes templates) + + + April 2006: +-------------------------------------------------------------------------------- + + Added a visual verification code to the registration function. (index.php, Register.php, Subs-Graphics.php, Register template) + & Added a few entries for the above feature. (Login language files, Error language files) + * Added a font directory to the default theme for visual verification code feature. (Themes/default/fonts) + + Added code to allocate more RAM for bridges (Mambo/Joomla bridge/smf.php) + + Added checking to the topic, board, and action REQUEST parameters to ensure they are not arrays prior to processing (QueryString.php) + ! Firefox doesn't like entities in Javascript (Mambo/Joomla bridge/admin.smf.php) + + RSS feeds go to wrapped forum posts (Mambo/Joomla bridge/smf.php) + ! As long as there is an exit integration hook, let it handle all pages, special or not (Subs.php, Mambo/Joomla bridge/smf.php) + ! Member information wasn't being used properly in the registration hook (Mambo/Joomla bridge/smf.php) + ! Fixed an undefined variable error (News.php) + ! Fixed a bug when adding members to a membergroup through the admin control panel (ManageMembergroups.php) + ! Fixed an empty delimiter using explode (Subs-Post.php) + ! The password was not being checked properly when modifying your own profile. + ! VirtueMart compatibility wasn't always working (Mambo/Joomla Bridge/mod_smf_login.php) + ! Added a spoken version of the visual verification code. (Register.php, Subs-Sounds.php, Register.php, Register template) + & Added a few language tags for the above change. (Login language files) + * Added 'sound font' to the default theme dir. (Themes/default/fonts/sound/*.english.wav) + ! Added a bit of random noise to the visual verification code. (Subs-Graphics.php) + ! Optimised the code for moving a topic. (MoveTopic.php) + ! Added new index to log_topics table to help speed up admin functions. (upgrade_1-1.sql, install_1-1.sql) + + Added wrapped/unwrapped option to Xoops bridge (Xoops bridge/index.php, xoops_version.php, modinfo.php) + ! Load the SMF session before the Xoops session for cleaner session handling (Xoops bridge/index.php) + ! Make sure that Mambo/Joomla installer files have proper PHP tags (Mambo/Joomla bridge/install.smf.php, uninstall.smf.php) + + Add warnings about use of Synching buttons (Mambo/Joomla bridge/admin.smf.php) + ! Logout integration hook wasn't redirecting properly in some cases (Mambo/Joomla bridge/smf.php) + ! Mark unread wasn't taking you back to the right place when you revisited the topic. (Display.php) + * Editing posts with two dollar signs in it wasn't working. (xml_topic.js) + ! If host_from_ip fails still cache the result as system commands can take time. (Subs.php) + ! If a server doesn't support the timeout option on host then disable it. (Subs.php) + ! There doesn't appear to be any reason to slash square brackets in emails. (Subs-Post.php) + * The help template had some incorrect text entries. (Help template) + ! Don't put defunct package servers in upgrade. (upgrade_1-1.sql) + ! Group Synch did not insert new rows for SMF group IDs greater than 8 (Mambo/Joomla bridge/admin.smf.php) + ! When tracking a user only look at their latest posts to save query time. (Profile.php) + ! Add a little overhead when picking a theme incase the admin is hiding some. (Themes.php) + + + March 2006: +-------------------------------------------------------------------------------- + * Tab order now includes the spell check button for the quick reply box (Display.template.php) + ! Added an extra check on the HTTP_X_FORWARDED_FOR input variable. (QueryString.php) + + Added Joomla 1.0.8 session compatibility (Mambo/Joomla bridge/smf.php) + + Added VirtueMart session compatibility (Mambo/Joomla bridge/mod_smf_login.php) + ! Limit the number of words that can be searched for a little bit. (Search.php) + ! Prevent votes to be cast multiple times (Poll.php) + + Added mambot so that Joomla! users do not have to add code to their Joomla! templates (install.smf.php, smf.php, smf.xml, SMF_header_include.php, SMF_header_include.x) + + Added the option to use Mambo/Joomla default or CB registration (Mambo/Joomla bridge/admin.smf.php, smf.php, mod_smf_login.php) + + Added Instant Synch tab to the Mambo/Joomla bridge admin panel (Mambo/Joomla bridge/admin.smf.php) + ! After a successful password reset, the bridge was redirecting to the registration page (Mambo/Joomla bridge/smf_registration.php) + + Added function to migrate SMF users to Mambo/Joomla (Mambo/Joomla bridge/admin.smf.php) + + Added option for MamboCharge registration (Mambo/Joomla bridge/admin.smf.php, smf.php, mod_smf_login.php) + ! Make sure to update the Mambo/Joomla ACL, in case of name change (Mambo/Joomla bridge/smf.php) + + Added function to update Mambo/Joomla members to the group synch settings (Mambo/Joomla bridge/admin.smf.php) + + Added 3rd party developer tab structure to the bridge config panel (Mambo/Joomla bridge/admin.smf.php) + + Updated instructions for bridge installation (Mambo/Joomla bridge/readme.html) + ! Make sure that header code is XHTML compliant (Mambo/Joomla bridge/smf.php, SMF_header_include.php) + ! Fixed typos in Mambo/Joomla bridge (Mambo/Joomla bridge/smf.php) + ! The config wasn't loading in all cases for the bridge login module (Mambo/Joomla bridge/mod_smf_login.php) + ! New users were not being entered into the Mambo/Joomla ACL when migrated during login (Mambo/Joomla bridge/smf.php) + ! Logout in Joomla 1.0.8 was not working without register_globals on (Mambo/Joomla bridge/smf.php) + ! Integrated redirecting for errors and Immediate Registration was not working properly (Mambo/Joomla bridge/smf.php) + ! Membergroup Settings page was missing it's title (ManageMembergroups.php) + ! Fixed censor-function not working properly in all cases. (Load.php) + * Login forms now use the number of minutes in 30 days instead of 210 days for the value of a month (index.template, Themes.php) + ! Code cleanup in redirect integration hook (Mambo/Joomla bridge/smf.php) + ! Make sure that users migrated in from Mambo/Joomla have post groups (Mambo/Joomla bridge/smf.php) + ! Let Mambo/Joomla handle admin notifications with bridge registration (Mambo/Joomla bridge/smf_registration.php) + ! Bridge header bot for Joomla was not pulling a full URL with SEF turned off (Mambo/Joomla bridge/SMF_header_include.php) + ! Fixed group synch for Joomla (Mambo/Joomla bridge/admin.smf.php) + ! Bridge registration hook was receiving blank information. (Mambo/Joomla bridge/smf.php) + ! Mambo/Joomla does not check for unique names -- bridge should ((Mambo/Joomla bridge/smf_registration.php) + ! Unchecking all BBC tags in the BBC settings page now saves the settings correctly and no longer causes an error (ManagePosts.php) + ! Migrating users from Mambo/Joomla on login inserted blank username into stats (Mambo/Joomla bridge/smf.php) + ! Not all default options inserted in component installation (Mambo/Joomla bridge/install.smf.php) + - Removed the check for duplicate membergroup names (ManageMembergroups.php) + & Removed error string for duplicate membergroup names (Error language files) + ! Group synch panel wasn't showing inside a tab (Mambo/Joomla bridge/admin.smf.php) + ! Users migrated from Mambo/Joomla were always using real name as display name (Mambo/Joomla bridge/smf.php) + ! The [hr] and [br] tag no longer use style="clear: both;" + ! The description for the active admin tab should now showing up properly in the Core theme. (Admin.template) + ! Users will now be able to see their own hidden email address on the profile summary page. (Load.php) + ! Email notifications of PMs should now be sent in the language the user selected. (Subs-Post.php) + ! Make sure database field is large enough for some very long absolute paths (Mambo/Joomla bridge/install.smf.php) + ! Bridge config wasn't loading for bridge registration (Mambo/Joomla bridge/smf_registration.php, smf_registration.html.php) + ! When creating a new ban, triggers for hostname and email should now be saved properly (ManageBans.php) + ! Unwrapped option was not displaying properly (Mambo/Joomla bridge/smf.php) + + + February 2006: +-------------------------------------------------------------------------------- + ! updateLastMessages was unnecessarily slow, and sometimes wrong. (Subs-Post.php, Load.php) + + Finished basic UTF-8 support throughout the forum. (several files) + ! Moved $modSettings['lang_character_set'] to the Settings.php file as '$character_set' to ensure the characterset before retrieving the database stored settings. (install.php) + ! Fixed a warning shown during the install when detecting the MySQL version before connection. (install.php) + ! The installation could not finish properly due to the function array not being initialized. (install.php) + ! Sending Personal Messages resulted in an error. (PersonalMessages.php) + ! Change the header for the installer to make sure the proper character set is shown. (install.php) + ! Removed the client version check of MySQL for the UTF-8 option. (install.php) + ! The initial values of a UTF-8 installation weren't properly inserted. (install.php) + ! Make a better distinction between database character set and the character set of the data. (install.php, Load.php) + ! Upgrade did not work properly due to functions that weren't initialized. (upgrade.php) + * Mark as read on unread posts did not add any queried boards (Recent template) + * Recent posts template was missing a closing div tag (Recent template.php) + + Added a maintenance option to convert both the data and the database to UTF-8. (Admin.php, Admin template, index.php) + & Added a block of tags for the above change. (Admin language files) + ! The title link wasn't updated properly after xmlHTTP editing of a message. (xml_topic.js) + ! Added an index to a temporary field in the Ikonboard converter to make the conversion go faster. (ikonboard_to_smf.sql) + ! Additional internationalization fixes. (several files) + ! The strpos function didn't work properly. (Load.php) + + Added a HTML entities to UTF-8 characters converter as addition to the UTF-8 converter. (Admin.php, Admin template) + & Added a block of languages strings for the above change. (Admin language files) + ! The HTML entity converter showed a session check error. (Admin.php) + + + January 2006: +-------------------------------------------------------------------------------- + + Added check for user activation in Mambo/Joomla before writing to SMF (Mambo/Joomla bridge/smf.php) + * Closed a open tag. (index template) + ! MaxMsgID could take a value which was not numeric. (Subs.php) + ! Repair boards has an incorrect language tag. (RepairBoards.php) + ! Multiple errors in the query to insert members into Xoops & PhpNuke on registration. (Xoops bridge/index.php, PhpNuke bridge/index.php) + ! The field named 'id' in the Mambo/Joomla users table isn't the same as a SQL query ID. (Mambo/Joomla bridge/smf.php) + ! Mambo/Joomla uses the real name for the ACL, not the member name. (Mambo/Joomla bridge/smf.php) + ! Missing queries preventing users from being inserted into Mambo/Joomla. (Mambo/Joomla bridge/smf.php, smf_registration.php) + ! integrate_outgoing_email hook was throwing an error if there were no links in the email. (Mambo/Joomla bridge/smf.php) + ! testing for $result should have been $smf_user. (Xoops bridge/index.php) + * Sticky button had a wrong check. (Display template) + ! There is no need to tear apart non-SEF URLs for outgoing emails. (Mambo/Joomla bridge/smf.php) + & Updated French language file for Mambo/Joomla bridge. (Mambo/Joomla bridge/language/french.php) + * Typo in javascript routine. (xml_topic.js) + ! Added a modSetting to allow overriding the character set from the language file. (Load.php) + + Added an option to install SMF using UTF-8 as database and output character set. (install.php) + & Added a few language strings for the above change. (install language files) + ! The upgrade wasn't checking for the existence of an old column. (upgrade_1-1.sql) + ! Updated copyright dates. (All files) + ! Changed some of the timeout values to be much more generous on servers with low timeouts. (upgrade.php, Admin.php, RepairBoards.php, ManageAttachments.php, ManageSearch.php) + * Fix sticky topic background problems on old themes. (MessageIndex template) + ! Fudge a fix to stop people complaining about no borders on their images. (Combat template) + ! Mambo/Joomla bridge installation should link to the SMF license. (Mambo/Joomla Bridge/install.smf_registration.php) + ! Fix the upgrade table backup not copying the table type. (upgrade.php) + ! Include the character set and collation of each table for the upgrade backup. (upgrade.php) + ! An invalid default action was selected in 'News and newsletters' for users without the edit_news permission. (ManageNews.php) + ! Redirect for login failure was not being handled by the integration hook (Mambo/Joomla bridge, smf.php) + * Default template was trying to use a calendar string which didn't exist. (Display template) + ! Rewrote the upgrade process for calculating last board ID's - for the tenth time. (upgrade_1-1.sql) + * Logic for second add poll/mark read check was wrong. (Display template) + ! Failing a valid username and password in the Mambo/Joomla bridge should switch back to the SMF password before returning (Mambo/Joomla bridge/smf.php) + ! Fixed URLs in javascript for insert quote to work with SEF (Mambo/Joomla bridge/smf.php) + ! Unwrapped pages were not output (Xoops bridge/index.php) + ! Changed the way findMembers works, to ensure it works on MySQL versions which seem to have bugs using IN with strings. (Subs-Auth.php) + ! Fixed the calendar editing such that people without permission to modify posts can still modify the right events. (Calendar.php, Post.php, Calendar template) + * Made a small tweak to the search results template for Personal Messages. (PersonalMessage template) + ! Increased size of smileyOrder to allow for more smileys. (upgrade_1-1.sql, install_1-1.sql) + * Tweaked the star javascript on manage membergroups to not force more than 0 stars. (ManageMembergroups template) + * Changed old style buttons to use button_strip routine. (Search template, PersonalMessage template) + * Mark as read on recent posts did not add any queried boards (Recent template) + ! Jumpto on wrapped forums was jumping to stand alone forum (Mambo/Joomla Bridge/smf.php) + ! Bridged logout might throw an error with register_globals off (Mambo/Joomla Bridge/smf.php) + + Added vB 3.5 converter. (vbulletin35_to_smf.sql) + ! Fixed converter to work with new log table system. (convert.php) + ! Censored words had a PHP 4.4.0 incompatibility. (ManagePosts.php) + * Removed javascript on "Delete Selected" button for tabbed themes. (Display template) + & Cleaned up some text strings related to permissions. (ManagePermissions.english.php) + ! Creating a new board wasn't inheriting permissions correctly. (ManageBoards.php, Subs-Boards.php) + * Added a little protection against index errors in the Xml template - for stupid people. (Xml template) + + Added option to installer to collect statistics. (install.php, Stats.php) + ! Make sure that $_REQUEST variables exist before testing for their values (Mambo/Joomla bridge/smf.php, mod_smf_login.php) + ! Some versions of Mambo and Joomla do not like $_REQUEST variables that are not cleaned first (admin.smf.php, admin.smf_registration.php) + + Added integrate_change_member_data function to the Mambo/Joomla bridge (smf.php) + ! Think I may have got to the bottom of the "subject/topic cache problem". (SplitTopics.php, MoveTopic.php, RemoveTopic.php) + ! Don't mark a message as edited if it was done through javascript and not actually changed. (Post.php) + ! When repair boards deletes orphaned topics, delete orphaned log_topics too. (RepairBoards.php) + * Keepalive was causing problems due to a bug with FireFox 1.5. (script.js) + ! Hardcoded text in Help template line 2219 (Help template) + ! Fixed outgoing email bug with UTF-8 encoding (Mambo/Joomla bridge/smf.php) + + Moved Mambo/Joomla bridge config to a database table instead of flatfile (several files) + + Added Mambo/Joomla bridge options to main bridge config as a tab (several files) + + Added Mambo/Joomla bridge group synchronization. (several files) + + Added ID_GROUP to the change member data integration hook (Subs.php, Mambo/Joomla bridge/smf.php) + ! Some SSI submission forms resulted in some cases in a session check error. (SSI.php) + ! Updated all HTML forms to ensure enforcement of the input character set. (several files) + ! Fixed embed and object URLs for Arcade Mod (Mambo/Joomla bridge/smf.php) + ! Array was not handled correctly in component installer (Mambo/Joomla bridge/install.smf.php) + ! Made automatic menu item installer MySQL 5 strict compliant (Mambo/Joomla bridge/smf.xml) + ! Fixed real name/user name selection in Bridge Registration (Mambo/Joomla bridge/smf_registration.php) + + + December 2005: +-------------------------------------------------------------------------------- + ! User migration query from Mambo/Joomla to SMF was failing (Mambo/Joomla bridge/smf.php) + ! You couldn't send newsletters to post based membergroups. (ManageNews.php) + ! Upgrade was changing log tables in chunks too large for some installs. (upgrade_1-1.sql) + + +SMF 1.1 RC2 December 31, 2005 +================================================================================ + + December 2005: +-------------------------------------------------------------------------------- + ! Upgrade wasn't using sub steps for the log table conversion. (upgrade_1-1.sql) + ! Try to parse &? URLs in Mambo/Joomla in case of outdated modules and links. (Mambo bridge/smf.php) + + Added login/logout redirect code for Mambo/Joomla bridge. (Mambo bridge/smf.php) + + Added some tools in draft stage for help file conversion. (help-convert.php, help-list.php) + * Recent template was showing the wrong links for topic replies. (Recent template) + + Started moving help files into the template system. (Help.php, Help template, Manual language files) + ! Package manager was sometimes requesting ftp information when not really required. (Subs-Package.php) + ! Added the ability to emulate forum version through package manager (albeit with no interface). (Packages.php, PackageGet.php) + ! When editing theme settings the "template settings" from the current template were being lost. (Themes.php) + * Added ability to customise the button strip alignment to template_button_strip. (Combat and index templates) + * helplogo.gif replaced with helplogo.jpg in classic theme. (helplogo.gif, helplogo.jpg) + * Started adjusting the help templates to match the current theme. (Help template) + ! The enableSpellChecking setting wasn't always checked the right way. (Display.php, PersonalMessage.php, Post.php, Profile.php, Search.php) + + Added searching within a topic, when searching from the Display screen. (index template, Search.php) + ! Sort custom index search terms before querying the index. (Search.php) + ! Prevent too high numbers being inserted/queried in the search index. (Subs.php) + ! Mambo bridge "Forgot Password" was missing a component location for redirection upon success (Mambo bridge/smf_registration.php) + * Removed hardcoded color and corrected typo in the stylesheet. (index templates, style.css) + * The add poll button wasn't always shown in NDT. (Display template) + ! Fixed PHP 5.1 problem in feature settings. (ModSettings.php) + ! Added large_text as a possible setting type, to generate a textarea if desired. (ManageServer.php, Admin template) + * Changed the colouring/template of personal messages to make it more consistant - and work well in other themes. (PersonalMessage template) + ! Fixed inconsistancy with use of newtime and new_from causing error in recent messages. (Recent.php, Profile.php, Recent Templates) + ! Empty logs was cleaning a non-existant log. (Admin.php) + ! Recent topics was showing topics that were already marked as read. (Recent.php) + * Some Recent templates showed old-style new-links. (Recent templates) + * Fixed tab index conflict in Classic theme's Post screen. (Post template) + ! Made Mambo/Joomla login/logout redirects SEF (smf.php, mod_smf_login.php) + ! Some of the URL-anchors in 'Recent replies' were incorrect. (Recent.php) + ! The ICQ-link and images URL's have changed...again. (Load.php) + ! Add an index to the log tables before converting them. (upgrade_1-1.sql) + ! Allow adding indexes to non-existent columns when upgrading. (upgrade.php) + * Fixed validation errors in index.template for guests. Opera and IE showed jumpbox wrong in Display. (index template, Display template) + * Modified tabs routines to adjust for right-to-left setting. (index template) + ! Made Mambo/Joomla posting redirects and outgoing emails SEF (smf.php) + - Removed function is_admin(), which is obsolete (Security.php) + & Added French language files for Xoops bridge (xoops/language/french) + + Moved help files into Help template, updated Help.php to use these files. (Help template, Help.php) + & Added new language file for all user manual language entries. (Manual language files) + ! Added conversion tool for converting existing xml language files. (help-convert.php, help-list.php) + ! Removed old help directory from default theme. (help directory) + ! Added small fix to start variable in the member list. (MemberList.php) + ! Don't allow membergroups to have the same name on editing. (ManageMembergroups.php) + ! Jumping to a personal message wasn't working if you had the newest shown at the top. (PersonalMessage.php) + * added a titlebg container to the participation icon area. (Recent template) + ! Fixed typo in searchSort function. (Search.php) + ! Store the SMTP password base64_encoded to make it slightly more difficult to read from a backup. (ManageServer.php, Subs-Post.php, upgrade_1-1.sql) + ! Marking messages as unread wasn't working. (Subs-Boards.php, Display.php) + ! Fixed minor MySQL 5.0.12 compatibility flaw. (PersonalMessage.php) + ! Fixed problem in unread replies to your posts incorrectly showing nothing on big boards. (Recent.php) + ! PHP Syntax highlighting wasn't happening on all code tags. (Subs.php) + ! Mambo/Joomla login module was using bridge password reminder even if set to SMF registration (mod_smf_login.php) + ! Updated copyright message to minimise confusion as to who owns the copyright on the content. (Subs.php, index language files) + ! Moved some bits around in the upgrade script to ensure if it times out important things don't get done twice! (upgrade_1-1.sql) + ! One of the obsolete indexes on the messages table wasn't dropped. (upgrade_1-1.sql) + ! Improved performance for the main MessageIndex query, especially for other than the first or last page. (MessageIndex.php) + ! Non-SEF redirects were not always working correctly in Mambo/Joomla bridge. (smf.php, mod_smf_login.php) + ! Reversed primary key columns of the log_mark_read table for speed optimization. (upgrade_1-1.sql, install_1-1.sql) + + Mambo/Joomla login module is published on all pages by default upon installation (install.smf.php) + ! Added a sub-menu config item for Mambo/Joomla bridge registration admin panel (install.smf.php) + + Added PhpNuke bridge + ! Updated upgrade script to make it slightly more friendly for the log table upgrade. (upgrade.php, upgrade_1-1.sql) + ! Updated the session rewrite function to use an ampersand entity in the URL and not semi-colon for compatibility. (QueryString.php) + ! Repairing boards wasn't pausing very efficiently. (RepairBoards.php) + ! Reversed primary key columns of the log_topics table for speed optimization. (upgrade_1-1.sql, install_1-1.sql) + + Added Xoops to SMF user migration upon login. + ! Prevent an error while upgrading in case knownThemes isn't set. (upgrade_1-1.sql) + ! Selecting invalid boards for recent topics/replies was showing a database error. (Recent.php) + ! Added back the subject-only search. (Search.php) + * In NDT the right tab wasn't always shown. (index template) + & Changed 'Edit Profile' into 'Profile' to match with the other tabs in the main button bar. (index language file) + ! Topic search wasn't in all cases working. (Search.php) + ! Fixed a few minor upgrade issues. (upgrade.php, upgrade_1-0.sql) + ! New member stats weren't updating in Mambo/Joomla bridge registration. (smf_registration.php) + ! Check for the sequences of index columns when performing an index check during upgrade .(upgrade.php) + ! Moved the column swapping of the log_topics index to the protected table alteration. (upgrade_1-1.sql) + ! Borrow upgrade_query's error handler for protected table alteration queries. (upgrade.php) + ! integrate_pre_include was being tested before integration settings were loaded. (Load.php) + ! BoardIndex links to the last topic of each board weren't linking to the last post for guests. (BoardIndex.php) + * Member search did not check for using_tabs setting. (Memberlist template) + * Added PM tab and removed "you have x messages" + merged 2 bars to 1. (index template, languages/index.dutch, languages/index.english , languages/index.german, languages/index.spanish) + ! Fixed small bug in upgrade for users coming from YaBBSE. (upgrade.php, upgrade_1-0.sql) + ! Made upgrading the search index resumable. (upgrade_1-1.sql) + ! Updated default value for date columns to be MySQL-strict compatible. (several files) + ! Holidays weren't updated properly on the boardindex after modifying them. (ManageCalendar.php) + ! Some direct message links were linking to the wrong page. (Display.php) + ! Finally fixed problem with upgrading from YaBBSE on windows server, plus a few other tweaks. (upgrade.php, upgrade_1-1.sql) + ! Some Mambo/Joomla field names need to be enclosed in back-ticks. (install.smf.php, smf_registration.php) + * Fixed error with legend in the default unread template. (Recent template) + ! Copyright was throwing an error for old language files. (Subs.php) + ! Some feature settings wern't saving as a result of a missing loadLanguage. (ModSettings.php) + * Use the current themes help.css file if it exists. (Help.php) + ! When detecting crawlers, ensure we pick up on Google. (Load.php) + ! Some activation emails did not contain the full URL in Mambo/Joomla bridge registration. (smf_registration.php) + ! Make sure all text columns have no default value. (install_1-1.sql, upgrade_1-1.sql, upgrade.php) + ! Fixed error with the Calendar, which caused a problem when New Years came about. Calendar.php) + ! Added newtime as an index in context from MessageIndex.php for compatibility with old themes. (MessageIndex.php) + ! It was possible to generate an error from the ManageSmileys screen. (ManageSmileys.php) + ! For some unknown reason the log_banned table had a column for ban_ids. (install_1-1.sql, upgrade_1-1.sql) + ! Fixed a few things in the MySQL STRICT elements of the converter, including posterIP and checking the size of message body. (upgrade_1-1.sql, upgrade.php) + ! Prevent 1.0 upgrade from being done more than once. (upgrade.php) + ! Made all inserts/replace insert all columns that have no default value. (several files) + ! Include the registration date from the Mambo user table on import. (Mambo bridge/smf.php) + ! Some registration data wasn't properly inserted by the registration hook. (Mambo bridge/smf.php, Phpnuke bridge/index.php, Xoops bridge/index.php) + ! The profile birthdate couldn't be updated to a date before 1970. (Profile.php) + ! loadMemberData now adds the banned status to the context correctly in all situations. (Load.php) + & Added a warning about making the uploaded avatar directory the same as the server-stored one. (ManageAttachments template, ManageAttachments language files) + * Changed language strings in usersection. "Hey" becomes "Hello" and "PM" tab becomes "Messages" (index template, index.english.php, index.spanish.php, index.german.php, index.dutch.php) + * Moved "go down" and "Go up" a bit to the right. (Display template, MessageIndex template) + * Added linktree to PM section. (PersonalMessage template) + * Help template updated to use language buttons where appropriate. (Help template) + * The babylon recent template was showing the board information twice. (Recent template) + ! In a timeout situation it was possible that upgrade would be left broken. (upgrade_1-1.sql) + ! Searching without temporary table support wasn't working properly. (Search.php) + ! Merging topics with no polls under strict MySQL settings wasn't working properly. (SplitTopics.php) + ! The validation code wasn't properly updated when requesting a password reminder, causing a database error in MySQL strict mode. (Reminder.php) + & Changed "Messages" into "My Messages" to make a distinction between the forum messages and the personal messages. (index language files) + ! Install was putting the admin's IP address in their personalText settings. (install.php) + + Added images for active mirror tabs. (mirrortab_active_first.gif, mirrortab_active_last.gif, mirrortab_active_back.gif, style.css) + * Ensured tabs on memberlist suit the new themes. (Memberlist.php, Memberlist template) + * The admin tab wasn't selected when modifying themes. (index template) + ! Fixed error when creating a new membergroup. (ManageMembergroups.php) + ! On some servers Server Settings wasn't working. (ManageServer.php) + + + November 2005: +-------------------------------------------------------------------------------- + ! The eblah converter couldn't be run multiple times. (eblah_to_smf.php) + ! The IPB 2 converter didn't convert all members if the same email address was used twice. (invision2_to_smf.sql) + ! The phpBB 2 converter didn't work properly with some modifications. (phpbb2_to_smf.sql) + ! Updated the read me documents to the new default theme. (readme_install.html, readme_update.html, readme_upgrade.html, readme_convert.html) + + Added Orstio's Mambo/Joomla bridge, from now on officially supported by SMF. (several files) + ! Added an HTML version of a readme to the above bridge. (readme.html) + ! Fixed the deadlock prevention that might not work properly in all cases. (Errors.php) + + Added bridges for XOOPS and IGaming. (several files) + ! Remove the showing in the error messages in 'Track User' and 'Track IP'. (Profile.php) + ! Show the most recent errors first in 'Track IP'. (Profile.php) + + Added group-sensitive member migration to iGaming bridge (igaming/smf.php) + ! Fixed blank fields in iGaming bridge configuration (igaming/sources/admin/smf.class.php) + ! Don't show the absolute number of posts in the profile statistics on large message boards. (Profile.php, Profile template) + ! Fix a notice when performing activation or approval on multiple members. (ManageMembers template) + ! In some cases the upgrade created the same backup tables more than once. (upgrade.php) + + Added outgoing email integration for iGaming. (igaming/smf.php) + ! Searching with a user specified resulted in some cases in a database error. (Search.php) + + Added module admin panel and language files for Xoops. (several files) + ! Adjusted all queries with one or more OUTER JOIN for MySQL 5.0.12 compatibility. (several files) + ! Fixed 'Mark board as read' not working properly for >MySQL 4.0. (Subs-Boards.php) + ! Fixed split function showing a database error with an empty selection. (SplitTopics.php) + ! Simpleboard converter stopped working if duplicate values were inserted into the log_notify table. (simpleboards_to_smf.sql) + + Added a converter for e107 CMS. (e107_to_smf.sql) + ! Binary compare text columns in order to prevent potential mixed collation errors. (ldu_to_smf.sql, mybb_to_smf.sql, myphp_to_smf.sql, openbb_to_smf.sql, oxygen_to_smf.sql, phpbb2_to_smf.sql, phpnuke_to_smf.sql, xmb_to_smf.sql, yabb2_to_smf.php) + ! By default, disable the spell checking option. Without the proper dictionaries installed, it might prevent search from working. (upgrade_1-0.sql, install_1-1.sql) + + Added login integration for Xoops bridge. (xoops/index.php) + ! Fixed session issue with logging out in Xoops bridge. (xoops/index.php) + ! Xoops integrated login functional. (xoops/index.php) + + Added logout and registration integration for Xoops bridge. (xoops/index.php) + + Added bridge login block for Xoops bridge. (several files) + ! Fixed guest being a selectable group for some inline permissions. (ManageAttachments.php, ManageBoards.php, ManageMembergroups.php, ManageNews.php, ManagePermissions.php) + ! Removed the ability to give guests admin permissions. (ManagePermissions.php, upgrade_1-1.sql) + ! Added an option to move boards to an empty category. (ManageBoards.php) + & Added a language tag for the above change. (ManageBoards language files) + ! Fixed the problem with |  appearing in messages of some forums. (Subs-Post.php) + * Javascript error generated because xml_enable was used instead of xml_enable_check. (ManageNews template) + * Wrong declaration class attribute in style caused errors. (style.css) + ! Force character set in HTTP headers to prevent ambiguous character interpretation. (Display.php, Load.php, News.php, Subs.php) + & Added an error string if a message icon has more than 16 characters. (ManageSmileys.php, Errors language files) + ! Upgrade should now backup tables with the correct engine type. (upgrade.php) + ! Do a better job at keeping the is_activated flag correct for banned members. (Security.php) + * Show ban information in a users profile. (Profile.php, Profile template) + & Added several language entries for the above change. (Profile language files) + ! When showing who is viewing a board/topic it now sorts user in click order, and shows them as buddy if applicable. (Display.php, MessageIndex.php) + + Added option (max_pm_recipients) to limit number of recipients a user may message at once. (PersonalMessage.php, ModSettings.php, install_1-1.sql, upgrade_1-1.sql) + & Added language strings for above change. (PersonalMessage, ModSettings and Help language files) + * Added XML editing to the message index. (MessageIndex, XML templates, Post.php, MessageIndex.php) + * Who's online will now default to ascending when clicking "sort by name". (Who template) + * Added new javascript file for xml editing in the message index. Also cleaned up the javascript. (MessageIndex templates, xml_board.js) + + Added interface for caching, and moved several server settings into the server setting area. (ManageServer.php, ModSettings.php, Admin.php) + * Added new template for server settings. (Admin template) + & Added new language entries for above changes. (Help, ModSettings and Admin language files) + ! Update the online log if the user is viewing a topic/board and who's online is enabled. (Subs.php, Display.php, MessageIndex.php) + ! Moved around some settings under feature settings. (index.php, Subs.php, modSettings.php, ManageServer.php, Admin template) + & Changed some language strings for the above change. (ModSettings language file) + ! The time tag now takes an optional parameter, absolute, for not offsetting the time, and edits better. (Subs-Post.php) + + Added personal message integration hook. (Subs-Post.php) + ! Added component variables to Mambo/Joomla login module form submission to make sure they don't get wiped. (mod_smf_login.php) + ! Remember the sort status when deleting attachments. (ManageAttachments.php, ManageAttachments template) + ! Rewrote the custom search index to make it faster and more compact. (ManageSearch.php, RemoveTopic.php, Search.php, Subs.php, Subs-Posts.php, ManageSearch template) + ! The steps for creating the custom index are now time limited. (ManageSearch.php) + ! Allow resuming the creation of a custom index. (ManageSearch.php) + ! Stopwords are now removed in a stepped way. (ManageSearch.php) + ! Added a warning in case two indexes are created on the messages table. (ManageSearch template) + & Added and removed language strings for the above changes. (Search language files) + ! The detection of whether fulltext tables could be created or not didn't always work properly. (ManageSearch.php) + ! The reattributePosts() function didn't add the proper amount of posts to a member. (Subs-Members.php) + * Added optional theme setting, 'use_buttons', for showing buttons with text alongside them. (index templates) + * Fixed some problems with older themes using the above setting. (Calendar, PersonalMessage, Search, Profile and SplitTopics templates) + ! Added new function, create_button, for creating buttons in themes without all the checks. (Subs.php) + * Added function to index template for generating rows/tabs of buttons, and implemented it in Display. (index, Display templates) + ! Added new template, Combat, to ensure old themes work with new functionality. (Subs.php, Combat template) + * Moved modify_inline.gif image to icons directory and added to babylon and classic themes. (modify_inline.gif) + & Repair boards now finds thumbnails without parents. (RepairBoards.php, Admin language files) + * Moved some Display javascript into a seperate file for better caching, and made the javascript more templatable. (xml_topic.js, xml_board.js, Display and MessageIndex templates) + ! Xoops registration integration wasn't holding the old URL in the session. (xoops/index.php) + + Changed recount function in "Attachment Manager" to a maintenance function for fixing all manner of attachment problems. (ManageAttachments.php, ManageAttachments template) + & Added new language entries for above change. (Admin language files) + * Added Recent template back into babylon theme. (Recent template) + & Added "Reply to All", but only in the NDT at the moment. (PersonalMessage template, PersonalMessage language files) + * Remove smf_session_id in favour of passed session id's. (script.js, xml_board.js, xml_topic.js, MessageIndex, Display, Login, index, ManageSmileys, Packages, Themes templates, mod_smf_login.php) + * Display template now only does inline editing on the babylon/classic theme if the subject area is double clicked. (Display templates) + * Browsing attachments will now retain sorting direction when switching between browse types. (ManageAttachments template) + * Cleaned up NDT for backwards compatibility further, removed some instances of windowbg3. (ManageBans, ManageSmileys, BoardIndex, MessageIndex templates) + * Updated search to show participation information. (Search.php, Search template) + ! Fixed package manager not extracting seperate files correctly. (Subs-Package.php) + ! Attachment maintenance was deleting custom avatars in error. (ManageAttachments.php) + * The NDT was showing some information twice. (index template) + ! Caching of button strips was working a little too well. (index, Combat, MessageIndex, Display, Recent templates) + ! Stop users who are already logged in attempting to log in again by redirecting them. (LogInOut.php) + & Removed language entry associated with old error message for users logging in twice. (Errors language files) + ! Upgrade was messing up the order of categories. (upgrade_1-1.sql) + * Sending out newsletters wasn't preserving the parse HTML value. (ManageNews template) + ! Sending out newsletters was stripping slashes one too many times thanks to sendmail doing it too. (ManageNews.php) + ! Sending out newsletters was always missing off some poor guy. (ManageNews.php) + ! Added new optional attribute to a package to force a redirect after installation. (Packages.php, Subs-Package.php, Package template, package-info.dtd) + & Added three language entries for above change. (Packages language file) + ! Updated version numbers to SMF 1.1 RC2. (all files) + ! Editing templates was reporting the wrong line numbers, and adding in extra line breaks. (Themes.php) + ! Bypass the SSI session errors if it's being called from the command line. (SSI.php, QueryString.php) + ! Made defining of WIRELESS conditional, in case it's already been defined elsewhere (index.php) + ! The BBC parser could cause apache problems when parsing emails. (Subs.php) + ! Avatars were still being deleted unnecessarily during maintenance. (ManageAttachments.php) + ! Fix URLs and email addresses being autolinked inside linked tags. (Subs.php) + ! Updated the log table system in order to improve performance, especially for the unread posts and replies. (several files) + * Moved some more functions from the Display template to xml_topic.js (Display template, xml_topic.js) + ! Inline modification wasn't storing the modified body and subject in the Javascript environment. (xml_topic.js) + * Updated all non-NDT themes to do the inline editing with a button instead of doubleclicking. (Display template) + ! BoardIndex was showing an undefined index error for guests. (BoardIndex.php) + ! Install wasn't showing some database errors properly. (install.php) + * Hide buttons if xmlHTTP or JavaScript isn't supported. (Display template, xml_topic.js) + + + October 2005: +-------------------------------------------------------------------------------- + ! Let upgrade base other themes off babylon if specific templates are missing. (upgrade_1-1.sql) + + Added Grudge's PM search feature to the interface. (PersonalMessages.php) + & Added two new language strings, most_online_ever and most_online_today for the new theme. (index language files) + * Added a theme setting to determine whether tabs are used in the admin center. (Admin template, Index template) + * removed "welcome guest.." in the login form. Appeared twice. (Index template) + * Added next-previous links to the top. Only showed on bottom. (Display template) + ! The base theme URL wasn't inserted in the upgrade of the new default theme. (upgrade_1-1.sql) + ! Added integrate_change_member_data to allow integration after updating a member profile. (Subs.php) + - Removed integrate_change_email as it's already covered by the above function. (Profile.php, Register.php) + * Updated the new theme's thumbnail. (thumbnail.gif) + ! The first-letter-anchors in the member list weren't always working. (Memberlist.php) + ! deleteBoards() didn't work properly with an empty array of boards as input (Subs-Boards.php) + * Added extra styles to classic and babylon theme, used in default theme (style.css) + * Added check for use of tabs on buttons. If not set, use the buttons from images/(language) folder (Recent template, Profile template) + * Added session check to mark-as-read buttons. (BoardIndex template, MessageIndex template, Recent template) + * New colors on install and upgrade files. (install.php, upgrade.php) + ! The installer now attempts to automagically fix potential mod_security problems. (install.php) + & Added a language string for the above change. (install language files) + ! Updated the installer to add the Babylon theme and add 'Core' to the name of the default theme. (install.php, install language files) + ! Checking a modified date of a cached attachment didn't in all cases work properly. (Display.php) + ! Add an extra check to make sure users aren't logged in twice. (LogInOut.php) + & Added a language string for the above change. (Errors language files) + ! Moving of large numbers of topics is now done in chunks. (MoveTopics.php) + ! Highlight the poll options that a user has voted. (Display.php, Display templates) + ! Fixed a PHP 5.0.5 compatibility issue causing ssi_whosOnline() to malfunction. (SSI.php) + ! Fix false entries of locks and stickies showing up in the moderation log. (Post.php) + ! Don't show non-moderator lock entries in the moderation log. (Post.php) + ! News and newsletter settings didn't have a page title. (ManageNews.php) + ! Fix a notice when setting a group to no permissions. (ManagePermissions.php) + ! XML-feeds with restricted/non-existing boards didn't show the proper error. (News.php) + ! Topic wasn't properly marked as read after inline modification. (Post.php) + ! Don't trim the replacement of a censored word. Allows to replacement containing smiley codes. (ManagePosts.php) + + Added an option to create a custom index for searching messages. (ManageSearch.php, ManageSearch template, Subs.php, Subs-Post.php, upgrade_1-1.sql, install_1-1.sql) + & Added and removed several language tags for the above change. (Search language files) + & Added a help-tag explaining the why of search indexes. (Help language files, Search language files) + ! Rewritten the search engine to better accomodate the fulltext and custom indexes. (Search.php) + ! Added a search setting to limit the amount of results of a search. (ManageSearch.php, ManageSearch template, Search language files) + ! Split the search index itself from the properties of the index in the admin interface. (ManageSearch.php, ManageSearch template) + + Stickyness of a topic can now be used as factor in the relevance rating of a search. (Search.php, ManageSearch.php, ManageSearch template) + & Added language tags for the above change. (Help language files, Search language files) + * Added margin to user logo (index template) + * Changed moderation buttons to a bar similar to the main navigation buttons. (Display template, style.css) + ! The installer was trying to create one of the tables twice. (install_1-1.sql) + ! One of the MySQL errors in the installer wasn't recognized by non-english MySQL versions. (install.php) + + +September 2005: +-------------------------------------------------------------------------------- + ! Fixed [/] parsing issue in announcements and notifications. (Subs-Post.php, Post.php) + ! Trying to create an icon in first place might result in an error. (ManageSmileys.php) + ! Deleting message icons plain didn't work. (ManageSmileys.php) + ! When moving a board with the new interface, make the first first child - where you clicked. (ManageBoards.php, Subs-Boards.php) + ! Properly handle non-English error messages from MySQL. (Errors.php, upgrade.php, convert.php) + ! Parameter count error on every account approval - din't hurt anything, though. (ManageMembers.php) + ! Deleting an empty category probably wouldn't work. (Subs-Boards.php) + ! Don't include the port in cookie domains. (Subs-Auth.php, smf_api.php) + ! In debug mode, flush like a crazy person - it works. (upgrade.php) + ! Do the fixing of the subject cache in chunks. (RepairBoards.php) + & Fixed some language ambiguities. (ManageBoards language files, ManageBoards template, ManagePermissions language files, Themes language files) + ! Staff reports now show members with global moderating powers too. (Reports.php, Reports language files) + ! Staff report entries are now sorted by display name. (Reports.php) + ! groupsAllowedTo() function wasn't working properly for global board permissions. (Subs-Members.php) + + Added an XMLhttp-option to doubleclick messages in order to edit them inline. (Post.php, Display template, Xml template, index language files) + ! Add a log entry to the moderation log after stickying/locking a topic in the post screen. (Post.php) + + Added initial files for new default theme. (babylon Theme directory, Settings language files, index template, style.css) + ! Upgrade now bases the theme paths off the default theme. (upgrade_1-1.sql) + + +SMF 1.1 RC1 September 20, 2005 +================================================================================ +September 2005: +-------------------------------------------------------------------------------- + ! Ignore user aborts while posting. (Subs-Post.php) + ! Make status.php output defunct processes correctly. (status.php) + ! Snitz password checking - SHA-256 - was not working. (LogInOut.php) + ! Unread replies couldn't sort on everything properly. (Recent.php) + ! New stepped forum error checking in repair - now works even on large forums reliably. (RepairBoards.php) + ! Created a work-around for the Firefox bug that causes the password manager to store the SHA'ed password instead of the original. (Load.php, Login template, Index template) + ! A few query optimizations. (Profile.php, SplitTopics.php) + ! Added a general function to retrieve the membergroups that have a specific permision (Subs-Members.php) + ! Added a general function to retrieve the members that have a specific permision (Subs-Members.php) + ! 'Report to moderator' notifications are now sent to anyone with the 'moderate_board' permission. (SendTopics.php) + ! When a guest reports a message to the moderator the notification will now contain the user's IP address. (SendTopics.php) + ! Strip out potentially sensitive strings from shown queries. (status.php) + * Make some more sub templates for WAP users, and use card titles - like Google does ;). (Wireless template) + & Show an error message if someone using wireless tries to go somewhere not ready for them. (Subs.php, Wireless language files) + ! Package installation readmes weren't showing! (Packages.php) + ! Fix some minor wording problems in some converters. (all converters) + * The Register template still had incorrect COPPA JavaScript. (Register template) + ! Add support for the MyPHP Forum password hashing. (LogInOut.php) + ! Added ADODB support to the converter for Windows, which seems to work for more providers/drivers than ODBC. (convert.php) + ! updateSettingsFile() didn't like having newlines at the end. (Admin.php) + ! Remove the namespace for RSS 2.0 feeds, let's see if they still work everywhere, though. (News.php) + & Changed whoall_repottm to whoall_reporttm. (Who language files) + ! Get the necessary information on FreeBSD too. (status.php) + ! Some post counts, like those over 1000, weren't showing properly in the memberlist. (Memberlist.php) + ! Add the ability to do more efficient stepped queries - not just LIMIT. (convert.php) + ! Add a MyPHP Forum 3.0 converter. (myphp_to_smf.sql) + * Moved the hashLoginPassword() and hashAdminPassword() functions to script.js. (index template, Login template, script.js) + ! Switched some repair steps to prevent unique-key insert conflicts. (RepairBoards.php) + ! Don't show a reply link if the topic is locked. (SSI.php) + ! Add a percentage bar to the repair process, and cleanup timeout detection. (RepairBoards.php) + & Added a check for missing members on log_notify. (Admin language files, RepairBoards.php) + ! Add file and line number information to the hacking attempt message for injection sniffing. (Subs.php) + ! Add support for Solaris and show proper command names. (status.php) + ! Added information about character set searching and fulltext not working on InnoDB. (ManageSearch.php, ManagePosts.php, Search language files) + ! Send unknown image attachments as image/gif and let the user agent figure out what they are. (Display.php) + ! Make the staff report use the new membersAllowedTo(). (Reports.php) + ! Don't bother with stats if there are no posts in any boards to speak of. (Profile.php) + & Added the word "now" to the remove old posts feature for clarity. (Admin language files) + & Clarified "cleanup permissions" strings to more strongly convey that it's an action. (Packages language files) + ! Separated functions for creating, modifying and deleting boards and categories. (ManageBoards.php, Subs-Boards.php) + + Added a function that allows to easily move boards between categories. (ManageBoards.php, ManageBoards template, ManageBoards language files) + ! If we just have free, use it. (status.php) + ! Put a little more work into the PHPSESSID showing check just in case. (Load.php, QueryString.php) + ! Be more tolerant of paths to the component itself. (simpleboard_to_smf.sql) + ! Rewrote parts of the search engine for faster searching. (Search.php, ManageSearch.php, Subs-Post.php, Subs.php, RemoveTopic.php, upgrade.php, upgrade_1-1.sql, install_1-1.sql) + ! Add warnings if a filename in a modification is not used as a full path. (Subs-Package.php) + ! Searching by guest name wasn't working. (Search.php) + & Added a separate error message for the case when you try to quote a deleted post. (Post.php, Errors language files) + ! Newsletters still weren't sending to regular/ungrouped members. (ManageNews.php) + ! Show permissions wasn't taking access permissions into account. Added a list of restricted boards. (Profile.php, Profile template, Profile language files) + ! Include topic cache maintenance in the repair boards function. (RepairBoards.php, Admin language files) + ! Fix empty unimportant logs for the new search tables. (Admin.php) + ! Fix error messages given by subject match overlapping word match in cases. (Search.php) + ! PHP-Nuke converter was checking for "attachments" not "bbattachments". (phpnuke_to_smf.sql) + ! Added support for XMLHTTP previewing for Opera 8.01+. (Post template) + ! Added detection of temporary tables in Search. (Search.php) + & $txt['cannot_move_own'] was missing. (Errors language files) + ! Got rid of the use of eval() in the new parser, although it still uses some lambda functions. (Subs.php) + ! Remove some package servers from the default install. (install_1-1.sql) + ! The call_user_func() function doesn't support reference parameters. (LogInOut.php, Subs-Members.php, Subs.php, Subs-Post.php) + ! Add a [time] tag. (Subs.php, Subs-Post.php) + ! Regular search wasn't doing subject searching properly. (Search.php) + ! Make the MarkRead() action support c, boards, and board. (Subs-Boards.php) + ! Require sesc for markasread actions. (Recent.php, Subs-Boards.php, MessageIndex template, Display template, Recent template, BoardIndex template) + ! Anywhere that used base64_decode() wasn't friendly to +'s. (ManageErrors.php, Search.php, PersonalMessage.php, ManageMembers.php, Modlog.php) + ! Parser didn't like :-\[/quote] etc. (Subs.php) + ! Back out the unicode filtering stuff because it did effect UTF-16. (QueryString.php, Subs.php) + ! Try to avoid some joins on the boards table for longer queries. (Recent.php) + ! Editing censored words now requires the moderate_forum forum permission instead of the admin_forum permission. (ManagePosts.php, Subs.php) + ! Handle 's in email addresses for newsletters better. (ManageNews.php) + * Mark as read was in some cases not sending a session code. (Recent template) + ! Fixed some of the repair steps that didn't repair properly. (RepairBoards.php, Admin language files) + ! Changed the way subjects are broken down into words in the subject caching table. (RepairBoards.php, Subs.php, install_1-1.sql, upgrade_1-1.sql, upgrade.php) + ! Make the installer able to handle the default subject for searching. (install.php) + & Minor case change to $txt[454] and $txt[455]. (index language files, index.english.xml) + ! Fix searching by user, wasn't working since my last fix :/. (Search.php) + ! Search results per page weren't ordered properly. (Search.php) + * Remove the membergroup selection for attachments that didn't work anyway. (ManageAttachments.php, ManageAttachments template) + & And its associated language entries. (Admin language files) + ! The guest_post_no_email setting didn't work properly for previewing posts. (Post.php) + + +August 2005: +-------------------------------------------------------------------------------- + ! PHP code wasn't being posted correctly in some cases. (Subs.php) + ! Try harder to recover from deadlocks. (Errors.php) + ! Fixed a typo in sendpm() that was never being a problem. (Subs-Post.php) + ! Cleaned up internationalization handling. (Search.php) + ! That stupid upgrade_php?step thing was still happening in the upgrader. (upgrade.php) + ! The Burning Board converters were not properly converting polls. (burningboard2_to_smf.sql, burningboardlite_to_smf.sql) + ! The package manager was not properly matching version ranges. (Subs-Package.php) + ! The package manager wasn't doing quotes properly. (Subs-Package.php) + ! Improved the speed of a query in the statistics panel a little. (Profile.php) + ! Improved the Simpleboard converter a little. (simpleboard_to_smf.sql) + ! The status script didn't like not having /etc access. (status.php) + ! Improved the itemcode parsing inside quotes and next to each other. (Subs.php) + ! Quoted printable and character 128 still weren't friends. (Subs-Post.php) + ! Made the installer able to log you in even if the account exists, as 1.0 did. (install.php) + * Fix the newsfader for Internet Explorer 4. (fader.js) + * Minor aesthetic improvements to improve XHTML compatibility. (MessageIndex template, script.js, Post template, index template, BoardIndex template, Subs.php, Admin.php, ModSettings.php) + & Since administrators are discouraged from adding post groups to board access rights, show regular members in announce. (Post.php, Post language files) + ! With search engine friendly URLs, you couldn't have commas in parameter values. (QueryString.php) + ! Try harder not to show errors during a database dump. (DumpDatabase.php) + ! Handle url="" and email="" in vBulletin converters. (vbulletin_to_smf.sql, vbulletin3_to_smf.sql) + ! Don't backup the error log, it's not worth it. (DumpDatabase.php) + ! The converters weren't doing image attachments and avatars correctly. (176; convert.php) + ! If a filter had no results, sort direction was lost. (ManageErrors.php) + ! When filtering is using a wildcard, and you filter on something that would be the same, don't filter on the same wildcard. (ManageErrors.php) + ! The error message you got when trying to access edit news was wrong. (ManageNews.php) + ! Try to make sure register_globals is never a problem. (SSI.php) + * JavaScript error changing COPPA settings. (Register template) + ! Skip evil directories when making a backup. (Subs-Package.php) + ! Do the custom avatar directory properly and better check against doing directories twice in cleanperms. (Admin.php) + ! Do the registration date correctly. (phpnuke_to_smf.sql) + ! Added Land Down Under converter. (ldu_to_smf.sql) + ! Add ODBC support to the converter - now we can convert Snitz, etc. directly. (convert.php) + ! Strip characters we don't like from the subject line. (Post.php) + ! Make it so open_basedir doesn't affect status.php... (status.php) + + +SMF 1.1 Beta 4 August 20, 2005 +================================================================================ +August 2005: +-------------------------------------------------------------------------------- + * Changed color: #FF0000 to red in a few places. (Admin template, Calendar template, PersonalMessage template, Profile template, Subs.php, Printpage template, Reports template) + ! Small optimization to the member statistics. (Profile.php) + ! Added censorWholeWord to the list of settings to skip if they don't exist. (upgrade_1-1.sql, upgrade_1-0.sql) + ! Fix "space at the beginning of a line" regression. (Subs.php) + ! Added mail_strip_carriage and disableRegisterCheck. (Register.php, Subs-Post.php) + ! Use file_get_contents() instead of implode/file because it's more efficient. (Subs-Compat.php, Themes.php, Subs-Package.php, Load.php, ManageRegistration.php, Packages.php, Register.php, Display.php, Subs-Graphics.php) + ! Use md5_file() instead of md5 on the file contents because it's more efficient. (Subs-Compat.php, Display.php) + * Cleaned up lots of form usage to better fit with standards. (script.js, Admin.php, ModSettings.php, MessageIndex template, Display template, Search template, index.xslt, Login template, SplitTopics template, Search template, Themes template, Poll template, MoveTopic template, Calendar template, Admin template, ManagePermissions template, ManageBans template, ManageAttachments template, ManageBoards template, ManageMembergroups template, ManageMembers template, ManageNews template, ManageSmileys template, Profile template, spellcheck.js, Post template, searching.xslt, loginout.xslt, webinstall.php, Register template, posting.xslt) + * Fixed the jumpto menu when an ? was in scripturl. (MessageIndex template, Display template, Search template) + ! Avoid doing topicseen more than necessary. (Display.php, Subs-Boards.php, Subs-Post.php, MessageIndex.php) + ! Removed avatar settings 'avatar_allow_server_stored' and 'avatar_allow_upload' in favour of permissions for each avatar storage method. (Load.php, ManageAttachments.php, ManagePermissions.php, Profile.php, Subs.php, , install_1-1.sql, upgrade_1-1.sql, ManageAttachments template, Profile template, phpbb_to_smf.sql, phpnuke_to_smf.sql, yabbse_to_smf.sql) + & Added some new (and removed some old) language entries for the above change. (Admin, Errors, ManagePermissions and Reports language files) + ! Make it a lot easier to make post group statistics based on more than just posts. (Subs.php, Subs-Members.php, ManageMembers.php) + ! Cache xml/rss feeds for guests - much of the time - too. (News.php) + ! Make it so that searching for errors that have variable text in them can show them all - I know this may be controversial, but we need to make it at least an option. (ManageErrors.php, LogInOut.php) + ! Resort the boards table after a repair. (RepairBoards.php) + ! Approximate calendar dates as mid-day since we don't know the actual time. (Display.php) + * Local Moderator cannot have group permissions, so don't allow the selection thereof - also sort groups and handle checkboxes better. (ManagePermissions.php, ManagePermissions template) + ! Cache those permissions too! (Load.php) + * Add inline permissions for viewing and posting attachments. (ManageAttachments.php, ManageAttachments template) + & Add language entries for the above permissions interface. (Admin language files) + ! Make status.php command-line friendly. (status.php) + & Removed the part about the username not having special characters allowed. (Login language files) + ! Make the converter handle prefixes better. (convert.php) + * Make poll option choices labels. (Display template, Display.php, SSI.php) + ! Cleanup handling of non-validating xml characters. (Subs.php) + ! Update version numbers to SMF 1.1 Beta 4. (all files) + ! The percentage complete for a maintenance recount wasn't updating correctly. (Admin.php) + ! Extra ban triggers weren't being done properly when adding a new ban from a profile. (ManageBans.php) + * Add a stylesheet for the print media. (index template, print.css) + ! Validate input as proper, valid XHTML. (QueryString.php, Subs.php) + ! Don't use accelerator caching when in command line mode. (Load.php) + ! Fine, just bump the memberGroups column to 255 and be done with it. (install_1-1.sql) + ! Avoid using a REPLACE INTO for log_topics. (Display.php, Subs-Post.php) + ! Prevent a notice when catching actions. (Themes.php) + & Tweak the notify_send_type strings a bit to clarify them. (Profile language files) + ! Fixed an error given when no pms are found from a search. (PersonalMessage.php) + * Splitting topics wasn't working 100% correctly. (SplitTopics template) + ! If you make the max message length too high, increase the size of the body column. (ManagePosts.php) + * If the body column is mediumtext, don't allow creation of a fulltext index. (ManageSearch.php, ManageSearch template) + & Added a warning to describe the situation in which the fulltext index CANNOT be created. (Search language files) + & Clarify the globalCookies help text. (Help language files) + ! Support in package-info.xml files. (Subs-Package.php, Packages.php, package-info.dtd) + ! Ignore case and handle 1.0.* properly in matchPackageVersion(). (Subs-Package.php) + ! Add a conditional comment ON UPDATE to the log_online table for MySQL 4.1.2 and above. (upgrade_1-1.sql, install_1-1.sql) + ! Fix coloring, validating, and formatting issues with the parser error message. (Load.php) + * Use smf_session_id where possible in JavaScript. (index template, Login template, Themes template, Packages template, ManageSmileys template) + ! After editing smileys, flush the cache immediately. (ManageSmileys.php) + ! Fix a notice you might get when saving your profile with a bad external avatar. (Profile.php) + ! Cookieless logins, etc. weren't working properly on PHP 5. (Load.php) + ! XML stuff didn't work without cookies enabled. (QueryString.php) + ! Let things expand toward a higher cache level. (News.php, Load.php, Subs.php) + ! Permissions weren't working properly from smf_api.php. (smf_api.php) + ! Made sure everything was ship-shape with PHP 4.1.0. (Subs.php, Subs-Compat.php, Load.php, index.php) + ! The status script was generating errors when MySQL was not available. (status.php) + ! Prevent a package manager error when no searches are found. (Subs-Package.php) + ! If the package manager can get enough free ram, keep a workspace of modified files and modify none before all can be. (Subs-Package.php, Themes.php, Packages.php) + ! When showing xmlArray errors, do a backtrace if possible. (Subs-Package.php) + + Now packages can have multiple modification files which depend on each other. (Subs-Package.php) + ! Save uploaded avatar widths and heights into the database. (Profile.php, Subs-Graphics.php) + ! Allow the subject to be passed in the query string of a new post. (Post.php) + ! Make sure to check the moderator's session on lock/sticky. (LockTopic.php) + ! An extra (unnecessary and slow in this case) query was being run on some searches. (Search.php) + ! People want the copyright shown for SSI, so show it. (Subs.php) + ! Double check the user when deleting posts from the recent posts list. (Recent.php) + ! Try to do even more safety checks in updateSettingsFile(). (Admin.php) + ! If topics rows go missing, try to put posts in the right boards. (RepairBoards.php) + ! Automatically switch URLs to https when the forum is accessed that way. (Load.php) + ! Fix some avatar and attachment problems in the phpBB converters. (phpbb2_to_smf.sql, phpnuke_to_smf.sql) + ! Move some data to be cached at level 3. (Subs.php, Load.php, MoveTopic.php) + ! Minor improvements to the package manager's template. (Packages template) + ! Improved (I think) a search query to use better indexes. (Search.php) + ! Add support for APC. (Load.php, Admin.php) + ! The removeMessage() function was doing too much work. (RemoveTopics.php) + * Open a window with scrollbars always if the window is expected to be too large for the screen. (script.js) + ! If caching is enabled at level 2, don't let people start a new search until the last one finishes, or 90 seconds pass. (Search.php) + ! Don't allow prefetching when it comes to searching and unread topics/replies. (Search.php, Recent.php) + ! Make upgrade work with 1.0 Beta 4. (upgrade_1-0.sql) + ! Fix a possible race condition involving totalTimeLoggedIn. (Subs.php) + ! Don't dump the user_settings cache so often. (Subs.php, Load.php) + ! Make sure the feed title is always properly escaped. (News.php) + + +July 2005: +-------------------------------------------------------------------------------- + ! The profile_edit_own and profile_edit_any permissions had not been properly removed. (install_1-1.sql, upgrade_1-1.sql) + ! Repairing your forum didn't work properly for versions of MySQL older than 4.0.4. (RepairBoards.php) + * Hits were not showing properly in the stats. (Stats template) + & The installer now warns you if you install over an older version. (install.php, Install language files) + ! Improved the use of temporary tables to increase the performance of unreadreplies. (Recent.php) + ! Did the above for all unread topics. (Recent.php) + ! The shorten_subject() function wasn't properly handling "...&#". (Subs.php) + * The first found result wasn't being quoted in find members. (Help template) + ! Temporary tables are now better supported. (ViewQuery.php) + ! Upgrader didn't support going from older versions of 1.0. (upgrade.php, upgrade_1-0.sql) + ! Link back to maintenance was wrong. (RepairBoards.php) + ! Crazy ol' Safari didn't like the progress bar. (Admin template) + ! Use POST for JavaScriptModify. (Post.php) + ! Added the first and last post ids to context. (MessageIndex.php, Recent.php) + * Added setting for enabling buddy list. (ModSettings.php, Subs-Post.php, Profile.php, Load.php, Profile template, upgrade_1-1.sql) + & Added language string for the buddy list setting. (ModSettings language files) + * Moved the buddy section to it's own area in profile, and added much more contextual information to the buddy screen. (Profile.php, Profile template) + & Added/removed some language strings for the above change. (Profile language files) + ! Typo using imagesavealpha. (Subs-Graphics.php) + ! The package manager now better displays installability on download. (PackageGet.php, Subs-Package.php) + * Messed with the look of the package download listing a little. (Packages template) + ! Show MySQL's error message for the table_test. (convert.php) + ! Properly detect what an attachment's filename should be in all the converters. (all converters, upgrade_1-1.sql, Subs.php) + ! Allow links of the format . (Subs.php) + ! Typo on registration from admin center. (Subs-Members.php) + ! Create backups upon package installation and uninstallation. (Packages.php, Subs_package.php) + ! The copytree() function wasn't actually copying files. (Subs-Package.php) + ! Admin member activation language usage wasn't up to spec. (ManageMembers.php) + ! A few typos in the phpBB/phpNuke and Burning Board converters. (phpbb2_to_smf.sql, phpnuke_to_smf.sql, burningboard2_to_smf.sql, burningboardlite_to_smf.sql) + ! Added a bunch of new repair options to the repair feature - needs more cleanup. (RepairBoards.php) + & Added an error for profile's showPosts when load averages are high. (Profile.php, Errors language files) + ! Some versions of MySQL didn't support the search manager's detection of fulltext indexes. (ManageSearch.php) + ! Smileys with special characters in them didn't render well. (ManageSmileys.php, Subs-Post.php) + * Clean up a few minor invalid HTML bits here and there. (various files) + ! Persist the template debugging stuff in links and redirects. (Subs.php, QueryString.php) + * Don't use document.write(). (ManageMembergroups template, ManagePermissions template, Admin template, ManageNews template, Profile template) + * Avoid the usage of # as a link target. (Subs.php, index template, Admin template, Help template, ManageMembergroups template, ManagePermissions template) + ! Fixed quite a few bugs in the MercuryBoard converter. (mercuryboard_to_smf.sql) + ! Updated a few things in all the converters. (convert.php, all converters) + ! Fixed some minor bugs in the Oxygen and XMB converters. (oxygen_to_smf.sql, xmb_to_smf.sql) + ! The UBB.threads converter now works with 6.5 as well as 6.4 afaik. (ubbthreads_to_smf.sql) + ! Fixed some problems with the YaBB SE converter. (yabbse_to_smf.sql) + ! Added new-style version of the Snitz converter. (snitz_to_smf.sql) + ! Added converters for MyBulletinBoard (which is amazingly like vBulletin...) and UseBB. (mybb_to_smf.sql, usebb_to_smf.sql) + ! Added a converter for Deluxe Portal 2.0. (deluxeportal2_to_smf.sql) + * Fixed the latest and greatest themes alignment. (Themes template) + ! In some rare cases, members could delete replies to their own topics even if not granted that permission. (Recent.php, RemoteTopics.php) + ! Added a converter for eblah p9. (eblah_to_smf.php) + ! The YaBB converter was missing the getAttachmentFilename function. (yabb_to_smf.php) + ! Added converter for FUDforum. (fud_to_smf.php) + ! Autolink links like "www.example.com" at the beginning of posts. (Subs.php) + ! The YaBB and E-Blah converters now better handle merging. (yabb_to_smf.php, eblah_to_smf.php) + ! The OpenBB converter is now tested and working properly. (openbb_to_smf.sql) + ! Apparently fetch_web_data() is more reliable than fopen'ing the URL, even with allow_url_fopen off? (PackageGet.php) + ! Added Zorum 3 converter - wow, I hate wannabe oop database structures. (zorum3_to_smf.sql) + ! Added IkonBoard converter and its style of hashing to LogInOut.php (ikonboard_to_smf.sql, LogInOut.php) + ! Some converters weren't doing attachments/uploaded files properly. (all converters) + ! Remove yesterday as well from the who's online list time. (Who.php) + ! Actually reconnect on lost connection errors. (Errors.php, upgrade.php) + ! Added a MyTopix converter. (mytopix_to_smf.sql) + ! Work around a bug in PHP 4.1.x for labeling. (PersonalMessage.php) + ! Added a page title to the manage boards settings page. (ManageBoards.php) + ! Don't auto-close tags for fake tags. (Subs.php) + ! Make convert.php automatically work out ID_CATs and ID_PARENTs and childLevels - simplifying a bunch of converters. (convert.php, all converters) + ! Moved the Forum Error Log from Errors.php to ManageErrors.php (index.php, Errors.php, ManageErrors.php) + ! Reversed the default sort order of the error log to match the Moderation log, the Ban log and the default topic order. (Subs.php) + ! The converters now properly delete old attachments before converting new ones. (all converters) + ! Simplified the method of attachment conversion in all converters. (all converters) + ! Possible file_exists() error. (Subs-Package.php) + ! Fixed the permission_mode error while modifying a board. (ManageBoards.php) + * The help files were not compatible with Firefox. (index.xslt, loginout.xslt, pm.xslt, posting.xslt, profile.xslt, registering.xslt, searching.xslt, style.xslt) + ! Cleaned up poll option conversion in XMB/Oxygen's converters. (xmb_to_smf.sql, oxygen_to_smf.sql) + ! Fix login redirection when using queryless URLs. (QueryString.php, LogInOut.php) + * Sorting and better pagination is now supported in unread/unreadreplies. (Recent.php, Recent template) + ! Add unreadMessages to the maintenance recount. (Admin.php) + ! Cleaned up the latest packages section, made it work mostly as intended - needs more work, though. (latest-packages.js) + ! Add integration for outgoing emails to fix links in them. (Subs-Post.php) + ! Add apache_reset_timeout calls in a few places, if the function exists. (upgrade.php, convert.php, yabb_to_smf.php, eblah_to_smf.php, create_backup.php, restore_backup.php, DumpDatabase.php, Subs-Package.php) + ! Create attachment thumbnails on posting, not just on display... and ask for more memory. (Post.php, Subs-Graphics.php) + * Bans, groups, imode pms, and moderators are now selected by display name *properly*, not username. (ManageBans.php, ManageBoards.php, ManageBoards template, ManageMembergroups.php, Subs-Auth.php) + ! The buddies section really wasn't looking up members properly, and the ignore list wasn't working either. (Profile.php) + ! The querystring wasn't being parsed properly for PHP 4.1.x. (QueryString.php, Subs-Auth.php) + ! Don't continue on if the member data is cruft. (Load.php) + ! Manage labels from the pm center was conflicting with PHP-Nuke. (PersonalMessage template, PersonalMessage.php) + ! Added some actual search code to personal messages - still a lot to do with the context. (PersonalMessage, PersonalMessage template) + & Renamed showBuddies to editBuddies. (Profile.php, Profile template, Profile language files) + ! Sped up counting of members for newsletters - and ensured it respects whether post groups are enabled or not. (ManageNews.php) + * The boardindex no longer shows collapsed/empty categories. (Wireless template) + ! There was a typo that only affected the forum when stickies were off. (Display.php) + ! Greatly speed up the "delete old log_topics entries" query by forcing an index. (Subs.php, Subs-Boards.php) + ! Don't search by email address if there's no @. (Subs-Auth.php) + ! Fix calendar moderation action notice. (Post.php) + & Modified note in $txt['ftp_setup_why_info'] to include 755. (Install language files) + ! Better support PHP suExec hosts in the installer. (install.php) + ! When sending emails and when posting, try to ignore timeouts from PHP or Apache. (Subs-Post.php, Post.php) + ! Make searching by member name a lot like searching for member's names elsewhere. (Search.php, PersonalMessage.php) + ! Updated a small thing for PHP 4.1.x. (Themes.php) + ! Searching wasn't respecting age restrictions properly. (Search.php) + ! To avoid errors, skip broken topics in converters. (all converters) + ! Added versatileBulletinBoard 1.0.0 converter. (vbb_to_smf.sql) + ! Some tags weren't allowed to be mixed case. (Subs.php) + ! Cache user settings periodically if cache_enable is set to 2. (Load.php, Subs.php) + * Display template wasn't validating if you had quick moderation on but couldn't do any moderation. (Display template) + ! Alright, now both sets of tags can be either case. (Subs.php) + * Memberlist searching was missing label's for checkboxes. (Memberlist template) + * Fixed entity parsing in remove/apply label drop down. (PersonalMessage template) + ! The Snitz converter now does moderators, censored words, and notifications. (snitz_to_smf.sql) + ! Ignore bad cached data. (Load.php) + ! Simplify some table upgrade stuff to make upgrades from beta 1.0 versions easier. (upgrade_1-1.sql) + ! Include in the cache keys the modification time of Load.php. (Load.php) + ! Finally fixed the nasty bug that was removing theme options. (Profile.php) + ! Cache settings, theme settings, and theme options - it isn't perfect when mucking up theme settings, but it should be okay. (Load.php, Profile.php, Themes.php) + * Disable both order boxes on changing categories. (ManageBoards template) + ! Administrators could still be deleted. (Subs-Members.php) + * Guests' emails weren't properly being hidden. (Display.php, PersonalMessage.php, Display template, PersonalMesasge template) + ! Installer wasn't working properly anymore. (Load.php) + ! Remove periods from the cookie name on save. (Admin.php) + ! The package manager now works much better under safe mode/suexec hosts. (Packages.php, Subs-Package.php) + & Slightly improved the personal message search feature, and added two new language entries. (PersonalMessage.php, PersonalMessage template, Personal Message language files) + * Added label searching to the pm interface, and made the search results link back to the relevant folder. (PersonalMessage.php, PersonalMessage template) + & Added a few additional language entries for the above change. (PersonalMessage and Errors language files) + & Somewhat minor pm typos. (PersonalMessage language files) + ! Made it easier to add additional protocols for url tags. (Subs-Post.php) + ! Cleaned up more package manager stuff when permissions are weird and safe mode is on. (Subs-Package.php, PackageGet.php, Packages.php) + ! Remove all members from a group when it is changed to a post group. (ManageMembergroups.php) + ! Fix some problems with SMF working on MySQL 3.23.28, and raise the requirements to that. (Admin.php, ManageErrors.php, ManageBans.php, upgrade.php, install.php, webinstall.php, convert.php, yabb_to_smf.php, eblah_to_smf.php) + ! Use a full path to Load.php instead of __FILE__ to improve things. (Load.php) + ! Guests weren't getting 'Guest' as their name properly. (Load.php) + ! The vBulletin3 converter now supports file-based attachments. (vbulletin3_to_smf.sql) + & Cleaned up the interface for and errors reported by the repair feature, added checking of pm senders. (RepairBoards.php, Admin language files, index language files) + ! Added command-line support to the upgrader because that won't timeout. (upgrade.php) + ! Added YaBB 2 converter, and made conversion system able to understand php files. (yabb2_to_smf.php, convert.php, yabb_to_smf.php, eblah_to_smf.php) + ! Expanded the SMF Database Error message emailed to the admin. (Subs-Auth.php) + ! Moved isReservedName() function from Security.php to Subs-Members.php. (Security.php, Subs-Members.php, Post.php, Profile.php, Register.php, Subs-Auth.php) + ! Use an explicit GMT in newsfeeds to avoid problems with various webservers. (News.php) + * Don't use $user_info in templates, and show IPs to those who can see them. (Profile template, Profile.php) + ! Stop lazy bumkins from trying ssi_boardNews() on an empty board. (SSI.php) + & Clarify error_query_not_specific_enough. (Errors language files) + ! Trying to add zero members to a group broke things bad. (Subs-Members.php) + ! Latest posts weren't getting cached at all. (BoardIndex.php) + * Allow double clicking a suggestion to replace automatically when spell checking. (Post template) + ! Use SHOW TABLES if and when SHOW TABLE STATUS fails. (Admin.php) + ! Don't depend on $db_prefix not containing the database name. (Load.php, Admin.php, DumpDatabase.php, create_backup.php, upgrade.php, SSI.php, ManageSearch.php) + ! Don't create the poll until we know there are no errors with the attachments. (Post.php) + ! If possible, when quoting pms, show a link to the poster and the time it was sent. (PersonalMessage.php, Subs.php) + ! Fix over-caching of dates. (Subs.php) + ! Made a createAttachment() function to compliment the others. (Subs-Post.php, Post.php) + ! Moved the sha1 functions from Security.php to Subs-Compat.php to reduce compilation time a bit for users with PHP >= 4.3.0. (index.php, Security.php, Subs-Compat.php, SSI.php, install.php) + ! Sticky the show buddies option in find members. (Subs-Auth.php) + ! Increase the strictness of the icon filename check. (Post.php) + ! Show a pretty error message if PHP isn't supported and they run the installer. (install.php) + * Finding members to add to your buddy list wasn't working. (Profile template) + * Make the member search template look more like its other friends, add check all. (ManageMembers template) + ! Avoid REPLACE/INSERTs into log_online when an UPDATE is sufficient. (Subs.php) + & Automatically detect/fix wrong SMTP port for SSL. (index language files, Subs-Post.php) + ! The Snitz converter still wasn't doing order properly. (snitz_to_smf.sql) + ! Search engines weren't always getting the profile, etc. pages. (QueryString.php) + * Added a guest_post_no_email setting to make it easier not to require guest emails. (Post.php, Post template) + * Further improved the template for pm searching - made full display of messages more useful. (PersonalMessage.php, PersonalMessage template) + & Added a new language string to Personal Message language file for the search option on the side bar. (PersonalMessage language files) + + +SMF 1.1 Beta 3 Public June 28, 2005 +================================================================================ +June 2005: +-------------------------------------------------------------------------------- + ! Ignore misspelled search words that are only misspelled in case. (Search.php) + ! Make logins redirect, back to the topic they came from, even with quick login. (LogInOut.php) + ! Don't allow a fixLongWords setting that could break preg_replace. (Subs.php) + ! Cleaned up after some copyright bad mojo. (Subs.php) + ! Moved updateLastMessages() to Subs-Post.php. (SplitTopics.php, RemoveTopic.php, MoveTopic.php, Subs-Post.php, Subs.php) + * Added smf_avatarResize() to script.js. (script.js, Subs.php) + ! Make it so people who are not actually viewing a board or topic don't show as doing so. (Load.php, Security.php) + ! Cleaned up pages listing for when boards= is specified. (Recent.php) + * Moved table optimization template into Admin template. (Admin.php, Admin template) + & Removed outdated language string $txt[137] and replaced with $txt['maintain_return']. (RepairBoards.php, Admin language files) + & Removed all iterations of the 'Settings' string and replaced them with a global string in the index language file. (index language files, Admin language files, Login language files, ManageBoards language files, ManagePermissions language files, ManageSmileys language files, Search language files) + * Several template changes for above change. (ManageBoards.php, ManageMembergroups.php, ManageNews.php, ManagePermissions.php, ManageRegistration.php, ManageSearch.php, ManageSmileys.php, ManageBoards template, ManageNews template, ManageSearch template, ManageSmileys template, Register template) + ! Added page titles to some Manage Permissions pages. (ManagePermissions.php) + ! Tightened up permission checking when delete all posts of a member. (Profile.php, Profile template) + ! Attempted to do something to help make the permission manager not crawl along on big forums. (ManagePermissions.php) + ! Made differences from global permissions bold. (Reports.php) + ! Redirect back to board permissions after changing board to local/global. (ManagePermissions.php) + ! Add more filtering to the reports. (Reports.php) + ! Languages in multiple themes were being listed more than once. (Profile.php, Admin.php) + * I really can't stand titlebg'd boxes *inside* other boxes, it's like having a button on a button. (Packages template) + * The classic theme had NOT been updated for the release. (Post template) + ! Holidays were only getting added if they already existed, and not if they didn't. (upgrade_1-1.sql) + ! Attempted to use characters alone when possible, if iconv is not available. (Subs-Post.php) + * Don't expand the stats twice in a row. (Stats template) + ! Added some more path identifiers to the package manager. (Subs-Package.php) + & Updated $helptxt['allow_disableAnnounce']. (Help language files) + ! Smileys with brackets in them could break some parsing. (Subs.php) + ! The registration integration function was misnamed. (Subs-Members.php) + ! The status.php script now works for 1.x and old 1.1 Beta versions, and detects its path a lot better. (status.php) + ! Support using a querystring in $scripturl better. (Subs-Auth.php) + ! The bbc parser now does printpage as well, fixing a LOT of various bugs. (Subs.php, PrintPage.php) + * Missed a th in style.css. (style.css, Themes.php) + ! Sent notifications should be stripped of slashes before parsing. (Subs-Post.php, Post.php) + ! Breaking up long words works a lot better now. (Subs.php) + ! Ignore the recycle bin in ssi_topBoards(). (SSI.php) + ! Edit message icons didn't show anything but defaults for some themes. (ManageSmileys.php) + ! Now deeper caching works better by clearing things. (Subs.php, MoveTopic.php, Load.php) + ! Don't always pipe host for lookups, try to vary it. (Subs.php) + ! Fix icon theme detection. (Post.php, ManageSmileys.php, Search.php, Recent.php, MessageIndex.php) + ! Don't try to assign the database result by reference since it doesn't matter to Zend. (Subs.php, smf_api.php) + ! Cleaned up some debugging stuff. (Subs.php, ViewQuery.php) + ! Cleaned up some places where the language was changed and not changed back. (Subs-Post.php) + ! Sending out reminder emails, etc. now respects the user's language. (ManageMembers.php) + ! The template error messages are now more i18n-friendly. (Load.php) + ! Now, downloadAvatar() should work even with allow_url_fopen disabled. (Subs-Graphics.php) + ! Added a bunch of missing mysql_free_result()'s. (various files) + ! Added AfriNIC as new Regional Internet Registry. (Profile.php, Profile language files) + ! Cleanup handling of avatars that *aren't* URLs. (Profile.php, Subs-Graphics.php) + * Added a not_done sub template to the Admin template, and used it for maintenance. (Admin template, Admin.php) + & Added a few strings for the above interface. (Admin language files) + * Cleaned up some bad HTML (style.css, various files) + ! Now, if you use ?debug and have a decent (non-IE) browser, sends as application/xhtml+xml. (Subs.php) + ! The quote tag wasn't taking a single parameter. (Subs.php) + ! Updated a bunch of converters to wrap at 80 and other minor changes. (all converters) + ! Added a OpenBB converter from Kirby. (openbb_to_smf.sql) + ! Added a Simpleboard converter, also from Kirby. (simpleboard_to_smf.sql) + ! Changed the converter engine a decent amount: now supports parameters, looped updates, no settings file, nicer debugging, and fixes some bugs. (convert.php, all converters) + ! Add some preliminary injection protection to validate queries. (Subs.php) + ! Make the restriction on having bbc in your name a bit tighter. (smf_api.php, Subs-Members.php, Subs-Auth.php) + ! Added [code=...] syntax, which invites alternative color coding opportunities. (Subs-Post.php, Subs.php) + ! Clean up HTML, no longer allow cross nesting of HTML and tags. (Subs.php) + & Updated $helptxt['registrations'] and $helptxt['smileys'], and removed $helptxt['disabledBBC']. (Help language files) + ! Template errors now work even with allow_url_fopen disabled. (Load.php) + * Added interface to change file permissions automatically. (Admin.php, Packages template) + & Added language files for the cleanup permissions interface. (Packages language files) + ! Notifications often were not sending. (Subs-Post.php) + ! Changing your password salt was not working either. (Subs.php, LogInOut.php) + * Poll editing wasn't working properly. (Poll.php, Post.php, Poll template) + ! Don't use PCRE to check db strings, because it segfaults under PHP 4 sometimes. (Subs.php) + ! Cleanup fixTags() for the new parser. (Subs-Post.php) + + Added optional load average detection, currently hidden. (Load.php, Subs-Auth.php, Errors.php, Search.php, Recent.php) + & Added language entries for the above change's messages. (Errors langauge files) + + +SMF 1.1 Beta 3 June 9, 2005 +================================================================================ +June 2005: +-------------------------------------------------------------------------------- + ! Fixed some parsing problems that could lead to injection holes in IE. (Subs.php) + * Added an update window in the admin area, to inform users when new updates become available. (Admin template) + & Added two new language entries for the above change. (Admin language files) + & Clarify $txt['package_install_options_ftp_why']. (Packages language files) + ! A few tabs and permissions were wrong after the split. (ManageMembers.php, ManageMembergroups.php, ManageBoards.php, ManageNews.php) + * Added icons to the personal messages section to show if a message has been replied to. (PersonalMessage template, added pm_read.gif & pm_replied.gif) + & Added some additional language strings for the above change. (PersonalMessage language files) + ! Fixed some corrupt buddy online images. (buddy_useron.gif) + ! Clean up some error messages. (ManageBans.php, ManageMembergroups.php, ManagePermissions.php, Errors.php) + ! The current board wasn't being cached properly in cache level 2. (Load.php) + & Removed $txt['smf89'] and added a few strings to repairs. (RepairBoards.php, Admin language files) + & Update $txt['activate_after_registration'] with a message about junk and spam. (Login language files) + ! Moved adminNotify() function to Subs-Posts.php. (Register.php, Subs-Post.php) + ! Moved (admin) registration center to ManageRegistration.php. (Register.php, ManageRegistration.php) + ! Added a general function for registration of members to Subs-Members.php. (Register.php, ManageRegistration.php, Subs-Members.php) + ! Moved Revered names to the Register Center (ManageRegistration.php, Subs.php, index.php) + & Removed $helptxt['edit_reserved'] and updated $helptxt['view_members'] and $helptxt['registrations']. (Help language files) + & Added $txt['admin_register_done']. (Login language files) + & Replaced Admin center shortcut to the Registration center by a shortcut to the Member center. (Admin language files, Admin.php) + ! Force a re-evaluation of all bans after upgrading. (upgrade_1-1.sql) + ! Some speed improvements. (Search.php, Profile.php) + ! Don't log banned web accelerators. (Security.php) + ! Fixed parse error flash BBC. (Subs.php) + ! Fixed email not always hidden in RSS feeds. (News.php) + ! Removed server.list in favor of a new database table for package servers. (Admin.php, Packages.php, PackageGet.php, upgrade_1-1.sql, install_1-1.sql) + ! Add board-specific titles to the news feeds. (News.php) + ! Try to allow intranet IPs through a proxy as long as the proxy is behind the intranet too. (QueryString.php) + * Never allow deny for guests. (ManagePermissions.php, ManagePermissions template, upgrade_1-1.sql) + ! Use bitwise shifting and logic for setBit() and getBit(). (Security.php) + * Fix a zillion places where the HTML for selected or checked was ugly. (Register template, Calendar template, ManageBoards template, ManageMembers template, ManageCalendar template, Poll template, Post template, Profile template, SplitTopics template, Reports template, ManageSmileys template) + * Fixed some extra semicolons in pm reply links. (PersonalMessage template) + ! Fixed the js resize not working if one of the limits was not set. (Subs.php) + ! Go, go, update files racer! (all files) + ! People who weren't administrators couldn't delete other members. (Subs-Members.php) + * Typo which made invalid XHTML. (Display template) + * Cleaned up a little more bad HTML. (Post template, ManageAttachments template, Poll template, Profile template) + ! Put in the new bbc parser. (Subs.php, Subs-Post.php, Display.php, Load.php, ManageNews.php, MessageIndex.php, News.php, PersonalMessage.php, Post.php, Profile.php, Recent.php, Register.php, Search.php, SplitTopics.php) + ! Fixed some places where body was being pulled and parsed in full when it shouldn't. (SSI.php, Recent.php) + ! Made it much more possible to cache the output of parse_bbc(). (SSI.php, Display.php, News.php, PersonalMessage.php, Post.php, Profile.php, Recent.php, Register.php, Search.php, SplitTopics.php, Subs.php) + ! The split topics interface was not respecting smileysEnabled. (SplitTopics.php) + ! Mixed up the admin center links a little. (Subs.php, Admin template) + & Adjusted $txt[426], $txt[427], $txt[428], $txt[501], $txt['layout_controls'], $txt['censor_case'], $txt['smf231']. (Admin language files) + ! Moved censored words to ManagePosts.php and updated its interface. (index.php, Admin.php, Admin template, ManagePosts.php) + & Added some tags for the above change. (Admin language files) + ! Fixed some issues with the bbc parser - wasn't handling unclosed tags well, double scanned some areas when parsing smileys, wasn't parsing links and long words correctly all of the time. (Subs.php) + + Created seperate screens for post settings, BBC settings and topic settings. (ModSettings.php, ManagePosts.php, Admin template) + & Moved several settings from ModSettings to Admin for above change. (Modsettings language files, Admin language files) + ! Moved setting countChildPosts to 'General board settings'. (ModSettings.php, ManageBoards.php, ManageBoards template) + & Moved a tag from ModSettings to ManageBoards for above change. (ModSettings language files, ManageBoards language files) + & Removed $helptxt['edit_censored'] and added $helptxt['posts_and_topics']. (Help language files) + ! If "allow events not linked to posts" was enabled, new events couldn't be created by normal users. (Calendar.php) + ! Add caching to parse_bcc(), but only if the post is fairly long. (Subs.php) + * Clean up some bad HTML. (ManageMembergroups template, Register template, ManageBoards template, Admin template) + * Add a size rule for th. (style.css) + ! Make things easier to mod as far as adding BBC and allowing them to be disabled. (Subs.php, ManagePosts.php) + ! More speed improvements for the search function. (Search.php) + ! Display was showing the remove topic button even if you didn't have permission to remove the topic. (Display.php) + * Added an example to search to show what it's capable of. (Search template, Search language files) + * Cleaned up the package installation template, to make it clearer what is going on, and any problems that may exist. (Packages.php, Packages template) + & Removed package43 language string, and added several new strings for the above change. (Packages language files) + + +May 2005: +-------------------------------------------------------------------------------- + ! The profile wasn't loading default theme options. (Profile.php) + ! Some timestamps weren't being calculated properly. (BoardIndex.php) + ! Change internal sanity error message format. (Load.php, LogInOut.php, Errors.php, Post.php, Security.php, Subs-Package.php, Subs-Post.php) + * Make the log_floodcontrol table fixed-width, and changed a few IP columns. (install_1-1.sql, upgrade_1-1.sql, Modlog.php, Subs.php, Errors.php, ManageMembers.php, smf_api.php, Errors template) + ! Speed up updateLastMessages() or break it - let's find out. (Subs.php) + * Use the context for common statistics. (Subs.php, index template, BoardIndex template, SSI.php, Stats.php, BoardIndex.php, Stats template) + * Change show_vBlogin to show_quick_login. (Subs.php, index template) + ! Add [nobbc] and [html] to printing pages. (PrintPage.php) + ! More fixes for the UBB.threads converter, even more proven to work than before. (ubbthreads_to_smf.php) + + Added functionality for generating reports in the admin center. (index.php, Subs.php, Reports.php) + * Added a template for printing and viewing reports. (Reports template) + & Added a rather large amount of language entries for the above. (Admin language files, Reports language files) + ! UBB.threads converter now also converts polls, personal messages and moderators. (ubbthreads_to_smf.php) + ! The COPPA template shown after registration was not using the main template layer. (Load.php) + + validatePassword() is now called when a password is created/changed to check it matches current forum rules. (Profile.php, Register.php) + & Added some language strings for the above change. (Errors language files) + + Added converter for MercuryBoard. (mercuryboard_to_smf.php) + + Added converter for Oxygen. (oxygen_to_smf.php) + + Added converter for XOOPS/newBB (xoops_to_smf.php) + ! Add hidden setting for forum_alias_urls. (Load.php, Subs-Auth.php) + * Don't allow prefetches to mark topics read/etc. (Display.php, MessageIndex.php, index template) + ! Added beginnings of new conversion system. (convert.php, vbulletin_to_smf.sql, yabbse_to_smf.sql) + ! Changed some 1.x compatbility code. (BoardIndex.php, Load.php) + + Added new style converter for MercuryBoard. (mercuryboard_to_smf.sql) + ! Fixed a few bugs with the new converter. (convert.php) + + Added setting for password strength to the admin center. (Register.php) + * Modified the template for the registration center to reflect the above. (Register template) + & Added a selection of language entries for the above addition. (Login language files, Help language files) + & Fix discrepency with username/display name on register. (Register.php, Login language files) + ! Added capability to do javascript subject change, icon change, lock, sticky, etc. (Post.php, Subs-Post.php, index.php) + + Added a new converter for converting from Phorum. (phorum_to_smf.sql) + ! Updated a few statistics to play nicer with InnoDB tables. (Subs.php) + ! When using pngs, output is now with alpha values to avoid black background. (Subs-Graphics.php) + ! Unless something is horribly wrong, realName should never be NULL. (ManageMembers.php, Subs.php, all converters) + ! Updated the new converters so they include even less duplicated code. (yabbse_to_smf.sql, vbulletin_to_smf.sql, mercuryboard_to_smf.sql, phorum_to_smf.sql, convert.php) + ! Cache a few more things. (Load.php, BoardIndex.php) + + Added new style converter for miniBB. (minibb_to_smf.php) + ! Fix some improper parsing of img tags with autolinked URLs in them. (Subs.php) + * The quotefast popup was not using the right character set. (Post template) + ! The stats page wasn't always showing 10 items for everything. (Stats.php) + ! Changed version numbers from 1.1 Beta 2 to 1.1 Beta 2+. (all files) + * Added start of searching for pms. (PersonalMessage.php, PersonalMessage template) + & Added some strings to make it internationalizable. (PersonalMessage language files) + ! Reformatted a few things and removed recycled topics from totalTopics. (Subs.php) + ! Fix a notice for invalid referrers. (Security.php) + * Add a nowrap span around total members on the index. (index template) + ! Trim the search query for memberlist searching. (Memberlist.php) + + Added new style converter for UBB.threads, and increased the converters capabilities. (ubbthreads_to_smf.sql) + ! Just use double quotes in the eval replacements because preg expects it. (Subs.php) + ! Forms weren't being hidden properly in edit news interface. (Admin.php) + ! CleanupPermissions() is basically done now, just needs interface. (Admin.php) + ! Fixed typo in the search function that could be bad. (Search.php) + + Added the option to add multiple ban items to a single ban. (install_1-1.sql, upgrade_1-1.sql, phpbb2_to_smf.php, xmb_to_smf.php, yabbse_to_smf.php, ManageMembers.php, Register.php, Security.php) + + Each ban can now have multiple restrictions, including a new restriction 'cannot login'. (ManageMembers.php, ManageMembers template, Security.php, LogInOut.php) + ! Added a counter to count each 'ban hit'. (Security.php, Login.php, Register.php) + ! Updated the ban center interface, to reflect above changes. (ManageMembers.php, ManageMembers template) + ! Split off the ban center functions in ManageMembers.php to ManageBans.php. (index.php, ManageMembers.php, ManageBans.php) + * Split off the ban center templates in ManageMembers.template.php to ManageBans.template.php. (ManageMembers template, ManageBans template) + ! Prevent guests from posting when their email is banned. (Post.php) + ! Expired bans are not deleted automatically anymore. (ManageBans.php, ManageBans template) + ! Each ban can (must) have a name to make it identifyable. (ManageBans.php, ManageBans template) + ! Banned members are removed from the 'users online' list immediately. (ManageBans.php, Security.php) + & Added several language strings for above changes. (Admin language files, Help language files) + ! Option to add ban triggers in the 'Add new ban' screen. (ManageBans.php, ManageBans template) + & Updated $txt['ban_add_notes'] for above change. (Admin language files) + ! Added a general inline permission settings. (ManagePermissions.php, ManagePermissions template) + ! Changed the avatar permission settings to use the general inline permissions. (ManagageAttachments.php, ManagageAttachments template) + * Inline permissions now allow toggling. (ManagePermissions template) + ! Added inline permissions for calendar settings and search settings. (ManageCalendar.php, ManageSearch.php) + & Added language strings for the above change. (ManageCalendar language files, Search language files) + ! Added tabs for manage boards and categories. (ManageBoards.php, ManageBoards template) + ! Moved recycle bin settings from Modsettings to 'Manage Boards and Categories'. (ModSettings.php, ManageBoards.php, ManageBoards template) + & Moved the recycle language tags from ModSettings to ManageBoards and added some extra tags there. (ModSettings language files, ManageBoards language files) + * Added an icon in 'manage boards and categories' to show which board is the recycle board. (ManageBoards template) + + Added (default) option to not use 'deny'-permissions. (Load.php, ManagePermissions.php, ManagePermissions template, upgrade_1-1.sql, install_1-1.sql) + & Added language strings for the above change. (ManagePermissions language files, Help language files) + & Removed $txt['avatar_select_permission_desc'] and added $txt['ban_partial_ban']. (Admin language files) + * Added some help tags to the predefined membergroups. (ManagePermissions template, ManageMembers template, Help language files) + ! Added support for unsalted sha1 passwords, for users converting from PunBB and others I'm sure. (LogInOut.php) + + Added a (default) option to disable post count based group permissions. (ManagePermissions.php, ManagePermissions template, Reports.php, install_1-1.sql, upgrade_1-1.sql) + & Added some new language tags for the above change. (ManagePermissions language files, Help language files) + ! Simplified the permissions shown in the profile screen, there was too much information imo. (ManageMembers.php, ManageMembers template) + & Removed language tags $txt['showPermissions_all_boards'], $txt['showPermissions_all_boards_except'], $txt['showPermissions_boards'], $txt['showPermissions_boards_denied'], $txt['showPermissions_local_only']. (Profile language files) + ! Show a warning if the new converter hasn't been deleted from the forum directory. (Subs.php) + * Send the current forum version with the detailed version template, incase we ever want to do clever stuff with it. (Admin template) + ! Updated new converter to give more flexibility when fetching settings. (convert.php) + + Added the (default) option to simplify local permissions to four choices (normal, no polls, reply-only, and read-only). (Load.php, ManageBoards.php, ManageMembers.php, ManagePermissions.php, Reports.php, Security.php, ManageBoards template, ManagePermissions template, install_1-1.sql, upgrade_1-1.sql, most converters) + & Adjusted language files for above change. (Admin language files, Help language files, ManageBoards language files, ManagePermissions language files) + ! The permission view in Profile now also takes users being a moderator in account. (ManageMembers.php) + ! Fixed inline permissions showing postgroups when they were not supposed to. (ManagePermissions.php) + ! Typo in the YaBB converter which only affected attachment-less installs. (yabb_to_smf.php) + ! If there's a database error loading the settings table, assume a connection problem. (Load.php) + ! Even though only admins should touch it, validate the groups for sending emails a bit more. (ManageMembers.php) + ! Add an icon to recentTopics()' output. (SSI.php) + ! Allow the reporting of your own posts when you cannot modify them. (SendTopic.php) + * Make mark unread keep your place. (Display.php, Display template, Subs-Boards.php) + ! Smileys weren't uploading properly on some servers. (ManageSmileys.php) + * Jump to the right label and message after smiting/applauding. (PersonalMessage template, Karma.php) + * Fixed some invalid HTML in the edit poll interface. (Poll template) + ! Use fetch_web_data() for downloading attachments. (Profile.php) + & Removed $txt['avatar_url_wrappers_required']. (ManageAttachments.php, Admin language files) + * Removed the no longer needed url wrappers warning. (ManageAttachments template) + ! Make radio groups which only have one item still work. (script.js) + * Fix selecting external avatars from the profile without other options enabled. (Profile template) + * Add an option to resize avatars with javascript, add an avatar class for all avatars. (Subs.php, Load.php, ManageAttachments template) + & Add $txt['option_js_resize']. (Admin language files) + ! 'Post poll' permission is now independent of the permission to post topics. (Post.php) + & Adjusted $txt['permissionhelp_poll_post'] to reflect the above change. (ManagePermissions language files) + ! Full bans are now also supported by cookies. (Security.php) + & Added character set and right-to-left for installer. (install.php, Install language files) + ! Censor outgoing PM notifications. (Subs-Post.php) + ! Added integrate_delete_member. (ManageMembers.php) + * Add labels to the additional groups for usability. (Profile template) + ! Added status script - not completely ready yet. (status.php) + ! Cleaned up some emails from RSS feeds. (News.php) + ! Merged news and newsletters into a single admin center screen. (index.php, Subs.php, ManageNews.php, ManageMembers.php, Admin.php, ManageNews template, Admin template, ManageMembers template) + & Added some language tags for the above change. (Admin language files) + ! Moved modsettings 'xmlnews_enable' and 'xmlnews_maxlen' to the news and newsletters settings screen. (ModSettings.php, Modsettings language files, ManageNews.php, Admin language language files) + & Moved $helptxt['email_members'] to $txt['news_mailing_desc'] (Help language files, Admin language files) + ! Allow replacing a searched-for string with nothing. (Subs-Package.php) + ! Add the beginnings of POP3 before SMTP login. (Subs-Post.php) + * Polls now quick-preview properly without the post_new permission. (Post template) + ! Add various list style types to the [list] bbc. (Subs.php, Subs-Post.php) + ! Censor poll options on edit and display. (Poll.php, Display.php, SSI.php) + ! Messed with and cleaned up a lot of the new converters, removed old. (all converters) + ! PHP highlighting wasn't completely compatible with PHP 5. (Subs.php) + ! Backup wasn't restarting properly after a timeout. (upgrade.php) + ! Add some warnings for moderation logging to help track problems. (Subs-Boards.php, Subs.php) + ! Sorting by file size didn't work properly. (ManageAttachments.php) + ! The upgrader wasn't doing attachments properly. (upgrade_1-1.sql) + ! Adding a poll to a topic should now work regardless on modify permissions. (Poll.php, Display template) + ! Improved edit/add poll interface to work better with preview and error messages. (Poll.php, Poll template) + ! When editing or removing a poll look at who started the poll, not just the topic. (Poll.php) + & Added some language error strings for the above changes. (Errors language files) + ! Updated reports to support the new board permissions - and fixed a few bugs with board permissions. (Reports.php) + & Added some language entries for the above change. (Reports language files) + ! Moved the profile functions trackUser, trackIP and showPermissions from ManageMembers.php to Profile.php. (index.php, ManageMembers.php, Profile.php) + * Moved the templates of above mentioned functions from ManageMembers.template.php to Profile.template.php. (ManageMembers template, Profile template) + * Moved the announcement sub templates from ManageMembers.template.php to Post.template.php. (Post.php, ManageMembers template, Post template) + ! Split ManageMembers.php into ManageMembers.php, ManageMembergroups.php, and Subs-Members.php. (index.php, ManageMembers.php, ManageMembergroups.php) + * Split ManageMembers.template.php into ManageMembers.template.php and ManageMembergroups.template.php. (ManageMembers template, ManageMembergroups template) + ! Created admin tabs for 'Manage Membergroups', including a new settings tab. (ManageMembergroups.php, ManageMembergroups template) + & Removed language tag $txt['membergroups_members_all_current_desc'] and updated tags $txt['membergroups_edit_post_group'], $txt['membergroups_new_board_desc'], $txt['membergroups_can_edit_later'], $txt['membergroups_new_as_copy'], and $txt['membergroups_star_image_note'], $txt['membergroups_max_messages_note']. (MangageMembers language files) + & Changed all occurences of 'member group' to 'membergroup'. (ManageMembers language files, Errors language files, Admin language files, Reports language files) + ! Show an error when adding an already existing membergroup. (ManageMembergroups.php, Errors language files) + ! Board access can now also be adjusted when editing membergroups (ManageMembergroups.php, ManageMembergroups template) + ! Added a general function to Subs-Members.php to remove one or more membergroups. (ManageMembergroups.php, Subs-Members.php) + ! Added a general function to Subs-Members.php to remove one or more members from one or more membergroups. (ManageMembergroups.php, Subs-Members.php) + ! Added a general function to Subs-Members.php to add one or more members to a specified membergroup. (ManageMembergroups.php, Subs-Members.php) + ! Added a few more cleaning queries to deleteMembers(). (Subs-Members.php) + & Removed $helptxt['edit_agreement'] and put it into $helptxt['registrations']. (Help language files) + ! Clean up the saving of temporary attachments with weird filenames. (Post.php) + ! The staff report didn't properly handle denied permissions. (Reports.php) + ! Fix database session lifetime usage and cache things more when set to "super cache". (Load.php) + ! Expanded on some browser identifications. (Load.php) + ! Fix cli usage of SSI.php. (SSI.php) + * Remove Gecko only CSS from index template. (index template) + * Tweaked the calendar layout a bit. (Calendar template) + ! Fix international characters in the custom title. (Profile.php) + ! Add more hacking attempt warnings for those goons who want to remove obExit()s. (Security.php) + ! Minor tweak to the sha1 functions. (Security.php) + ! Moved 'members awaiting approval/activation' from the register center to the member center. (Register.php, Register template, ManageMembers.php, ManageMembers template) + & Moved several language tags from Login to ManageMembers for above change. (Login language files, ManageMembers language files) + & Split $txt['admin_browse_no_members'] into $txt['admin_browse_no_members_approval'] and $txt['admin_browse_no_members_activation']. (ManageMembers language files) + & Updated $txt['admin_browse_awaiting_approval'], $txt['admin_browse_awaiting_activate'] and $txt[9]. (ManageMembers language files, Admin language files) + ! Lots of fixes related to the move of members awaiting activation to ManageMembers. (Themes.php, Register.php, ManageMembers.php, index template) + & Added an option to approve a member yet require their account to be activated. (ManageMembers.php, ManageMembers language files) + ! Use for flash instead of content inside <embed>. (Subs.php) + ! Look in at least the last five groups of new posts in ssi_recentTopics()/ssi_recentPosts(). (SSI.php) + + +SMF 1.1 Beta 2 May 8, 2005 +================================================================================ +May 2005: +-------------------------------------------------------------------------------- + ! Try not to reset stats ever, under any circumstances. (Subs.php) + ! Make sure not to generate errors when showing template parse errors. (Load.php) + ! Update the credits. (Admin.php) + ! Make the statistics show without errors when there are no members. (Stats.php) + ! Don't log an error if the image file is corrupt. (Subs-Graphics.php) + ! Make sure not to assume $_POST['sc'] means form submission. (ManageSmileys.php, ManageCalendar.php, ManageMembers.php, Calendar.php) + ! Error was caused when saving several labels containing quotes. (PersonalMessage.php) + ! The webinstaller now uses POST to make requests. (Subs-Package.php, webinstall.php) + ! Fixed serious bug with deleting members from "Manage Member" section, and made it handle search better. (ManageMembers.php, ManageMembers template) + ! Find and fix errors wasn't properly reporting poll and event errors. (RepairBoards.php) + ! Fixed offset error when viewing the moderation log. (Modlog.php) + ! Thumbnails can now be seperately browsed. (ManageAttachments.php, ManageAttachments template) + & Added a language tag for the above change. (Admin language files) + ! The attachment links in the 'Browse files' screen shows a popup for images. (ManageAttachments.php, ManageAttachments template) + ! Don't show a scrollbar for popups containing an image of a known size. (ManageAttachments.php, Display.php, script.js) + ! The link to 'manage boards and categories' was not shown on the admin panel for users with only the manage_boards permission. (Subs.php) + & Changed hits into 'page views'. (Stats language files, Help language files, ModSettings language files) + ! Improved the UBB converter by a huge amount, and added attachment support. (ubbthreads_to_smf.php) + ! Fixed thumbnails showing as attachments in the post screen. (Post.php) + ! Fixed removing thumbnails from 'Browse files' screen. (ManageAttachments.php, ManageAttachments template) + ! Some general fixes for thumbnail removing. (ManageAttachements.php, Profile.php, RemoveTopic.php, Subs-Graphics.php, ManageMembers.php) + ! Prevent Google web cache from caching URL's that perform certain actions. (Security.php) + ! Add image dimensions for custom avatars to the attachment table. (upgrade_1.1.sql) + ! Fixed events in topics not respecting all possible date values. (Display.php, ManageMembers.php) + ! Allow queryless URLs in CGI mode if cgi.fix_pathinfo is enabled. (News.php, QueryString.php, Subs.php) + ! Upgrade from 1.0 had a few MySQL 3 incompatible statements in it. (upgrade_1-0.sql) + + +April 2005: +-------------------------------------------------------------------------------- + * Only the first + was being properly escaped. (Post template) + ! Updated everything to 1.1 Beta 1+. (all files) + & Attempted to clarify $txt['package_action_failure'] and $txt['package_action_success']. (Package language files) + ! You weren't able to erase your MSN account from your profile. (Profile.php) + ! Change the order of notification sending a bit to ensure it's done after other things. (Post.php) + ! Fixed up the calendar to handle week numbers correctly when the week start day is not Sunday, and to handle week 53 better. (Calendar.php) + ! When editing a poll, if you put in less than two options the error message wasn't being displayed properly. (Poll.php) + * When editing a ban an error would result due to a missing field in the template. (ManageMembers template) + ! Send notifications for quick moderation properly. (Subs-Boards.php) + ! Don't reverse to fix the block if the block still won't match. (QueryString.php) + * Fixed border staying red on post preview errors. (Post template) + ! Label changes were sometimes leaking across to other receivers. (PersonalMessage.php) + * Make the selected state of a pm persist one page view to facilitate relabeling. (PersonalMessage.php, PersonalMessage template) + ! Fixed a possible but minor error in the spell checker. (Subs-Post.php) + ! Made the webinstaller work with the new password encryption. (webinstall.php) + ! Added 'disableQuotedPrintable' setting to disable the use of quoted-printable. (Subs-Post.php) + * The quick login was *not* being hased properly because of a typo. (index template) + ! Pretend with glow using background-color, and use text-shadow for shadows. (Subs.php) + * The news fader was broken in some browsers. (BoardIndex template, fader.js) + ! Fade smoothly (images/links/color) if possible by browser - only Mozilla/IE/Safari. (fader.js) + ! Don't use tables for glow/shadow in IE, as it has its own problems too. (Subs.php) + * In the default theme, make the new/no new icons link to unread topics for that board. (BoardIndex template, MessageIndex template) + ! Okay, this time I think I really did fix the race condition of double posting with caching. (Security.php) + ! The unread messages count was getting miffed up. (PersonalMessage.php) + ! Notifications weren't sending for new posts in a board. (Post.php) + * The memberlist was showing websites for everyone, whether they had one or not. (Memberlist template) + ! Attempt to send emails without changing the encoding, if possible and disableQuotedPrintable is set. (Subs-Post.php) + ! Optimized the ID_BOARD index of the messages table (upgrade_1-1.sql, install_1-1.sql) + ! Improve the efficiency of cache_put_data() when there's no cache available, add debug info. (Subs.php, Load.php) + ! Don't send off an email with a blank mysql_error(). (Subs-Auth.php) + ! Increase the accuracy and efficiency of the total time logged in caching. (Stats.php) + * Selecting a color, even without text highlighted, should focus the textarea. (Post template) + ! Since boardsAllowedTo() is kinda slow, cache its result if possible. (Security.php) + ! The package manager should now function even without allow_url_fopen enabled. (Subs-Package.php, PackageGet.php) + ! Try harder to get the birthdate right. (yabb_to_smf.php) + ! In cases, unread messages weren't being shown properly. (PersonalMessage.php) + * Handle poll option tabindexes better. (Post template) + ! Properly handle images that have &s in their filenames, and try to detect size with GD even if allow_url_fopen is off. (Subs-Post.php, Subs.php) + ! Attempt to use caching in url_image_size() if it took a long time. (Subs.php) + ! Try to preparse lists/tables a little more cleanly and logically. (Subs-Post.php) + * The avatar selection interface wasn't showing your external avatar properly. (Profile template) + ! Show version information about PHP accelerators in the admin center too. (Admin.php) + ! Use array_map() instead of foreach in a couple places since it's likely faster. (QueryString.php) + ! Don't use array_key_exists() since it is slower than isset. (ManageMembers.php, Memberlist.php, Register.php) + * Fixed some invalid html. (Display template, MessageIndex template, Search template, ManageCalendar template, ManageMembers template, Admin template, Errors template, Profile template) + * Added xmlns attributes to <html> tags. (Post template, index template, Printpage template, Wireless template, Help template) + ! Speed up updateLastMessages() for larger forums. (Subs.php) + ! Sped up the recent posts of members, and used a temporary table for larger forums to avoid locks. (Profile.php) + ! Use a temporary table for "replies to your posts" on larger forums, if available. (Recent.php) + ! Add preview to SSI.php's recentPosts and recentTopics. (SSI.php) + ! Fix the &#039; problem in signatures, I think. (Profile.php) + ! Sped up ssi_topTopics a bit for larger forums, and boardsAllowedTo() for all. (SSI.php, Security.php) + * Use a more xhtml friendly commenting scheme for script and style elements. (various files, various templates) + ! Fix the profile html saving issue once and for all. (Subs-Auth.php) + ! Tweaked a couple column sizes - doesn't really matter. (install_1-1.sql, upgrade_1-1.sql) + ! Fixed some issues with logging moderation actions on your own posts. (Post.php) + ! Strip out any <br />s after [/html]. (Subs.php) + ! Don't allow posts with a blank subject via remote form. (Post.php) + ! It was possible to screw up post notifications with an incorrect board id. (Subs-Post.php) + ! Increase the limits when looking for recent posts. (SSI.php) + ! Add a bit of safety checking when looking up the form_stack via cache. (Security.php) + ! Always send remove topic notifications before removing the topics. (Subs-Boards.php) + * Ignore errors using filters in the fader, just keep going. (fader.js) + & Added $txt['countChildPosts'] and $helptxt['countChildPosts']. (ModSettings.php, ModSettings language files, Help language files) + & Allow a bit more in the way of basic HTML. (Subs.php, Help language files) + ! Make the ID_BOARD change in upgrade_1-1.sql conditional. (upgrade_1-1.sql) + ! Add attachment converting support. (yabb_to_smf.php) + * Even when one avatar directory has a name similar to another one, keep the files in the right places. (Profile template) + ! Fix the "there are no unread topics" link when in a board/category. (Recent.php) + ! Remove PHPSESSID=... from logged URLs. (Errors.php) + ! Speed up the upgrade process just a little bit. (upgrade.php, upgrade_1-1.sql) + ! Added integrate_verify_user and integrate_verify_password settings. (Load.php, Profile.php, Security.php) + ! Renamed the customAvatarDir column in table attachments to attachmentType (install_1-1.sql, upgrade_1-1.sql) + ! Register thumbnails and image dimensions in the attachments table for better performance and maintainability (install_1-1.sql, upgrade_1-1.sql, Display.php, Load.php, ManagaAttachments.php, Post.php, Profile.php, Subs.php, Subs-Graphics.php) + + Added function validatePassword for determining if a password is strong enough to be allowed on the forum - not yet used. (Subs-Auth.php) + * Added JavaScript function to expand thumbnails inline. (Display template, Display.php) + ! By default, ignore recycled messages for the ssi_recentPosts and ssi_recentTopic functions. (SSI.php) + + Added automatic detection of wireless protocol (index.php) + + Added recent topics and recent replies for the wireless protocol (Recent.php, Wireless template) + & Added two language strings for the above change (Wireless language files) + + Added functionality for reporting personal messages to administrators - no interface for enabling yet though. (PersonalMessage.php, PersonalMessage template) + & Added a bunch of language strings for the above changes. (PersonalMessage and Errors language files) + * Make time offset guess positive when it should be, usually. (Profile template) + & Removed $txt['themeadmin_settings_uses_default']. (Themes language files) + * Got rid of the restriction from modifying a theme's settings if it used the default's. (Themes.php, Themes template) + ! Encrypted the (very temporarily) encrypted FTP password. (Subs-Package.php) + ! Minor tweaks to the theme installation process. (Themes.php) + ! Allow feeds to be made for a list of boards or categories. (News.php) + ! Added $mysql_set_mode for those people using MySQL 4.1 or above. (Load.php) + ! Explicitly insert NOW() for some configurations of MySQL 4.1. (Subs.php) + ! Make queryless URLs work for redirects, and fix some bugs with them. (Subs.php, QueryString.php) + ! Fixed the personal message label boxes appearing even if you didn't have any labels. (PersonalMessage.php) + * Added setting and link in template for reporting a personal message. (PersonalMessage template, ModSettings.php) + & Added a couple of language strings for the above change. (PersonalMessage, ModSettings, Help language files) + ! Fixed an error in the conversion of attachments during upgrade. (upgrade.php) + ! Fixed a bug causing the avatars in a custom directory not to be shown. (Load.php) + & Tweaked $txt['database_error_versions']. (index language files) + ! Added some cleanliness to checkSubmitOnce(). (Security.php) + ! Notice when editing some groups. (ManageMembers.php) + ! Don't show where they're trying to be on who's online if they are banned. (Security.php) + ! Fixed a typo in the smf_api.php file and in invision_to_smf.php. (smf_api.php, invision_to_smf.php, invision2_to_smf.php) + ! Fixed a bug not showing thumbnails of attachments with a space in the file name (Display.php) + ! Don't count thumbnail views as 'downloads'. (Display.php) + * Show images that are too large in a popup instead of inline (Display template, Display.php) + * Added the date a ban was set to the ban list, to ease tracking the most recent bans. (ManageMembers.php, ManageMembers template) + & Added one text entry for the above change. (Admin language files) + ! Members without an avatar weren't being handled well. (invision_to_smf.php, invision2_to_smf.php) + ! If no actions are specified, just kick back. (PersonalMessage.php) + ! Glow and shadow weren't working, and some bloke finally noticed. (Subs.php) + ! Don't fail miserably if a backup file cannot be created. (Subs-Package.php) + ! Updated version numbers to 1.1 Beta 2. (all files) + + +March 2005: +-------------------------------------------------------------------------------- + ! The "settings" link on the smileys page didn't work. (ManageSmileys.php) + & Added a few targets to external links. (Admin language files, Help language files, Install language files) + ! Cache the most immediately successful path in the settings table. (Subs-Package.php) + ! Calculate member list pagination properly. (Memberlist.php) + ! Typo causing reserved names to default strangely. (install.php) + ! Optimized a little bit of SSI.php's boardNews. (SSI.php) + ! Fixed a bug that was preventing correct YaBB SE conversion. (upgrade_1-0.sql) + ! Turn on maintenance mode before the backup is started. (upgrade.php) + ! Fixed a couple version numbers. (ssi_examples.php, ssi_examples.shtml, news_readme.html, readme_convert.html, readme_upgrade.html, readme_install.html, readme_update.html, webinstall.php, smf_api.php, yabb_to_smf.php) + ! Don't choke and die if the Settings.php file isn't writable when sending an error message. (Admin.php) + + +SMF 1.1 Beta 1 March 28, 2005 +================================================================================ +March 2005: +-------------------------------------------------------------------------------- + & Show a note in the manage boards and manage groups interfaces about post groups. (ManageBoards language files, ManageMembers language files) + * The above, and don't check post groups by default. (ManageBoards.php, ManageBoards template, ManageMembers template) + * Make the star preview support $language as well. (ManageMembers template) + * Add support for copying templates from the default into a theme. (Themes.php, Themes template) + & Add copy stuff to language files as well. (Themes language files) + ! Don't let quotes break the censored words tester. (Admin.php) + ! Don't log deadlocks in the error log. (Errors.php) + ! Use CDATA for the rss/etc. descriptions. (News.php) + ! Try to avoid changing the last visit on xml and rss calls. (Load.php) + ! Attempt to avoid shrinking/expanding avatars and thumbnails if that's the size they already are. (Subs-Graphics.php) + ! Lengthen quote author=... a bit. (Subs.php, Printpage.php) + * Don't just say "total topics" for topics started. (Profile template) + & Update the language files with a couple new strings. (Profile language files) + ! Redirect to the poll they voted in after an SSI-related vote. (SSI.php) + * Minor typo in the folder view. (PersonalMessage template) + ! Compress the backup on-the-fly, if possible and use _rowid ascending if we can. (create_backup.php) + ! Removed session authentication for track IP/Member. (Profile.php) + ! Added some extra checks to make sure messages to be split still exist (SplitTopics.php) + ! Moved splitting of topics into a general function (SplitTopics.php) + ! Events that aren't linked to a topic should be visible to anyone (BoardIndex.php, SSI.php) + ! Store the type of the login cookie in the cookie itself; this solves "unable to logout" problems. (LogInOut.php, Load.php) + ! Don't use get_cfg_var() to check for safe mode. (PackageGet.php) + ! Fix lacnic whois server link. (ManageMembers.php) + * If possible, select the correct whois server and bolidfy its link. (ManageMembers.php, ManageMembers template) + ! Sometimes (much of the time), using multiple search conditions could cause your add code to be miffed up. (Subs-Package.php) + ! The RSS feeds were using overly unique links. (News.php) + ! Try to parse blank URLs and a few other things more correctly. (Subs-Post.php) + ! Properly use MIME for sending HTML emails, clean up things for spam checkers. (ManageMembers.php, Subs-Post.php) + * Add stylesheet editor preview functionality. (Themes.php, Themes template, Admin template) + & Add a button as well to refresh the preview. (Themes language files) + * Attempt to make css preview a little more lightweight. (Themes template) + ! Allow unread/replies/recent to take lists of categories/boards. (Recent.php) + ! Make it possible to use %3$s for the name of the poster in new topic notification emails. (Post.php) + ! When a banned member logged in they were incorrectly being marked as "un-activated" (Load.php, LogInOut.php) + ! Use SQL_NO_CACHE for creating backups. (DumpDatabase.php, create_backup.php) + ! Update with the password changes in the smf_api.php file. (smf_api.php) + ! Tell MySQL not to cache the data from admin recount stuff either. (Admin.php) + * Use lowercase for color dropdown. (Post template) + & Don't allow registration/name change when the new name has censored words in it. (Security.php, Errors language files) + ! Changed cache_disable to cache_enable - default, off. (Load.php) + * Cleaned up a bunch of settings so nothing coughs if they don't exist. (MessageIndex template, Display template, upgrade_1-0.sql, install_1-1.sql, upgrade_1-1.sql, Subs-Post.php, repair_settings.php) + ! Add ignore_user_abort to things that need to finish cleanly. (all converters, upgrade.php, webinstall.php) + * Clean up message application strategy, add labeling multiple messages. (PersonalMessage.php, PersonalMessage template) + & Add language entries for said behavior. (PersonalMessage language files) + * Mark as read on recent posts for a specific board should only affect that board. (Recent template) + ! Fixed some bugs in html and li parsing. (Subs-Post.php, Subs.php) + ! Now that caching is off by default, additionally cache smileys and message icons. (Subs.php, Subs-Post.php, Post.php) + ! Fix a couple unlikely situations which would just generate notices anyway. (Search.php, PersonalMessage.php) + ! Fix a minor code layout problem in Gecko browsers. (Subs.php) + ! Don't actually change the REQUEST_URI, since others might use it. (QueryString.php, Subs.php, Subs-Auth.php, Security.php) + ! Properly set and keep track of the locked status (moderator vs. user) when replying. (Post.php) + ! Use queryless URLs by default even with just SSI.php included. (SSI.php) + ! When activating/deactivating accounts, keep banned status properly. (Profile.php) + ! Handle more cases where is_activated is not 1 or 0. (ManageMembers.php) + ! Make in what ways the member is banned available in the profile summary's context. (Profile.php) + ! Cache slow hostname lookups if caching is enabled. (Profile.php, Subs.php) + ! Don't give an error when you try to delete nothing from the news. (Admin.php) + & People think the "ungrouped" adjective is bad - use "regular members" instead. (Admin language files, ManageBoards language files, ManagePermissions.php) + ! Who's online was showing the wrong time. (Who.php) + ! Increased the efficiency of the statistics page - especially for large forums - by a large amount. (Stats.php) + * Give 30 more pixels of leg room for posts in show posts under a member. (Profile template) + ! Improved the speed and efficiency of the latest posts from a member functionality. (Profile.php) + ! Try to speed up recent unread topics a bit more. (Recent.php) + * Only list those labels you can apply to the selected messages - more logical. (PersonalMessage template) + ! Try to handle caching of attachments just a little better. (Display.php, Subs.php) + ! Use %20 instead of + in aim links for spaces. (Load.php) + ! Handle the usage of curly braces in automatically parsed links. (Subs.php) + ! Removed the mailprog_error() function and cleaned up its error message a bit. (Reminder.php) + ! Cleaned up URL rewriting in xml/rss syndication. (News.php) + ! Added a lot of integration stuff. (index.php, Load.php, QueryString.php, News.php, Profile.php, Register.php, Reminder.php, Subs-Auth.php) + ! Allow the definition of SMF_INTEGRATION_SETTINGS to override any $modSettings. (Load.php) + * If something goes wrong with live preview, fall back. (Post template, script.js) + & Added message when you've replied to or forwarded a pm. (PersonalMessage language files) + * Made is_replied_to work. (PersonalMessage.php, PersonalMessage template) + ! Prettied up the optimize function a bit. (Admin.php) + ! Fixed a couple bad links not including $scripturl. (RepairBoards.php) + * Fixed a lot of jumpto javascript problems - losing session without cookies, etc. (MessageIndex template, Display template, Search template, help/index.xslt) + * Cleaned up a lot of label handling and marking, etc. Now shows unread messages per label. (PersonalMessage template, PersonalMessage.php) + ! Changed the way microtime() is used. (Subs.php, repair_ID_MSG.php, repair_ID_PM.php, repair_ID_TOPIC.php, ssi_examples.php) + ! Don't change the actual cal_days_for_index setting. (BoardIndex.php, SSI.php) + ! Optimized a couple things a bit more. (SSI.php) + ! Updated part of the ftp connection stuff. (install.php, Subs-Package.php, webinstall.php, create_backup.php) + ! Added transactions to the backup tool. (create_backup.php) + ! Show the proper (first) error message when you can't view the admin center. (Security.php) + * Properly fixed the usage of PHPSESSID in URLs with the jumpto box. (script.js, MessageIndex template, Search template, Display template) + ! Added a recount of pm totals to the upgrader, and fixed the old one to be speedy-speedy. (upgrade_1-0.sql, upgrade_1-1.sql) + ! Read/unread status of individual posts was wrong. (Display.php) + ! Added upgrade from the mod of messsage icons. (upgrade_1-1.sql) + ! The edit_disable_time setting now applies to deleting posts as well. (RemoveTopic.php, Recent.php, Profile.php, Subs-Boards.php, Display.php) + * Fixed a small HTML typo. (Search template) + ! Set session.save_handler to files before any session_start(). (install.php, upgrade.php, convert.php, create_backup.php, repair_settings.php, restore_backup.php, webinstall.php) + ! The board index wasn't calculating its children's most recent posts correctly. (BoardIndex.php) + * The live preview didn't like plusses. (Post template) + * Keep track of the current label better when deleting, replying, etc. to pms. (PersonalMessage.php, PersonalMessage template) + ! Handle 127 errors as well, if it's possible to do so. (Errors.php) + ! The database error email message just wasn't sending. (Subs-Auth.php) + ! Try to make Settings.php less volatile; use caching if at all available. (Subs-Auth.php, Errors.php) + ! Autocomplete banned member's names because it does make sense for admins at least. (Subs-Auth.php) + ! Made it possible to have an admin "tab" contain a full URL, independent to the current section. (Admin template) + ! Upon entering the registration center, the user will now be directed straight to the activation page, if appropriate. (Register.php) + ! Combined several different package manager actions into one controlling function. (index.php, PackageGet.php) + * Added "tabs" to the Package Manager interface, and removed the existing Package Manager layer. (Packages.php, PackageGet.php, Package templates) + & Added/Removed language entries to reflect the changes above. (Packages and Who language files) + ! Don't bother inserting settings with a zero value, if they are not currently set. (Subs.php) + ! Clean up better after multiple spaces/etc. in usernames. (Register.php) + ! Handle single quotes in email addresses from the profile better. (Profile.php) + ! Handle single quotes in censored words better. (Load.php) + ! Try to apply quoted-printable a lot less of the time. (Subs-Post.php) + * Make smf_codeFix iterate backwards over divs to prevent breakage. (index template) + * Cleanup post preview and insert quote in IE, etc. (Post.php, Post template) + ! Turn off list code fixing, since it's borken anyway. (Subs-Post.php) + * Fixed "fetching preview" never going away - I think. (Post template) + ! Lists were not working well, it seems, all of the time. (Subs-Post.php) + ! Attempt to show, in debugging mode, errors even in attributes. (Errors.php) + ! Add spam protection to sending topics and reporting posts. (SendTopic.php) + & Added error string for a user not entering their password during a profile change. (Errors language files) + ! Minor tweak to package manager's FTP handling. (Packages.php) + ! Add actual speed measurement, use pconnect where possible. (create_backup.php) + * Made the default theme significantly more usable in right-to-left. (BoardIndex template, Display template, Calendar template, index template, MessageIndex template, Profile template) + ! Made it possible to change the display status of more than one smiley at once. (ManageSmileys.php, ManageSmileys template) + & Added some language strings for above change. (ManageSmileys language files) + * Fix key stats breaking with too much data for compliant browsers. (index template) + ! Handle multiple integration buffers. (Subs.php) + ! Fixed a case where calendar events wouldn't be handled properly. (Calendar.php) + + Added option to display linked events in a topic. (ManageCalendar.php, Display.php, Display and ManageCalendar Templates) + & Added language entries for the above addition. (index and ManageCalendar language files) + + +February 2005: +-------------------------------------------------------------------------------- + ! Converters should clear the log_topics/mark_read/boards tables. (all converters) + ! Add converter readme. (readme_convert.html) + ! If you change the cookie name, reset the cookie before redirecting. (Admin.php) + ! Redesigned 'Attachments & Avatars' to look more consistent with the other admin center screens (ManageAttachments.php, ManageAttachments template) + ! Moved modSettings for attachments and avatars to the 'Attachments & Avatars' center. (ModSettings.php, ManageAttachments.php, ManageAttachments template) + & Moved block of language strings for above change. (ModSettings language files, ManageAttachments language files) + - Removed modSetting avatar_check_size as it only eats resources. (Load.php, Subs.php, ManageAttachments.php, ManageAttachments template) + & Removed $txt['avatar_check_size'] and $helptxt['avatar_check_size'] for above change (Admin language files, Help language files) + - Removed modSetting avatar_allow_external_url as it is already a permission. (invision_to_smf.php, invision2_to_smf.php, phpbb2_to_smf.php, Load.php, Subs.php, ManageAttachments.php, ManageAttachments template, Profile.php, Profile template) + & Removed $txt['avatar_allow_external_url'] for above change. (Admin language files) + + Added an option to let all external avatar URL's be downloaded. (Profile.php, ManageAttachments.php, ManageAttachments template) + + Added an option to host uploaded avatars in a seperate directory from attachments for better server performance (ManageAttachments.php, ManageAttachments template, Profile.php, Subs-Graphics.php, Load.php, Subs.php, upgrade_1-1.sql, install_1-1.sql) + ! Fix for passing "GLOBALS" to register_globals. (SSI.php) + ! Who's online wasn't properly showing those actions which require permission to see. (Who.php) + ! Use sendmail if the smtp_host is blank, trim smtp_host on usage. (Subs-Post.php) + ! Fixed mod settings avatar_action_too_large getting the wrong value (ManageAttachments.php, ManageAttachments template) + * Added "tab" system to the admin template, to improve efficiency when making tabbed admin templates. (Admin template) + * Changed ManageSmileys to use this new style tab system. (ManageSmileys.php, ManageSmileys template) + ! Keep contextual information for WebTV as well. (Load.php) + ! Avoid generating errors for searches that don't have blank quotes in them, etc. (Search.php) + ! Attempt to handle very-large attachments better. (Display.php) + * Remove height from post/personalmessage because IE 5 is too good for it. (style.css) + * Show number of users online to even those who can't view the link. (BoardIndex template) + ! Couldn't send pms to some members, again. (InstantMessage.php, Subs-Post.php) + ! Make it so break up long words won't screw up links. (Subs.php) + ! Edit permissions wasn't sorting boards properly. (ManagePermissions.php) + ! Added an ID_TOPIC index to the calendar table. (upgrade_1-1.sql, install_1-1.sql) + ! Provide contextual information for calendar events. (Display.php) + & Added $txt['poll_return_vote'] for going back to vote after viewing the results. (index language files) + * You are now shown a link to go back to vote after viewing poll results. (Display template) + & Removed the punctuation in $txt['smf29']. (index language files) + * Registration now uses the admin tab functionality. (Register.php, Register template) + ! Simplified part of the converter basic code. (all converters) + ! Calendar events weren't being updated completely properly. (Post.php) + ! If the mail server rejects the EHLO, send HELO. (Subs-Post.php) + ! Don't let entities show in pm notifications. (Subs-Post.php) + ! Don't send RSET for the last email. (Subs-Post.php) + ! Allow email messages to have lines that start with "." using SMTP. (Subs-Post.php) + + Emails are now sent with MIME compliant UTF-8 encoding. (Subs-Post.php) + ! Fixed some internationalization problems with the pm autocomplete. (InstantMessage template, Subs-Auth.php) + ! Properly censor the first post's subject and show it in the main link, not the matched subject. (Search.php) + ! In Invision, usernames should already have entities. (invision_to_smf.php, invision2_to_smf.php) + ! Error message when deleting/changing a member ban. (ManageMembers.php) + ! Cleanup RSS feed link and per board/topic links. (index template) + ! Fixed a typo that was causing permission problems for non-administrators. (Profile.php) + & Remove $txt[209] because it is sadly no longer in use. (index language files) + ! Attempt to send the User-Agent as well for HEAD requests - doesn't fix anything because PHP is broken too. (Subs.php) + ! If someone's data isn't loaded yet, they have no permissions; return false instead of generating errors. (Security.php) + ! Don't generate errors if the footer is for any reason shown twice. (Subs.php) + ! Make the personal messages load faster. (InstantMessage.php, upgrade_1-1.sql, install_1-1.sql) + * Theme settings cleanup part one - the revenge of the paths. (Themes.php, Themes template) + & Updated a bunch of language entries for the above. (Themes language files) + ! The sorting links in the attachment manager were wrong. (ManageAttachments.php) + * Small typo when trying to edit icons. (ManageSmileys template) + * Unable to delete attachments/avatars from the admin panel. (ManageAttachments template, ManageAttachments.php) + ! Fixed search not narrowing down the results of multiple fulltext and-branches. (Search.php) + ! Spanning of events should now be editable everywhere, and some additional checks on validity are now carried out. (Post.php, Calendar.php, Post template) + * Added tabs to the Search and Calendar templates. (ManageCalendar.php, ManageSearch.php, ManageSmileys.php, Register.php, ManageCalendar template, ManageSearch template) + ! Don't let updateSettingsFile() write if it read in a broken file. (Admin.php) + ! Only find activated members when searching for pms/ignore list/etc. (Subs-Auth.php) + ! Trim notification email bodies for fun. (Subs-Post.php, Post.php) + ! Fixed a lingering internationalization issue. (Subs-Post.php) + & Send the full text of announcements in announcement notification emails. (Post.php, Post language files) + ! Make search properly use entities and search for them. (Search.php) + ! You couldn't set the strength for glow to more than 2 without using zeros. (Subs.php) + ! Send (optionally) custom Message-Id headers with messages sent by SMF - this makes it theoretically possible to handle replies. (Subs-Post.php, Post.php, ManageMembers.php) + ! Make the "I don't like people to see my full path which can easily be seen in other ways anyway" people happy. (SSI.php, ssi_examples.php) + ! Attempt to use iconv to fix things, if it exists. (Subs-Post.php, Subs-Auth.php) + ! Make the subjects of pms properly internationalized. (Subs-Post.php, InstantMessage.php) + ! Purdy up the parse error message output and try to detect sub templates if possible. (Load.php, Errors.php) + ! Change [quote=...] to [quote="..."] in the vBulletin converters. (vbulletin_to_smf.php, vbulletin3_to_smf.php) + ! Quote results from other pages using find members. (Subs-Auth.php) + ! Don't show birthdays for those who aren't activated/are banned. (Calendar.php) + ! Set $_SERVER['REQUEST_URI'] properly, even if that's not what it is :P. (QueryString.php) + ! The guest/default theme options are now used if none are found. (Register.php, Load.php) + * Don't show an option to change theme settings if only the default theme's settings are used. (Themes.php, Themes template) + + Allow the *removal* of theme options, not just the change thereof. (Themes.php) + * Cleanup the interface for changing options a lot, made it more logical and perform as it should. (Themes.php, Themes template) + & Made a bunch of language changes to further this effort. (Themes language files) + ! Backup restoration utility wasn't loading long enough lines. (restore_backup.php) + ! Set up a basic way to do mail from an address other than $webmaster_email. (Subs-Post.php) + + Rewrote the interface for selecting messages to be split and added XMLhttp support. (SplitTopics.php, SplitTopics template, Xml template) + * Added two image buttons for above feature (split_select.gif, split_deselect.gif) + & Added three language strings for the above feature. (index language files) + ! Removed numerical sub-actions for splitting and merging topics. (SplitTopics.php, Subs-Boards.php, SplitTopics template) + ! Added option to insert start numbers dynamically for function constructPageIndex. (Subs.php, Display.php, MessageIndex.php, Recent.php, ManageMembers.php, SplitTopics.php) + ! Fixed last split post causing a JavaScript error in IE. (SplitTopics.php) + * Make a few things in the theme manager look nicer under dumb ol' IE. (Themes template) + ! Make font sizes work properly. (Subs.php) + * Fixed a minor javascript error when replying. (Post template) + ! Use ignore_user_abort when strategic. (Admin.php, Load.php) + ! The html tag wasn't working on multiple lines correctly. (Subs-Post.php) + ! Try to continue on permission denied errors, which we hopefully won't get anyway. (Subs-Package.php) + ! Gender has been stored as a number for quite some time now. (Register.php) + * Use a session check in karma links. (Display template, InstantMessage template, Karma.php) + ! Remove some (hopefully) unnecessary indexes on the messages table. (install_1-1.sql, upgrade_1-1.sql) + ! Added the cache_put_data and cache_get_data functions. (Load.php) + ! Cache the male vs. female ratio. (Stats.php) + ! Cache the individual parsecode'd blocks for an hour. (Subs.php) + ! Added get_memcached_server and support for multiple mmcached servers with wannabe load balancing. (Load.php) + ! Tweak order of settings table changes. (upgrade_1-1.sql) + ! Use mmcache/eaccelerator sessions when database driven sessions are off and the cache is enabled. (Load.php) + ! There's no reason for secretAnswer to ever be longer than 64 characters. (upgrade_1-1.sql, install_1-1.sql) + ! Make the recent posts for rss feeds look harder for posts. (News.php) + ! Cleanup delete_replies, etc. permissions and possible problems/flaws. (RemoveTopic.php) + ! Modified log_online table and code slightly for speed optimization. (install.sql, upgrade_1-1.sql, Subs.php) + ! Added indexes to the log_errors table to speed up track IP/member. (install.sql, upgrade_1-1.sql) + ! Cleaned up last traces of mod setting 'enableReportToMod'. (SendTopic.php, upgrade_1-0.sql, install.sql) + ! Switched the primary key ID's of the log_boards table for (a little) faster performance. (upgrade_1-1.sql, install_1-1.sql) + ! Split some queries in the updateLastMessages() for faster performance. (Subs.php) + ! PHP was sometimes not being highlighted properly. (Subs.php) + ! Rename InstantMessage.php to PersonalMessage.php. (index.php, ManageMembers.php, PersonalMessage.php) + * Rename the InstantMessage template to PersonalMessage, but still support the old. (PersonalMessage template, PersonalMessage.php, Load.php, detailed-version.js) + & Rename the InstantMessage language files to PersonalMessage, but support old as above. (PersonalMessage language files, Subs-Post.php, PersonalMessage.php, detailed-version.js) + ! Fixed new poll choices sometimes getting messed up after editing polls. (Poll.php) + & Attempt to minimize/counter session problems with SSI.php. (SSI.php, index language files) + ! Using caching, avoid doing DELETEs more often than necessary on log_online, and do UPDATEs if possible. (Subs.php) + ! Add a bunch of security-related warnings and such for people changing code. (Errors.php, Security.php, Subs-Auth.php) + ! Do version number checking inclusive. (Subs-Package.php) + ! Don't cache parsed post data - too much memory. (Subs.php) + ! Send emails using the realName, if it was specified. (Register.php) + & Added a few missing documentation strings, cleaned up mail_type description. (ModSettings.php, Help language files, ModSettings language files) + * Don't show the delete selected button until it's useful. (Display template) + ! Cache the $board_info with moderators if we're only viewing the board directly. (Load.php) + ! Solve the submit once race condition problem using caching, if possible. (Security.php) + ! Clean up query string handling. (QueryString.php) + ! Don't resize images down to anything by zero. (Subs-Post.php) + * Show, optionally, resized down thumbnails for image attachments. (Display.php, ManageAttachments.php, Post.php, Subs-Graphics.php, Display template, ManageAttachments template, upgrade_1-1.sql, install_1-1.sql) + & Added help and descriptions for the above added options. (Admin language files, Help language files) + ! Moved creation and modification of posts into a function. (Post.php, Subs-Post.php, MoveTopic.php, Load.php) + * Clean up handling of internationalized names. (Profile.php, Subs-Auth.php, PersonalMessage.php, PersonalMessage template) + * Store labels on pms numerically instead of as text - sorry, this scraps all current labeling. (PersonalMessage.php, PersonalMessage template, upgrade_1-1.sql) + & Make it possible to remove messages from the inbox, and store them only in labels. (PersonalMessage.php, upgrade_1-1.sql, install_1-1.sql, PersonalMessage language files) + ! Use backticks when optimizing tables with potentially unknown names. (Admin.php) + ! Only send Content-Length for attachments/images if output compression is off or it is huge. (Display.php) + ! Create thumbnails on first un-thumbnailed view. (Display.php) + & Mark all those strings used in emails. (index language files, Login language files, PersonalMessage language files, Post language files, Profile language files) + * Fix some places where ?> is used bare in PHP code. (Load.php, News.php, PersonalMessage.php, Post.php, Register.php, Subs-Package.php, Wireless template, invision_to_smf.php, invision2_to_smf.php) + ! Adjust ID_MEMBER size on attachments to the same size as the column on members. (upgrade_1-1.sql, install_1-1.sql) + ! Properly delete thumbnails when deleting their attachment. (ManageAttachments.php) + ! Don't touch ID_MSG_LAST_VISIT/totalTimeLoggedIn under SSI. (Load.php, Subs.php) + * Add whitespace between smileys in the popup so they wrap better. (Post template) + ! Tweak indexes on calendar and themes. (install_1-1.sql, upgrade_1-1.sql) + ! Cache the smileys, if possible, for a short period. (Subs-Post.php) + ! Don't set the notifications as sent if none were even touched. (Subs-Post.php) + * Cleanup internationalization of message labels. (PersonalMessage.php, PersonalMessage tmeplate, install_1-1.sql, upgrade_1-1.sql) + ! Fix posting updating post count. (Subs-Post.php, Post.php, MoveTopic.php) + * Major/dumb typo in script.js that made collapsing things not work. (script.js) + & Changed $days to $txt['days'], $days_short to $txt['days_short'], $months, etc. (index language files, Subs.php, Profile.php) + * A few template changes for the above too. (Calendar template, Profile template, Settings template) + ! Fix time formats for today/yesterday again. (Subs.php) + * Add stuff-doing to the list of things the theme manager can do. (Themes template, Themes.php) + & And add some language entries for the above. (Themes language files) + ! Collect and validate information about available languages on registration. (Register.php) + ! If you log in as an administrator, get rid of the no-double-register lock. (LogInOut.php) + ! Check a few other things in registrations a bit more throughly. (Register.php) + & Changed $txt['whoallow_sendtopic'] to $txt['whotopic_sendtopic']. (Who.php, Who language files) + * When editing polls, the choice ordering could be messed up. (Poll template) + ! Try to properly handle the case where karmaWaitTime is set to 0. (Karma.php) + ! Only try to create the thumbnail once, and fail if it doesn't work. (Display.php, Subs-Graphics.php) + ! Cache moderator group info and tweak how it's used. (Load.php) + ! Clean up the information about ssi_boardNews, which was way old. (news_readme.html) + * Cleaned up some old bgcolor usage. (index template, Printpage template) + ! Optimized the administrative totals recount by a rediculous amount. (Admin.php) + * Automatic notification was still being turned off sometimes on replies with quick reply. (Display template) + * Attempt to control the size of avatar previews, if possible. (Profile template) + ! URLs within URLs in certain situations didn't auto link properly. (Subs.php) + + +January 2005: +-------------------------------------------------------------------------------- + ! The calendar was not showing birthdates properly at the end of the year. (Calendar.php) + & Fixed a typo in the can't reply to locked topics message. (Errors language files) + ! Add a integrate_redirect setting for redirection. (Subs.php) + ! Don't show "2: ", or "8: " error level indicator in error messages, show file/line to admins. (Errors.php) + ! The attachment directory's size was not being counted properly. (ManageAttachments.php, Post.php) + ! Search had some problems still with "temporary table emulation". (Search.php) + ! If readfile is disabled, check for null instead of false. (Display.php) + ! Updated path detection for windows servers. (install.php, Packages.php, PackageGet.php, create_backup.php) + & Add a new language string for the installer detecting a path. (Install language files) + ! Fixed a minor typo in the package manager. (PackageGet.php) + & Add a new language string for the installer to not say deleting the file doesn't work on all servers. (Install language files) + & Added $txt['mysql_error_space'] and a check for out of diskspace errors. (Errors.php, Errors language files) + ! Updated the backup creation tool, adding better FTP handling. (create_backup.php) + ! Don't log notices from PHP 5 in the error log. (Errors.php) + * The news fader now updates just slightly less often. (fader.js) + & Updated $txt[739] to better reflect what the setting does. (Admin language files) + * Slightly tweaked the look of recent and searched posts... very minor. (Recent template, Search template, Recent.php) + ! Added who action information to the profile contextual information. (Who.php, Load.php, Profile.php) + ! Don't show the option to move a topic if there's only one board (you can see.) (MessageIndex.php, Display.php) + * Added Mark selected read to the quickmoderation options. (Subs-Boards.php, MessageIndex template) + & Added $txt['quick_mod_markread'] for the above. (index language files) + * Don't show merge-to target boards if this is the only board they can see. (SplitTopics template) + ! If there are no topics to merge with available, show an error message. (SplitTopics.php) + ! Added web installer and mirror listing. (webinstall.php, mirrors.xml) + ! Fixed some group-related bugs in the YaBB SE converter. (yabbse_to_smf.php) + ! For older themes, provide the $modSettings['memberCount'] value. (BoardIndex.php) + ! Don't use strtr replacing a blank string with a blank string. (PackageGet.php, create_backup.php) + & Made $txt['package45'] more descriptive. (Packages language files) + ! Use MySQL conditional comments for TRUNCATE, etc. (ManageMembers.php, Errors.php, Admin.php, Search.php, ManageSearch.php) + ! Escape any input made from the login page, just in case. (LogInOut.php) + + Registration management will now allow filtering by different approval types where appropriate. (Register.php, Register template) + ! Removed duplication of actions in registration management (Delete/Reject). (Register.php, Register template) + & Added several language strings to aid the filtering in registration management. (Login language files) + ! Added some backend functionality for COPPA support, in particular post registration screens. (index.php, Register.php, Register template) + & Added many language entries to Login language files for above addition. (Login language files) + & Added $months_titles to ease translation in Greek, etc. (index language files) + * The calendar now uses $months_titles for capitalized titles. (Calendar template) + ! Handle attachments with quotes in their names properly. (all converters) + & Added string to show current filter when you are on a non-standard filter in registration management. (Register.php, Register template, Login language files) + ! Use isset with multiple parameters when possible. (Admin.php, Errors.php, InstantMessage.php, ManageBoards.php, ManageMembers.php, ManageSmileys.php, MoveTopic.php, Post.php, Profile.php, Register.php, Security.php, all converters) + ! Provide SHA-1 support for old PHP versions, allow it in a login cookie - no real changes yet. (Load.php, Security.php) + ! Implement SHA-1 hashing for passwords; use the passwordSalt for cookies. (SSI.php, Load.php, LogInOut.php, Profile.php, Register.php, Reminder.php, Security.php, Subs-Auth.php, install.php) + ! Update copyright notices. (all files) + ! Use a proper id under WAP for categories. (Wireless template) + ! If you want people to retain their stars, member group, etc... just blank the moderator group out. (Load.php) + ! On the profile statistics page, show the member's name for clarity. (Profile template) + ! Number format wasn't saving properly from the settings. (Subs.php) + ! Use proper checks for FULLTEXT even under 3.23.x.. (ManageSearch.php, DumpDatabase.php, create_backup.php) + ! Admin registration will now only let admins register admins. (Register.php) + * Added front end for COPPA implementation, including settings in the admin panel, and checkbox on registration. (Register.php, Register template) + & Added some language entries for above change. (Login and Help language files) + ! Activation should be disabled for COPPA users - for the time being at least. (Register.php) + & It is now possible to receive a reminder of COPPA contacts/forms if you try to log in as a minor without approval. (LogInOut.php, Register.php, Login language files) + ! Use mhash for sha1 instead of the PHP version if at all possible for speed. (Security.php) + ! Moved "edit agreement" to a subaction of regcenter; could do more. (Subs.php, Register.php, Admin.php, Admin template, index.php) + & Improved the text descriptions shown when viewing an unapproved/unactivated profile. (Load.php, Profile.php, Profile template, Profile language files) + ! Requesting a new activation email after changing your email address was causing an error. (Profile.php, Register.php) + & Registration center can now handle approving deleted accounts. (Register.php, Register template, Login language files) + ! Moving topics should use default redirect notice in forum's default language. (MoveTopic.php) + ! Attempt to handle colons (:) in links... (Subs.php) + ! Check to make sure uploaded temp files actually exist when validating them. (Profile.php, Post.php, Themes.php, PackageGet.php, ManageSmileys.php, test_modification.php) + ! Fix pagination in trackip functionality. (ManageMembers.php) + ! For mark unread's purposes, don't check that they aren't the last poster. (Recent.php) + ! Whether an email was public or not (email_public) was not properly reflecting guest_hideContacts. (Load.php) + ! Add an .htaccess for attachments/ - if it doesn't work or causes a 500, no loss... only gain. (attachments/.htaccess) + ! Edit poll was duplicating slashes, etc., on preview. (Poll.php) + ! Don't add entity's to the sender's name in email notifications. (Subs-Post.php) + ! If there were karma or im options involved with only some members, failure resulted. (yabb_to_smf.php) + ! Theme converter was adding too many slashes to PHP code from template.php. (Themes.php) + ! Fixed a small bug causing the XML output to show incorrectly. (News.php) + ! GlobalCookies setting was not properly disabled for IP addresses. (Subs-Auth.php) + ! Fixed unable to set delete_own/delete_any permission. (ManagePermissions.php) + ! HTML-ify everything in the smiley's description text. (Subs.php) + ! Allow whitespace within the actual table cells, and fix a little bit more bad table syntax. (Subs.php, Subs-Post.php) + ! Optimized poll option deletion. (Poll.php) + ! Don't cough and die on filenames with tildas in them in the detailed version checker. (Admin.php) + * Search now features quick moderation... sorta. (Search.php, Search template, Subs-Boards.php) + * Improved error messages displayed on login screen for unapproved accounts. (LogInOut.php, Login template) + * Approval on account deletion added, so admin can give the thumbs up before account removed. (Subs.php, ModSettings.php, Profile.php, Register.php) + & Language entries added for above changes. (Login language files, Errors language files, Profile language files, ModSettings language files) + + Added two new notification settings, notifyTypes and notifySendBody. (install_1.1,sql, upgrade_1.1.sql, Load.php, Post.php, Profile.php, Subs-Post.php) + * Altered Profile template to accomodate above settings. (Profile template) + & Added new language strings for above changes. (Profile and Post language files) + ! Show "all unread" on the linktree separately from "unread"... (Recent.php) + ! Minor notice in login if you went to login2 directly. (LogInOut.php) + * Allow the easy uninstallation/upgrade of non-modification packages. (Packages template) + ! Attempt to turn off session.use_trans_sid in loadSession(). (Load.php) + ! If Invision's upload directory cannot be found for some reason, use .../uploads. (invision_to_smf.php, invision2_to_smf.php) + ! The recount functionality is now (somewhat) paginated. (Admin.php) + ! If the package file is zero bytes in size, don't die horribly. (Subs-Package.php) + & Added an option to *disable* notifySendBody. (ModSettings.php, ModSettings language files, Help language files) + * Approval from a users profile now works correctly, and gives a warning before approving the deletion of an account. (Profile.php, Profile template) + & Increased the options for pruning messages from forum maintenance. (RemoveTopic.php, Admin template, Admin language files) + ! Don't create a backup of the "Settings.php" file using the normal tilda method. (Subs-Package.php) + ! Blank out url_rewriter.tags so SMF can just do it itself. (Load.php) + * Because of the markread option, always show checkboxes for quick moderation. (MessageIndex template, Search template) + * If you can't post in a specific board, and there are no topics, and only children, just show the children. (MessageIndex.php, MessageIndex template) + ! Added shorten_subject() function to deal with internationalization problems. (SSI.php, Subs.php, BoardIndex.php, MessageIndex.php, Recent.php, Post.php) + ! Use the name instead of the username in the page title for profiles. (Profile.php) + ! Disabling BBC also disabled smileys without warning. (Subs.php) + ! Work a little harder to sanitize filenames. (ManageSmileys.php, Themes.php, QueryString.php) + ! Fixed a couple minor typos. (Help files, various files) + * Add logo setting... too many questions. (Settings template, Themes template, Themes language files, index template) + * Tweak a few things for right-to-left support. (BoardIndex template, Calendar template) + & Tweaked $txt['own_profile_confirm']. (Profile language files) + - Don't allow output compression on PHP 4.1.2, etc. either - causes problems. (index.php, SSI.php) + ! Make "don't send me any email" notification setting also apply to report-to-moderator and registration notifications - HURRAH!! (Register.php, SendTopic.php) + ! Removed isBanned from members table in favor of is_activated, taking the unlucky value of 13 for a banned member. (install_1-1.sql, upgrade_1-1.sql, ManageMembers.php, Post.php, Security.php, Subs-Post.php) + ! Optimize tables one at a time to avoid unnecessary locks. (Load.php) + ! Don't die horribly if the compression data in an archive is corrupt. (Subs-Package.php) + ! Warn also if "webinstall.php" exists. (Subs.php) + ! Properly escape entered text for error messages, etc. (Search.php) + ! Copy over the entire images directory when creating a copy of the default theme. (Themes.php) + ! Show the correct path to the Themes directory even on Windows servers. (Themes.php) + ! Don't send forum-wide emails to unactivated people, or any emails really. (Post.php, Subs-Post.php, ManageMembers.php) + ! Fixed a minor notice when searching without create temporary. (Search.php) + ! Pagination was still a bit wrong in unread topics, etc. (Recent.php) + ! Search has been rewritten in order to increase speed, integrate fulltext search and improve accuracy of search results. (Search.php, ManageSearch.php) + + Search now supports excluding words by adding a minus sign (e.g. animals -sheep). (Search.php) + ! Search no longer uses temporary tables. (Search.php) + ! Search cache can no longer be disabled. (Search.php, ManageSearch.php) + ! ModSettings 'disableTemporaryTables' and 'search_cache_size' are no longer used. (ManageSearch template, ManageSearch.php, upgrade_1-1.sql) + & Removed language tags for above changes. (Search langugage files, Help language files) + * Removed submit buttons if there are no PM labels defined yet. (InstantMessages template, InstantMessages.php) + + Attempt to fix "incorrect key file" errors too. (Errors.php) + ! Upgrader didn't show the error message properly for when agreement.txt was not writable. (upgrade.php) + ! Fix lists and tables a bit better on preview/post ;). (Subs-Post.php, Subs.php) + + Allow [url=?topic=1.0], [img]/images/...[/img], [url=#blah], [anchor=blah], etc. (Subs-Post.php, Subs.php) + + Added backend functionality for additional message icons. (SSI.php, Display.php, MessageIndex.php, Post.php, Recent.php, Search.php, install_1-1.sql, upgrade_1-1.sql) + * Updated templates to respect the location of message icons. (Display template, MessageIndex template, Post template, Recent template, Search template) + + Added a very basic message icon editor, to allow for adding, editing, sorting and deletion of message icons. (ManageSmileys.php, ManageSmileys template) + & Added language strings for message icon manager. (Admin language files, Errors language files, ManageSmileys language files) + ! phpBB converter now handles code sections and some other bbcode better. (phpbb2_to_smf.php) + ! Fixed a few typos, logging of mark read for guests in split. (SplitTopics.php, Security.php) + * Small change to style.css - limit the max-height of code blocks. (style.css) + ! CGI server check for logins, etc. wasn't working with FastCGI. (Load.php) + ! Cleanup better when deleting a member. (ManageMembers.php) + ! Don't add to the moderation log when a moderator deletes his or her own post. (RemoveTopic.php, Subs-Boards.php) + + Make it easier to run something that uses SSI.php from the command line. (Load.php, QueryString.php) + ! Allow login cookie length to be longer. (Load.php, smf_api.php) + ! Get basic support for feeds other than RSS and SMF's xml ready - not done. (News.php) + ! Fix "never expire" not being properly checked bug. (LogInOut.php) + ! Allow forum moderators to blank email addresses and allow board moderators to edit those posts. (Post.php) + + Attachments are no longer lost on preview/error. (Post.php, ManageAttachments.php) + * Modified the templates to no longer give warnings about preview, and to allow uncheck of attachments to stick. (Post template) + ! Removed attach_lose and attach_preview, which are no longer needed. (Post language files) + * The "additional options" area now sticks better. (Post template) + ! Send announcement now links to first post. (Post.php) + ! Don't show the email icon for members or guest posters whose email is blank. (Load.php) + ! If is_activated is greater than 10, it means a ban is in place. (upgrade_1-1.sql, ManageMembers.php, Security.php) + ! Add base functionality for "cleanup permissions". (index.php, Admin.php) + ! Themes based on other themes now work properly. (Themes.php, upgrade.php) + * Added hits and average hits per day to the stats page. (Stats.php, Stats template) + & Added num_hits and average_hits. (Stats language files) + * Don't allow registration submission at all with a blank name/email. (Register template) + * Minor efficiency tweak to ssi_boardNews. (SSI.php) + ! The db_query() function now takes false, false as to mean no error checking. (Subs.php, ManageSearch.php, Search.php) + ! The search interface wasn't able to remove the fulltext index. (ManageSearch.php) + ! Updated the YaBB converter. (yabb_to_smf.php) + ! Make sure "since last visit" is always more than five hours ago. (Load.php) + ! If anything goes wrong extracting a file, fail more silently. (Subs-Package.php) + & Change $txt[350] just slightly. (Admin language files) + & Internationalize the installer a bunch more. (install.php, install_1-1.sql, Install language files) + & Renamed notifyAnncmnts_UserDisable to allow_disableAnnounce. (ModSettings.php, Post.php, install_1-1.sql, upgrade_1-1.sql, Help language files, ModSettings language files) + * Changed the setting also in one template. (Profile template) + * When you uncheck attachments and hit preview, now allow more attachment uploads in good faith. (Post.php, Post template) + ! Recent posts counter was starting over from 1. (Recent.php) + * The board management interface now warns you when checking a board's permissions will make it local. (ManageBoards.php, ManageBoards template) + & Added confirmation string to language files. (ManageBoards language files) + ! Recount all forum totals was not properly affecting the last messages on boards. (Admin.php) + ! Add contextual information describing whether they consider YOU a buddy. (Load.php) + ! Add backend for resetting the login hash and for challenge logins. (LogInOut.php) + ! Added backend for the horrid countChildPosts - which turns on and off without affectings boards. (BoardIndex.php, MessageIndex.php) + ! Attachments with "&" and " " characters in them weren't converted properly. (phpbb2_to_smf.php) + ! Don't check that uploaded files exist if open_basedir is set. (Post.php, Profile.php, Themes.php, ManageSmileys.php, PackageGet.php) + ! Attempt to detect whether local and global cookies should be on at installation. (install.php) + ! Keep tabs in PHP code blocks properly under PHP 5. (Subs.php) + * Deleting a single PM wasn't working correctly. (InstantMessage.php, InstantMessage template) + & Took the word "10" out of $txt[234]. (index language files) + ! Make upgrade.php accept an older language file for minor releases. (upgrade.php) + ! Change default of 'smiley_enable' to off. (upgrade_1-1.sql, upgrade_1-0.sql, install_1-1.sql) + ! Clean up statistics sorting in all the converters. (all converters) + ! The Burning Board converter should now transparently handle 1x and 2x. (burningboard_to_smf.php) + ! Simplify shorten_subject() by quite a bit, let's hope I don't regret this. (Subs.php) + ! Links with : in them were fixed, but with http:// in them (all) were broken. (Subs.php) + ! Sessions now look slightly slightly nicer with/without queryless URLs on. (QueryString.php) + * Changed everywhere that used ;id to something else for dumb servers. (Display.php, InstantMessage.php, Load.php, LogInOut.php, ManageBoards.php, ManageCalendar.php, ManageMembers.php, ManagePermissions.php, ManageSmileys.php, Register.php, Subs.php, Themes.php, ManageBoards template, ManageCalendar template, ManageMembers template, ManagePermissions template, ManageSmileys template, Profile template, Register template, script.js, Themes template) + & Fixed a link in $txt['smf3']. (ModSettings language files) + ! Resetting all theme options didn't always set the default registration options right. (Themes.php) + * Allow a Settings template to specify that a option is a default option. (Settings template, Themes template) + & A few links were using _new instead of _blank. (Errors language files, Help language files, Subs.php, news_readme.html) + ! Cleaned up the flash output - it should look/work better with non-IE browsers now, and use less bandwidth in such situations. (Subs.php) + * Renamed im_email_notify and im_ignore_list to pm_*. (Profile template, all converters, upgrade_1-1.sql, install_1-1.sql, smf_api.php, repair.php, Subs-Post.php, Profile.php, Load.php, Register.php) + * Fixed few minor errors in InstantMessage sending error handling. (InstantMessage.php, InstantMessage template) + & Changed the error message for ALTER to include CREATE/DROP. (upgrade.php, Install language files) + ! In most cases, you couldn't properly select theme-dependent language files. (Admin.php, Profile.php) + ! $modSettings['enableErrorLogging'] can now be set to 2 to log ALL ERRORS. (Errors.php) + ! Don't force a referer check for just a single attachment's deletion. (ManageAttachments.php) + ! Fixed the 'parentheses not balanced' error. (Search.php) + ! Make it so is_read can be things other than 1. (InstantMessage.php) + + Log whether a message has or has not been replied to. (InstantMessage.php) + ! Use tinyint(3) instead of 4 for poll columns. (upgrade_1-1.sql, install_1-1.sql) + * Add an autocompleter to to/bcc for pms - soon to have more, and buddies. (InstantMessage template, Subs-Auth.php) + ! MIME encode the sender's name when sending emails. (Subs-Post.php) + ! Don't let the "space left in the attachment directory" show below 0. (ManageAttachments.php) + ! A database error could occur if you tried to create a smiley with a single quote in its name. (ManageSmileys.php) + ! That should do it for RDF and ATOM feeds. (News.php, Load.php) + ! Where possible, load the language file before the template. (Help.php, InstantMessage.php, LogInOut.php, ManageMembers.php, ManagePermissions.php, Post.php, Register.php, Reminder.php, Security.php, SendTopic.php, Stats.php, Subs-Auth.php, Subs-Post.php, Themes.php) + * Properly (I think?) handle multiple recipients in auto complete. (InstantMessage template) + * Cache the previous results to increase speed of searches. (InstantMessage template) + ! Only search autocomplete by display name. (Subs-Auth.php) + ! Use <br /><br /> instead of \n\n for the notice. (ManageAttachments.php) + ! Properly handle special characters at the beginning and end of the search word. (Search.php) + ! Don't show the pm popup when action is pm. (Subs.php) + ! Package manager wasn't properly skipping missing files. (Subs-Package.php) + ! Minor unimportant but still dumb typo in all converters. (all converters) + ! Fixed a notice that could occur when a guest posts. (Post.php) + * Work around dumb server security settings. (Subs.php, ManageMembers.php, ManageMembers template) + ! Inherit font style and color in table tags. (Subs.php) + + Added XMLHttpRequest support for previewing messages (Post.php, Post template, Subs.php, script.js) + * Added an XML template for the above feature. (Xml template) + ! Quotefast feature now does XMLHttpRequest asynchronously. (Post.php, Display template, Post template, Xml template) + * Created a sub-template for the quotefast using a popup (non-XMLHttpRequest). (Post template) + ! LoadTheme() doesn't load the index template anymore for certain actions. (Load.php) + + Added a 'new' icon for new posts that were added while posting/previewing. (Post.php, Post template) + & Moved $txt['error_while_submitting'] from errors to index. (Errors language files, index language files) + & Added $txt['login_hash_error']. (Login language files) + * Logins are now hashed against a challenge code for security. (Login template, index template, LogInOut.php, Security.php, Subs-Auth.php) + ! Added a password hash disable function to give upgraders time to not be annoyed. (Subs.php, upgrade_1-1.sql, all converters) + * Add textToEntities function to aid internationalization. (script.js, Post template, InstantMessage template) + ! Correct post counts when moving between two boards that count differently. (MoveTopic.php) + ! Track events properly so they won't overlap when more than a month of them are loaded. (Calendar.php, SSI.php, BoardIndex.php) + ! Keep track of event spans with just one row - properly, in other words. (SSI.php, Calendar.php, Post.php, upgrade_1-1.sql, install_1-1.sql, yabbse_to_smf.php) + & Added $txt['smileys_forum_board_default']. (index language files) + * Profile now shows a choice for forum/board default smiley set. (Profile.php, Profile template) + ! Profile no longer uses special redirect unless it needs to. (Profile.php) + + Implemented XMLHTTP support for the stats page. (Stats.php, Stats template, Xml template) + ! Adjust the session vars on collapse of statistics. (Stats.php, Stats template, script.js) + ! Tweaked entity internationalization code, since we now have to deal with 127+. (MoveTopic.php, Poll.php, Post.php, Search.php, SplitTopics.php) + ! Use sprintf instead of str_pad when possible. (Stats.php, Calendar.php) + ! Fix the security warning and make it more readable. (Subs.php) + ! If a "duplicate entry" error occurs while voting, just ignore it. (Poll.php) + ! Cleanup a few notes and file saving in the converters. (all converters, various files) + ! Arranged admin center items more logical (imho) and shortened the names. (Subs.php, Admin language files) + ! Merged 'registration agreement' into 'registration center'. (Register.php, Subs.php, Admin template, Register template) + * Added a template layer for the registration center menu. (Register template, Register.php) + ! Adjust (lower) a few timeouts. (Subs.php, Subs-Post.php) + + If spell checking is on and working, search keywords are now checked. (Search.php) + * Added contextual information for revising the search - for no results/misspelling, more if template wishes. (Search template) + & Add a few entries to the language files for the above. (Search language files) + ! Don't allow the deletion of administrators by non-administrators. (ManageMembers.php, Profile.php) + ! Use LOW_PRIORITY in a few queries on topic/attachment display so as not to lock other things up. (Display.php) + ! Fix some issues in the YaBB and YaBB SE converters. (yabb_to_smf.php, yabbse_to_smf.php) + ! Allow the removal of the first post by quick moderation if it's the only post. (Subs-Boards.php) + ! Did you mean was ignoring excluded search terms. (Search.php) + * Allow an override of the preview magic with Ctrl. (Post template) + ! A few minor cleanups with label handling in personal messages. (InstantMessage.php, InstantMessage language files) + * Editing the span of an event should now work correctly, in unlinked events anyway. (Calendar.php, Calendar template) + * Buddies are now shown when using who's online via SSI (SSI.php) + ! Replaced two mysql_fetch_array()s with mysql_fetch_assoc()s. (DumpDatabase.php, create_backup.php) + ! Hard code an error message since it's better than nothing. (Load.php) + ! Do internationalization cheating on personal text as well. (Profile.php) + ! Changed several mysql_query()'s to db/convert/upgrade_query()'s. (upgrade.php, upgrade_1-0.sql, burningboard_to_smf.php, ViewQuery.php, DumpDatabase.php, Errors.php) + ! Logging in after the password hashing is enabled with a non-sha1 password wasn't working. (LogInOut.php) + * Correctly list hidden and buddy users in who's online. (BoardIndex template) + * Fix a few XML preview typos, make is_not_guest() die in xml mode. (Post.php, Security.php, Xml template) + ! Add tgz to the allowed package extensions. (Package.php, PackageGet.php) + * XML nodes are sent in 4 kilobyte chunks. (Post template, Display template) + ! Calendar wasn't showing birthdays and holidays properly. (Calendar.php) + & The package manager now caches your FTP information so it can use it more actively. (LogInOut.php, Packages.php, Subs-Package.php, Packages language files) + * Now, when you do things that require temp or others to be writable, you're prompted for FTP info. (Subs-Package.php, Packages.php, Packages template) + + The package manager now works on safe mode servers without PHP suExec - mostly. (Subs-Package.php) + ! Make undo work with position="end" as well. (Subs-Package.php) + ! Added a function to make chmod'ing files easier and more suExec compliant. (Subs-Package.php, Packages.php) + + +December 2004: +-------------------------------------------------------------------------------- + ! Cleaned up the gif loading code and removed quite a few unnecessary functions. (Subs-Graphics.php) + ! The forum URL is now checked to make sure it starts with a scheme. (install.php, repair_settings.php, Admin.php) + ! Don't prefill the "To" box with "". (InstantMessage.php) + ! Cleaned up some comments and minor coding conventions. (various files) + ! Added package SDK to the repository. (tools/package) + ! Attempt to stop time and memory limits from causing problems with attachments. (Display.php) + ! Attempt to prevent a timeout when recounting forum totals and statistics. (Admin.php) + ! Don't allow editing of own custom title by default. (install_1-1.sql) + ! Cleanup permissions and make upgrades not set permissions each round or affect post groups. (install_1-1.sql, upgrade_1-0.sql, ManagePermissions.php, phpbb2_to_smf.php, yabbse_to_smf.php) + ! Changed remove_replies to delete_replies, remove_own/any to delete_own/any, delete_own/any to remove_own/any. (Display.php, ManagePermissions.php, MessageIndex.php, Profile.php, Recent.php, RemoveTopic.php, Security.php, Subs-Boards.php, Errors language files, ManagePermissions language files, install_1-1.sql, upgrade_1-0.sql, upgrade_1-1.sql, invision2_to_smf.php, invision_to_smf.php, phpbb2_to_smf.php, yabbse_to_smf.php) + & Updated $txt['mlist_search'] and $txt[303]. (index language files) + ! Add holidays to the calendar until 2020. (install_1-1.sql, upgrade_1-1.sql) + ! Add language installation notes to all and remove requirements from update, since they are the same. (install_readme.html, upgrade_readme.html, update_readme.html) + ! Speed up mark read notably for larger forums/boards. (Subs-Boards.php) + ! Do we really want to switch to "view all" after deleting from "view members"? (ManageMembers.php) + ! Show "last online" or "last active" in italics if they are not activated. (ManageMembers.php) + ! Add the darn online colors to "who's viewing" information. (Display.php, MessageIndex.php) + ! Added an index on ID_MEMBER to log_topics; yes, it's more index and more space, but... it'll be faster in some parts. (install_1-1.sql, upgrade_1-1.sql) + ! In the few cases where it would be more efficient not to do the extra lookups for updateStats, they are no longer done. (Post.php, Register.php) + * Update script.js to handle an evil change to Safari 1.3's handling of readonly inputs/textareas. (script.js) + ! Let the people have their email addresses of one character. (Post.php, Register.php, Profile.php, SendTopic.php) + ! Only allow email addresses for the MSN field. (Profile.php) + ! Lay down basic frame work for "theme x based on theme y". (Load.php, Help.php) + ! XMB converter wasn't handling the post text correctly. (xmb_to_smf.php) + * Added ability to add members to a buddy list. (Profile.php, Load.php, BoardIndex.php, Subs-Auth.php, index.php, upgrade_1-1.sql, install_1-1.sql, BoardIndex and Display templates) + ! Small fix with default permissions in the permissions manager. (ManagePermissions.php) + ! It's no longer possible to delete the last admin account from their profile. (Profile.php) + & Added language entry for the above change. (Errors language files) + ! Fixed a small bug in adding an unlinked calendar event. (Calendar template) + ! Added settings for displaying holidays etc on the calendar. (install_1.1.php, upgrade_1.1.php, ManageCalendar.php, Calendar.php, ManageCalendar template) + & Added and altered language entries for the above change. (ManageCalendar language files) + ! Themes could be set by URL even if this was turned off. (Load.php) + ! A quote with only an author followed by a quote with more caused problems - now it doesn't as much. (Subs.php) + ! Format lists with </li> (for validity) even when using [*], etc. (Subs-Post.php) + ! Made a check for form_stack_pointer just in case. (Security.php) + ! Check for session support in the installer to avoid getting a blank screen. (install.php) + & Added $txt['error_session_missing']. (Install language files) + ! Polls now use the same psuedo-international character set fixing. (Post.php, Poll.php) + ! Allow the subjects to be actually (or near) 100 characters counting &#***;, &quot;, etc. as only one. (Post.php) + ! Added ENABLE/DISABLE KEYS support to the create_backup.php utility. (create_backup.php) + ! Don't log dlattach as the last page view... (Subs.php) + ! Always show "me" on who's viewing this topic/board. (MessageIndex.php, Display.php) + ! Roll over previous/next instead of showing an error - also optimize previous/next query. (Display.php) + & Removed $txt['previous_next_end']. (Errors language files) + ! Updating stats which would leave a negative post count was causing an error. (Subs.php) + ! To/Bcc will no longer be filled with quotes upon an error sending a post. (InstantMessage.php) + & Updated error message shown when a message can't be sent due to size of someones inbox. (InstantMessage.english.php) + * Administrators are now completely exempt from capacity limit. (Subs-Post.php, ManageMembers template) + ! Return a 304 HTTP response if an avatar/attachment is still cached by the user agent and hasn't been modified. (Display.php) + ! You can now view the unread posts or replies in just a single category. (Recent.php) + & Removed the word "beta" from the default theme description. (Settings language files) + * Clean up page indexes and links for WAP/WML. (Wireless template) + & Added $txt['cannot_announce_topic']. (Errors language files) + & Added some helpful information to $helptxt['fixLongWords']. (Help language files) + * Fix links to board/category specific recent posts; show current location. (Recent template, Recent.php) + ! XMB converter was adding slashes in bad/annoying places. (xmb_to_smf.php) + ! vBulletin 2 and 3 converters were not properly converting the last post information. (vbulletin_to_smf.php, vbulletin3_to_smf.php) + ! Passwords with single quotes in them weren't being set properly from the profile. (Profile.php) + * Reformatted the password reminder a bit; no longer have to specify whether it's a username or email. (Reminder.php, Reminder template) + & Removed $txt['smf101'] and $txt['smf102'], and now $txt[194] is 'Password reminder' not just 'reminder'. (Profile language files, index language files) + ! Updated the help in various places, layout and language information. (various files in help) + & Added $txt['register_subject'], $txt['register_immediate_message'], $txt['register_activate_message'], $txt['register_pending_message'], $txt['resend_activate_message'], and $txt['resend_pending_message']... removed $txt[700], $txt['activate_mail'], and $txt['approval_email']. (Login language files) + ! Registration now uses a much more translatable/changeable string for registration. (Register.php) + ! Image URL protection was affecting links in some cases too when images were next to each other. (Subs-Post.php) + ! Removing nested quotes removed text between when using just [quote]. (Post.php, InstantMessage.php) + ! Don't show html tags in board titles in the <title> element. (MessageIndex.php) + * Cleaned up Search template and made it not search the recycle bin by default. (Search.php, Search template) + ! Searching with symbols now works better even in fulltext mode. (Search.php) + ! Match highlighting was having problems with special characters. (Search.php) + ! Administration panel sometimes gave errors if you had very few permissions to it. (Admin.php) + * Tweaked the layout of search results and put the relevance to the left. (Search template) + ! Don't spell check ALL CAPS words, and don't think 'word' is spelled wrong. (Subs-Post.php) + & Updated $txt['send_validation_onChange']. (ModSettings language files) + ! If a birthday from next year is shown on the board index, calculate the age correctly. (Calendar.php) + & Add board listing into context for simplicity, make categories select boards, collapse board list. (Search.php, Search template) + ! Fixed a few typos. (Install language files, Admin language files, Errors language files) + ! Big clean up in the moderation log, including much better handling of sorting/pagination after a search. (index.php, Modlog.php, Modlog template) + ! Updated Invision 2 converter to (hopefully) deal with personal messages correctly. (invision2_to_smf.php) + + Added a basic converter that will hopefully convert from UBB.threads to SMF. (ubbthreads_to_smf.php) + ! Searching by action in the moderation log will now work across all languages. (Modlog.php) + ! Check cookie before unserializing to patch PHP security hole. (Load.php, smf_api.php) + ! Don't use unserialize() in Search, instead fake it. (Search.php) + & Add half a sentence to $txt['smiley_editsets_explain']. (ManageSmileys language files) + * Add backend for searching topics specifically. (Search.php, Search template) + & Added $txt['search_specific_topic'] for the search parameters page. (Search language files) + ! Trim the end of templates and language files when including them. (Load.php) + ! Search wasn't handling escaped bbc properly. (Search.php) + * If you click advanced, carry over the string you're searching for. (Search template) + ! Backup utilities didn't support FULLTEXT. (DumpDatabase.php, create_backup.php) + ! Prevent changing a board's parent to one of its children. (ManageBoards.php) + ! Make sure people can't remove posts if they have no permission at all to. (Subs-Boards.php) + & Added $txt['timeoffset_autodetect']... (Profile language files) + * Added "auto detect" to profile's time offset. (Profile.php, Profile template) + ! Don't delete attachments that have internationalized filenames on maintenance. (ManageAttachments.php) + ! Registering a new member would cause an error to be flagged if the ban session variable didn't exist. (Register.php) + ! Added functionality to registration management to allow admins to quickly perform actions on outstanding users. (Register.php, Register template) + & Added langauge entries for above change. (Login language files) + * If an error occurs when saving on the profile page, an error message will now appear to say why. (Profile.php, Profile template) + & Several error strings added to language files. (Errors language files) + + Implemented PM support for WAP 2 & I-mode. (Wireless template, InstantMessages.php, Subs-Auth.php) + & Added some language tags for the above change (Wireless language files) + ! Fixed small upgrade bug. (upgrade_1-0.sql) + ! Fixed error in label display if PM has only BCC recipients. (InstantMessages.php) + ! Fixed a small bug in searching PM recipients. (Subs-Auth.php) + ! Fixed members with an apostrophe could not be sent a PM. (InstantMessages.php) + ! Smileys are no longer parsed for the wireless protocol. (Subs.php) + ! Invision 2 converter will now warn the user if files are not writable - and will not attempt what it cannot achieve. (Invision2_to_smf.php) + * Added a "Check All" button to the "Email Your Members" screen.(ManageMembers template) + * Position the caret properly when nothing's selected in Internet Explorer. (script.js) + ! Handle many elements with the same name in invertAll(). (script.js) + ! Fill the email your member variables even for custom email addresses. (ManageMembers.php) + * Show an attachment's actual width and height even if it is resized down. (Display.php, Display template) + * It was possible to have a member name that you couldn't pm. (Subs-Auth.php, InstantMessage.php, InstantMessage template) + ! Automatically recognize /home2/ as well as /home/. (install.php, Packages.php, PackageGet.php, create_backup.php) + ! Completely clear the settings for any guest. (Load.php) + ! Only allow a clear text password if there is no salt in the database. (LogInOut.php) + ! All the converters now use convert_query for query error prevention. (converters/*) + ! Renamed the title of "New Year's". (upgrade_1-0.sql, install_1-1.sql) + ! Don't list all holidays/birthdays ever - 0000-12-30 to 0000-01-01. (Calendar.php) + ! Show a guest-friendly error message when a guest can't see a topic/board. (Load.php) + ! The registration date changed when editing profiles and getting errors. (Profile.php) + ! Because the registration date can be changed, order by it specifically when viewing the member list. (Memberlist.php) + ! Allow arrays as theme options and settings. (Themes.php, Profile.php) + ! Registration from the admin screen now handles permissions correctly, and choosing activation will now always work. (Register.php) + ! Improved the menus of the registration center to ensure they appear when required - and only when required. (Register.php) + * Added a new menu to the registration center for registration settings, and moved existing settings across to this area. (modSettings.php, Register.php, Register template) + & Several language changes for above changes (Migration, removal and a few additions), and adaption of the current help text. (Admin, Login, modSettings and Help language files) + ! Fixed some minor notices when getting pm errors and loading missing language files. (Load.php, InstantMessage.php) + ! Add integrate_buffer setting to obExit for buffering purposes, as well as output_buffers theme setting. (Subs.php) + & Tweaked description of time format. (profile help files, Profile language files) + ! Package manager was sometimes uninstalling xml-style packages incorrectly. (Subs-Package.php) + ! Only accept a value of 1 for is_activated, when checking to see if an account is active. (SSI.php, Load.php, LogInOut.php, ManageMembers.php, Reminder.php, smf_api.php) + ! Spell checking *misspelled* 'quoted words' didn't work properly, sometimes accents were ignored. (Subs-Post.php) + ! Now, when a user is awaiting admin approval is_activated is set to a value of 3. (Register.php, upgrade_1-1.sql) + * Don't use $txt[94] + something for total numbers, it's not very internationalizable. (BoardIndex template) + ! The package manager tried to show 'package_no_gzip' but it should have been 'package_no_zlib'. (Packages.php) + ! Just in case, make sure any files from simplemachines.org don't contain dlattach. (PackageGet.php, Themes.php, ManageSmileys.php) + & Fixed some problems in the Spanish language files. (all language files) + ! The XMB converter now properly parses the [align] bbc tag. (xmb_to_smf.php) + & Fixed a minor typo - "through out". (Profile language files, profile help files) + ! Fixed a typo in the phpBB converter. (phpbb2_to_smf.php) + ! Avoid marking boards and topics read for guests upon deletion. (RemoveTopic.php, MoveTopic.php) + ! Smileys couldn't have special characters (<, >, &, etc.) in their alt/description text. (Subs.php) + ! Fixed a few problems in the YaBB SE to SMF converter. (yabbse_to_smf.php) + ! Don't even allow files to be uploaded as avatars if they have no proper size. (Profile.php) + ! Fix a small notice made when you change your password from your profile. (Profile.php) + & Merged language changes with branch. (all language files) + * Fixed a bit of button display logic. (Recent template) + + +November 2004: +-------------------------------------------------------------------------------- + ! Handle the case where the bbcode_uid is blank. (phpbb2_to_smf.php) + ! You weren't able to upload a theme directly without clearing the "copy of" box. (Themes.php) + ! Updated the create_backup.php utility to offer to create files via FTP. (create_backup.php) + ! Fix line-endings of text files on attachment download. (Display.php) + ! If you used two dots in an attachment filename, for any reason, it could fail. (Subs.php) + ! There was a potential problem in the installer if something got double-spaced. (install.php) + ! Updated restore_backup.php to have the option to restore paths, etc. (restore_backup.php) + ! Converters now try a cross-database check before doing anything. (all converters) + ! Added style="clear: both;" to hr and explicit br's because it makes sense. (Subs.php) + & Updated $txt['mboards_moderators_desc'] to reduce confusion. (ManageBoards language files) + ! Prevent an error in the profile when the avatar path is not properly accessable. (Profile.php) + * Added $context['tabindex'] to the Post and InstantMessage templates. (Post template, InstantMessage template, Load.php) + & Added $txt['register_only_once'] error message to Register. (Errors language files) + ! Upgrade now checks for broken tables and lost connections, and automatically recovers. (upgrade.php) + ! Only ignore certain types of errors in upgrade_query(). (upgrade.php, upgrade_1-0.sql, upgrade_1-1.sql) + ! Prevent [0] from messing up posts when not in lists. (Subs.php) + ! Renamed the instant_messages table to personal_messages, im_recipients to pm_recipients. (all converters, upgrade.php, upgrade_1-0.sql, upgrade_1-1.sql, install_1-1.sql, repair_ID_PM.php, Admin.php, InstantMessage.php, Load.php, LockTopic.php, ManageMembers.php, Security.php, Subs-Post.php) + & Cleaned up $txt['deleteAccount_posts']. (Profile language files) + & Added $txt['mail_send_unable'] for mail that couldn't send. (index language files) + ! Added 'bar_width' information for polls. (Poll.php) + ! Added 'add above' option for boardmod-style files, which means basically add before. (Subs-Package.php) + * When possible, use XMLHttpRequest for "insert quote". (Post template, Display template, Post.php) + * Fixed a minor error in package uploading. (PackageGet.php, Packages template) + ! The recentTopics and recentPosts SSI functions were not well scalable. (SSI.php) + * Gosh, how long has that typo been there? (BoardIndex template) + * Capacity bar added to personal message center to show how close to having a full inbox the user is. (InstantMessage.php, InstantMessage template) + * Ability to "Label" messages added to personal message center, including manager to add/remove labels. (InstantMessage.php, upgrade_1.1.sql, install_1.1.sql, InstantMessage template) + & Language entries added for above changes. (InstantMessage language files) + ! Fixed small bug which was occuring when trying to increment personal message count. (Subs.php) + ! Added [nobbc] tag (which anyone can use) that disables bbc/autolinking. (Subs.php, Subs-Post.php) + ! Fixed a notice in ssi_recentPoll. (SSI.php) + ! Cleaned up upgrade.php, nearly done. (upgrade.php, upgrade_1-0.sql) + ! Don't show uninstall for older versions of installed mods. (PackageGet.php, Packages.php, Packages template) + ! Don't warn about the deletion of a package if it's not the current version. (Packages template) + ! Adjusted the margin for the ssi_examples page... san. (ssi_examples.php) + ! Don't compress attachments on PHP 4.1.x. (Display.php) + ! Don't show a blank screen if template files are missing. (Load.php) + ! Attempt to get past session.auto_start, but don't start a session otherwise if one was already started. (Load.php, install.php, Install langauge files, Help language files) + ! Support incorrect usage of [td]/[tr]/etc. better. (Subs.php, Subs-Post.php) + ! Cleaned up a lot of whitespace issues. (various files) + ! Updated credits. (Admin.php) + * Hidden emails are now properly italicized in profiles. (Profile template, Load.php) + & $txt['smf24'] now more correctly says "total voters". (index language files) + * Added tabindexes to the Register template to aid registration. + ! Added debugging information in the package manager when data is appened to a file. (Subs-Package.php) + ! Avatars were being resized even if the "check every time" option was enabled. (Subs.php, Load.php) + * Removed the Search template from the classic theme, moved post overflow style into style.css. (Search template, Post template, Profile template, Recent template, Display template, style.css) + * Tweaked a classic template a bit. (Recent template) + ! Small notice when an author wasn't specified for modifications. (Package template) + ! The extension of an uploaded avatar wasn't being determined properly for well-sized bitmaps. (Profile.php) + ! Modifications weren't always uninstallable. (Subs-Package.php) + ! The installer wasn't setting up the session properly with database driven sessions. (install.php) + ! Impose a hard limit (potentially configurable) of 200 pages of results in search. (Search.php) + ! Attempted to fix that darn entity thing in profiles. (Profile.php) + * Added a line-height to codeheader/quoteheader so they would look proper. (style.css) + * The newsfader now attempts to detect the colors to fade from/to from the stylesheet. (BoardIndex template) + ! Announcing a topic on a board no one could access was giving errors. (Post.php) + ! Started poll and topic counts were including junk posts. (Profile.php) + ! The session auto rewriter could adversely affect the latest-news... (Admin template) + ! Check the output_handler directive to see if output compression is already being done. (SSI.php, index.php) + ! Converters were not auto-continuing properly. (all converters) + * Updated the inserted code for lists to use breaks. (Post template) + ! Attempted to make the dump database functionality ask for more memory. (DumpDatabase.php) + * Added a few labels to the avatar section of the profile. (Profile template) + ! Handle & -> &amp; in board descriptions on creation and edit. (ManageBoards.php) + ! Fixed a possible error in topic annoucenment if permissions were already screwed up. (Post.php) + ! [quote] and /me are no longer effected negatively in [code] blocks. (Subs-Post.php) + ! Fixed a small typo in the help. (profile help files) + * The wrong language strings were being used for 'Total Topics'. (BoardIndex template) + ! Always log notices and all other errors, no matter what. (Errors.php) + + Added a mod setting named "integrate_exit". This option can be used to call a function on exit. (Subs.php) + ! Session keep alive was being called too often. (script.js) + * Show the rss feed as an alternate relative link. (index template) + ! When "whole words" was enabled, you couldn't properly censor special characters sometimes. (Load.php) + ! The create_backup.php utility wasn't handling field names properly, a few other minor things. (create_backup.php) + ! Avoid conflicts with other MySQL-driven scripts in SSI.php by selecting no database. (SSI.php) + ! Automatically try to handle deadlocks and such from InnoDB tables. (Errors.php) + * Fixed a javascript error to do with attachments. (Post template) + + Added a Search center in admin panel. (Subs.php, ManageSearch.php) + * Created ManageSearch template for above feature. (ManageSearch template) + ! Removed Search section from 'Features and Settings'. (ModSettings.php) + + Added fulltext search support. (Search.php) + * Search errors won't be fatal anymore, but shown as warnings like Post and Profile. (Search template, Search.php) + ! Search remembers whether simple search was used or not. (Search.php) + & Created a language file for Search.php and ManageSearch.php. (Search language files) + & Moved several search-related language strings from ModSettings and index to the Search language file. (Search language files, Admin language files, Errors language files, index language files, ModSettings language files) + ! Debug stuff should also work for MySQL < 4.1 now. (Subs.php) + ! Give an extra courtesy five minutes for typing when edit_disable_time is on. (Post.php) + ! RSS was outputting non-queryless URLs. (News.php) + ! Added an ID_MSG to SSI's boardNews output. (SSI.php) + ! Recent topics speed up now no longer ignores topics marked unread. (Recent.php) + * To help find people using old versions of SMF, the current-version.js script now takes the forum's version. (Admin template) + ! I may have just fixed the last posts per board problem. (invision_to_smf.php, invision2_to_smf.php) + ! The view query debugging functionality now works on INSERT... SELECT queries too. (ViewQuery.php, Subs.php) + * Updated some parts of the BoardIndex template to use permissions properly. (BoardIndex template) + ! "Randomize" the cookie on installation, based on the database info. (install.php) + ! Added is_topic_starter to each post's contextual information. (Display.php) + ! If any of the pagination settings are zero, reset them to defaults... they're used in many many places. (Load.php) + ! Don't overwrite the error message in install.php when trying a different database name. (install.php) + ! Don't automatically optimize when using SSI. (Load.php) + ! Cleaned up the default output of boardNews and added comment_link/comment_href. (SSI.php) + + Added an isBanned column to the members table - when set, they don't receive notifications. (install_1-1.sql, upgrade_1-1.sql, Post.php, Subs-Post.php) + ! Now, when you ban someone explicitly, it will set said field... if later they are unbanned, it should be unset. (ManageMembers.php, Security.php) + + +October 2004: +-------------------------------------------------------------------------------- + ! If the first word in the string was misspelled, spell checking failed. (Subs-Post.php) + * The signature length checker wasn't functioning exactly correctly. (Profile template) + ! Now, the referrer check works even if the server is configured incorrectly. (Security.php) + ! The referrer check should now work even across subdomains, when "global cookies" are enabled. (Security.php) + ! It was possible to search the member list based on aim/msn/etc. even when guest_hideContacts was enabled. (Memberlist.php) + * Updated a comment in index.template.php to make it sound a little less weird. (index template) + ! Upgrader wouldn't actually reset the language to english. (upgrade.php) + ! If you enter an invalid value for ICQ (such as 0) the field should be blanked. (Profile.php) + ! If you can't administrate the forum, you can't add members to the Administrator group. (ManageMembers.php) + ! The boards in your notification list were erroneously showing as unread when they weren't. (Profile.php) + * The notification list in your profile was not paginated; it needs to be for longer lists of topics. (Profile.php, Profile template) + ! In Opera, only the first " in a javascript-inserted quote was fixed for entities. + & Updated $txt['theme_options_reset'] to make more sense. (Themes language file) + ! Invision converter was giving a notice for some uploaded avatars. (invision2_to_smf.php) + * Very slightly tweaked the padding in the main administration area. (Admin template) + ! Started some work on the YaBB converter. (yabb_to_smf.php) + ! The access this template file directly was not working for some windows servers. (Load.php) + ! If you picked 'http://' as your own avatar, it saved it and made errors everywhere. (Profile.php) + & Changed "everytime" to "every time". (ModSettings langauge files, Profile language files) + & Fixed various typos throughout comments and English language files. (various files) + ! Got rid of some more ereg_replace's and made them str_replace's ;). + ! Don't require database backup to come from ?action=maintain. (DumpDatabase.php) + ! BBC Tags weren't being disabled properly when sending personal messages. (Post.php, Subs-Post.php) + ! Removed redirectMetaRefresh option. (Subs.php, ModSettings.php, ModSettings language files, Help language files) + ! Now, redirectexit() automatically detects the presence of a full URL. (Help.php, Karma.php, LogInOut.php, PackageGet.php, Subs.php, Themes.php, ViewQuery.php) + * Now, the session keep alives are done automatically on every page. (index.php, Display template, Post template, script.js) + * It is now much easier to set theme options with javascript. (Subs.php, index template, script.js) + * Added a version number to the theme settings. (index template) + & $txt['statPanel_noPosts'] now sounds better even when it's not your profile. (Profile language files) + ! Error when uploading a file that wasn't a zip or tar.gz file to the package manager. (PackageGet.php) + ! The smf_api.php file should not do nothing if it's included into SMF or with SSI.php. (smf_api.php) + ! The value of theme options and theme settings can now be much longer. (smf_1-0.sql, upgrade_1-0.sql) + ! Don't send the subject mime encoded if it doesn't need to be. (Subs-Post.php) + & Now, the package manager will say "uninstallation actions for archive..." (Packages language file) + * Fixed it so it would say that in the template too. (Packages template) + ! The getPackageInfo() function no longer allows selection of a specific package-info.xml file. (Subs-Package.php, Packages.php) + ! Significantly optimized the "show all unread topics" function so it is of bearable speed. (Recent.php) + ! Basic HTML now works again, and you're allowed to use alt with it, after the src for img tags. (Subs.php) + ! Quoting posts works better now from Internet Explorer if they have < or >. (Post.php) + ! Fixed subject internationalization cheating in split, merge, and move. (SplitTopic.php, MoveTopic.php) + ! First subject of a merged topic was getting "Re: " on the front. (SplitTopic.php) + ! Subject internationalization was also not working well in the "last post" column. (MessageIndex.php, BoardIndex.php) + ! Insert quote feature was not behaving well with Opera 7.60's implementation of DOMParser. (Post.php) + * The calendar now shows a "board to post in" even on preview. (Post template) + * The message being sent when a person was deleted was.... weird. (Register template, Login language file, Register.php) + ! Who's online wasn't showing people on a board if they viewed a topic in it. (Load.php) + ! Recent posts through xml/rss were not being limited by board properly. (News.php) + ! The parsing of links with autolinked links in them still wasn't working 100%. (Subs.php) + ! The above problem was still occurring for basic html links. (Subs.php) + ! By default, send personal messages to members from "email" by bcc. (ManageMembers.php) + ! Updated topics weren't sorting properly. (Recent.php) + ! Cleaned up more of upgrade, almost done. (upgrade.php, upgrade_1-0.sql) + ! Search no longer requires administrator action to fix "CREATE TEMPORARY" problems. (Search.php) + ! Don't pollute the settings table with "empty" settings. (upgrade_1-1.sql, install_1-1.sql) + * Renamed 'memberCount' to 'totalMembers'. (various files, BoardIndex template, index template) + ! Tags should be disabled more cleanly. (Subs.php) + ! Sticky and notify didn't properly default when previewing a new topic. (Post.php) + ! You can now register with parts of your birthdate. (Register.php) + ! Significantly optimized "show new replies to your posts" by cheating :P. (Recent.php) + ! The package manager didn't handle missing files well. (Packages.php) + & $txt['pswd4'] was talking about things that didn't really happen. (loginout help files, Profile language files) + ! To avoid confusion, allow default_options for registration too, same as options. (Register.php) + ! Fixed email and FTP autolinking too, ugh... (Subs.php) + ! If you got an error on the theme profile page, it didn't remember post data well. (Profile.php) + ! The 'send_welcomeEmail' didn't matter; it always did this. (Register.php) + ! Upgrade clears out more settings that shouldn't be there anymore now. (upgrade_1-1.sql, install_1-1.sql, upgrade_1-0.sql) + ! You can now use variables in the subjects of mass emails. (ManageMembers.php) + ! Fixed more autolinking issues with [email=...]... (Subs.php) + & $txt['permissionname_move'] was slightly confusing. (ManagePermissions langauge files) + ! Fixed attachments with quotes and spaces in them. (Post.php, Display.php, ManageSmileys.php) + ! Optimized a few membergroup queries. (Memberlist.php, SSI.php, BoardIndex.php, Subs-Post.php, Who.php, Modlog.php) + ! SSI.php was not using the correct ID_GROUP for online status. (SSI.php) + & Added help text for the smiley center. (ManageSmileys langauge files) + * Used the above text in many places. (ManageSmileys template) + & Renamed maxwidth and maxheight to max_image_width and max_image_height. (Display.php, Subs-Post.php, install_1-1.sql, upgrade_1-0.sql, upgrade_1-1.sql) + & Changed some $helptxt indexes that were using wrong values. (Help language files) + * Updated templates to use the above. (Modlog template, Errors template, Themes template, ManageBoards template, Admin template, ManageMembers template) + ! The mail_type setting is now 0 or 1, not smtp or sendmail. (Subs-Post.php, ModSettings.php, install_1-1.sql, upgrade_1-0.sql, upgrade_1-1.sql, phpbb2_to_smf.php) + ! Updated server list. (server.list) + ! Package manager should give a hint to the package server for the language to use. (PackageGet.php) + ! Clear the remembered language and theme upon login. (LogInOut.php) + ! Fix typo causing theme options not to be loaded properly. (Register.php) + ! The upgrader should offer to delete its sql files as well. (upgrade.php) + ! Fixed numerous issues with the upgrader and its sql. (upgrade.php, upgrade_1-0.sql) + ! Settings.php was, in cases, being outputted double-spaced. (upgrade.php, upgrade_1-0.sql, upgrade_1-1.sql, Admin.php, all converters) + * Now, in Gecko-based (Mozilla, etc.) browsers, clicking bbc buttons will select text better. (script.js) + * Additionally, Gecko browsers were akwardly scrolling the text back to the top. (script.js) + ! Fixed the login bug for some darn versions of Apache too. (Load.php) + ! You should be able to set theme options for theme x. (Themes.php, script.js) + * The "delete selected" button in the Classic theme shouldn't have had a background. (Display template, classic only) + ! Just in case people misread the installer's stuff, try using the table prefix if the given user/database fail. (install.php) + & Slightly updated the removeNestedQuotes description in the help. (Help language files) + ! The remove nested quotes feature no longer leaves newlines everywhere in the post. (Post.php, InstantMessage.php) + ! Remove the removeNestedQuotes setting if it's not used. (install_1-1.sql, upgrade_1-0.sql, upgrade_1-1.sql) + * Upped the username limit in the Register template to 25 characters. (Register template) + ! Disabling [shadow] tags had disastrous results, in cases. (Subs.php) + ! Use INSTR instead of LIKE in a few places where it'd be faster. (Modlog.php, MessageIndex.php, Display.php) + ! If you clicked an IP address directly, pagination didn't work. (ManageMembers.php) + ! If the username is defaulted, but blank, focus it instead of the password. (Login template) + ! Message preview was taking off the first 128 characters. (Recent.php, MessageIndex.php) + ! Packages weren't always downloading properly, in most cases in fact. (PackageGet.php) + ! Added smf_loadThemeData to the smf_api.php file. (smf_api.php) + ! If you can't download a package, give a better error message. (Subs-Package.php, PackageGet.php) + ! Attempt to log a person out of the subdomain if globalCookies is on. (Subs-Auth.php) + ! Installer should delete install_1-1.sql too. (install.php) + & Fixed $helptxt['attachmentEnable'] to actually correspond to the current options. (Help language files) + ! When connecting to FTP, try another possible path just to make it easier. (Packages.php, PackageGet.php, install.php) + * Added a "go to last post" icon to the topic listing. (Recent template, MessageIndex template, last_post.gif) + ! Corrected last_post hrefs and added a 'new_href' to simplify things. (Recent.php, MessageIndex.php) + * Used the new 'new_href' in a few templates. (Recent template, MessageIndex template) + ! Increase the search pointer as soon as possible. (Search.php) + ! Don't show upgrade error message when using SSI. (Errors.php) + ! Up cookie name to SMFCookie11 by default. (repair_settings.php, Settings.php, Settings_bak.php) + ! Spell check was not handling slashes (\) properly. (Subs-Post.php) + ! If flash was enabled, a notice was being given. (Subs.php) + ! Added create_file and create_dir functionality to the ftp client. (Subs-Package.php, install.php) + ! Don't leave broken files in the database when avatar upload fails... (Subs-Graphics.php, Profile.php) + ! Minor improvements to the installer and its code. (install.php) + ! Added zip to the default allowed attachment extensions, alphabetized them, changed default to off. (install_1-1.sql) + ! Marked some internationalization-needing places in the install script. (install_1-1.sql) + & Added $txt['installer_language_set'] for the JavaScript-less (Install language files, install.php) + ! Properly tell the server that the connection should be closed. (Subs.php) + & Corrected $helptxt['m_queryless_urls']. (Help language files) + & The forum copyright now opens in a new window. (index language files) + ! Stopped using <h4> in the tools and converters, and moved on to <h3> (which was what should have been used.) (various files) + ! No longer should Settings.php ever be corrupted; this happened because of \r's in the file. (various converters, install.php, upgrade.php, repair_settings.php, Admin.php) + ! Small notice upon initial installation. (Subs.php) + ! Flash wasn't being parsed properly if someone added tags inside it. (Subs.php) + * Added forum version to latest package information. (Packages.php, Packages template) + ! Updated version numbers to 1.1 Beta 1 (no it's not out yet) to fix confusion. (all files) + ! Added un_preparsecode() and changed the second parameter of preparsecode(). (Subs-Post.php, Post.php, InstantMessage.php) + + You can now post html by adding it in [html] tags - administrators only. (Subs-Post.php) + ! Only list activated members on the member list. (Memberlist.php) + ! When member registration approval is enabled, only show the number of approved/activated members. (Subs.php) + ! Major improvements to the restore_backup utility: percentage, proper query string handling, etc. (restore_backup.php) + ! Moved a couple options around, and cleaned up option handling. (ModSettings.php, upgrade_1-1.sql) + ! The forum_time() function now takes a timestamp, which it will adjust as well. (Subs.php) + ! All the timestamp contextual information now includes the proper offsetting. (various files) + * The memberlist now loads and shows member data the same way everywhere else does, e.g. theme options ;). (Memberlist.php, Memberlist template) + * There are now anchors in the memberlist to jump more directly to a letter. (Memberlist.php, Memberlist template) + ! Renamed $themeUser to $memberContext. (Display.php, InstantMessage.php, Load.php, Memberlist.php, News.php, Profile.php, Search.php, Who.php) + * Emails are now sent in chunks, which added the email_members_send sub template. (ManageMembers.php, ManageMembers template) + & Added $txt['email_continue'] and $txt['email_done'] to show progress. (Admin language files) + ! The dump database feature was not totally compatible with MySQL 4.1.x. (DumpDatabase.php) + ! Added a create_backup.php tool, not totally tested yet, to create backups without worry of timeout. (create_backup.php) + ! Package manager should show images in file listing. (Packages.php) + ! Highlight PHP code when viewing files from that listing too. (Packages.php, Subs.php) + ! Default username wouldn't save for FTP info. (Packages.php) + ! Subjects in notifications were being sent with html in them. (Subs-Post.php, Post.php) + ! Renamed notifyUsersBoard() to notifyMembersBoard(). (Post.php) + * If the avatar preview is available, and they enter an image in the url field, preview it there. (Profile template) + + Added Calendar Manager to admin center. From here admins can manage the calendar settings and holidays on their forum. (ManageCalendar, ManageCalendar template) + ! Calendar settings migrated from feature settings to the calendar manager. (modSettings.php) + & Language strings for calendar migrated from modSettings language file to ManageCalendar language file. (modSettings, ManageCalendar language files) + & Language strings added/edited for new calendar manager. (ManageCalender, Help, Admin language files) + + New settings added to allow posting of events not linked to a post. (ManageCalendar.php, Calendar.php, SSI.php, Subs.php, BoardIndex.php) + * New template added for posting an event which is not linked. (Calendar templates) + * Templates changed to adapt to new method of event posting. (BoardIndex, Calendar templates) + & Language entries added for the above. (index language file) + ! SSI.php was returning null for an error in ssi_boardNews(). (SSI.php) + ! Upgrades weren't being checked for properly. (Subs-Package.php) + ! The phpBB converter didn't check for the attachments table properly. (phpbb2_to_smf.php) + + Added ability to set a time limit after which posts can no longer be edited. (install_1-1.sql, upgrade_1-1.sql, modSettings.php, Display.php, Post.php) + & Language entries added for change above. (Errors, modSettings, Help language files) + ! Added a host_from_ip() function, now used for hostname lookups, uses shell if possible. (ManageMembers.php, Profile.php, Security.php, Subs.php) + ! A maximum of 32 Administrators are now listed in the admin center. (Admin.php) + * Small template change to add "more" link if more than 32 administrators are present. (Admin template) + & One useful language entry added to language files for above change. (index language files) + * It is now possible to choose Saturday as a calendar start day. (Calendar.php, Profile and Settings templates) + ! Fixed a humbling number of my own typos. (various files) + & Renamed $txt['package_ftp_neccessary'] to $txt['package_ftp_necessary']. (Packages language files) + ! Avoid a notice in install.php for old versions of PHP. (install.php) + ! In permissions management, the "Ungrouped Members" box would show as checked in error if no groups had access. (ManagePermissions.php) + ! The repair_settings.php tool no longer saves database settings without a connection, checks for dirname(), and detects a value for db_prefix. (repair_settings.php) + * Stick the state of the additional options drop down on preview. (Post template, Post.php) + ! The case of [php]/[PHP] shouldn't matter. (Subs.php) + ! Prevent the package manager from timing out with long modification files. (Subs-Package.php) + * The search value being prefilled in "simple search" was incorrect. (Search template) + ! The FTP path was not being autodetected well except in /home/xyz/ style hosts. (Packages.php) + * IP addresses in the ban log should be links. (ManageMembers template) + + Added new setting for warning the user when they are replying to a topic older than a set amount of days. (ModSettings.php, Post.php, install_1-1.sql, upgrade_1-1.sql) + & Added language entries for above addition. (ModSettings, Help and Post language files) + ! Renamed 'option' to 'options' for polls - compatibility with Mambo. (SSI.php, Display.php, Poll.php) + ! Sometimes, packages weren't properly reversable. (Subs-Package.php) + * Added confirmation screen to board manager when deleting boards. (ManageBoards.php, ManageBoards template) + & Language entries added for the above change. (ManageBoards language files) + ! Changed embarassed.gif to embarrassed.gif in smileys. (install_1-0.sql, upgrade_1-1.sql, various converters, posting help files, Subs-Post.php, Subs.php) + * Quick moderation didn't work in topics with "buttons as text". (Display template) + ! Added posts to topPoster information. (SSI.php) + & There, the SMF link in the copyright may now optionally include the title "Simple Machines Forum". (index language files) + ! If someone entered no "To" and no "Bcc" a notice *might* have been caused. (InstantMessage.php) + ! If there are extra "[quote]"s at the end of the message, or extra "[/quote]"s at the beginning, get rid of them. (Subs-Post.php) + ! The default FTP server options were not being used properly. (Packages.php, PackageGet.php) + ! Unread topics/replies weren't checking properly for which board you were looking for. (Recent.php) + * Added a post class and tweaked the css a bit. (Post template, Display template, Search template, Profile template, Recent template, style.css) + & Added package_no_zlib error, shown when the server has no zlib support. (Packages.php, Packages language files) + ! Behold the return of the debugging information! (Subs.php, Load.php) + & Added some language file entries for repair boards - $txt['repair_poll_gone'], $txt['repair_event_link_gone'], $txt['maintain_no_errors']. (Admin language files) + ! Tweaked many things in the repair functionality, and made it no longer ask you if you wanted to fix no errors. + + +September 2004: +-------------------------------------------------------------------------------- + ! Do a check to allow IP address access transparency. (Load.php) + ! Don't trim or mess with lines of Settings.php that aren't variable definitions. (Admin.php, all converters, repair_settings.php, install.php, upgrade.php) + ! Slightly better error handling to do with temporary tables and searching. (Search.php) + ! Search results now handle line breaks and quotes better. (Search.php) + ! Activation check was preventing email change from going through properly. (Register.php) + ! Insert quote feature now supports tabs, MSIE and Mozilla. (Post.php) + ! New upgrade script tested and working, at least it runs. Needs testing on old YaBB SE/SMF's. (upgrade.php, upgrade_1-0.sql) + * Added delete_selected.gif image to classic. (delete_selected.gif, Display template) + * Cleaned up im_new.gif so it looks better cleaner. (im_new.gif) + * Updated javascript information urls - old ones still work. (Admin template, Packages template, ManageSmileys template, Themes template) + ! I can only assume that this is regressing the profile_remote_avatar upgrade bug. (upgrade.php) + & Added $txt['mark_unread_confirm'] to confirm mark unread. (index language file) + * Added javascript confirmation when marking a whole board unread. (MessageIndex template) + * Added delete_selected.gif to the classic theme and made it use it. (Display template) + ! If a member has a smiley set that doesn't exist, use the default. (Load.php) + ! Removed $modSettings['search_max_cached_results'] as it was too confusing, and not useful enough (Search.php, ModSettings.php, Help language files, ModSettings language files, smf_1-0.sql, upgrade_1-0.sql) + + Added an option to disable the use of temporary tables (Search.php, ModSettings, Help language files, ModSettings language files, smf_1-0.sql, upgrade_1-0.sql) + & Added an error message just in case temporary tables cannot be created. (Error language files, Search.php) + & Added an error message when you access SSI.php by URL directly. (SSI.php, index language files) + ! Agreement now allows and parses any bbc in it. (Register.php) + ! Attachments can now have foreign (localizable) characters in their names when "encrypt filenames" is on. (Post.php, ManageAttachments.php, Subs.php, Subs-Graphics.php, Profile.php, Display.php) + & Bad attachments now give a 404 and a slightly better error message. (Errors language file, Display.php) + ! Moved a bit more into upgrade_1-0.sql from upgrade.php. (upgrade.php, upgrade_1-0.sql) + ! Merged topics will now take the ID of the first topic instead of a new ID. (SplitTopics.php) + + Added a user option to disable the post warning. (Post.php, Profile template, Profile language files) + ! The upgrader now includes all settings from YaBB SE, just to facilitate best conversion. (upgrade.php) + ! Optimized a query or two and cleaned up some comments. (various files) + & Removed the enableNewReplyWarning setting because... it is no longer useful. (smf_1-0.sql, upgrade_1-0.sql, ModSettings.php, Post.php, Help language files, ModSettings language files) + * You can now always chose to enable/disable the warning, and it can be reset globally. (Profile template, Settings template) + ! Topics started, boards, views, and replies are no longer counted for posts in the recycle bin. (Stats.php) + ! Cleaned up hide contacts from guests. (Memberlist.php, Display.php) + ! Reordering categories wasn't working - only if you saved a board. (ManageBoards.php) + & Added a check for the ALTER privilege. (install.php, Install language files.) + * Added a minor comment to the index template's key stats box. (index template) + ! Gender and a number of other things should be available on every profile page. (Profile.php) + * Cleaned up logic for mark unread showing so it won't affect most users. (Display template) + ! Don't waste time censoring things when the censor list is empty. (Load.php) + ! Don't censor message previews twice for the same message. (Recent.php, MessageIndex.php) + ! Clip message previews before censoring - thanks Elissen. (Recent.php, MessageIndex.php) + ! Template errors are now shown with correct error messages whether templates are eval()'d or not. (Load.php, Errors.php) + ! Notifications on topic removal should work again. (Subs-Boards.php, RemoveTopic.php) + ! The "a new member has registered" emails had extra line breaks in them. (Register.php) + & Made it clearer that who_view was only for Who's Online. (ManagePermissions langauge files) + ! An extra break was being send with moderator reports. (SendTopic.php) + ! The showPosts section was linking to the end of the topic, not the right post. (Profile.php) + * Removed board-level mark unread. (MessageIndex template) + & Removed mark_unread_confirm because it's no longer needed/used. (index language file) + ! Fixed calendar updating again. (SSI.php, BoardIndex.php) + ! Fixed an issue in the vBulletin 3 converter with birthdays. (vbulletin3_to_smf.php) + ! Removed some unnecessary table joins. (Search.php) + ! Fixed a bug in color parsing for topic printing, and a couple minor code issues. (Printpage.php, Subs.php) + ! Fixed a small issue with updating the latest registered member when there were none. (Subs.php) + ! Ignore bad smiley set directories while searching. (ManageSmileys.php) + ! Fixed a small issue with avatar saving. (Profile.php, Subs-Graphics.php) + ! The return to post feature wasn't persisting on preview. (Post.php) + ! Removed some unused settings - no visual difference. (Admin.php) + ! Don't permanently dump big avatars unless the "refuse it" option is used. (Load.php) + ! Alternative password checks were being done more often than they needed to be. (LogInOut.php) + ! Search in the memberlist should use * as a wildcard. (Memberlist.php) + ! Attachment manager wasn't passing permissions properly for attachment download. (ManageAttachments.php) + ! Minor tweaks to the "new username/password" email. (Subs-Auth.php) + ! Added SHA-256 hash downgrade support if mhash is compiled in. (LogInOut.php) + ! Updated smf_api.php with more functions, such as smf_formatTime and smf_allowedTo. (smf_api.php) + ! Database dumps should provide column information for the INSERTs. (DumpDatabase.php) + ! The smf_api.php file now provides access to the SMF session too, when used in the database. (smf_api.php) + & Made $txt['notifyXOnce1'] read about 8 times better - imho. (Profile langauge file) + ! Links might be autolinked inside [url=...]...[/url] which would cause weird code display. (Subs.php) + ! Fixed a few minor issues/potential issues in profile viewing and editing. (Profile.php) + * The smileys popup is now user-resizable. (Post template) + * Announcement countdown wasn't working exactly right. (ManageMembers template) + ! Announcement needs to go back to the user's langauge after partial sending. (Post.php) + ! Bumped announcement chunk size up just a wee bit... 50 -> 75. (Post.php) + & Translated a few strings in the package manager and finished some documentation. (Subs-Package.php, Packages langauge files) + ! Contextual data should be provided for the avatar's href when it's uploaded. (Load.php) + ! URL autolinking could cause a stack overflow on very very long URLs. (Subs.php) + ! Get rid of the poll_choices error when upgrading twice. (upgrade_1-0.sql) + ! Attempted to resolve the reversing of some IP addresses. (QueryString.php) + ! Use [:alpha:] instead of A-Za-z etc. where possible, to solve localization problems even if it's ugly. (Subs-Post.php, Subs.php) + ! Send Topic feature was not checking email addresses properly. (SendTopic.php) + ! Further updated the upgrader so more was in upgrade_1-0.sql. (upgrade.php, upgrade_1-0.sql) + ! Don't allow automatic package download of any package without session check. (PackageGet.php) + ! If a package no longer exists (404, etc.) don't show any errors from PHP. (PackageGet.php) + * Made several things work with the "latest" stuff. (ManageSmileys template, Subs-Package.php, PackageGet.php) + ! Upped required version to MySQL 3.23.6. (install.php, upgrade.php) + & Fixed punctuation on $txt['number_recent_posts']. (Themes langauge file) + ! Creating a file on error should be optional when parsing modifications. (modification.dtd, Subs-Package.php) + ! Splitting a topic should mark the topic read for the user, because they split it. (SplitTopic.php) + & Error message should be shown when PHP is not compiled with MySQL support. (install.php, Install langauge files) + ! If you use a form in the news, show <form> in the preview. (Admin.php) + ! Don't mark the category as new if the only new board is a recycle bin. (BoardIndex.php) + ! Added readmes for installation, upgrade, and update. (readme_install.html, readme_upgrade.html, readme_update.html) + ! Activation from the profile plumb wasn't working. (Profile.php) + ! The invision converter wasn't doing birthdates and wasn't parsing signatures properly. (invision_to_smf.php, invision2_to_smf.php) + & Changed "Minutes to login" to "Minutes to stay logged in" because it sounds better. (index language files) + * Tweaked the Reminder template slightly to look markedly better. (Reminder template) + ! Various tweaks to help files. (all files in help/) + ! Avoid calling exit; if at all possible for integration, e.g. Mambo. (Subs.php, index.php) + ! Printpage wasn't parsing quotes correctly at all. (Printpage.php) + ! By default, the karma_edit permission should be allowed. (smf_1-0.sql) + ! Updated some problems in the Burning Board converter. (burningboard_to_smf.php) + ! Invision converter now works properly, thanks Hal9000. (invision_to_smf.php, invision2_to_smf.php) + & Updated $helptxt['karmaMode'], $helptxt['enableStickyTopics'], $helptxt['cal_enabled'], $helptxt['avatar_allow_external_url'], and $helptxt['pollMode'] to talk about permissions. (Help language file) + ! Consecutive [url=] tags were getting linked together. (Subs.php) + ! Sigh, now email links work okay when in [email] tags too. (Subs.php) + * Replying with quick reply, with "auto notify" off, turned off notifications. (Display template) + ! RSS was saying it was version 0.91, it was actually 0.92. (News.php) + * Smiley popup would open multiple times without closing itself. (Post template) + ! RSS feeds were being sent with the wrong doctype, and an improper character set. (News.php) + * Added something to stop Firefox (hopefully) from following previous/next links automatically. (index template) + ! You weren't able to add smileys with different case than other smileys. (ManageSmileys.php) + ! Tweaked the column size of ID_PM to 10 instead of 11. (upgrade_1-0.sql, smf_1-0.sql) + ! When marking unread, mark the page you were on unread, not the whole topic. (Subs-Boards.php) + ! The ssi_boardNews() function should give the topic ids too. (SSI.php) + ! Quote people by their real name, not their login name. (InstantMessage.php) + * Don't show people's usernames in their profiles. (Profile template) + ! Send emails with mime-encoded subjects. (Subs-Post.php) + * Editing a group's name that had html in it caused an error. (ManageMembers template, ManageMembers.php) + * Add something to mask out XMLHttpRequest for Internet Explorers. (script.js) + ! Printing links in lists didn't always seem to work. (Printpage.php) + ! In some cases, dumping the database would generate errors for empty tables. (DumpDatabase.php) + ! Missing error message for a blank password - Login wasn't loaded. (Reminder.php) + * You can now edit category names with html in them properly. (ManageBoards.php, ManageBoards template) + * The "category name" and "(modify)" are now separate links. (ManageBoards template) + * Administration "quick tasks" section had an extra row. (Admin.php, Admin template) + + +August 2004: +-------------------------------------------------------------------------------- + ! Allowed elements, e.g. <br />, before email addresses that are automatically linked. Let's hope this doesn't backfire. (Subs.php) + ! Now, a "special" redirect is used on just the post page to make the return to topic option work properly. (Post.php, Subs.php) + ! After calls to "setLoginCookie" the same special redirect is now used. (Register.php, Profile.php, LogInOut.php) + ! Backed up language files (index.language.php~) are no longer treated like actual language files. (Admin.php, Profile.php) + & Removed $txt['membergroupss_min_posts'] because it wasn't being used and was misspelled anyway. (ManageMembers langauge files) + & Added $txt['membergroups_postgroups'] to fix an untranslated (hardcoded) string. (ManageMembers language files) + * Removed some hardcoded text ("Post groups") and put in a language string instead. (ManageMembers template) + ! As an administrator, registering a new member should use the guest theme options not your own. (Register.php) + ! To ease misunderstandings, the upgrader now specifies that it converts the existing *YaBB SE* template. (upgrade.php) + ! Moved the place database errors are marked in the database so that two people accessing it at once won't cause a race condition. (Errors.php) + ! Fixed a case where profile theme options might not be saved properly. (Profile.php) + ! Now, if you're using Opera, quoting a post with "insert quote" won't show &quot;s, although it still has i18n problems. (Post.php) + ! Added $context['server'] array for very basic information about the server. (Load.php) + ! Now, for using refresh instead of location, $context['server']['needs_login_fix'] is used along with $context['browser']. (Post.php, Profile.php, LogInOut.php, Register.php) + ! The Queryless URLs function now uses $context['server']. (QueryString.php) + ! Added a session check to registration to make it more difficult to register large numbers of accounts in sequence. (Register.php) + ! SSI.php now always returns a value, true or false, which represents whether it could load or not. (SSI.php) + ! Changed the way duplicate events were handled in SSI.php minorly, although it shouldn't make a difference. (SSI.php, BoardIndex.php) + ! Now, ssi_recentEvents no longer shows duplicate events. Man I'm think headed. (SSI.php) + ! Fixed a few coding conventions problems like spaces missing after, or present before, commas. (numerous files and templates - unimportant changes) + * You can now use smileys with quotes in their description in the smiley popup... (Subs-Post.php, Post template) + ! If you used spaces in the disabled bbc list, it would not work properly. (Post.php) + * Due to a typographical error, bbc tags were not being properly disabled all of the time. (Subs.php, Post template) + ! Updated version numbers to 1.0 RC1 to avoid release confusion. (all files) + + Split permission moderate_forum into moderate_forum (search/delete/track members, track IP, register center, reserved names), manage_membergroups (manage/assign membergroups) and manage_bans. (several files) + + Split permission admin_forum into admin_forum (package management, mod/server/theme settings, maintenance, error/mod logs) and manage_permissions. (several files) + + Split permission edit_forum into edit_forum (attachments, smileys, censored words, registration agreement) and manage_boards (manage boards and categories.) (several files) + ! Split permission group 'Forum administration' into 'Forum administration' and 'Member administration'. (several files) + ! Fixed bug clearing the admin membergroup when changing someone's membergroup settings having only moderate_forum permission. (Profile.php) + ! Fixed language and 'hide email' setting not changeable without profile_extra permission. (Profile.php) + ! 'View/Delete members' doesn't allow deleting members anymore without the profile_remove_any permission. (ManageMembers template) + ! 'Edit censored words' and 'edit registration agreement' now requires moderate_forum instead of edit_forum permission. (Admin.php, ManageMembers.php, Subs.php) + ! Dissolved edit_forum permission into manage_smileys and manage_attachments. (several files) + ! Fixed hide user online showing in MessageIndex and Display. (MessageIndex.php, Display.php) + ! isAllowedTo() now accepts arrays of permissions. (Security.php) + + Added board permission announce_topic replacing announcement boards. (several files) + + Added announcement of topics allowing to select membergroups and sending in batches. (Post.php) + ! For polls, moderator locks now override member locks, but administrator's locks do not override those of moderators. (Poll.php) + ! Double posting shouldn't cause problems if the session hasn't even been initialized properly yet. (Security.php) + ! Even if Ungrouped Members have permission to moderate_forum, do not send new member notifications to them. (Register.php) + * Previously, if you didn't allow people to hide their emails, but you hid emails from guests, the emails would not be hidden - fixed. (Load.php, Profile template, InstantMessage template, Display template) + ! If you set the default theme to Default, and then disallowed people from selecting default but allowed them to select other themes, an error was shown. (Themes.php) + ! Don't email members who have been DENIED the moderate_forum permission when members register. (Register.php) + ! Emails meant to be sent in html were not being sent with the correct Content-Type. (Subs-Post.php) + & Removed $txt['whoadmin_ban2'] because the action is no longer being used. (Who language file) + * Removed the "notify_announcements" section. (ManageBoards template) + ! Streamlined the server_parse() function and made it take a parameter for the message to send. Notably, this allows for better error handling. (Subs-Post.php) + ! Attachment size should round up, not say "0" if there are more than 0 bytes in the file. (Display.php) + ! Fixed a mistake in the package manager which made xml style modifications not work. (Subs-Package.php) + ! Depending on your server configuration, sometimes $_GET was keeping its slashes. (QueryString.php) + * Fixed a small bit of invalid html in the spellcheck sub template. (Post template) + ! Gave some more contextual information to the Post template - $context['is_new_topic'], $context['is_new_post'], $context['is_first_post']. (Post.php) + ! Changed a small unimportant piece of javascript to look nicer. (upgrade.php, all converters.) + ! Using move or announce in conjunction with go back to topic did not mark the board properly read. (Post.php) + ! The installer could not be used again on an already installed forum to refresh things. (install.php) + ! Fixed a potential issue in the way the messages table was initially created. (smf_1-0.sql) + & Added $txt['user_refresh_install'] and $txt['user_refresh_install_desc']. (Install language files) + ! Posted guest names and emails are now always trimmed and verified. (Post.php) + ! In some cases, deleting a member might not remove them from being the latest member. (ManageMembers.php) + * Detailed version checker did not work with the default theme. (Admin template) + * Code blocks now show in Safari properly... though, the code isn't pretty. (index template) + * Added $context['browser']['is_mac_ie'] and made it the code block fix code work on it too. (Load.php, index template) + ! The query to reset options has now been broken up into many smaller queries.... it's more queries, but it's not a humungous query anymore. (Themes.php) + ! Fixed a small possible error if someone was looking for trouble. (InstantMessage.php) + ! Fixed a typo which was causing "topics and posts" deletion when deleting a member not to work. (Profile.php) + ! Fixed a typo in member specific theme options. (Theme.php) + ! You were not allowed to change a member's username to either its current value or their realName. (Subs-Auth.php) + ! If you send a topic, and don't provide a comment, it shouldn't send "and the comment:" in the email. (SendTopic.php) + ! Listing members from a membergroup based on posts did not include pages properly. (ManageMembers.php) + ! Archive extracting routines didn't make destination directories. (Subs-Package.php) + * After spell checking, the message box should be focused. (spellcheck.js) + & Updated $txt['whoall_helpadmin'] in the English files to more accurately affect what's happening. (Who language file) + * Use javascript to pass what form to use in the spell checker. (InstantMessage template, Display template, Post template, Subs-Post.php, spellcheck.js) + * If the next word has no possible suggestions, clear the "change to" box. (spellcheck.js) + ! Fixed some minor coding convention issues. (Subs-Graphics.php, MoveTopic.php) + * Updated some of the code in the spellchecker to make it cleaner. (spellcheck.js, Post template) + ! Improved spellchecker highlighting of "abcd abc" and similar. (Subs-Post.php) + ! You couldn't always use apostrophes in your email address. (Post.php, Register.php, Profile.php, Security.php) + ! If you can delete any posts on a board, it shouldn't matter if it's your own or a reply. (RemoveTopic.php) + ! The phpBB converter should convert \n -> <br /> and " -> &quot;. (phpbb2_to_smf.php) + ! Fixed expand/collapse modSettings not working on IE with META refresh enabled. (Subs.php) + ! The Invision converters didn't do [list] properly. (invision_to_smf.php, invision2_to_smf.php) + ! Apparently, Opera can actually do innerText - fixed "quotefast" internationalization. (Post.php) + ! Internationalized characters not in the current character set were not being highlighted properly by search. (Search.php) + ! Added option to copy board permissions from one board to another on a group by group basis. (ManagePermissions.php, ManagePermissions template) + & Added $txt['permissions_copy_from_board'] and $txt['permissions_select_board']. (ManagePermissions language file) + ! Editing the theme template and style sheet no longer switches to that theme. (Themes.php, Themes template) + + Changed option guest_hideEmail to guest_hideContacts, which now hides messenger information from guests too when enabled. (SQL/Upgrade, ModSettings.php, Load.php, Memberlist.php) + & Changed two language entries to accommodate above change. (Help and ModSettings language files) + ! Fixed bug in for ID_TOPIC repair file. (repair_ID_TOPIC.php) + ! The phpBB converter should now convert gallery avatars. (phpBB2_to_smf.php) + + When viewing members in a group you can now sort by column, and added extra information to the template. (ManageMembers.php, ManageMembers template) + * Adjusted the placement of the "why isn't moderator here?" text. (Profile template) + ! The upgrader couldn't handle moderators with ' in their name - or queries :P. (upgrade.php) + * Adjusted breaks in the index template to look cleaner. (index template) + ! Typographical error was causing, in some cases, permissions not to be calculated correctly. (RemoveTopic.php) + ! The am/pm indicators were displayed inconsistently with the Today "mod" enabled. (Subs.php) + ! Creating a new post group didn't resync the post groups of members. (ManageMembers.php) + ! Ignore list conversion now cleans out apostrophes. (upgrade.php) + ! Don't make any change to the registration date if they didn't change it. (Profile.php) + ! Rolled back a major spot of idiocy in calendar checking. (BoardIndex.php, SSI.php) + ! Editing a post made by a guest, previous to when they became a member, should not throw a reserved name error. (Post.php) + & Added $txt['register_passwords_differ_js'], modified register_agree a bit, and made a note that they were used with javascript. (Login language files) + * Now, if you try to register with two different passwords, it stops you right off ;). Also cleaned up the javascript a bit. (Register template) + ! Admin registration wasn't always working properly. (Register.php) + ! Added Snitz converter (not nearly done yet!) for MySQL only. (snitz_to_smf.php) + ! Fixed a minor issue with IP detection. (QueryString.php) + ! Shadow tags weren't allowing other tags to apply within them. (Subs.php) + ! Shadow and glow didn't take fonts outside of them, now they do in *certain good browsers*. (Subs.php) + ! If you changed your email before activating, you might be able to get past a ban. (Register.php) + * Now, when a file is not writable (index template or style.css) it warns you of this fact. (Themes.php, Themes template) + & Added the language entries necessary for this change. (Themes language file) + ! You can now use &, (, ), and several other characters in avatar filenames and category names. (Profile.php, Load.php, Subs.php) + ! Minor tweak to trimming in registration. (Register.php) + ! Fixed typo in upgrade preventing boardOrder column to be changed. (upgrade.php) + ! Removed boards table keys 'children' and 'boardOrder' and added key 'ID_PARENT'. (upgrade.php, smf_1-0.sql) + ! Removed categories table key 'catOrder'. (upgrade.php, install.php, smf_1-0.sql) + ! Changed sorting of the boards table into a permanent table sort. (upgrade,php, ManageBoards.php, Admin.php, BoardIndex.php, Load.php, ManageMembers.php, ManagePermissions.php, MessageIndex.php, ModSettings.php, MoveTopic.php, Post.php, Search.php, SplitTopics.php) + ! Changed sorting of the smileys table into a permanent table sort. (upgrade,php, ManageSmileys.php, Subs-Post.php, all converters) + ! Removed the smileyOrder key from the smileys table as it wasn't used anymore. (upgrade.php, smf_1-0.sql) + ! Adjusted the SSI whosOnline to be consistent with the way the online list is shown on the board index. (SSI.php) + * Fixed some bad paths to package_installed.gif and package_old.gif. (Packages template) + ! You can't make backups of files that don't exist, I guess :P. (Subs-Package.php) + ! Archiving -> 1.0 upgrade stuff into a parsable sql file - part 1, not done. (upgrade.php, upgrade_1-0.sql) + ! More archiving of upgrade stuff - still not done, and still needs some reworking. (upgrade.php, upgrade_1-0.sql) + * Fixed the document.postmodify.attachmentPreview.value javascript error. (Post template) + ! Fixed a dumb and annoying typo that mainly affects debugging. (index.php, SSI.php) + ! Moderation log was reversing sort in cases when you deleted any. (Modlog.php) + ! Updated phpBB converter to do more avatars and group conversion - thanks packman. (phpbb2_to_smf.php) + & Cleaned up mark unread and made it usable; added a string too. (Subs-Boards.php, index language files) + * Added images for mark unread. (markunread.gif) + * Added mark unread to topics and boards, replacing one of the old topic buttons on one side. (Display template, MessageIndex template) + ! Fixed a minor typo with the package manager; minor but it was causing big problems. (Subs-Package.php) + ! Another large chunk of upgrade.php converted, nearly done with it now to parse ;). (upgrade.php, upgrade_1-0.sql) + ! More work on upgrader, minor tweak to installer. (upgrade.php, install.php) + ! Various coding convention updates. (various files) + ! Fixed a hardcoded string - N/A. (ManageAttachments.php) + ! The package manager should assume a link absolute if it starts with http://. (PackageGet.php) + + +SMF 1.0 RC1 August 10, 2004 +================================================================================ +August 2004: +-------------------------------------------------------------------------------- + + repair_settings.php now checks that Settings.php is writable. (tools/repair_settings.php) + * Packages now only show links to install if they are install-able. (Packages.php, Packages template) + & You can no longer access ?action=activate when activation is not available, nor is the "Did you miss your activation email? link shown. (Subs.php, Register.php, index language files) + * Added class="signature" to where ever signatures are displayed to ease stylesheeting. (Display template, Profile template, InstantMessage template) + * Migrated a few remaining colors from index.template.php to style.css in the default theme. (index template, style.css) + * Tried to add better documentation in the style.css file under the default theme. (style.css) + ! Updated version numbers to SMF 1.0 RC1 in preparation for release. (all files) + ! Upgrade no longer clears the moderation log, just for David. (upgrade.php) + ! Fixed typo that made quotefast functionality not work with "s. (Post.php) + ! Mozilla now does entities properly with quotefast ;). (Post.php) + ! Now, if someone replies while you were reading you still get the subject line. (Post.php) + * Changed the stylesheet link to style.css to include ?rc1. (index template) + ! Profile now erases default options from any/all themes. (Profile.php) + ! The IP icon was not being shown properly to guests. (Display template) + ! The IP detection routine no longer allows "forwarded for 127.0.0.1", etc. (QueryString.php) + ! Fixed a problem with new reply notification. (Post.php) + ! When removing ones vote from a poll, and one has not voted, nothing should be done at all. (Poll.php) + & Changed the English error messages "Username does not exist" and "Password field is empty" to read better. (index and Login language files) + * Registering a member from the Admin interface should hide the password with ***s. (Register template) + ! The "Require user to activate the account" checkbox in the admin registration interface did not work. (Register.php) + ! Released RC1 to Charter Members. (xml/latest-news.js) + * Made a small update to the Internet Explorer overflow fixing code. (index template) + * Version checker no longer shows current version in red if it is old, and checks by greater than not compare. (Admin template) + ! Upgrade was turning off persistent connections. (upgrade.php) + & Changed the "Do you really want to delete your own profile?" string to use the word account. (Profile language file) + ! Fixed an issue that was causing, in most cases, default theme options not to work. (Register.php) + * Fixed some invalid xhtml in set theme options and settings. (Themes template) + * You couldn't uncheck a default option for new members. (Themes template) + ! Saved a little bit of memory by not passing something that shouldn't be passed to the template. (PackageGet.php) + * Packages you cannot install from a server are no longer shown in bold. (Packages template) + ! Fixed pagination error when browsing avatars in the attachments manager. (ManageAttachments.php) + ! Avatar listing and management should go by ID_MEMBER not being zero, not ID_MSG being zero for mods' sake. (ManageAttachments.php) + * Fixed some invalid html to do with event editing. (Post template) + & Finished the dutch translation and removed unused language tag membergroups_members_all_current. (ManageMembers language file) + * Fixed a minor bug in the Post template (only evaluting $context['event'] if it is set). (Post template) + ! A typo was making the memberGroups upgrading code in upgrade.php run for versions it shouldn't. (upgrade.php) + ! You can now list more groups per board under access rights. (upgrade.php, smf_1-0.sql) + ! Permission keys can now be 30 characters long, which should fix a possible issue with truncated permissions. (upgrade.php, smf_1-0.sql) + ! Fixed a small notice to do with seqnum that would hardly ever occur. (Security.php) + ! Some hosts ban readfile for no reasons, so handle that case. (Display.php) + ! Removed some commented code which was not being used, and should not be used. (Subs.php) + ! Tested Burning Board converter against wBB 2 - it at least runs. (converters/burningboard_to_smf.php) + ! Followed up on more "look at me later" comments. (converters/yabbse_to_smf.php, converters/xmb_to_smf.php) + ! Updated just a little bit of documentation. (Admin.php) + ! Fixed a small possible and minor problem in upgrade for some versions of SMF. (upgrade.php) + ! Some quotes from YaBB SE weren't parsing correctly. (Subs.php) + + +July 2004: +-------------------------------------------------------------------------------- + + Now, if you put ;board=Z on the URL for unread or unreadreplies, you will see said things only for that board. + * Search now shows the board the result was in under the topic link. + ! Tweaked the entity magic so that it supports more characters, hopefully this won't cause problems. + ! Now, subjects are also magick'd. This should make it work better on bad character sets. + ! Upped the subject length limit to 100 characters from 80. (internationalization.) + ! Fixed a reserved MySQL word being used as label. + & Removed some unused language tags. + & Made some consistency adjustments in the dutch language files and translated all untranslated lines. + * Fixed some accesskey properties for the wireless template that were missing. + + Added a preview column in the news center and checkboxes for fast removal of news items. + * Disallowing hide online status was not working at all because of a typo in Profile.template.php. + ! Font size was wrong in the ssi_examples.shtml page. + & Tweaked $txt['modlog_moderation_log_desc'] so people can see why it won't delete things. + ! The require/require_once functions are faster than include/include_once - use them instead when possible. + ! Subject shortening wasn't very i18n... still isn't, totally. + ! MoveTopic didn't do board names with single quotes in them properly :(. + + Now, when there is a parse error in a template or language file, an error message is displayed with debugging information. + & Added some language entries to the index language files to show when handling this problem. + ! Several fixes made the the vBulletin 3 converter. + + Added "Male to Female ratio" and "Users Online Today" to the statistics page. + & Two new language strings added to Stats.language.php for above change. + + Added a utility function, reattributePosts, to ManageMembers.php for attributing guest posts to members. + ! Fixed a bug in upgrade causing it to run indefinitely. + ! When using flash, add AllowScriptAccess="never" for Flash Player 6+. + ! Fixed some quote and code tag preparsing issues. + ! Changed some ordering in upgrade.php so that, even if you tried to duplicate your attachments it wouldn't happen now ^_^. + ! Fine, fixed the list tag a different way. + ! Fixed upgrade not converting single quotes for news lines properly. + ! Trying to change the PHP setting to add xHTML compatible URL separators for URLs that automatically get an added session ID (needed for SSI and template links). + + Added link in attachment manager to recount file sizes. + + Added avatar management to the attachment manager. + & Several new language entries added to Admin.language.php for above change. + + Added backend for "Reply to All" in personal messages. + ! Fixed problem with previewing instant messages losing the "to" field. + + New setting, number_format added, this is a string that allows the admin to choose how values are displayed in the forum. + & Two new strings added to Help and ModSettings.language.php for describing the above change. + + Added function, numberformat, for formatting numbers from the above setting. + ! Fixed possible bug in Profile which would result in posts per day being wrong for an extreme geek. + + Converters for Invision 2 and vB 3.0 now convert passwords and salt data. + ! Users who have come from Invision 2 and vB 3.0 can now log in without resetting their password. + + It is now possible to specify whether or not a board theme should override a users preference. + & Two new languages entries added to ManageBoards.language.php to reflect above change. + ! SSI function, topPoster, can now take amount of posters to return as a parameter. + + If new replies are made between reading a thread and replying, a warning message is displayed. + & New lanaguage strings added to Post.language.php for above change. + + New function, ResetPassword, added to Subs-Auth.php for resetting password and/or username. + + Admins can now change their members usernames from their profile - although this resets their password too. + & New language strings added to Profile and Login language files to reflect above changes. + ! Fixed some incompatibility problems with old templates, I think, and a small notice in ModSettings.php. + ! Email auto-linking wasn't always working properly. + * A bug in IE caused gifs to stop animating if they were clicked.... (as a link) in the Post template. + * Access checkboxes shouldn't be shown for moderator/admin. + * Small oddity in the Profile template under notifications. + ! Cleaned up and commented some of the ftp stuff more - hopefully this still works everywhere. + ! Package manager was not removing files properly. + + Added setting to allow disabling of BBC tags by listing them. + & Two new language strings added to Help and ModSettings language files for above change. + * Post template adapted significantly to handle above change. + + Viewing members from a group now has a new template, with options to easily add and remove members from said group. + & Several language entries added to ManageMembers.language.php to accommodate above change. + + Added ability to notify the admin team whenever a new member registers. + & New language strings added to ModSettings and Login language files to reflect this addition. + ! Fixed avatar download and resize option not working properly. + & New langauge entry added to Packages.language.php to enable better browsing of packages. + + Restyled the ban center to match the layout of other admin center screens. + + Added a ban log viewer. + + Added an ID_MEMBER column to the ban log. + * Updated the templates in ManageMembers to reflect above changes. + & Added several language tags to the ManageMembers and Errors language files. + ! Added some extra checks to the is_not_banned function. + ! Cannot register ban is now logged in the log_banned table. + * Some javascript cleanup for the detailedversion checker, and cleanup for the latest news when offline. (Admin template) + * The "im_delete.gif" image now reads "Delete All" to avoid confusion with deleting checked messages. (im_delete.gif) + * Changed the placement of the delete button and made it read "Delete Selected". (InstantMessage template) + ! Updated the documentation sections in many source files to sync with current functions. (Sources/*.php) + ! Attempted to fix pm notifications being sent in the wrong language - needs more investigation. (Load.php) + ! The "quote fast" feature now properly supports entities - but only in Internet Explorer. Support on other browsers is pending. (Post.php) + ! Not showing your online status was not working because of a typo. (Profile.php) + * Fixed the typo "hilight" in the spell checking javascript and Post template. (spellcheck.js, Post template) + ! Made a small change to the default wording of the agreement. (agreement.txt) + ! Saving a theme option for the default theme did not erase said option in the current theme, making changing your options not work. + ! Who's online was showing members who choose to not be shown online, but as guests. + & The IP address logged for unsuccessful admin login attempts was incorrect at times; should now be fixed. (Admin langauge files) + * Spell check now scrolls to the word that changed in IE and Mozilla. (Post template, spellcheck.js) + ! Typo was making full-ban reasons not display properly on registration failure. (Register.php) + ! Two members online at exactly the same time might be mistakenly shown as not online. (BoardIndex.php) + ! SSI.php now truncates on line breaks (<br />) as well as spaces. (SSI.php) + ! Reduced the spam-ness of html emails sent by the forum. (Subs-Post.php, ManageMembers.php) + + +June 2004: +-------------------------------------------------------------------------------- + * Fixed a small issue with SendTopic.template.php's version not updating properly. + ! A setting in ModSettings.php was named wrong; search_max_cache_size -> search_cache_size. + * Fixed some layout problems in the Search template. (caused by old style HTML :P.) + * Changed any "checkAll()" functions to calls to invertAll() from script.js. + ! PHP 5 can be configured to make sessions shorter than 32 bytes... maybe longer? + ! If there are no smileys in location x, you might get a small error. + * Recommitted those darn icons.... stupid cvsnt is adding kb twice or not at all :(. + ! [color=#......] should allow A-F too, not just numbers. + ! Made it so clear text color codes can be a *little* longer. (mediumpurple, etc.) + ! Fixed a typo (one missing letter!) that was causing very substantial problems on some servers. + ! .htaccess wasn't working properly for Apache 1. (it wasn't even applying :/.) + ! Made it so you only have to change one line to make index.php something else. + ! Added emphasis to "nothing's wrong" in the conversion timeout. + ! Small typo affecting downloading updated packages. + ! The attachments feature no longer allows uploads with the filename ".htaccess" or "index.php". + ! If encrypted filenames are on, these are allowed. + + It is now possible to upload images to the Smiley center. + & Many new language entries added to ManageSmileys.language.php for above change. + * Smiley template cleaned up some more to make it more efficient. + ! Don't show the database upgrade error message for CVS or Demo versions. + * Whitespace shouldn't wrap in error log URLs. + * Only show a link to the IP if they can actually click it. + ! Cleaned up the permission loading in Display.php so it's a little clearer... + * Emails in manage members should be clickable -> mailto:. + * Added more commenting in index.template.php around the forum name stuff. + ! Commas should be allowed after email addresses. + ! Smileys could not be put in the first place on a row. + ! Resetting everyone's theme options was redirecting to the wrong place. + + You can now upload different smileys to different sets if you wish. + * Javascript added to the import smileys template to improve usability. + ! Resetting default theme options should override theme-specific ones. + * Now, when you change another member's language it doesn't affect your own. + ! Holiday titles are no longer censored, because they shouldn't be. + ! Code Review: Calendar.php. (updated documentation, fixed some overly restrictive permissions checks.) + ! More characters are now allowed in email addresses. + ! Cleaned up the way heading and rules were handled in ModSettings.php. + & Added some entries to the index langauge file - unread_topics_visit, unread_topics_visit_none, and unread_topics_all. + * It's now clearer that you are seeing posts since your last visit, and there is a link to see ALL unread topics. + ! Fixed a typo in SSI.php related to birthdays. + ! Moved the check for double post submission down in Post for error messages. + ! Added a loadAttachmentContext() function to Display.php. + ! You shouldn't be able to register with the email address of someone registered. + * You shouldn't get a javascript error message if you don't have the code fix on. + * Added warning text under Answer: in the profile for the Secret Question. + & Edited $txt['secret_desc'] and added $txt['secret_desc2'] in Profile language files. + + YaBB SE converter should now convert banned members, calendar events and have an attempt at settings! + ! MSIE wasn't styling ul's properly. + ! Made the title for posted calendar events slightly longer. (48) + ! Updated version numbers to 1.0 Beta 6+ to avoid confusion with the actual release. + * Slightly improved the look of displayed poll results in Display. + ! Don't show the "upgrade template.php" checkbox if it's already been upgraded. + & Edited $txt[677] - explains how to create child boards + ! Fixed a bug that was resulting in database sessions not being deleted. + + phpBB2 converter should now convert attachments if the attachment mod was installed. + + YaBBSE converter should now convert over membergroups successfully. + + It is now possible to upload new packages in the package manager. + & Several new language entries added to Packages.language.php and Errors.language.php for above change. + & Some language entries moved from ManageSmileys.language.php to Errors.language.php. + ! Inlined some element calls in the helpfiles. + ! Added message body preview *information* so the templates *can* display it if they so wish to. + * Took a typo back out of smf_codeFix, having reintroduced it last beta - oops. + * Added inital changes for "someone posted while you were *reading*". + ! Double post prevention/sequencing should no longer break old templates. Now, scripts could use this to their advantage if they bothered to keep the session *anyway*, but they don't so it doesn't matter. + ! Don't allow the deletion of just any file with the package manager remove thing. + & Added default_time_format to the Install language files and the installer. + * Minor template change in the package manager to make links consistent. + + Made a custom imagecreatefrombmp(). Needs more testing, but works in most cases. (yes, this means bitmap support for GD ;).) + + Added pagination to the trackIP function, to stop hefty page loads. + * Altered trackIP template to be more similar to trackUser to improve consistancy across functions. + & Two new language entries added to Profile.language.php to accommodate above change. + + New option added, allow_editDisplayName, to dictate whether a user may or may not edit their display name. + & New language string added to ModSettings.language.php to accommodate above change. + + Added a backend for repairing attachments (filesize and those deleted). + * Fixed guest posting so that: guest editing guest works, admin editing guest allows name/email editing, and post errors are handled well. + & Renamed poll_few to error_poll_few so poll error handling can work properly. + & Removed two language strings made obselete by the above changes. + ! If they preview/post with no options, give an error but make sure there are at least two boxes. + ! ssi_examples.php should show the proper path, not one with mixed slashes. + * Don't bother fading in fader.js if there is only one item. + ! Small typo in the recent rss/xml feed stuff was making stuff inaccurate. + ! Personal messages should be sent by memberName with "s. + ! Installer was screwing up the indentation of Settings.php, if any. + ! Fixed attachments, personal messages, and children in vBulletin 3 converter. + ! All the converters were "leaving" attachments if they weren't converted - fixed. + ! Added an Invision 2 converter, works for the most part, needs tweaking. + & Edited one text string and added another to clearly state that admins need to enter an email address when registering new users + & Edited the registration template to reflect the above language file change. + ! SSI.php should just not load if it can't connect. + ! The current_forum_time was without server offset as well as user offset. + ! Printpage shouldn't put PHP code in a box, since Display doesn't. + * Changed some spans to divs in BoardIndex and several other templates because it looks slightly nicer. + * Added label elements to the additional options in Post. + * Show IP address help text in the Display template ;). + & Added see_admin_ip and see_member_ip to the Help language files. + ! Upgrade wasn't handling news properly - should just remove one set of slashes. + + Pagination added to "Who's Online" page. + + +SMF 1.0 Beta 6 June 12, 2004 +================================================================================ +June 2004: +-------------------------------------------------------------------------------- + * When previewing a post with attachments, you are now warned you will have to reattach them. + & Added attach_lose language string entry. + & Changed $txt[247] to $txt['hello_member'], and $txt[248] to $txt['hello_guest']. + * These two strings changed a few templates as well... + + You can now use your email address to login instead of just your username. + ! PNGs and JPEGs were getting two dots for avatar filenames. (avatar_1..png) + * The "deliminator" index was changed to "delimiter" - a real word :P. + ! Some names wouldn't work in the ignore list - namely ones with underscores. + ! You *SHOULD* now be able to use any characters you want in your username - have fun! + ! The "Find Members" function now quotes usernames in several places so the above can work well. + ! Image resizing was sometimes messing up the aspect ratio. + + Added javascript for adding poll options to New/Edit Poll screens. + & New language entry added to Post.language.php for above change. + ! The error generated when incorrectly attempting to answer a secret question, will now show the membername in question. + & New language entry added to Profile.language.php for above change. + + If you are banned from posting you will get a notice on each page telling you so, with the reason why. + & New language entry, $txt['you_are_post_banned'], added to index.language.php to reflect above change. + + Search now can cache results. + ! Several search optimizations. + + The viewNewestFirst setting has been replaced in preference to user option view_newest_first, for choosing ordering of topics. + & New language string, $txt['recent_posts_at_top'], added to Profile.language.php for above change. + & Old language strings for removed setting removed from modSettings.language.php and Help.language.php. + ! Users who are moderating are now less affected by spamProtection(). + ! Fixed minor problem with spamProtection() which would reset flood control whenever anyone logged in. + ! Replaced default laugh smiley with one that is different from cheesy. + ! Deleting an attachment didn't *always* work well with translated language files. + * You can now preview poll editing again. + ! When you preview - if there's no subject, it should use "(No subject)". + ! Sometimes lists were getting blank spaces after their start. + * Back to topic and move now work together in conjunction. + ! Added a notice to smf_1-0.sql so people don't think they need to run it or anything. + ! The percentages for a poll now add up a little better. + * Show "no messages" in the error log when there are none. + * Fixed some layout issues in the error_log sub template, Errors template. + ! Fixed additional slashes issue with "applying filter". + * Made a lot of updates to the MessageIndex template, none of which change really anything. (just cleanup.) + ! Cleaned up the ugly and generated whitespace in the help files. + * Changed the order of the icons in the Who template. + * Banning page now offers more explanation as to the difference between a reason, and notes! + & Two new language strings added to Admin.language.php for above change. + ! Added sanity check to sendpm(), to protect against manually deleted entries. + + New setting added which, when enabled, will stop non-administrators from hiding their online status. + & New language string added to ModSettings.language.php and Help.language.php for above setting. + * Settings template now has labels for all the checkboxes, so that it looks better. + ! It's no longer easily possible to copy permissions from the Administrator group. + + Whenever a database error is thrown, if the user is an admin, a version check is carried out, and reported if it fails. + & New language string added to index.language.php for above change. + ! It's now possible to tell printpage to show images - but only in the backend. + & Added upshrink_description so you can tell what the button does. + * Made the index template actually use the above string. + * Don't show search quote section when there's no quote to be had. + ! Upgrade now uses a default for time format string in case it got blanked out. + ! Observe PHP's session.auto_start setting, and don't start a session if it's on. + * Search templates shouldn't do highlighting themselves. + * Added some basic column sorting to "Who's Online". + ! Several fixes to the password reminder/activation features - particularly for forums with approval enabled. + & New language strings added to the Profile and Register templates, for above changes. + + Theme setting 'display_recent_bar' replaced with theme setting 'number_recent_posts', allowing the admin to specify amount of recent posts. + & New language strings added to Themes.language.php to describe new setting. Old string $txt[509] removed. + + YaBB SE converter should now convert over all important logs and moderators. + ! deleteMembers now does some sanity checks on permissions - particularly to protect against rogue calls. + ! deleteMembers now logs the deleting of a members account into the moderation log. + & New string added to Admin.language.php to reflect above change. + ! Updated a message in the upgrader so it makes more sense. (agreement.txt message.) + ! Cleaned up a few more "Hacker?" error messages - there are not many left. + ! Upgrade was not removing slashes from YaBB SE news entries. + + Added bbc parsing for [acronym]s and [abbr]s just like the corresponding html elements. + ! Current forum time in profile now shows with your format. + ! If you're printing you want the full date; not today/yesterday. + ! Printing images, by default, now shows (http://www.url.to/image.gif). + ! Added D-Day, 06/06, to the calendar holidays. + & Changed enableVBStyleLogin's help... changed its description, changed autoLinkUrls' description. (English only.) + ! The installer now supports using ftp://hostname.tld/ as the server. + ! SSI.php now always starts the session again - let's see if this still causes problems. + ! Quick simplification to the [me=] bbc expression. + ! The vBulletin 3 converter now does the table prefix right, but not that much else. + + It's now possible to use database driven session functions. Not totally ready yet. + ! Updated repair_settings.php and the installer to deal with database sessions. + & Added databaseSession_enable, databaseSession_lifetime, and databaseSession_loose as settings - and their associated text. + & Removed the disableCaching option since it didn't do anything at this point anyway. + + Added two new user options, view_newest_pm_first and calendar_start_day, representing order of personal messages and day a week begins. + & Two new language strings added to Profile.language.php for above change. + ! vBulletin 3.0 converter updated to convert boards, threads, messages and polls. + ! Email autolinking had become broken. + ! Help files are now being cleaned by HTML Tidy after generation, so they look nicer. Related cleanup. + ! Code Review: News.php. (optimized the main recent query BIG TIME, and cleaned up things into CDATAs.) + * Cleaned up minor layout issues and things in many templates, and added proper versioning information. + * If you type in a URL to a star image, make it at least have one image. + * Happy Image Optimization Day to all, and to all a good night. + * Modified the topic status icons so "sticky" is clearer. + ! Several fixes and optimizations in search. + ! Moved sorting from the second search screen to the first in order to accommodate search caching. + * Removed sorting links in search result screen, added an option list to the main search screen. + + Added a lot of search options in to a new section of the modsettings. + & Added a language block to Settings, a block to Help, a block to Search and a single tag to Error. + ! Fixed an issue with form sequence checking when previewing PMs. + ! Censoring wasn't working perfectly with &amp;. + + You can now use \* to censor the character * instead of a wildcard. + * Fixed some xhtml problems in the Display, Profile, Settings, SplitTopics, and ManageMembers templates. + ! Help files no longer have the xml namespace on them. + ! Some converters weren't automatically continuing properly... most, actually. + * Fixed more xhtml errors in more templates, ugh. Mostly typos. (algin!?) + * The script.js file, and all the templates no longer use fetchById... a better solution has been introduced. (better now than after RC...) + ! Wrote up the parseModification() function... should be... actually... DONE! + ! vBulletin 3.0 converter should now convert instant messages and notifications. + + You can now specify default values for theme options in the theme settings. + ! Guests now use the default theme options. + * The Settings templates are now used only as a listing. The settings are all in Profile now. + & Added some language entries to Themes for the administration interface. + + You can now reset everyone's theme options to whatever you want. (example: turn on quick reply!) + ! Made ID_BOARD conversion on messages a little faster. + & Changed $txt['smf124'] to be a lot wordier. (attachment upload error.) + ! Renamed tar_gz_file() and tar_gz_data() to read_tgz_file() and read_tgz_data(). + + Added read_zip_data() which means zip file support - not just tar.gz. Yea. (still requires zlib, mind you!!) + ! The package manager can now check and etc. zip files too ;). (note: they are still bigger.) + ! Queryless URLs now use .html to prefix all pages. + ! Clearing unimportant logs now affects log_search as well. + ! If you download two versions of the same mod (same filename, at least...) it will rename the second. + ! There is now backend support for showing whether packages are installable or not. (for this version of SMF...) + + You can now specify what format the modification is in using package-info.xml in packages. Default is now xml. + & Replaced a "Hacker?" error message in the package manager with a translatable and informative string. + ! Renamed the get*Message() type functions to prepare*Context(). + ! The package manager's parseModification() should now handle multiple search rules properly. + * Removed all the special doctype stuff.... it was only for debugging reasons only, and did indeed help. + ! Wee, updated everything to Beta 6 ;). + ! I'm almost certain that sendmail doesn't like \r\n, and wants just \n. + ! Optimized a whole bunch of avatars by quite a decent bit, and some of them even look better now imho. + * ManageSmileys template split into more manageable chunks. + + Upgrade should now be capable of converting old settings into member theme settings to options. + ! The discontinued viewNewestFirst and cal_startmonday settings are now converted over in the upgrade process. + + Added pagination to the ban screen and user tracking. + & Ban will expire within was misleading in Admin language file - changed to "expire after". + ! Upgrade now drops a few keys that some had which were unnecessary. + ! Adjusted some minor aesthetics in install.php. + ! Now repair_settings.php uses JavaScript to set the boxes on a click ;). + ! The package manager now warns you if you can't write to the Packages directory. + * Modified the Package template with a few minor additions. + & Added some strings to the Package language files for these purposes. + ! Package installation *MAY* now fully use FTP. Not sure, needs more testing. + * Moved the following into the icons folder: online.gif, assist.gif, members.gif, calindex.gif, package_installed.gif, package_old.gif, info.gif, login_bindex.gif + * Renamed icons/login_bindex.gif to icons/login.gif, icons/calindex.gif to icons/calendar.gif. + ! Made a few changes to formatting and stuff; nothing that should affect anything. (sql file, a few source files, etc.) + & Now the default message, topic subject, board, category, etc... is all translatable. + + +May 2004: +-------------------------------------------------------------------------------- + ! The .htaccess file now works properly on Apache 2 and Apache 1. + ! Settings.php/index.php was redirecting to //install.php in some rare cases. + ! Added MySQL connection error message to the installer if there was a problem. + ! Installer wasn't automatically logging you in, like ever. + * Package manager now respects the option to make backups or not! + ! Package manager now detects when it would need to chmod files over FTP. (but it doesn't do that yet...) + * Renamed the view_other and extract_other sub templates to view_package and extract_package. + ! Package manager more consistently uses file_put_contents() now. + * Tied checkboxes together with javascript in InstantMessage template. + ! Auto submit added to all converters. + ! Fixed problems with Invision converter making random members moderators and not converting sub forums correctly. + ! YaBB SE converter can now convert boards/topics and messages. + * Removed the ReportToModerator template and merged it with the SendTopic template. + ! Removed ReportToModerator.php and merged its contents with SendTopic.php. + ! Help can't work in Mozilla and produce valid xhtml at the same time, it seems. + ! Under permissions for guests, only permissions a guest can use are listed. + * Corrected a minor issue with lone own/any permissions in ManagePermissions. + ! The package manager can now chmod files via FTP, given a password. + * You can now set default values for the FTP stuff under installation options. + * Added a warning when you try to delete a package that is currently installed. + & Added some language entries to make sure this works internationally ;). + ! The package manager now handles missing server.list files much better. + * Package manager no longer lists package groups that... don't exist. + & Made translatable some previously untranslatable sections of the package manager. + & Added some cleaner error handling for incorrect directories - and added an error message to Errors. + * Changed JavaScript usage of void(0) to returning false on events which is cleaner. + ! Sending announcements on posting was not working properly with permissions. + ! MoveTopic was doing the log tables in the wrong place, so they weren't working. + ! InstantMessage's preview wasn't doing entities properly.... dunno why, it looked purposeful. + ! Unread replies should be much faster now. + ! Marking unread replies read didn't really work if queryless urls was on. + ! Package manager now takes the path off files you require, etc. in package-infos. + ! For ModSettings.php, you no longer have to add checkboxes to the list at the bottom. + ! Calendar was not always checking permissions properly. + ! MAJOR CHANGE: All Members group changed to mean Ungrouped Members only. + & Changed some language entries to reflect the above change in . + * Removed javascript that forces "All Members" to mean all other groups in ManageBoards. + & Updated $helptxt['cal_enabled'] because it was referencing old settings. + * Added check all to ManageBoards. + * fetchSessionTemp image was missing from the Display templates. + ! The BoardIndex now provides $context['online_groups'] for legends, etc. + ! Now writelog() doesn't bother to write online log data when who is not enabled. + ! Minor changes to filesystem error handling in the package manager. + ! ManageBoards wasn't looking hard enough for some entities. + ! Made a minor optimization to the statistics page. + + After registration a new user will automatically be logged in. + * Minor change to Register.template.php so the registration message looks a little nicer. + ! If you are already logged in you can't go to the registration page! + ! Added some extra checks into the Invision and phpBB converters. + ! YaBB SE converter should now be able to convert polls and instant messages. + * Changed file= to package= in links in PackageManager screens (made sense to allow the next change...) + + Can now view files from the PackageManager "list files" screen. + & Added some strings to the Packages language file for this purpose. + ! Glow and shadow were adding extra slashes than they needed to... + ! Added urldecode__recursive() for get parsing. + * Put check all/remove selected/remove all at the top and bottom in Errors. + ! SMTP is not a reasonable length for timeouts on the SMTP server. + ! FTP links with passwords didn't work out too well. + * Showing members' IPs now works on posts by the permission. + ! In BBC, width now *must* come before height... you can't do [img height=y width=x] anymore. + + Font size in posts is limited to a reasonable size. + ! Took the margin off the list bbc because it was just confusing. + ! Redid the BBC parser so it makes more sense and is easier to change... also has better restrictions. + * You can now limit searches to only the topic subject. + & Added an entry to the index language files for this purpose. + & Moving topics now has a better message that you can customize better... includes board, etc. + * Minor change to the MoveTopic template to make this possible. + ! You can now use SSI.php to redirect somewhere after login/logout. + ! Now repair_settings.php handles queryless URLs too. + ! Replaced all the ereg's with preg_match's for speed reasons, mainly. + ! It turns out vBulletin could use admin/config.php or includes/config.php. (drat, I think I tested it with the wrong version...) + ! For compatibility, re-allowed board=x; in quote links. + ! Changed some very typographic text in upgrade.php.... + ! Typo in the XMB converter's attachment upgrading - it wouldn't do binary files right. + + You can now specify your language in the URL with ?language like with themes. + ! SSI.php shouldn't try to send headers if they've already been sent. + * Added an option to import smileys from a directory... + & Added some strings to ManageSmileys and cleaned up the confirmation string. + ! Removed send_announce permission since it wasn't used anywhere anyway. + ! Made the notification function a little more robust, renamed it to sendNotifications and moved it to Subs-Post.php. + ! Changed error_reporting to without notices if debugging is off. ($db_show_debug.) + & Removed some unused strings from the Help language files. + ! Cleaned up after some "look at me later" comments. + ! Attachments can now be deleted more than one at a time. + * Cleaned up some of the attachment manager template. + ! Started a vBulletin 3 converter - as it happens the other one was not at all compatible. + ! Added / for compatibility reasons to [quote ... link=xxxxxxx]. + ! Attachment Manager now allows you to sort by column. + ! Added a basic Burning Board converter - may need to be updated for current version of BB. + * Changed email notification option in personal messaes section of profile to a checkbox. + & Changed $txt[327] in Profile.language.php to be more descriptive and removed $txt[328]. + + Added option to limit total amount of attachments per post. + & Added new language entries to ModSettings, Post and Errors.language.php for above changes. + ! Maximum attachment size per post will now work correctly when a user edits their post. + ! Default sort in attachment manager changed to date, descending, because it's more useful. + ! Drastically changed the ModSettings stuff again. Now you don't have to add the setting in two places. + ! The repair_settings.php script was forgetting the /images on the images url for the default. + * surroundText() didn't work at the beginning of a post in Mozilla. + ! Removed ssi_no_doctype in favor of ssi_layers. + & Added a "no entities" note to $txt['show_personal_messages'] in index. + ! Automatically linked URLs were getting the <'s linked too. + ! The installer could potentially mis-write Settings.php. + * The Search template did not handle having no boards well. + * Tidied up post template and added a lot of comments. + ! Fixed a bug that was causing the find user function not to work - at all. + * Now you can see if boards or topics you have requested notification on are new in your profile. + ! Messed with some of the debug stuff so it is cleaner... + ! Some server configurations didn't think of ['-1'] as [-1]. + & Merged in a large portion of Webby's Dutch translations... + ! Added a new key to attachments to make things better and faster. + * Don't show language selection unless there are other languages to select. + ! Disallowing theme selection did plum didley. + + Added new option to polls to allow users to change their vote on a poll. + * Post, Poll and Display templates to reflect the above change. + & New language entries added to index.language.php and Post.language.php files for above change. + + Added the ability to set your SMTP port to any value. + & New language string added to ModSettings.language.php to reflect this change. + ! Added .xml to guest action whitelist so rss can work... + ! XML now cleans output to get rid of the doctype. + ! An index drop for back in Beta 2.5 was causing upgrade to be much slower than it needed to be - and it's not *necessary*. Removed. + ! Optimized a query in the upgrade script. + + 'show permission' in the profile screen now shows all permissions and membergroups at once. + & Changed showPermissions_* tags in the Profile language file. + * Fixed the jump box in the 'show permission' profile screen (xhtml issue and session ID not properly passed). + * Split up the 'show permission' screen into a general permission part and a board specific permission part. + + Statistics can now be expanded. + * Changed the Stats template to show contract/expand for months. + ! Added rmdir() and mkdir() to the ftp in Subs-Package.php. + * Fixed a small layout problem with the Display template in the default theme under Safari. + ! Fixed bug in all converters resulting in statistics not being correct. + + Added converting of smiley data to the phpBB, invision and XMB converters. + ! All converters now correctly setup board access for membergroups. + + Deleting a category will now give you the option of moving any boards within to another category before deletion. + & Several language entries added to ManageBoards.language.php to accommodate above change. + * New template added to ManageBoards.template.php for above change. + + Added a theme option so that administrators can hide post groups of members who are already assigned a "real" group. + & Two language strings added to Themes.language.php to reflect above change. + ! vBulletin converters now also convert smileys. + ! All converters now properly support "weird" database names. + + Session timeout are now considered a post error, so nothing should be lost. + & checkSession() can now return an error, instead of calling fatal_lang_error(). + * Added texts to explain the post error. + & Added a block of error_* tags to the post language file. + + Javascript is now used to show extra attachments boxes in the default theme; but only as many as you can post. + * The Post template has been updated with these changes and made it handle the permissions better. + & Added two language entries to the language files for this purpose. + ! Fixed minor bug with invision converter failing on boards with sub boards. + ! Fixed bug with quick moderation which resulting in topic counts being deducted twice. + ! Fixed bug which resulted in moderation logging not occuring consistently when removing posts/topics. + ! Help sub-directory of images removed and help image (helplogo.gif) added to images directory. + * All help files updated to reflect above change. + ! Page links in the attachments manager were incorrect. + + You can now add ;all to unread to not show "since last visit"... no inteface yet. + * The option to delete current attachments was lost after previewing a post. + & Strings 562 and 561 updated to sound crisper and cleaner, removed DATE which was often incorrect and not needed anyway. + ! The value of viewNewestFirst was conflicting badly with personal message defaults. + & Added a description for membergroups and postgroups in the "Edit Membergroups" screen. + ! Fixed some minor bugs in the view permission profile screen. + + Sending a PM that has a timed out session no longer redirects to a fatal_error. + * Error message is now shown when something goes wrong sending a PM. + ! Make a distinction between 'to' and 'bcc' for error 'pm recipients not found'. + & Moved error tags from Post lanugage file to Error language file. + ! PM send report now shows all users that successfully received the PM. + & $txt['pm_successfully_sent'] changed to show individual recipients. + ! Fixed wrong formatted post data that is passed by the admin login session validation. + + Added session timeout protection in the template editor. + ! Fixed bug which meant search was not correctly sorting by date. + ! Fixed bug which would result in moderation logging errors when deleting topics. + ! Fixed bug in markBoardsRead which would cause an issue if not passing it an array. + + Restyled Smiley admin center. + & Changed and added several tags to ManageSmileys language files. + & Added some smiley_* tags to the error language files. + ! added modSettings for absolute path to the smileys dir ('smileys_dir'). + + Smileys can now be put in a popup. + * Changed the structure of $context['smileys'] for the Post interface. + & Added more_smileys* tags to the Post language file. + ! The calendar wasn't showing events, birthdays, etc. on the last day of the month. + * Made some asthetic changes to the smileys interface on the post page. + & New language entry added to Post.language.php to accommodate above change. + + A new child board will now automatically take its parents permissions. + ! Subdomain independent cookies were not working with domains with no subdomain or with .co.jp, etc. + ! Now, ssi_login() does nothing if you are not a guest. + ! Upgrade now does some cleanup for the memberGroups column. + ! Fixed errors you would get if you went to post2 directly - although you shouldn't do that. + * Attachment preview warning now works properly again. + * If you attach something and then hit preview/get an error, additional options will be expanded. + & Updated $txt['attach_preview'] so it makes more sense with the new functionality. + * Fixed it so auto-focus for login works in new builds of Firefox. + ! Fixed some bad links in the help files. (weren't internationalized...) + * Importing smileys into an existing set is no longer an option, but instead a link. + ! You can only import smileys if there are actually any in the directory that are not already installed. + & Several new language strings added to ManageSmileys.language.php for above changes. + + Added warning message to indicate if new replies have been made when posting to a thread. + & New entry, enableNewReplyWarning, added to Help and ModSettings language files for option text for above change. + & Two new entries added to the Post.language.php files for the errors messages for above change. + ! Changed karmaWaitTime so that it is treated as a float - so 0.5 means half an hour. + ! Calendar events should not be duplicated, but should show the first next spanned event. + ! The "Visit Simple Machines!" string in the help files was not translatable. + + Added file repair_ID_TOPIC.php, for resorting topic IDs. + ! Fixed some minor bugs in repair_ID_MSG.php. + ! Double submission prevention of forms is now handled by the session AND JavaScript (especially wireless devices need this.) + * Added an extra hidden field seqnum to several post forms. + & Added an error message in the error language file for duplicate form submission. + * Added a wireless post icon for posts sent from a wireless device. + ! Corrected a little design error with showPermissions. (border) + ! Hopefully install.php not being writable will be okay... more people will have to delete it manually. + & Added language entries to index for notifications of non-reply events. + + You are now notified when topics you have set for notifications are stickied, locked, removed, split, merged, etc. + ! Theme conversion was not always working on windows servers. + + +April 2004: +-------------------------------------------------------------------------------- + & The word "offline" was spelled wrong in onlineEnable in the ModSettings language file. + ! The url_image_size() function now handles 500 errors as well... + ! 'disableHostnameLookup' wasn't saving in ModSettings.php. + * The boardseen's in Recent.template.php were supposed to be topicseen. + & Minor grammatical errors in the Profile language files. + ! Invision converter wasn't doing avatars properly. + & Calendar was misspelled in ModSettings and Help sometimes as "Calender". + ! Help for profiles updated for the new look, at least mostly. + ! If you are not the poster, you no longer get the courtesy editing time. + * Now daily statistics are considered a "detailed" thing you can view by clicking a link; this makes the stats page significantly shorter, and makes it look better generally. + & Added a "more detailed" link to the Stats language files. + ! Minor notice if you didn't enter a code to set your password. + ! Installer's FTP now works properly on PHP 4.1.x. + & Added a listing to the installer for files that need to be made writable. + ! Installer no longer deletes the Install language files. + ! The default path for the FTP part of the installer was wrong usually. + ! The installer can now more reliably delete itself, even with nobody servers. + * Added a "credits" section to the admin, and to the Admin template. + & Added language entries for the new page. (not done yet!!) + * Corrected Classic's link to catbg.gif in style.css. + ! Don't log unable to load errors in the error log for the spell checker... there's nothing that can be done! + ! Put the "Forum Configuration" menu above the "Member Controls" menu. + ! Updated ICQ link to http://web.icq.com/whitepages/about_me/1,,,00.html?Uin=(id). + * Temporary "quicker" admin interface for some things, not final. + ! Don't bother with .fromXXXXXXX if there are no replies to the topic anyway on the message index. + * Solved td and tr bbc image discrepencies... again. + & Added a setting to use META refresh redirects instead of HTTP ones. + ! Updated version numbers to "1.0 Beta 5+" to avoid confusion. + ! Upgrade now checks the version of the index language file too. + ! Now, if arg_separator.input includes ; there will be a small speed boost ;). + ! Backed out is_writable check on session.save_path. + ! Some MySQL servers didn't like the backup method exactly... it should be working now. + ! Posting wasn't checking the lock_own permission exactly right. + ! Some PHP versions don't seem to like references... + ! Some calendar functions weren't affecting $context properly in SSI.php. + ! If upgrade does 2000 messages at a time it doesn't need to sort by ID_BOARD... (filesort anyway!) + ! It seems some hosts disallow the set_magic_quotes_runtime() and version_compare() functions. + ! Added an .htaccess file to be used with Apache; includes many optimizations and security settings. + & Cleaned up quick task list in admin and made it respect permissions. + + +SMF 1.0 Beta 5 Public April 26, 2004 +================================================================================ +April 2004: +-------------------------------------------------------------------------------- + * Added some breaks to the edit news screen so Opera can understand it properly. + ! Was missing some debugging information in Security.php.. + ! In PHP, 0 == '+' evaluates to true... which was causing problems in updateMemberData(). + & There's only one i in Uninistall :P. + ! Attachments and uploaded avatars should work in more buggy versions of browsers.. + & Added (in a new window) to show_personal_messages in the index language files. + ! You would get an error sometimes if you created a new group and didn't specify access... + * Fixed a small problem with the admin announcements... didn't look good is all... + ! Package Manager linked to "pre" instead of "install" after downloading a package. + ! Code review: Help.php. (cleaned some comments up, etc.) + ! Printpage now handles general tag parsing much better, faster, and cleaner. + ! Clarified the \n and <br /> discrepencies in doUBBC() and parsecode(). + ! Fixed a potential issue in statistics keeping... + * Removed remaining db_debug_junk_temp() calls from the index templates. + & Changed aditional_membergroups to additional_membergroups in the Profile language files, and added additional_groups_show. + * If you had a lot of groups, the profile was way long - now the additional groups are not listed until you need them. + * Moved avatar preview in profile and made it look better imho, and certainly so in 800x600. + * Moved posts to account settings, made karma use javascript to always have a correct total ;). + * Took out the "Personal Settings" section completely and merged things accordingly. + & Removed the now unnecessary $txt['theme2u'] and $txt['personal'] from Profile. + & Renamed all the profile sections to be "friendlier". + & Added specific information for each section, and removed $txt[698] - in the Profile language files. + ! Fixed bug where creating a new board was dying horribly if you didn't need to add access to specific boards. + ! The installer wasn't always output buffering everything. + & The ftp_path_info from the Install language files wasn't being used properly. + * Profile and post signature text size were different. + * Deny permissions were "dissapearing". + * You can now see the height, width, and file size of attached files. + ! Package Manager should work around issues with broken package lists. + ! SSI was sending a doctype as well. + ! ModSettings.php now handles missing settings MUCH better, so you don't need queries or anything for mods. + & Added a disableHostnameLookup option and various related language strings. + & Removed $txt['smf313'] from ModSettings language files and took out the "extended" type which was just fluff anyway. + ! Should no longer be using superfluous (e.g. marked by wastefulness :P.) obExit()'s. + ! Converters were using ; instead of &amp; for "incomplete" steps. + & Added permissions_deny_dangerous to the ManagePermissions language files. + * Now a warning is given when you are denying permissions, and the interface is a little easier to navigate - I think. + & Now if max_messageLength is set to 0, it means no max. Changed the label to make this clear. + & Removed $txt[57], $txt['smf116'], $txt['calendar32'], $txt['calendar49'], and $txt['smf104'] from ModSettings. + & Totally reorganized the ModSettings language files so they actually make some amount of sense... split smf69 into three parts. + ! Sometimes the features and options page wouldn't collapse certain areas, caused by an IE bug. Workaround made. + & Renamed some strings in Help, Admin, and ModSettings as well to simplify adding settings to ModSettings.php. + ! Updated version to SMF 1.0 Beta 5 Public in preparation of release. + * Now using the "delete_selected" button, but only for the Default theme since the others are missing. + ! matchPackageVersion() now trims whitespace. + ! You can now use reverse="true" on modifications to parse them backwards for uninstallation. + & Added new "easy" time format for using the forum default, changed "Ban User" to "Ban this user". + ! Removed my testing address from the package server list... + ! Package Manager options won't save the FTP stuff right now, I haven't decided on it. + ! Added contextual information for the darn group color. + & Moved previous/next into the language files from the settings table for i18n. + * Added fetchSession magic to the Display/Quick Reply stuff. + * Fixed wordwrapping in the most time online section of Stats. + ! Now, in most cases, if the user gets an error they won't be logged as doing x. + * The preview in Post now overflows properly in Internet Explorer ;). + ! Took out description of wildcards in searching help at least for now. + + +SMF 1.0 Beta 5 April 21, 2004 +================================================================================ +April 2004: +-------------------------------------------------------------------------------- + ! Session URL rewriting was broken for the wrong versions of PHP :P. + ! Added file_put_contents() to Subs-Package.php + ! Added some as-yet unused options to the package manager for FTP and backups. + + The installer can now chmod files using FTP so they can be written to. + & Added Install language files for the new installer. + ! The installer is now almost totally i18n friendly - just have to clean up some of the settings table and holidays. + ! The installer looks a bunch nicer now, but doesn't support ya anymore. + ! There's now an option in the installer for output compression. + ! The sql file can now have {$boarddir} and {$boardurl} in it. + & Manually migrated changes to old language files to Spanish ones, not all though. (too many...) + ! Regorganized default Settings.php so that it makes more logical sense, with better comments. + ! The MAJOR SECURITY RISK now looks much more annoying ;). + ! Turn off the moderation log on new installs. + ! The installer allowed a blank database name, which was bad for sure. + + It's now possible, in theory, to mark boards as unread. + + Added unread topics a simple and easy way that works ;). + ! Logs for topics and boards are assumed to be exclusive and should be kept clean. (topics takes precendence now!) + * The MySQL, PHP, and W3C links now all open in new windows. + ! Pruning topics no longer sends them to the recycle bin. + ! Typo in image resizing code would not resize some images properly. + ! Notifications can now be sent by post group access too... + * Put a dumb limit on post groups so they won't show for admins until 100 posts. + ! When you post a reply, uncache the last viewed topic so it can be "viewed" again. + ! Reviewed the phpBB2 converter and redid its layout, etc. + ! Registration disabled, activation and approval settings all merged into one setting. + & New language entries added to ModSettings.language.php and Help.language.php to reflect this. + ! Password is now emailed to new members regardless of whether the admin must approve the account. + & New language entry, $txt['approval_email'], added to Login.language.php to reflect this change. + ! Registration center will now allow admin to look at remaining unactivated/approved accounts after the settings have been disabled. + + Find Members option added to pm preferences section of users profile. + ! Fixed a bug in Subs-Package, related to file_put_contents. + * Cleaned up some admin templates so they all do the help the same way. + & Installer now tells you to, and how to, get to the admin center. + ! Upgrade wasn't working well with groups that had apostrophes. + ! HTTP_HOST should already include the port number... + ! Reviewed the Invision converter, improved its UI, redid the attachments converting, and made it do bbc properly. + * The Admin template, in general, now looks a lot better in 800x600, etc. It also works better... edit news, censored, etc. + & Now the "Edit News" and "Censored" sections *optionally* uses javascript to add items. + & Added more help next to the maintenance options. + ! The installer no longer allows invalid prefix characters - it just ignores them. + * Removed the unused cat.gif... + * Cleaned up some xhtml - inputs that weren't closed, etc. + ! Code review: Errors.php. (just some commenting, mainly.) + ! After-login redirection now works properly... again. + ! If you were a guest, and you tried to post with no name, you'd get an error. + & Put all of the "welcome" strings into one, namely $txt['welcome_guest'] - better for i18n. + * Changed the index template to use this new string; old index templates should still work. + + It's now possible to resend validation emails easily, and to change your email address after registering - with the correct password. + * Renamed the "activate" sub template in Login to "retry_activate", and added "resend". + & Some new entries addded to the Login language file for this purpose. + & Added $txt['whoall_pm_send2'] for pm sending... + & Attachments can now be limited by total size per post. + ! Now, attachmentSizeLimit or attachmentPostLimit, etc. = 0 means no max. + ! Attachments -> Disable New Attachments didn't work. + ! Remove old topics was removing stickies again. + ! When moving a topic, if you don't specify any text for the redirect, don't add two breaks. + ! QuickModeration2 wasn't logging actions properly... or at all. + ! Banning by email with wildcard should be possible even if ti might affect admins. + * Trying to edit the permissions from manage boards wouldn't work unless it was already local. + ! The installer should now properly update $webmaster_email when you set up your inital account. + ! When you banned registration, you got errors caused by seemingly minor typos. + * Signatures should now have scrollbars too. + ! The installer now does much better permissions checking on windows. + ! Reserved name search within/don't search within was broken - reversed. + ! Made upgrade.php look purdier, removed the javascript requrements. + ! Now updateMemberData() supports the '+' and '-' functionality. + ! Code review: index.php. (some minor reorganization, more comments, etc.) + ! Sessions in the log_online table should be session ids not hashes. + ! You shouldn't be able to add smileys with no code at all. + ! Spell check was checking words in code blocks too... + + Major security addition! checkSession() now has a new parameter to check the referring action via session. + ! Added action session check to remove old topics, dump database, and manage attachments. + + You can/should now use "quotes" around members you are sending messages to - ie. "I, Brian". + ! The installer now automatically logs you in after installing. + & Added no_dump_database to the Errors language files. + ! Code review: DumpDatabase.php. (cleaned up a lot of commenting.) + ! Reviewed the vBulletin (it actually works now!) and XMB (untested!!) converters, upgrading the UI, etc. + ! Don't cache fatal database error messages in the browser! + ! vBulletin converter can now convert attachments. + ! If you are missing YaBB SE settings in Settings.php, upgrade handles it better now. + ! Added ID_BOARD column to messages table. Should speed some things up. + * Fixed some very strange problems with the new index template by using a table instead. + ! The number of views now updates before the page view, not after. + + Show permissions now can be used to show either general permissions or permissions by board. + & New language entries added to Profile.language.php to reflect above change. + ! You could register members with no email address in the admin center. + ! You might get errors if you used deny permissions in some cases. + * Smileys should now sort, more or less, properly - and save on move. + + If you specify a theme in the URL, it is now "sticky" - works for guests too. + - Removed, at least temporarily, wildcard support in searching... it's much much faster now. + * Added a scrollbar to avatars on Display, and made the post summary overflow with scrollbars. + ! XMB Converter can now convert attachments. + * Added posts_per_day to contextual information and displayed it for Profile. + & Added a posts_per_day language string in the Profile files. + ! Added a few new logging events to the moderation log. + & Added 5 new language strings in Admin.language.php to reflect the above change. + + When creating a membergroup you can select which boards the new membergroup will have access to. + & Two new language strings added to ManageMembers.language.php to reflect the above change. + ! Upgrade wasn't always converting all messages to have ID_BOARD. + ! Avatar upload was mistakenly marking bitmaps as wireless bitmaps. + ! Code review: Who.php. (basically fine, just added a little commenting.) + * The invertAll javascript function no longer affects disabled checkboxes. + ! Banning wasn't always using the right name for the sorry message... + ! An error was thrown if two people hit the forum at the same time for the first time that day. + ! In SSI.php's pollVote, sometimes you'd get a weird error about copyright missing, if you accessed it directly. + ! SSI.php's topPoll/recentPoll were blank for anyone with the permission everywhere. (most people!) + ! You couldn't vote in polls with SSI.php in most cases. + ! Sometimes, poll results would word wrap in tight places - no longer. + ! If the userLanguage setting was off, sometimes strings wouldn't be loaded properly. (!) + * Messed with theme administration some, not done yet.. + & Added some help strings for themes in Help, Themes, etc. + ! parseBoardMod() handles missing files correctly now + ! Now, if $fatal is false in loadLanguage, it will always load the language file. + ! Don't add <?php and ?> to PHP sections twice. + * Fixed some minor browser compatibility issues with Post and Display... + * Permissions manager now uses radios instead of select boxes. + & Added an explanation at the top of ManagePermissions. + ! If you tried to preview a theme it got "stuck" on that one. + + Added the option to show a popup on new personal messages. + * Added some code to the Settings templates to show the popup option. + & Added entries to Profile and index language files for the pm popup. + ! The ssi_examples pages were mislabeling showPoll. + ! boardOrder just wasn't big enough. + ! Now all the version numbers say Beta 5 - doesn't mean release today, just soon. + ! Cleaned up online logging slightly, not much changed though. + ! Posts per day showed rediculous values if the member hadn't been registered for over one day. + & Removed some "Hacker?" error messages and put them in Errors. + + Now you can pick between jpeg and png for saved avatars. + & Added some help text for some of the avatar settings. + ! Added some values to the default entries for allowed attachment extensions. + & Internationalized the default "Hi.+Are+you+there?" message. + ! Added xml:lang to rss and xml output for internationalization. + ! The removeMessage() function didn't properly handle working without a board. + ! Reminder was giving an error from the wrong language file. + ! Now, if you received a message from a guest it says "Guest" instead of not showing anything. + * Tweaked the InstantMessage template so it looks a little better. + + You can now delete messages with "quick moderation" by checking them from your inbox/outbox. + * Solved an issue in Firefox with signature overflowing. + ! TRUNCATE should come before DELETE FROM. + ! Older versions of PHP used null instead of false for filesystem errors. + & Changed a few strings in ManagePermissions to sound better. + * Modified the ManagePermissions layout a bit more. + + You can now set access rights via permissions instead of boards. + & Added the permissions_quickgroups help string to Help. + ! Adding a new board/category now requires the user to fill in the details before it's actually added to the database. + & One new language string, $txt['mboards_add_cat_button'], added to ManageBoards.language.php. + * ManageBoards template updated slightly to accommodate above change. + * The "Edit Membergroups" page now limits total stars to 99, and does better updating of the preview. + ! Upgrade now "auto continues" after some time... 3 seconds actually. + ! Changed some aliases not to use reserved keywords, although this is usually allowed... + + New help system using xml based help files with a lot more information. + * Moved help.css, redid it, and cleaned it up a lot. + * Removed the old-style help from the Help template. + & Tore out the now useless stuff from the Help language files. + & ReportToModerator now sends your email address and warns you that it does. + ! Optimized some for loops to not count an array's length every loop. + * The BBC buttons were missing the title attribute... again. I swear I've fixed this three times at least. + & Minor aesthetic change to Profile's $txt['messages_from_ip']. + ! QuickModeration2 wasn't updating a topic's replies properly. + ! Moving topics no longer requires the move permission on both boards, for now. Will rethink. + * Moved the catbg definitions to style.css. It just is too hard for people to look everywhere, not worth making images portable. + + +March 2004: +-------------------------------------------------------------------------------- + ! Made MySQL error messages look nicer in the installer. + ! Search wasn't handling multiple/0/mixed searching very well. + ! Added "police" smiley. (sorry, it's an inside joke...) + ! Upgrade wouldn't work properly without English installed. + ! Guests would sometimes get errors if they tried to access a profile and had no permissions. + & Email, Login, and Logout should be consistently spelled. (English only...) + * Cleaned up some <label> elements so they work properly in IE 6. + ! Added a Cache-Control header after the session_start() call although I don't really like it. + * Added session keep-alive code so that your session shouldn't time out while posting. + ! Added another smiley, and set the update package to contain the stupid smileys.. + ! You couldn't edit a post with attachments if you couldn't post attachments. + * Adding polls and linking to the calendar used the first message on the page, not in the topic. + ! Changed the order of the recount so that the slowest thing was last. + * Sometimes the upshrink actions were being cached; fixed. + ! Tried to clean up after some code duplication in SSI.php. + ! Added setLoginCookie() to Subs-Auth to standardize the login cookie a bit more. + ! Small typo in template converter was causing very annoying problems in some cases... + ! Pspell wasn't loading external languages all the time, properly. + ! If you couldn't view ANY polls, recentPoll, topPoll, and showPoll died horribly. + ! The database dumper wasn't gzipping properly, or cleaning input properly. + * submitThisOnce() now makes <textarea>s read only on submit. + & Changed some remaining Who language file entries meant for im instead of pm. + ! Who now shortens the time to only what's necessary - not the "Today at" part. + * Who template should now look a bit better. + ! The installer and upgrader now have options to delete themselves when everything is done. + ! Removed the copyright referrer javascript code since it was causing problems apparently. + ! Calendar events weren't rolling over months/years. + & Added the $txt['rtm_not_own'] string to the Errors language files. + + Members can no longer report their own posts to moderators, it's almost always abuse. + ! There, now the md5_hmac function looks more confusing :P. + + Added [rtl] and [ltr] bbcode support, no button. + + Added hidden disableCheckUA setting and made it so the user agent is verified on session checks. + + Installer package should now redirect to install.php by default, but the check is removed upon install. + ! When you delete a membergroup, its access is removed from any boards it had access to. + ! Preventing a lot more bad IP addresses - 192.168.*.*, 0.*.*.*, and 255.*.*.*, 172.16.*.*. + * Fixed internationalization in the "find members" function's sub template. + * The DOCTYPE is now a theme setting in the init sub template - this makes debugging easier in IE. + * The BoardIndex template's categories should be much easier to theme now - backwards compatible. + + +SMF 1.0 Beta 4.1 Public March 27, 2004 +================================================================================ +March 2004: +-------------------------------------------------------------------------------- + ! Some theme conversion errors have now been fixed. + ! Non-administrators should not be able to register new administrators. + ! boardsAllowedTo now applies deny permissions properly, as well as handles allow permissions better. + ! Guests don't have passwords; they cannot validate, so don't even try - just make them login. + & The English version of $txt['notifyXAnn4'] now makes more sense as to what it does. + * Fixed a typographical error on one of the spanish buttons. + ! Installer was adding slashes to things twice... very bad actually. + ! Installer now checks for certain paths to exist. + ! The *expected* commas were screwing up a query where they were not handled properly in pick themes. + ! The installer now verifies that the username/email you are trying to use has not already been taken. + ! The installer makes sure Sources/Subs.php exists before trying to include it! + ! Minor typo was making boolean values from registrations not work. + * Hostname shouldn't distort Profile layout. + ! Recent failed when there were no posts at all. + * Removed any references to "form1" as a form - ManageMembers, ModSettings, and ManagePermissions. + * Today's birthday on the BoardIndex was displaying some invalid XHTML. + ! If none of the messages were new, it would not mark any as the "newest" message. + ! Changing the default sort of messages made the counter screw up. (Reply #.) + ! Jumping to a specific message or the newest message didn't work with viewNewestFirst. + ! Help still wasn't working after fixing the undefined sections. + ! Default value for stars changed to star.gif. + ! SSI's boardNews failed when there were no topics at all to display. + * Profile wouldn't preview the first avatar. + ! Minor notice if improper groups are set. + + Recent and unread should not include posts from the recycle bin board. + * Added information to explain why moderator isn't shown in account settings. + & Added strings to Profile and Help for this purpose. + * Replaced topic class icons for the classic theme. + * Calendar was linking to index.php not $scripturl. + * Removed cal_todaycolor and cal_captioncolor in favor of the template system. + ! What used to be $topic['extended_class'] is now known as $topic['class']. + ! Hits aren't logged when we're just downloading/viewing attachments/avatars. + ! Removed redirect option for attachments as it could be insecure and in some cases prevents attachments to work. + * Avatar directories with underscores (_) in their names weren't working. + ! Template converter didn't always convert the Content-Type correctly. + * Added help to the admin_login sub template in Login about what password it is, tweaked layout, added page_title. + & Added securityDisable_why to the Help language files. + + Now, if you set the number of days for the calendar on the board index to 1, it will say "Today's" instead of "Upcoming". + & Language files changed to reflect this new functionality. + * BoardIndex template changed to make this possible. + * Added a little helptext to the events listing to help administrators along - (how do you edit these events?). + & Added the necessary language strings to make this possible. + & Added a note to enableSpellChecking in ModSettings telling people that it doesn't always work! + ! Upgrade should now both update less and do gender properly. + ! The randomized session checks should now work on all server configurations. + ! Deleting boards now removes any permissions associated with that board. + ! deltree() was using chmod(..., 077) which in most cases did not work for obvious reasons. + ! Editing a post but not returning to it would show it as new. + ! Added something to stop people from trying fake PHPSESSIDs in their URLs. + & Added a note to smf244 - don't entity that string. + ! Package manager was naming the wrong file when it found one that was missing. + ! ssi_theme should be a "final decision" so to speak. + ! Creating a group based off another group caused big problems if that group had no permissions. + * Fixed some javascript mistakes (typos) in ManageMembers.template.php. + ! You shouldn't be allowed to delete the first message in a topic with quick moderation. + * Moved trackIP and trackUser to ManageMembers. + * There ain't gonna be no "<p>&nbsp;</p>" in MY code!!! + + Added showPermissions to profile so people can see permissions "at work". (not complete!) + * Added a showPermissions sub template to ManageMembers. + & Added strings to Profile for this... + ! Title for trackUser was very very wrong... + ! url_parts() and hence localCookies didn't work well with IPs as domain names. + * Added local time display to the profile summary. + & Added $txt['local_time'] to the Profile language files. + ! Removed some remaining <font>s. + * Printpage should look a lot better now, and use much better html. + * Distribution should no longer use deprecated elements like <u> and <s>. + & Profile strings updated to reflect members with no or all permissions. + * Show Permissions now makes a note of if you have all permissions or something. + * Printpage should now be totally black and white in most current browsers. + ! You should be able to see the results after expiration even if they chose "don't show until vote." + ! SSI's boardNews shouldn't clip strings that aren't too long. + ! Profile's dateRegistered wasn't very i18n. + ! Dumb typos in SSI.php's events function. + ! Nesting different *types* of [quote]s broke things sometimes. + ! Editing a board that had entities in its name didn't work well... + & $txt['theme_guests'] changed to 'Overall forum default' so it make more sense. + & Admin center now also includes 'Theme Settings'... added $txt['theme_current_settings'] to Admin. + ! Editing permissions now requires the admin_forum permission - at least for now. + & $helptxt['m_queryless_urls'] now shows whether it works or not; now doesn't work in CGI mode. + ! Now, if autoFixDatabase is on, "lost connection" errors will be tried a second time. + & $txt['who_hidden'] changed to be much more elusive and descriptive - 'Nothing, or nothing you can see...' + ! Search wasn't showing the correct last-post time. + ! There shouldn't be any more 'unknown' IPs, at least in theory. + ! Profile sidebar thing shouldn't be shown unless it is actually needed. + ! Profile was changing dlattach out, although I suppose avatars can be attachments. (?) + ! User's IP address is now contained in the $user_info array. + ! All the tables SSI.php outputs should now use the class "ssi_table". + * The default theme's css was using the theme URL not default URL. + ! Code review: QueryString.php. (cleaned up comments and redid some code to work better.) + + Queryless URLs should now work on more installs, because it doesn't change output size. + ! Code review: LockTopic.php. (minor clean up, not much to change at all.) + ! If the first character of a post is a space, it will now be displayed properly. + ! Code review: Notify.php. (minor clean up, should deal better with mods.) + ! Code review: SendTopic.php. (very minor clean up, just flows a little better.) + + Personal Messages can now be sorted by column. + * Changed the InstantMessage template so the links are clickable. + ! The poll functions in SSI should now follow permissions correctly. + + Added $context['browser']['is_ie5.5'], which makes it easier to fix things for older browsers. + * Fixed some Internet Explorer issues in older browsers... + * Moved the stupid darn avatar to the left side, happy? + * Fixed a small layout problem in the Profile template. + ! The Profile wasn't just listing images, but also other files it shouldn't have. + * Moved a lot of colors into the style.css from index.template.php for the default theme. + ! Now using Alienine's smiley set as the default, added classic. + ! Setting theme options with javascript shouldn't be logged. (jsoption) + & Minor aesthetic change to $txt['smf62'] so it reads a bit better. + ! Code review: Karma.php. (changed the flow of things to make it simpler...) + * Added an overflow for the attachments... + * You can now delete posts from the "show posts" page in a users profile. + & A language entry issues for manage_permissions was resolved - one duplicate entry removed (Admin.language.php). + ! You can no longer enter a negative amount of stars for member groups. + * Delete posts added to the recent posts interface as well. + ! Maximum message length shouldn't ever be less than, say, 100. (upgrade only.) + & Added (0 for no max) to all the $txt[689] strings in Admin. + * Fixed signature limit javascript and made it handle no limit. + * Added a "Spell Check" button to the signature in the Profile template. + ! Aspell now works on windows platforms. (using a workaround, though!) + ! Re-enabled language-specific spell checking. Should work out, needs more testing. + * The spellcheck sub template in Post now uses your forum's colors and proper i18n. (yea, I can test it now locally!) + & Added Change, Change All, Ignore, and Ignore All to the Post language files. + ! Reminder email should not work if the activation_code in the database is blank. + ! Changed everything from mt_rand to use just rand, since mt_rand was not always being seeded. + * Code review: ReportToModerator.php. (now using msg instead of mid, but still compatible.) + ! $_REQUEST['start'] now defaults to 0 if not passed or negative. If this is bad, $_GET['start'] should be used. + ! Avoid getting blank screens if there's an error shown before gzipping starts. + ! ob_sessrewrite shouldn't even be started until the header is outputted... + ! Added a check so $cookiename shouldn't ever get spaces in it, etc. + ! isAllowedTo() shouldn't ask for a password, and then give an error - if they can't. + * Fixed a small mistake in the Modlog template, and made it look a lot nicer. + & Removed modlog_object, modlog_expand, and modlog_retract from the Admin language files. + - You can no longer expand and contract the "objects" in the moderation log. + & Changed whoadmin_modifyModSettings and whoadmin_modifyModSettings2 because they were plum incorrect. + ! Renamed getMessage() to preparePostContext() in Display.php. + * Cleaned up a great deal in Display, and add a lot more comments that were missing. + ! Added a VERY BASIC and NOT DONE option to theme_info.xml to base it off another theme. + ! Moved registration agreement checkbox to the edit agreement page. + * Cleaned up the MoveTopic template so it looks nicer when things are collapsed, etc. + ! MoveTopic now properly handles permissions on both boards involved. + * Tweaked the Profile template's layout just a bit so as not to squish stuff so much. + ! Theme editor might edit wrong index template or style.css if use_default_images is on. + ! Personal messages in the outbox were being duplicated with multiple recipients. + ! Added sanity kludge so Settings.php wouldn't be saved improperly. + * No preview should be made of the star image if it is blank, in ManageMembers. + * Updated fix for overflow: auto; under Internet Explorer; now 5.0 and above only, but happens faster and works on all div blocks. + ! The srand() function should not be called too much, it degrades actual randomness. + & Changed a typo in ManagePermission's $txt['permissionhelp_moderate_board']. + ! Reminder should send the IP address... + * Using REPLACE for some log_topics inserts... althougb I suppose it's okay if it's already there. + ! Image size is now checked on posting, instead of viewing.... of course, it's so simple! + + Now image size limiting does even images *with* a specified width and/or height. + ! Updated all version numbers to SMF 1.0 Beta 4.1 Public in preparation for release. + ! The forum default theme's stuff wasn't being set properly when the default theme is disabled. + & Added a note to the admin_browse_w* language strings, saying not to entity them. + & Added error messages for profile_remove_own and profile_remove_any. + ! Changed cookie login time to a lot of years because it fixed some issues ;). (verified in many browers.) + + Deleting an account from a users profile now gives an admin the option of deleting posts made by that user. + * deleteAccount template changed to give option of deleting posts. + & New language strings added to Profile.language.php, all related to deleting posts of a user. + ! Fixed profile summary displaying incorrect local time. + + +SMF 1.0 Beta 4 Public March 14, 2004 +================================================================================ +March 2004: +-------------------------------------------------------------------------------- + * Reorganized topic classes, and updated the default ones with Alienine's. + ! Fixed problem with calendar being able to list boards off limit to a user. + ! Admins can no longer be banned by username, IP, or email. + ! Upgrade and install now set mostOnlineToday to 1. + ! Changed all version numbers to Beta 4 Public. + ! Fixed instant message bug in all converters. + ! Hits can now go up to a lot lot more than it could before ;). + ! Delete all messages can now force setting instantMessages to zero. + ! Register was adding slashes twice to the theme option names. + ! If you included SSI.php but didn't start a session, you'd get "SID"s in your URLs. + ! The calendar should no longer show spanned events in a row on the board index. + ! Deleting members now also hides their email addresses... + ! Quoting messages from a sender with special characters in there name was buggy. + ! Template conversion functions added to template.php. + ! Permissions of post groups are now added to the permissions of primary and secondary goups. + ! Split up membergroup management screen and permission management screen. + & Added language tag $txt['edit_permissions'] to Admin.lang.php and changed $txt[8]. + & Created new language file ManageMembers. + & Moved around some language tags between ManagePermissions, ManageMembers, and Admin. + * Moved modify membergroup, and add membergroup screens from ManagePermissions to ManageMembers + * Added Membergroup index to ManageMembers template. + ! Split up regular and post count based groups in 'Edit Membergroups'. + * Added member count to the 'Email Your Members' screen. + ! Fixed a few bugs in 'Email Your Members' and cleaned up some code. + ! Fixed number of members shown in permission screen. + ! Themepicker now only shows themes that are set in the 'knownThemes' setting. + ! Membergroups are sorted everywere like: fixed groups (ID<4), regular groups (name), postgroups (postcount). + ! Dissolved determinePostGroup in loadMemberData() and loadMemberContext(). + ! Removed loadMemberGroups() from several places were it wasn't needed anymore. + ! Modify permissions now also retrieves and stores 'deny' permissions. + * Changed checkboxes into drop downs in the Modify permissions screen. + ! Fixed wrong counting of permissions per membergroup. + ! Fixed SetQuickGroups and added the addDeny feature to it. + * Moved invertAll to script.js and added the function to the permission index and view members. + & Inverted subscribe function notifications (checked is unsubscribe). + * Changed the appearance of the notification settings into a messageindex style. + * invertAll now supports a mask among other things, and is faster and better. + ! Added an option added to hide users email from guests. + & $txt['guest_hideEmail'] added to modSettings.language.php and $txt['m_guest_hideEmail'] to Help.language.php. + * Moved copy_to_outbox to a different section of profile. + * Added Quick Moderation to Display templates. + & Added quickmod_delete_selected string to the index language files. + ! Moderator permissions should no longer inherit from parents. (logical; other permissions don't.) + ! The phpBB converter should now handle personal messages much better. + ! Upgrade should now convert the template.php file ;) :D! + ! Member list would not go away... although it respected permissions. + & Added $txt['mboards_permission'] so that permissions can be accessed via the board management. + * Permissions can now be set with a separate "board" screen. + * Permissions screen can now *show* who has access to the board. + & Added $txt['permissions_access'] for this purpose. + * The header can now be shrunken even if you are a guest. + ! Denied permissions are *better* supported in allowedTo() and boardsAllowedTo(). + ! Added move_own to permission interface. + ! Stickyness can only be changed now if the EnableStickyTopics modSetting is enabled. + * Quickmod will only show on Display.php if you have the proper rights. + ! Fixed access settings in $context for board permission index screen. + ! Replaced modSetting 'enableReportToMod' by permission 'report_any'. + ! Htmlspecialchar'ed the retrieved member IP in loadMemberContext. + * Moved the whois links to track IP. + & Added language tags whois_* to Profile.php. + ! Checked permissions (75% percent done). + & Added help tags for the permissions (75% done). + & Updated german language files thanks to Daniel D., including recent additions. + ! Fixed some permission bugs for polls. + ! Polls now always show options and (allowed) links. + ! Checked permissions (done). + & Added help tags for the permissions (done). + * Optimized english buttons and put in optimized german temporary buttons. + * Added some possible error handling in the Profile template for the avatar javascript. + & Added three new language entries in index.language.php. (All poll related) + * There is now visual information on when a poll will expire. + ! Fixed a massive security issue in regcenter allowing people to bypass validation. + * Added javascript code size fix to the default theme, annoying as IE can be. + * Reduced confusion in the permissions manager. + & Added some help texts for the permissions manager. + & Removed "(IE and NS6 only)" because it is dumb and stupid :P. + * All of the german/spanish/dutch images should be more or less updated. + ! Fixed ability to edit polls and leave them with no options/question. + & New language entry, $txt['poll_no_question'], added to Errors.language.php. + ! Upgrade should do auto_increment correctly now. + ! Latest member was doubly htmlspecialchar'd. + & Added some repair boards stuff, minor cleanup to some mistakes. + ! Notifications in the profile are now ordered by time posted... + ! The rss feed for latest members was using the wrong guid. + ! Error reporting should respect the error_reporting setting. + * The powered by and valid logos now light up on hover in the default theme. + * The default display template is now in sync with the classic one. + ! Fixed calculation of birthdays (birthdays in month < 10 were not found). + ! Reduced amount of cached birthday information (less memory used, more birthdays allowed). + ! Birth year can be left empty in profile settings (assumed to be 0000). + ! Fixed birthday not shown in profile summary if the birthyear is left empty. + * Today's birthday highlighted on boardIndex. + ! Tried to disallow Settings.php being saved shorter than 10 lines. + * Fixed a minor html mistake in the ManageSmileys template. + ! Fixed events on boardindex not showing for group 'registered users'. + ! Fixed query_see_board so that 0 means all not just ungrouped. + ! Fixed adding membergroup with pre-defined permissionsettings. + ! Remove nested quotes replaces nested quotes by a line break. + ! Resetting a birthday should be possible now by just removing fields. + ! Events were zero based in YaBB SE, not correctly upgraded. + ! Default board for calendar events had no effect. + ! Added a check for a bad session.save_path in the installer... and a plug for my bug report ;). + ! Tweaked the credits slightly so they display a lot better. + * Administration sidebar is now more font-size friendly. + ! URLs with single quotes are now autolink'd, although I still don't think it's valid :P. + & Modified $txt[430] so it could include the username. + ! Saving settings now updates the calendar too. + ! Calendar wasn't showing holidays properly. (t. Patrick's Day, ernal Equinox.) + ! The member who starts/adds a poll is now recorded in the polls table. + * Added confirmation Javascript for attachment remove action. + & Added $txt['confirm_delete_attachments*'] to the Admin language files. + ! Added session check to remove attachment(s). + ! Fixed age selection in admin Member Search. + ! Randomized session check code. + ! Fixed permissions not properly counted for 'Guest' membergroup (some PHP/MySQL versions). + ! Removed loadMemberGroups (there's not enough added value there anymore). + ! Profile Account settings remember additional membergroups when an error occurs. + * Profile Account settings use different context variables to select membergroups. + ! Upgrade should now solve any missing-language problems. + ! Typo was making the above not actually solve anything, but at least it warned you. + & Fixed a typo in the non-english language files in ManageSmileys. + * De-admin prevention works again, minor change to Profile template. + ! Backup in upgrade wasn't working for *some* versions of MySQL. + & Very minor typo in the description of a certain permission. + ! A ] is now allowed before automatically linked URLs. + * People without the "admin_forum" permission can no longer set other people as administrators. + ! See that line two above? It was bad. Very bad. ] are no longer allowed before links. + * Added some preview functionality to EditPoll - for later use. + & Fixed small version problem with ManageMembers language files. + & Translated some of the German language files. + ! Autolink wasn't verifying scheme properly. + ! Help defaulting was not working properly in some cases. + ! Time offset should be floating point in Features and Options. + + +February 2004: +-------------------------------------------------------------------------------- + ! The installer now asks directly for the forum name during installation. + ! VERY annoying typo in MessageIndex.php was causing children of children to be shown erroneously read/unread. + * Attempted to maximize desktop space economy-style in the ManagePermissions template. + ! Most every link is paired with a href now and vice versa, not done but very tired. + ! Trying to set a board as primary when there are no other boards no longer dies miserably. (now it dies silently ;).) + ! Maximum for constructPageIndex was the end, not the page before the end. + & Added the $txt['change_color'] string to all the Post language files. + * Now when you select a color it pops back to "Change Color" so you can pick it again. + ! Added an optional boards parameter to allowedto() and isAllowedTo(). + ! boardsAllowedTo() now also has a boards parameter. + * The search and profile's recent posts should now apply permissions more or less correctly. + * Ten most recent posts now also does this, as well as linking to the poster's profile ;). + ! Added 'is_guest' => false to loadMemberContext(). + * Removed an array initialization when commenting, oops. + ! Removed some references made on local variables. (doesn't save memory.) + ! Actually cleaned up the glow/shadow tags without removing them! + ! Calendar birthdays should now be in the proper month and day even on leap years. + ! Added 'Date Posted' to search screen, removed Reples/Matches. Left old code in if people want to change theme. + ! Theme installer works on more configurations now. (stupid $_FILES array.) + * Fixed some void(0) problems with a couple templates... + * Find Members shouldn't add the same member more than once, focuses box automatically. + + Each board can now be explicitely switched between Local and Global permissions. + + Post group permissions now add up to the other permissions. + + The number of permissions that are set for each membergroup is shown on the permissionindex page. + * Changed the local permission part of the permission index to reflect the changes in permission structure. + ! Who is online shows members as guest if the profile cannot be found. + + Added currently invisible function, IMPrune, for deleting personal messages over x days old. + & Two new language entries added to InstantMessage.language.php. + * New template, prune, added to InstantMessage.template.php. + ! Updated Instant Message functions to represent PM, not IM. + ! The calendar was calculating age based off the current year. + * $context['user']['avatar'] now is an array with more useful information. + * Added some spacing above the moderation buttons in Display. + ! Changed "TRUNCATE TABLE" to "TRUNCATE". + ! More im related variables renamed. + ! imsend() renamed to sendpm(). + ! Permissions im_read and im_send renamed to pm_read and pm_send. + ! I have *no idea* why it has to be done NOW, but if it's going to be done now, it should at least be done fully - im->pm. + * Changed lots of templates, as did the changes above, since if Display has to be changed screw it anyway. No patience. + * Added context links to the track IP stuff. + ! Having no gender when upgrading no longer causes problems. + * Post wasn't showing the "Delete Event" button. + ! Typo on SetJavaScript made it fill the error log rather quickly. + * Changed a bunch of "."s to ","s in the InstantMessage template. + ! The link to edit an event was using the poster's ID instead of the topic's. + * Help popup now always has a margin, even if it is 0 in style.css. + * Calendar now has more padding in the days... + * Cleaned up the context stuff a lot in MessageIndex for children. + ! Reversed the order of the participation key, should make more things faster. + ! Some minor fix's on the converters. + ! Added classic theme to installer and upgrader. + & Moved everything out of the Settings language files because they should be for language specific settings only. + ! Permissions can now be add/deny instead of just add. + * Changed the default theme, please don't be mad at me if you don't like it :P. + ! Deleting a theme now resets everyone who was using that theme to ID_THEME = 0. + + +SMF 1.0 Beta 4 February 21, 2004 +================================================================================ +February 2004: +-------------------------------------------------------------------------------- + ! Now there should be less difficulties with quotes and such tags. + ! Moderators should not double up parent/child now. + * The smiley set interface is much better now... ManageSmileys, etc. + & Added some new strings to ManageSmileys for the new interface. + ! Added more documenation to DumpDatabase.php and Notify.php. + * Moved images/im_sm_newmsg.gif to images/icons/im_newmsg.gif. + * Removed the unused images/im_sm_prefs.gif image. + ! Nearly finished the documentation in Errors.php. + ! MySQL 3 didn't like a certain query in notification settings, it seems. + ! Added a "package-list" parameter to the package server query... + ! Removed the last PackageCreate() functions from Packages.php. + ! Removed the Packer class finally ;). (still far from done.) + ! Completely removed the old $guestaccess, $MaxSigLen, and $MaxMessLen variables. + ! Put in some better memory saving devices... not completely used yet. + ! Removed the $showbbcbutt, $enable_ubbc, $timeformatstring variables. + ! Changed all the version numbers to 1.0 Beta 4. (not releasing yet!) + ! Version checker now looks for .template.php on the end. + + Added showPoll to SSI.php - this displays poll by topic ID - allows voting AND viewing of results. + ! Put showPoll into ssi_examples.php and ssi_examples.shtml. + ! Added is_last to boardNews function to make it look better. + ! Jack.R.Abbit's package server URL corrected to not have an extra /. + ! Cleaned up some of the PackageGet stuff to use xmlArray's auto_trim feature. + * Separated the sub templates in Packages because it was a mess. + & Added packages_latest/packages_latest_fetch to the Packages language file. + * Added a "Latest Packages" panel - to be moved to look better - in Packages. + * Added $context['support_info'] and $context['current_versions'] in preparation for some changes to ?action=admin. + ! Began the start of parsePackageInfo() and added matchPackageVersion() to Subs-Package.php. + ! Added an xml directory to the other stuff for DTDs and such stuff. + ! The upgrade script was creating a legacy $HeroPostNum variable in Settings.php. + ! Changed calendar_holiday DRASTICALLY into calendar_holidays. Don't worry, I have reasons. + ! calendarHolidayArray() now takes two dates and gets the events between them. + ! calendarBirthdayArray() now also takes two dates, almost done correcting the bug... (next is the calendar table..) + ! The calendar table now uses the same case as all the other tables ;). + ! calendarEventArray() now goes between two dates too. + ! SSI.php's recentPoll function has been greatly optimized now. + ! The calendar table has now lost day/year/month in favor of eventDate. (this is better for queries.) + ! Themes not edits the index.template.php file instead of index.php. (oops...) + ! The attachment downloading routine now restarts the output buffering, so as to fix any output already sent. + ! Moderators didn't seem to be working properly, only the first worked. + ! The moderatrion log no longer chokes if you click "remove" with nothing checked.. + ! Updated a lot of the documentation in Subs-Package.php. + ! Upgraded the xmlArray class to the most recent version, which adds a lot of functionality. + & Removed errlog_total and 657 from the Admin language files. + * Removed all the statistics from the administration panel. + & Removed the 425 language string from the Admin files. + * The administration interface now has an area for common support concerns. + & Added support related entries to the Admin language files. + ! Added the latest-support.js file to deliver the latest support concerns. + & Changed the average_members string to 'Average registrations per day' in the Stats files. + ! Added and cleaned up more documentation in Printpage.php, News.php, MoveTopic.php, and Memberlist.php. + ! Fixed a minor undefined index notice in Subs-Boards.php. + ! Clarified the xmlArray error messages by adding "XML" to them ;). + ! All entities (like &#039;) are now correctly parsed in xml files. + ! Added a fix that converts international entities to text in preparsecode(). + ! The calendar now shows birthdays for *any year*.... duh. + * Optimized a few more images. (namely blank.gif in default.) + ! saveInstalledPackages() now checks if the installed.list file is writable. + ! the getPackageInfo() function now conforms to the dtd as far as what's required and what's not. + ! Reorganized some of the package manager code and put comments as to what needs to be done. (not 100% complete or anything...) + - Removed basic/advanced permissions interface. + + Added a per permission interface. + - Removed permission wizard (was already replaced by the drop down boxes in the permission index page). + + Merged board and membergroup selector into one interface on the permission index page. + + Added option to switch between 'inherit permissions' and 'set local permissions'. + & Several language strings have been changed, removed and added in ManagePermissions.php. + & Language strings for permissions have been regrouped. + * Removed templates modify_board and wizard in ManagePermissions.php. + ! Fixed membergroup Global Moderator (=2) in upgrade.php. + * Decreased padding-space between admin center content and nav bar. + * Increased width of the index template to 95%. + + Version checker now checks language files in default directory. + & All language files altered to give version tags at top. + & Fixed some typos in the Help, index, Settings, Profile, Post, and Errors language files. + * Smite/Applaud links were wrong in the InstantMessage template. + ! Today mod no longer shows errors on negative dates. + * Quotes and code blocks should look nicer in wireless now... + + You can now activate your account manually by typing in the code. + & Moved some strings around to make this possible, and added a few - all to index. + * Added an "activate" sub template to the Login template to make all this possible. + * The Post template no longer shows "delete event" when creating a new event ;). + & Changed the wording of a few setting descriptions, added help for 3 existing options. + + Permission checking and documenting (25% done). + * Links removed when permission is not set (25% done). + + Info about online status added to theme user context. + & Serveral changes in the $txt['online?'] tags. + * Online icon and link are now constructed in the template. + ! Fixed moderators not getting proper permissions. + ! Fixed error 'missing topic ID' for 'post new event'. + + Post event now only shows those boards a user is allowed to post in. + - Removed option to set specific event posting members (already done by the permission system). + ! Made some big optimizations to the topics table and a few to SSI.php ;). + * Removed the big table that everything in view/delete members seemed to be contained in. + ! News feeds were double spaced because they used nl2br. + ! Feeds now use the email address instead of name for author. + ! Personal Messages weren't always deleting when you deleted a member. (ie. their sender.) + * Added some commenting to the index template. + * Changed a lot of the colors to just use lowercase, since a standard is needed. + - Package manager no longer uses "<TBLPREFIX>" in sql files; use "$db_prefix" instead. + ! The parsePackageInfo() function now does stuff, sorta... + & Removed some unslightly markup from the copyright statement. + ! The ssi_recentEvents() function wasn't going through the array properly. + ! Tabs have been kludged to produce good html except under MSIE 4 and 5, so they still work. + * Printpage now shows the topic subject in the title. + ! Added some more comments to important functions in Security.php. + * When clicking "mark all read" from the unread replies, it only marks those topics read. + - Global moderator no longer is a special membergroup. + ! Changed how events were cached to save space and actually do it properly. + * Changed a lot of the admin templates to make them more "Universal" in appearance. + & Added some extra help items to Help.language.php, and some minor additions in other language files. + * Simplified jump_to, although old stuff should still work fine. + * Quick moderation now remembers where you last moved the topic(s) to. + ! The upgrade script now saves Settings.php properly more of the time. + ! The upgrade script now redoes paths like './', etc. + ! Upgrade now skips updating the messages table if it doesn't need to be updated. + - Upgrade no longer converts attachments with a size but no name. + ! Most of the times ALTER IGNORE was being used in upgrade.php, it was unnecessary. (it only ignores duplicate uniques.) + ! You can now have no reserved names, without getting errors... or so I think. + ! Moved around some package management code to make it go smoother. + ! Optimized the calendar birthday query some more, because it was doing extra math before. + ! Reordered a few of the different actions in the administration sidebar. + * Spaced out the links on the administration sidebar more so they are easier to read. + * Added some more help links to administration templates and tweaked a few more things. + & Added latest_support, latest_packages, and latest_themes to $helptxt in the Help language files. + & Moved around a few strings in the index and Settings language files. + ! Clicking Install! on themes without doing anything no longer gives a "Hacker?" error message. + * The "Back" link after installing a theme now correctly reads "Back" ;), and it shows a sidebar. + ! If for some reason a topic has no messages, it should now cause less errors. + & Added one language entry ($txt[11]) to Admin.language.php. + & Several language entries added to Profile.language.php. + * Profile template changed quite a bit to make it more consistent. Notification and "Show Posts" sub templates changed considerably. + * Reformated the notification sub template of Profile so it looks nicer, and added a board notification listing. + & Redid all the language strings associated with the notification section and added a few. (Profile) + ! Deleting topic notifications no longer deletes all board notifications. + * The topic notification list now shows what board the topic is in, sub template changes are NOT compatible! + * Cleaned up the HTML in some templates that were using <b> inside catbg. (only decreases the usefulness of style.css!) + * Moved titlebg from td to tr in a few remaining places, cleaned up its <b>s too. + * Tweaked the Display templates so that the <on: date> line wouldn't have extra padding. + - Removed $user_info['is_moderator'] and $context['user']['is_moderator']. + + Added new permission moderate_board for all those extra things that make a moderator. + + Added permission edit_news. + + Added permission administrate_forum (merging repair_forum permission and admin-specific permissions). + + Added permissions profile_remove_own and profile_remove_any. + - Removed permissions profile_edit_own and profile_edit_any. + & Renamed profile_own_identity to profile_identity_own and profile_any_identity to profile_identity_any. + & Renamed profile_own_extra to profile_extra_own and profile_any_extra to profile_extra_any. + & Renamed profile_own_title to profile_title_own and profile_any_title to profile_title_any. + * Modified admin index to reflect the users' permissions. + ! Regrouped admin permissions. + ! Updated profile page to work with several combinations of permissions. + * Added an arrow in the registration center to indicate which screen is selected. + ! Removed some old group stuff from ModSettings.php, made it htmlspecialchars things. (I remember doing this before!) + ! After saving "Edit Forum Settings", you are no longer sent back to ?action=admin. + * "1 member awaiting approval" -> "There is one member awaiting approval." + & Added approve_thereis and approve_thereare to the index language files. + ! Renamed the is_banned() function is_not_banned(). + * Changed the formatting of comments because my editor is playing tricks on me. + ! Added some documentation to Register.php and ModSettings.php. + ! A typo in the installer was making installing on PHP 5 cause errors. + ! Fixed online image in memberlist not showing properly. + ! Upgrade now skips personal message recipients that don't exist. + ! Upgrade now skips recipients that don't exist. + + IMKillAll/IMKillAllQuery functions can now delete ALL messages (not just in certain folder). + & Added an extra language entry to InstantMessage.language.php, $txt[411]. + ! Upgrade now updates the basic statistics in the settings table. + * Really really long error URLs no longer distort the template. + ! Package browser now skips files that have no package-info.xml. + ! Packages were being looked for in /Packages/temp/, when they wouldn't be there ;). + ! The create-file action in package-info.xml should create an empty file. + ! The forum's version shouldn't include SMF in the package manager. + ! Repositioned some of the major functions so that they are sorted more logically. + ! Renamed the loadThemeUser() function to loadMemberContext() because the old name made no sense. + ! The loadMemberContext() function now returns true or false, depending on whether the data could be loaded. + ! Renamed the LoadUserID() function to loadMemberData() for the same reason as loadThemeUser(). + ! Moved the loadJumpTo() and getBoardParents() functions to Load.php from Subs.php. + & Added the $txt['theme_thumbnail_href'] and $txt['theme_description'] strings to the Settings language files. + + Themes are now shown by those instead of predefined things. + * Added $txt['theme_global_description'] to the Themes language files. + - Removed the description from the theme settings and database. + * Removed the interface for editing the theme description. + & Removed the $txt['actual_theme_description'] variable from the Settings language files. + & $txt['reminder_subject'] is now used for reminder email subjects. + ! The validation_code is only 10 characters in default installations. + ! Corrected counter for people using the forum default theme. + & Completely reformatted the "report to moderator" email so it looks nicer. (removing and adding things from index.) + ! Reporting posts no longer sends names with entities in them. + * You can install templates without 777 now, but only from a directory. (Themes) + ! Themes with slashes in their names weren't installing very well. + ! If you can moderate_forum, you can send messages to anyone. + & $txt['theme_user'] added for when only one person is using a theme (people are -> person is). + * Added check for amount of users using a theme to the themes template, so correct text is displayed. + & Added $helptext[17], containing help on theme settings. + * The language version checker now works properly. + & Fixed a minor typo in theme_edit_index in the Themes language files. + ! Guests can now access the help page even if they are restricted to only login, etc. + ! Cleaned up the news_readme.html file to reflect some of the changes made... + ! The addslashes and stripslashes recursive functions now affect keys as well for security. + ! Revised or added some documentation to Who.php, Profile.php, and QueryString.php. + ! Some PHP configurations weren't installing new themes from existing directories well. + * Fixed a checkbox in the Search template not using the check class. + ! A typo in Admin.php caused a strange error when clearing logs. + ! A missing global was causing "Try again" not to work in upgrade.php. + * Fixed a bug in Recent.template.php that caused an error when there weren't any recent replies. + * Changed the "Delete Account" sub template in Profile.template.php to be more appropriate. + & Added some missing language entries to the ManagePermissions language files. (non-english only.) + ! Fixed a typo in upgrade.php that was causing upgrade issues. + & Fixed a lot of typographical errors in various language files. (english or untranslated only.) + & Made some captitalization fixes to all language files... (even non-english ones, hope it's i18n.) + ! Added a "first_post" sort method to MessageIndex, although it's not used currently. + + Ban actions are now stored in the moderation log. + + Moderation log made slightly more useful as topics, members and board IDs are expanded to give their descriptions. + & Four new language additions added to Admin.language.php ('modlog_expand', 'modlog_retract', 'modlog_search' and 'modlog_by'). + * Moderation log template (Modlog.php) changed slightly. + ! Resolved some odd issues in the converters. + ! Upgrade now just says "SMF Upgrade Utility" to reduce confusion. + * Added Javascript function selectRadioByName(). + * The banning stuff now auto-selects the box you click on ;). + ! Upgrade now sets the calendar as not updated today, so it will be fixed ;). + * The index.php files in Themes/* were pointing to themselves instead of the one in Themes. + ! Missed an "empty delimiter" error in Post.php for the response_prefix. + & This may or may not be your profile :P? + ! Moved around a setting in "Edit Features and Options" to be more logical. + & Added a link to theme settings from the "Edit Features and Options" page. + ! The updateMemberData() function now attempts to keep certain values above -1 ;). + ! Recount totals now handles people with *no messages at all.* + ! SSI function showPoll now shows amount of votes per option when output_method is echo. + ! Avatar groups (directories) with underscores now get them converted to spaces. + ! The package manager now supports websites without titles ;). + ! parsePackageInfo() should now delete files from the correct place. + ! PackageOther() now uses parsePackageInfo(), and it works too! + & Used the &copy; entity although it isn't needed. + ! parsePackageInfo() now tries to clean up after restrictive permissions. + ! parsePackageInfo() checks if anything went wrong with $failure, but doesn't use it yet. + ! Fixed some very annoying errors about using ob_gzhandler twice... + ! Effectively renamed PackageOther() to PackageInstallTest(), but it's going to be used for more soon ;). + & Made a small change to package44... 'Installing *this* package will...'. + ! QuoteFast now quotes non-European, etc. text correctly. + * Packages now handle readmes better, parsePackageInfo makes a list of what wasn't done. + & Added some strings to the Packages language files for i18n reasons. + ! Fixed some annoying problems in upgrade.php's personal message upgrading. + ! Removed package avatar/language file sub action destinction... changed to install and install2. + ! Made some changes to upgrade.php to better support upgrading from heavily modded forum installs. + ! Added a parseBoardMod() function for board mod files, although I'd prefer xml style... not used yet. + * Linked username in registration center to profile. + ! Fixed 'set permission like this group' for group 0. + ! Fixed 'set permission like this group' where the source group has no permissions set. + ! 'set permission like this group' now also copies global board permission. + & Fixed some misplaced permission error strings in Errors. + ! Mangled around a few things so the package manager might parse mods, but I doubt it works well. + ! Updated xmlArray to current version; creates a new xmlArray of the correct class. + ! Added to_array() method for xmlArray, this easily fetches the contents of an element. + ! The xmlArray no longer stores empty strings when trim is on. + ! Exported XML no longer shows [] when it's a set of elements. + ! getPackageInfo now uses the to_array() method, which should be faster. + ! The server list now uses to_array() for the same above-mentioned reasons. + ! Links with : in them now auto-link, as long as it's not the last character. + * The Packages javascript now has the installed mods in it to avoid listing things twice. + * Made some changes to the package manager so it can actually install mods ;). (theory, and boardmod only!) + ! Some minor aesthetic changes to the installer and made it do some lycos stuff just because it can. + - Removed the "Skip This" option from the installer because it could cause problems. + ! Attachments, if "encrypt filenames" is off, now use a redirect if possible. + ! Installer, package installer, attachments, latest news, and dump database work in safe mode/lycos. (although dump database has an ad at the end!) + + Added theme option to save PMs to the outbox by default. + * Settings template now includes the theme option 'copy_to_outbox'. + & $txt['copy_to_outbox'] added to Profile.{lang}.php. + ! Messages from deleted members were not showing who sent them properly. + * Fixed some invalid xhtml in the package file listing. + & Aesthetic changes to some of the strings in Packages so they sound nicer. + * Migrated installation warning and no-upgrade warning to PackageInstallTest. Soon it shall replace Premod/Mod/Mod2/Mod3, etc. + & Added some new language strings in Packages.. $txt['package_installed_warning1'], $txt['package_installed_warning2'], and $txt['package_installed_warning3']. + ! Added the database connection link to all functions that need it; makes it a lot easier to use things like phpAdsNew with SMF. + ! If maintenance mode is on, SSI.php can now take $ssi_maintenance_off and dies with an error message. + ! It is now possible to reset members to use the board default theme. + & One language entry, $txt['theme_forum_default'], added to Themes.language.php. + + Report to moderator now sends emails out in correct language for the moderator. + & Several language entries moved from index.language.php to Post.language.php. + * Removed the mod_warning sub template from the Packages template file. + & Removed the PackageMod() function from Packages.php, along with its strings in Packages. + - Removed ability to use just a .sql file; now you should use a .php file with sql in it. + ! Started to remove some of the PackageMod3() function. + & Removed some old strings from the Packages language files. + & Corrected $txt['smf3'] so that it points to the correct theme settings. + * Moved enable_news to theme settings, although show_news is still filled. + & Phased out $enable_notification... mostly. Removed $txt[381] from Admin. + & Renamed "Edit Forum Settings" to "Edit Server Settings" in the Admin language file. + ! Minor aesthetic change: the "(confirm)" is now in italics. + ! Fixed a unique index problem with split topics, should work in all cases... + ! The package manager now lists all the actions of a boardmod file and lists failures. + & Removed some strings from Packages and got rid of PackageMod3(). + & Moved $txt[51] from index to Packages. + * Now Packages must be installed/uninstalled after testing that they work. + & Removed some language entries that are no longer needed. + + Theory dictates that the package installer/uninstaller should work, as well as upgrading; xml format mods and more not done yet. + ! Fixed the duplicate key error on splitting a topic; renamed some variables for consistency. + * Fixed some minor things in Packages.php related to installing/uninstalling packages. Confirmed working :D. + & Added some language stuff to make installing and uninstalling packages say stuff properly. + ! constructPageIndex() now protects against people not properly passing stuff to it :P. + * Renamed 'cur_page' to 'current_page' in the Wireless template. + ! Clicking on a new sort column in view members should start you back at the beginning, not where you are; same in mod log. + ! Sort order of error log will now be remembered after removing entries. + ! Added a selection triangle to the memberlist item that is selected. + * Fixed a search memberlist template glitch. + ! Error messages should be shown with entities, but not elements. + & Moved rtm8 back from the Post files to Profile; it isn't used anywhere else. + * Now there's a link to click about why the secret answer field is blank. + & Added $txt['secret_why_blank'] to Profile, and $helptxt['secret_why_blank'] to Help. + & Modified the English version of $txt[596] to sound better. + & Added $txt['secret_desc'] to Profile to describe what exactly the secret question is. + * Added this to the Profile template. + & Tweaked $txt['smf243'] and $txt['smf244'] because they used Password not password :P. + ! Moved around a couple of settings in feature settings and options to make more logical. + ! Where ever there was a time in the context, added a timestamp. Makes it easier ;). + * The default catbg.gif file is now taller and even smaller ;) - and it looks better too. + ! Added some missing href's to contexts that had link's but no href's. + + Added "find user" option to Personal Messages. + * New template, find_users, added to InstantMessage.template.php. + & Four new language entries added to index.language.php. ($txt['find_*']) + ! Fixed Display.php's start=new not always going to the right page. + & Added $txt['find_close'], and redid some of the language work. + * The find members thing has been moved to Help for now, and does a lot more. + ! Now posting should insert a time one second in the future so as to fix start=new issues for sure. + ! Added the semicolon to the list of characters allowed before a smiley. + ! Fixed an xhtml typo in the Display template. + & Added $txt['find_wildcards'] to the index.language.php files. + * Added wildcard information to the find members sub template. + * Added find members to the ManageBoards template for moderators. + * findMembers() now respects the hideEmail settings. + ! Probably/possibly fixed the table bbc parsing bug. + ! BBC wasn't highlighting properly from the first character. (index = 0) + ! Fixed a very special-case for the attachments not working. + ! Changed $linkid to $db_connection in install.php to fix installation problems. + & Added the vote and modlog actions to the Who language files. + ! Moved (in the defaults) the >:D smiley before the :D smiley. + & Added Modifications language file for modifications to put strings in ;). (always loaded.) + * Added the [!] box to the bcc field as well. (InstantMessage) + * The javascript function reqWin() now takes three parameters, but the last two are optional. + * Find Members and Quote Fast links all changed to use the new reqWin() format. + * reqWin() now always returns false, changed "reqWin(this.href); return false;" to "return reqWin(this.href);". + * Changed on[event]="javascript: to just on[event]=" + ! Last character of an event can't/shouldn't be ','. + + +January 2004: +-------------------------------------------------------------------------------- + * Reoranized language strings for SendTopic to be cleaner. + & Indexes changed for SendTopic's language strings in index. + * Renamed all templates ".template.php". + & Languages renamed to languages. + * Moved images/tree/*.gif to images/icons/*.gif. + ! Added repair_paths.php tool for changing servers, might need changing. + ! Removed the posts parameter of determinePostGroup(). + & Removed unreasonable poll length limit, changed Post. + ! The "hide until expire" poll option worked backwards; fixed. + ! fixed typo; DROP KEY -> DROP INDEX in upgrade.php. + & Moved 'poll_range_error' to the Errors language files. + ! Changed version check to use .template.php. + * Made the version line not include .php. + * Renamed the main sub template in Admin/Help to popup. + * Renamed the main sub template in Admin/Errors to error_log. + - Removed Admin dir in Themes/{theme}/ and Themes/{theme}/languages. + * Moved /Admin templates to the main template dir. + * Renamed Admin/index.php template to Admin.php + * Merged templates Help.template.php and Admin/Help.template.php + & Moved /Admin language files to the main language dir. + & Renamed language files Admin/index.{language}.php to Admin.{language}.php + & Merged language files Help.{langugage}.php and Admin/Help.{langugage}.php + * Renamed the error sub template in Errors to fatal_error. + ! Removed version tagging from templates not updated to use better xhtml. + ! Cleaned up ManageBoards and upgrade.php's board ordering. + + Added version information to the settings table. + ! Fixed bug in reordering of children. + * Cleaned up SplitTopics template a little. + * No longer shows "0 Hidden" if there are no users currently hidden. + ! Now counts hidden users as a user in the online list. + * Tweaked news fader display to play nicer with border colors. + ! Fixed using breaks after/before bbc tags like url, iurl, flash, and img. + ! If a board has no posts in it, it isn't new. + ! ssi_checkPassword now checks whether the member is activated. + ! Made line breaks show in MySQL error messages. + ! Fixed bug that happened when two people accessed the page for a new day. + ! Usernames with <'s in them appeared wrong in the error log. + ! Fixed a typo in profile's error handling for additional options. + ! Upgrade shouldn't add any permissions. + + Upgrade now checks the version number of certain file(s). + ! Upgrader now works better with non-standard theme paths. (still buggy.) + ! Simplified check for moderator status. (fixes moderators not showing in children.) + + Upgrade now makes sure Settings.php has no trailing whitespace. + ! Removed some unnecessary (and imho annoying) whitespace. + ! Fixed a typo in ManageBoards that made modifying a board impossible. + ! Upgrade was screwing up people's genders. + & Changed the show_children string slightly in Settings. + + The profile now warns you before taking away admin priv's. + * Added check in Profile for self de-admin. + & Added confirmation string for self de-admin to Profile. + ! More documentation; Admin.php and Calendar.php. + ! Merging two topics a person had notification on failed, fixed. + ! Made some changes to reduce over-comma'ing knownThemes. + + Added barebones smiley installer. + ! Fixed bug in installer that made it use the wrong path for Settings.php. + + Changed ssi_recentPosts and ssi_recentTopics so you can exclude certain boards. + ! Child boards in MessageIndex were not being marked as having new posts. + + Added ssi_ban option to SSI.php so it doesn't always ban. + * Made some minor html tweaks. + ! Fixed some vestages from when personal messages were called instant. + ! Made upgrade remove even MORE evil deletedBy keys... + * Changed the URL to spellcheck.js and fader.js to the default theme. + * Everywhere that points to script.js now uses the default URL. + ! Post groups were not working correctly. + ! Unable to load language file error message tweaked to show filename. + ! Fixed bug in SSI.php when showing hidden users. + ! Fixed a bug in banPermissions() that screwed up stuff if banning wasn't run. + ! Fixed bug that prevented proper member search within additional membergroups. + + Split table instant_messages into instant_messages and im_recipients + & Added language tags to the InstantMessage language file. + + Added a send report to PM posting if one or more of the recipients failed. + * Added an option to the PM post screen to store a copy of the message in the outbox. + ! Fixed a couple bugs in upgrade.php... (ims, Settings.php.) + ! Added some more documentation. + ! Changed IMPostError() to use less parameters. + ! Minor tweaks to package manager. (not done yet!) + ! Replaced all calls to getimagesize() with url_image_size() which times out. + ! Added an index.php to the theme directories to protect their contents. + ! Updated theme file copying as index.php is now index.template.php. + + Added user statistics panel to profile. May need some more stuff to be add/removed. + & Added language tags for user statistics to Profile language files. + * Added template function for user statistics to Profile. + ! Updated converters to work with new instant message tables. + ! Put $emailpassword, $emailnewpass, $emailwelcome and $allow_hide_email into $modSettings. + * Simplified statPanel sub template in Profile. + & Added $txt['statPanel_show'] to Profile. + ! Send messages to members wasn't loading Subs-Post.php. + ! Moved required version for buffer manipulation up to 4.3.0... ugh. + & Removed the Notify language files and migrated stuff from it to the index language files. + * Moved stuff around in the Profile and Settings templates. + ! Added label elements to the ModSettings ('Edit Features and Options') page. + & Took some ?s off of setting descriptions. + ! Moved buttons as images settings from admin to theme settings. + * Removed use_image_buttons part of index.template.php. (although keeping it there doesn't hurt.) + ! Updated some queries to fit slightly different guidelines. + ! Fixed a few mistakes in the statPanel stuff. + + Created a "default forum theme" for members who want to follow the forum default. + * Added checkbox to "parse" html in send member email. (ManageMembers) + & Added a language string $txt['email_parsed_html'] to Admin. + * Made the submit/preview buttons in InstantMessage the same order as in Post. + ! The link was wrong when quoting a message. + & Made a copy of $txt[130] in Post.*.php to fix notification errors. + + Added registration center; includes admin approval, reviewing outstanding activations and admin register member. + & Added language entries to Admin, Help, Login and index language files for registration. + * Registration template added to considerably, index template changed. (added # of unactivated.) + ! Added help text to email type modSettings. + ! Moved $guestaccess, $timeformatstring, $showbbcbutt, $enable_ubbc, $MaxMessLen and $MaxSigLen to $modSettings and theme settings. + & Language change on "allow guest access" to make it work non-inverted. + ! Bug fixed in theme settings where "show images" wasn't remembering what it was before switching. + * Fixed agreement so it warns you if it's not writable. + * Fixed a bug in Profile that didn't check mistakes correctly. + * Personal messages weren't saving when the box was checked. + ! Personal messsages now handle bad starts better. + ! Changed the case of DeleteMembers() to deleteMembers(). + ! The updateLastMessage() query no longer uses filesort and temporary. (shudder!!) + ! Renamed "updateLastMessage()" to "updateLastMessages()" and made it take an array. + ! Made the debugging stuff not allow you to click on anything not explainable. + ! Optimized updateStats('member'), updateStats('postgroups') (a lot), and updateSettings(). + ! Fixed a notice if you were at ?action=post. (calendar?) + ! Added a kludge for people who have removed all post groups. (which should not be done.) + + Notifications now tell you WHO posted the reply. (replies only!) + & Changed $txt[128] so it can have the poster's name in it. + ! Deleting all attachments caused problems. + ! Editing the "Newbie" group made it no longer a post group. + & No reason to log $txt['smf270']... + ! You couldn't post events to the calendar unless you were an administrator. + ! Moved some of the main forum settings around. + ! Cleaned up OptimizeTables() a little bit. + ! Added more logs to the "empty out unimportant logs" function. + ! Made some notes on queries that are slow. + ! Changed spamProtection() to only take a type. + ! Added a new key to messages that cleans a few things up some. + ! Optimized a minor query in the statistics. + ! Optimized some queries in Profile. + ! Fixed some minor issues with coding guidelines in queries. + ! Deleting a single post to the recycle bin didn't add to the counts. + ! A space at the beginning of a line didn't show. + ! Optimized some of the personal message queries. + * Who's Online IP links now open in a new window. + ! The updateSettings() function caused errors on *new* settings. + ! The "member doesn't exist" error can probably be ignored. + * Cleaned up the InstantMessage template a bit, you can now forward deleted members' PMs. + + Added option to choose membergroup in "register new member" function. + * Added said option to the Register template. + & Added three new entries to Login language files for this option. + ! Made loadTheme() do more of the theme selection. + ! Reordered the data loading order so boards are above the theme. + + Added a board by board selectable theme setting. (overridable) + ! Added ID_THEME column to the boards table, made the INSERT only specify certain columns in the sql file. + * ManageBoards can now set the theme for a board. + & Added $txt['mboards_theme'], $txt['mboards_theme_desc'], and $txt['mboards_theme_default']. + ! The loadUserSettings() function was setting the user's theme wrong... + ! The spellcheck popup had a trailing single quote. + ! Fixed a typo in the default previous/next links. + * Cleaned up Display template a lot. (not done; no mandatory changes.) + ! The deleteMembers() function now affects the log_online table. + ! Some dates for Winter Solstices were wrong. + ! Profile statistics should now respect time offsets. + & Spelling, 'stastics' should have read 'statistics' in Profile. + ! Making a mistake in editing someone's profile would show your username. + & The markasread action was missing from Who's Online. + ! Tweaked some of the search queries a bit, tweaked relevance. + * Fixed some minor issues with the Search template. + ! Typo in dump database made compression not work well. + + News.php is now dead. News moved to SSI. News accessed through boardNews() in SSI.php. + ! You can now pass length to boardNews() which limits characters per news item. + ! news_template.php removed in favor of SSI.php's templating. + ! news_readme.html updated to reflect new things... + ! Added session rewriting to SSI.php... + * The "Save to outbox" checkbox in InstantMessage now uses a label. + * "hideemail" in the Register template changed to "hideEmail". + + Register can now hold as much of profile as the theme wants, needs more work. + * Post's Additional Options drop down now switches between +/- alt. + ! ID_MSG resort tool now does things better. + ! Changed the activation center's default sort to dateRegistered. + * The activation center now has a "check all" box, fixed a javascript error. + & Fixed some grammar in Login. + ! After approval, the registration center goes back to the page you were on. + ! Put warning about missing copyright back in and made it work properly. + * Help now uses a different way of showing the copyright. + & Added $txt['help_copyright'] to Help language files. + & Removed $txt['smf_news_error1'] for SSI from Stats language files. + * Cleaned up ManageMembers template a very little bit. + ! Errors should now show breaks properly, or so it seems. + ! Now you can ban members with the IP address 'unknown'. + ! RSS feeds now max out at 255 limit for performance reasons; you really never need more. + ! The recent posts and news feeds now allow you to specify boards. + ! The author element in RSS feeds now use the name instead of link. + ! Oops, the guid in RSS feeds should be a URL. + ! The RSS feeds now use entities more often. + * Fixed some unimportant typos and spacing. + * Statistics should now should in YYYY/MM/DD format instead of DD/MM/YYYY. + & Changed $txt['smf_stats_6'] to reflect this new format. + * Fixed some spacing issues in spellcheck.js. + & Added package_installed_key, package_installed_current, and package_installed_old to Packages. + ! ModSettings no longer does groups, it was badly done and unused anyway. + ! Fixed an upgrade BBC bug. + ! Removed package creation function from the package manager. + & Removed the now unused language entries from Packages. + ! Fixed a bug in the template converter. + ! More documentation in some sources.... + ! Turning off polls should work better now. + & Added Wireless language files.... should be more internationalized now. + ! If you weren't directly an admin, changing your profile would cause a warning about removing your admin status. + ! Minor typo in search caused a regular expression error. + ! SendTopic message looks a bit different. + ! Added repair.php, hope it one day is worth something. + ! The template converter is now more or less working properly. + ! The updateLastMessages() function wasn't setting empty boards correctly. + ! Deleting a member now sets all their messages to be from a guest/deleted member. + ! Fixed a typo in the dutch $txt['spellcheck_done']. + ! Printing a topic with images did not go well. + ! Topic reply notifications didn't handle entities well. + ! Attachments with a size of -1 weren't handled well in Post. + ! New would sometimes go to the last post of the previous page... + ! Empty boards still made the category they were in mark as new. + ! Fixed a dumb typo that was causing double posting. + ! Tried to better some queries. + ! If pspell can't be loaded, the spellchecker gives up. + ! Added the "b" element to the allowed ones in error logging. + ! 10 most recent posts is now considerably faster on even smaller forums. + ! Added some new, but yet unused, information to topic viewing. + ! Added a "try again" link to errors in upgrade.php. + + Topic moving now remembers the last board you moved to. + * The MoveTopic template now selects it, not mandatory but recommended. + ! Upgrade was giving some post groups moderator permissions. + ! Permissions wizard thought it could affect administrators. + ! Password reset now works properly again; email looks better. + & Fixed some typos in ModSettings and Themes. + ! Logging in as an "additional" administrator now sets the admin_time. + ! Added ability to reverse sort list in error log. + & One text entry added to admin.language.php. + ! Added boardNews function to ssi_examples. + + Can now sort memberlist by date registered (ID_MEMBER). + & Removed a "click here" message from the Login language file. (English only.) + ! Moving a topic now moves over its mark_read data, in theory. + ! Personal Message notifications weren't being sent in the right language. + ! It now checks that the themes directory is writable before allowing you to install/make new theme. + + You can now add themes by directory, from anywhere ;). + ! There, register plugs people into groups right away. + ! SSI.php now uses topicseen instead of boardseen. + ! Added tabindexes to the manage categories page. + ! The spamProtection() function now only limits posting to the specified time. + ! The installer now specifically disallows "." as the install directory. + ! Made quoteheader/codeheader more themable. + ! Added a bunch of stuff to the recent/unread context variables. + * Added board information to unread/unreadreplies. + & Added an error message for if the attachments directory was read-only. + ! The url_image_size() function now handles empty URLs better. + ! Fixed some remaining issues with theme installation. + ! Made it so theme gzips do NOT contain the directory, rather their name is used. + ! Return to page after attachment deletion.. + + The theme copy routine is now capable of making a theme with a specific name. + + Themes from a URL are now checked to be on simplemachines.org. + & An error message is now shown if you try to install a theme and Themes is not writable. + ! Smileys should now be extracted like Themes, not just to the root of /Smileys. + ! Mark as read - no board specified or invalid board, send to board index. + ! Wireless mode doesn't require a copyright link. + ! The "Copyright removed" error is not also logged to get attention ;). + ! No need to log the "topic locked" error. + * Cleaned up default "addpoll.gif" images.. + * Optimized a whole junkload of images a lot. (decrease of about 13kb total...) + ! More documentation and stuff, mostly in Security and QueryString. + ! Fixed some more upgrade issues with instant messages. + ! Defined the actual_ URLs and paths in a different place so they'd get loaded earlier. + + repair_ID_IM.php added. Use it to repair the instant message tables. + + +Legend: +-------------------------------------------------------------------------------- + ! Minor change or bugfix. (don't bother to log typos except between releases.) + * Change like above, but affects templates. + & Change that affects a language file. (make two if it affects templates too.) + + Feature addition or improvement. + - Feature or option removal. \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/index.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,353 @@ +<?php + +/** + * Simple Machines Forum (SMF) + * + * @package SMF + * @author Simple Machines http://www.simplemachines.org + * @copyright 2011 Simple Machines + * @license http://www.simplemachines.org/about/smf/license.php BSD + * + * @version 2.0.4 + */ + +/* This, as you have probably guessed, is the crux on which SMF functions. + Everything should start here, so all the setup and security is done + properly. The most interesting part of this file is the action array in + the smf_main() function. It is formatted as so: + + 'action-in-url' => array('Source-File.php', 'FunctionToCall'), + + Then, you can access the FunctionToCall() function from Source-File.php + with the URL index.php?action=action-in-url. Relatively simple, no? +*/ + +$forum_version = 'SMF 2.0.4'; + +// Get everything started up... +define('SMF', 1); +if (function_exists('set_magic_quotes_runtime')) + @set_magic_quotes_runtime(0); +error_reporting(defined('E_STRICT') ? E_ALL | E_STRICT : E_ALL); +$time_start = microtime(); + +// This makes it so headers can be sent! +ob_start(); + +// Do some cleaning, just in case. +foreach (array('db_character_set', 'cachedir') as $variable) + if (isset($GLOBALS[$variable])) + unset($GLOBALS[$variable], $GLOBALS[$variable]); + +// Load the settings... +require_once(dirname(__FILE__) . '/Settings.php'); + +// Make absolutely sure the cache directory is defined. +if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache')) + $cachedir = $boarddir . '/cache'; + +// And important includes. +require_once($sourcedir . '/QueryString.php'); +require_once($sourcedir . '/Subs.php'); +require_once($sourcedir . '/Errors.php'); +require_once($sourcedir . '/Load.php'); +require_once($sourcedir . '/Security.php'); + +// Using an pre-PHP 5.1 version? +if (@version_compare(PHP_VERSION, '5.1') == -1) + require_once($sourcedir . '/Subs-Compat.php'); + +// If $maintenance is set specifically to 2, then we're upgrading or something. +if (!empty($maintenance) && $maintenance == 2) + db_fatal_error(); + +// Create a variable to store some SMF specific functions in. +$smcFunc = array(); + +// Initate the database connection and define some database functions to use. +loadDatabase(); + +// Load the settings from the settings table, and perform operations like optimizing. +reloadSettings(); +// Clean the request variables, add slashes, etc. +cleanRequest(); +$context = array(); + +// Seed the random generator. +if (empty($modSettings['rand_seed']) || mt_rand(1, 250) == 69) + smf_seed_generator(); + +// Before we get carried away, are we doing a scheduled task? If so save CPU cycles by jumping out! +if (isset($_GET['scheduled'])) +{ + require_once($sourcedir . '/ScheduledTasks.php'); + AutoTask(); +} + +// Check if compressed output is enabled, supported, and not already being done. +if (!empty($modSettings['enableCompressedOutput']) && !headers_sent()) +{ + // If zlib is being used, turn off output compression. + if (@ini_get('zlib.output_compression') == '1' || @ini_get('output_handler') == 'ob_gzhandler' || @version_compare(PHP_VERSION, '4.2.0') == -1) + $modSettings['enableCompressedOutput'] = '0'; + else + { + ob_end_clean(); + ob_start('ob_gzhandler'); + } +} + +// Register an error handler. +set_error_handler('error_handler'); + +// Start the session. (assuming it hasn't already been.) +loadSession(); + +// Determine if this is using WAP, WAP2, or imode. Technically, we should check that wap comes before application/xhtml or text/html, but this doesn't work in practice as much as it should. +if (isset($_REQUEST['wap']) || isset($_REQUEST['wap2']) || isset($_REQUEST['imode'])) + unset($_SESSION['nowap']); +elseif (isset($_REQUEST['nowap'])) + $_SESSION['nowap'] = true; +elseif (!isset($_SESSION['nowap'])) +{ + if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/vnd.wap.xhtml+xml') !== false) + $_REQUEST['wap2'] = 1; + elseif (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'text/vnd.wap.wml') !== false) + { + if (strpos($_SERVER['HTTP_USER_AGENT'], 'DoCoMo/') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'portalmmm/') !== false) + $_REQUEST['imode'] = 1; + else + $_REQUEST['wap'] = 1; + } +} + +if (!defined('WIRELESS')) + define('WIRELESS', isset($_REQUEST['wap']) || isset($_REQUEST['wap2']) || isset($_REQUEST['imode'])); + +// Some settings and headers are different for wireless protocols. +if (WIRELESS) +{ + define('WIRELESS_PROTOCOL', isset($_REQUEST['wap']) ? 'wap' : (isset($_REQUEST['wap2']) ? 'wap2' : (isset($_REQUEST['imode']) ? 'imode' : ''))); + + // Some cellphones can't handle output compression... + $modSettings['enableCompressedOutput'] = '0'; + // !!! Do we want these hard coded? + $modSettings['defaultMaxMessages'] = 5; + $modSettings['defaultMaxTopics'] = 9; + + // Wireless protocol header. + if (WIRELESS_PROTOCOL == 'wap') + header('Content-Type: text/vnd.wap.wml'); +} + +// Restore post data if we are revalidating OpenID. +if (isset($_GET['openid_restore_post']) && !empty($_SESSION['openid']['saved_data'][$_GET['openid_restore_post']]['post']) && empty($_POST)) +{ + $_POST = $_SESSION['openid']['saved_data'][$_GET['openid_restore_post']]['post']; + unset($_SESSION['openid']['saved_data'][$_GET['openid_restore_post']]); +} + +// What function shall we execute? (done like this for memory's sake.) +call_user_func(smf_main()); + +// Call obExit specially; we're coming from the main area ;). +obExit(null, null, true); + +// The main controlling function. +function smf_main() +{ + global $modSettings, $settings, $user_info, $board, $topic, $board_info, $maintenance, $sourcedir; + + // Special case: session keep-alive, output a transparent pixel. + if (isset($_GET['action']) && $_GET['action'] == 'keepalive') + { + header('Content-Type: image/gif'); + die("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B"); + } + + // Load the user's cookie (or set as guest) and load their settings. + loadUserSettings(); + + // Load the current board's information. + loadBoard(); + + // Load the current user's permissions. + loadPermissions(); + + // Attachments don't require the entire theme to be loaded. + if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'dlattach' && (!empty($modSettings['allow_guestAccess']) && $user_info['is_guest'])) + detectBrowser(); + // Load the current theme. (note that ?theme=1 will also work, may be used for guest theming.) + else + loadTheme(); + + // Check if the user should be disallowed access. + is_not_banned(); + + // If we are in a topic and don't have permission to approve it then duck out now. + if (!empty($topic) && empty($board_info['cur_topic_approved']) && !allowedTo('approve_posts') && ($user_info['id'] != $board_info['cur_topic_starter'] || $user_info['is_guest'])) + fatal_lang_error('not_a_topic', false); + + // Do some logging, unless this is an attachment, avatar, toggle of editor buttons, theme option, XML feed etc. + if (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], array('dlattach', 'findmember', 'jseditor', 'jsoption', 'requestmembers', 'smstats', '.xml', 'xmlhttp', 'verificationcode', 'viewquery', 'viewsmfile'))) + { + // Log this user as online. + writeLog(); + + // Track forum statistics and hits...? + if (!empty($modSettings['hitStats'])) + trackStats(array('hits' => '+')); + } + + // Is the forum in maintenance mode? (doesn't apply to administrators.) + if (!empty($maintenance) && !allowedTo('admin_forum')) + { + // You can only login.... otherwise, you're getting the "maintenance mode" display. + if (isset($_REQUEST['action']) && ($_REQUEST['action'] == 'login2' || $_REQUEST['action'] == 'logout')) + { + require_once($sourcedir . '/LogInOut.php'); + return $_REQUEST['action'] == 'login2' ? 'Login2' : 'Logout'; + } + // Don't even try it, sonny. + else + { + require_once($sourcedir . '/Subs-Auth.php'); + return 'InMaintenance'; + } + } + // If guest access is off, a guest can only do one of the very few following actions. + elseif (empty($modSettings['allow_guestAccess']) && $user_info['is_guest'] && (!isset($_REQUEST['action']) || !in_array($_REQUEST['action'], array('coppa', 'login', 'login2', 'register', 'register2', 'reminder', 'activate', 'help', 'smstats', 'mailq', 'verificationcode', 'openidreturn')))) + { + require_once($sourcedir . '/Subs-Auth.php'); + return 'KickGuest'; + } + elseif (empty($_REQUEST['action'])) + { + // Action and board are both empty... BoardIndex! + if (empty($board) && empty($topic)) + { + require_once($sourcedir . '/BoardIndex.php'); + return 'BoardIndex'; + } + // Topic is empty, and action is empty.... MessageIndex! + elseif (empty($topic)) + { + require_once($sourcedir . '/MessageIndex.php'); + return 'MessageIndex'; + } + // Board is not empty... topic is not empty... action is empty.. Display! + else + { + require_once($sourcedir . '/Display.php'); + return 'Display'; + } + } + + // Here's the monstrous $_REQUEST['action'] array - $_REQUEST['action'] => array($file, $function). + $actionArray = array( + 'activate' => array('Register.php', 'Activate'), + 'admin' => array('Admin.php', 'AdminMain'), + 'announce' => array('Post.php', 'AnnounceTopic'), + 'attachapprove' => array('ManageAttachments.php', 'ApproveAttach'), + 'buddy' => array('Subs-Members.php', 'BuddyListToggle'), + 'calendar' => array('Calendar.php', 'CalendarMain'), + 'clock' => array('Calendar.php', 'clock'), + 'collapse' => array('BoardIndex.php', 'CollapseCategory'), + 'coppa' => array('Register.php', 'CoppaForm'), + 'credits' => array('Who.php', 'Credits'), + 'deletemsg' => array('RemoveTopic.php', 'DeleteMessage'), + 'display' => array('Display.php', 'Display'), + 'dlattach' => array('Display.php', 'Download'), + 'editpoll' => array('Poll.php', 'EditPoll'), + 'editpoll2' => array('Poll.php', 'EditPoll2'), + 'emailuser' => array('SendTopic.php', 'EmailUser'), + 'findmember' => array('Subs-Auth.php', 'JSMembers'), + 'groups' => array('Groups.php', 'Groups'), + 'help' => array('Help.php', 'ShowHelp'), + 'helpadmin' => array('Help.php', 'ShowAdminHelp'), + 'im' => array('PersonalMessage.php', 'MessageMain'), + 'jseditor' => array('Subs-Editor.php', 'EditorMain'), + 'jsmodify' => array('Post.php', 'JavaScriptModify'), + 'jsoption' => array('Themes.php', 'SetJavaScript'), + 'lock' => array('LockTopic.php', 'LockTopic'), + 'lockvoting' => array('Poll.php', 'LockVoting'), + 'login' => array('LogInOut.php', 'Login'), + 'login2' => array('LogInOut.php', 'Login2'), + 'logout' => array('LogInOut.php', 'Logout'), + 'markasread' => array('Subs-Boards.php', 'MarkRead'), + 'mergetopics' => array('SplitTopics.php', 'MergeTopics'), + 'mlist' => array('Memberlist.php', 'Memberlist'), + 'moderate' => array('ModerationCenter.php', 'ModerationMain'), + 'modifycat' => array('ManageBoards.php', 'ModifyCat'), + 'modifykarma' => array('Karma.php', 'ModifyKarma'), + 'movetopic' => array('MoveTopic.php', 'MoveTopic'), + 'movetopic2' => array('MoveTopic.php', 'MoveTopic2'), + 'notify' => array('Notify.php', 'Notify'), + 'notifyboard' => array('Notify.php', 'BoardNotify'), + 'openidreturn' => array('Subs-OpenID.php', 'smf_openID_return'), + 'pm' => array('PersonalMessage.php', 'MessageMain'), + 'post' => array('Post.php', 'Post'), + 'post2' => array('Post.php', 'Post2'), + 'printpage' => array('Printpage.php', 'PrintTopic'), + 'profile' => array('Profile.php', 'ModifyProfile'), + 'quotefast' => array('Post.php', 'QuoteFast'), + 'quickmod' => array('MessageIndex.php', 'QuickModeration'), + 'quickmod2' => array('Display.php', 'QuickInTopicModeration'), + 'recent' => array('Recent.php', 'RecentPosts'), + 'register' => array('Register.php', 'Register'), + 'register2' => array('Register.php', 'Register2'), + 'reminder' => array('Reminder.php', 'RemindMe'), + 'removepoll' => array('Poll.php', 'RemovePoll'), + 'removetopic2' => array('RemoveTopic.php', 'RemoveTopic2'), + 'reporttm' => array('SendTopic.php', 'ReportToModerator'), + 'requestmembers' => array('Subs-Auth.php', 'RequestMembers'), + 'restoretopic' => array('RemoveTopic.php', 'RestoreTopic'), + 'search' => array('Search.php', 'PlushSearch1'), + 'search2' => array('Search.php', 'PlushSearch2'), + 'sendtopic' => array('SendTopic.php', 'EmailUser'), + 'smstats' => array('Stats.php', 'SMStats'), + 'suggest' => array('Subs-Editor.php', 'AutoSuggestHandler'), + 'spellcheck' => array('Subs-Post.php', 'SpellCheck'), + 'splittopics' => array('SplitTopics.php', 'SplitTopics'), + 'stats' => array('Stats.php', 'DisplayStats'), + 'sticky' => array('LockTopic.php', 'Sticky'), + 'theme' => array('Themes.php', 'ThemesMain'), + 'trackip' => array('Profile-View.php', 'trackIP'), + 'about:mozilla' => array('Karma.php', 'BookOfUnknown'), + 'about:unknown' => array('Karma.php', 'BookOfUnknown'), + 'unread' => array('Recent.php', 'UnreadTopics'), + 'unreadreplies' => array('Recent.php', 'UnreadTopics'), + 'verificationcode' => array('Register.php', 'VerificationCode'), + 'viewprofile' => array('Profile.php', 'ModifyProfile'), + 'vote' => array('Poll.php', 'Vote'), + 'viewquery' => array('ViewQuery.php', 'ViewQuery'), + 'viewsmfile' => array('Admin.php', 'DisplayAdminFile'), + 'who' => array('Who.php', 'Who'), + '.xml' => array('News.php', 'ShowXmlFeed'), + 'xmlhttp' => array('Xml.php', 'XMLhttpMain'), + ); + + // Allow modifying $actionArray easily. + call_integration_hook('integrate_actions', array(&$actionArray)); + + // Get the function and file to include - if it's not there, do the board index. + if (!isset($_REQUEST['action']) || !isset($actionArray[$_REQUEST['action']])) + { + // Catch the action with the theme? + if (!empty($settings['catch_action'])) + { + require_once($sourcedir . '/Themes.php'); + return 'WrapAction'; + } + + // Fall through to the board index then... + require_once($sourcedir . '/BoardIndex.php'); + return 'BoardIndex'; + } + + // Otherwise, it was set - so let's go to that action. + require_once($sourcedir . '/' . $actionArray[$_REQUEST['action']][0]); + return $actionArray[$_REQUEST['action']][1]; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/license.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/license.txt Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,27 @@ +Copyright © 2011 Simple Machines. All rights reserved. + +Developed by: Simple Machines Forum Project + Simple Machines + http://www.simplemachines.org + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + 3. Neither the names of Simple Machines Forum, Simple Machines, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior written + permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +WITH THE SOFTWARE. + +This license may be viewed online at http://www.simplemachines.org/about/smf/license.php \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/news_readme.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/news_readme.html Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,60 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html> + <head> + <title>News Scripting for SMF 2.0/title> + <style type="text/css"> + body + { + background-color: white; + color: black; + font: small Verdana, sans-serif; + } + h1 + { + font-size: 18pt; + margin: 0; + } + li + { + padding-bottom: 2ex; + } + </style> + </head> + <body> + <h1>News Scripting for SMF 2.0</h1> + <ol> + <li> + Setup the include statement on the page you wish your news to appear on according to the type of file you will be using (either PHP or SHTML.) This example will use PHP. + </li> + <li> + Start with the include to point to SSI.php in your root SMF directory.<br /> + + <em>e.g. &lt;?php include('<a href="http://www.simplemachines.org/community/SSI.php">http://www.simplemachines.org/community/SSI.php</a>'); ?&gt;</em> + </li> + <li> + Add ?ssi_function=boardNews;board=<em>n</em> to the end of the URL above. Make sure you replace the <em>n</em> with the number of the board you want news to appear from.<br /> + + <em>e.g. &lt;?php echo file_get_contents('<a href="http://www.simplemachines.org/community/SSI.php?ssi_function=boardNews;board=9">http://www.simplemachines.org/community/SSI.php?ssi_function=boardNews;board=9</a>'); ?&gt;</em> + </li> + </ol> + + That is all you need to do to get your basic script running. You can add any of the following to the end of that, but make sure you put a semicolon (<strong>;</strong>) between each variable. These variables aren't needed, but provide better versatility.<br /> + + <dl> + <dt>limit=<em>n</em></dt> + <dd>Limits the amount of news items to show (the default is <em>5</em>.)</dd> + + <dt>length=<em>n</em></dt> + <dd>Limits the length of each post to <em>n</em> characters (the default is to impose no limit.)</dd> + + <dt>start=<em>n</em></dt> + <dd>Starts the news items at a certain offset. (the default is <em>0</em>)</dd> + </dl> + + <em>e.g. &lt;?php echo file_get_contents('<a href="http://www.simplemachines.org/community/SSI.php?ssi_function=boardNews;board=9;limit=7">http://www.simplemachines.org/community/SSI.php?ssi_function=boardNews;board=9;limit=7</a>'); ?&gt;</em><br /> + <br /> + This shows an example of how one might change the limit on the news items. + <br /><br /> + Copyright &copy;2013 <a href="http://www.simplemachines.org/about/license.php" title="License" target="_blank">Simple Machines</a>.<br /> + </body> +</html> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/readme.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/readme.html Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,255 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>SMF 2.0 Upgrade Guide</title> + <link rel="stylesheet" type="text/css" href="Themes/default/css/index.css?fin20" /> + <style type="text/css"> + #upper_section .user + { + height: 4em; + } + #upper_section .news + { + height: 80px; + } + #main_screen + { + padding: 0 40px; + } + #main_screen h2 + { + font-size: 2em; + border-bottom: solid 1px #d05800; + line-height: 2em; + margin: 0 0 0.5em 0; + color: #d05800; + } + #content_section + { + position: relative; + top: -20px; + } + #main_content_section .panel + { + padding: 1em 2em 1em 1em; + line-height: 1.6em; + } + #main_content_section h2 + { + font-size: 1.5em; + border-bottom: solid 1px #d05800; + line-height: 1.5em; + margin: 0 2em 0.5em 0; + color: #d05800; + } + #main_content_section li + { + line-height: 1.6em; + font-weight: bold; + } + #main_content_section li li + { + font-weight: normal; + line-height: 1.6em; + } + #liftup + { + position: relative; + top: -70px; + } + #footer_section + { + position: relative; + top: -20px; + } + #footer_section + { + position: relative; + top: -20px; + } + tt + { + font-family: verdana, sans-serif; + letter-spacing: 1px; + font-weight: bold; + font-size: 90%; + font-style: italic; + } + dt { + font-weight: bold; + margin-bottom: .1em; + } + dl { + margin-top: .2em; + margin-left: 2em; + margin-bottom: .5em; + } + dd { + line-height: 1.5em; + margin-left: 2em; + margin-bottom: .1em; + } + </style> + </head> + <body> + <div id="header"><div class="frame"> + <div id="top_section"> + <h1 class="forumtitle">SMF 2.0 Upgrade Guide</h1> + <img id="smflogo" src="Themes/default/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum" /> + </div> + <div id="upper_section" class="middletext" style="overflow: hidden;"> + <div class="user"></div> + <div class="news normaltext"> + </div> + </div> + </div></div> + <div id="content_section"><div class="frame"> + <div id="main_content_section"> + <div id="liftup"> + <div class="panel"> + <h2 id="contents">Upgrading your forum</h2> + <p>Thank you for deciding to upgrade to <a href="http://www.simplemachines.org/smf/">SMF</a>. Before you get started, please remember that there is <a href="http://www.simplemachines.org/community/index.php">a place for help at www.simplemachines.org</a> if you run into any problems at all.</p> + <p>You can find the following information in this file:</p> + <ul style="list-style-type: none;"> + <li>&raquo; <a href="#requirements">Minimum installation requirements</a></li> + <li>&raquo; <a href="#backups">Backing up data</a></li> + <li>&raquo; <a href="#uploadingftp">Upload files method 1: using FTP</a></li> + <li>&raquo; <a href="#Set_File_Permissions">Set file permissions</a></li> + <li>&raquo; <a href="#Run_the_upgrading_tool">Run the upgrading tool</a></li> + <li>&raquo; <a href="#webinstall">Upload files method 2: using webinstall.php</a></li> + <li>&raquo; <a href="#finishing">Finishing the upgrade and cleaning up</a></li> + </ul> + </div> + <div class="panel"> + <h2 id="requirements">Minimum installation requirements</h2> + <p>Your server must meet a few requirements to be able to run SMF. If you are unsure as to whether your webserver meets these, please try to upgrade anyway - it should detect any problems.</p> + <ul> + <li>Any webserver that properly supports PHP, such as <a href="http://httpd.apache.org/" target="_blank">Apache</a> or <a href="http://www.microsoft.com/iis" target="_blank">Internet Information Services (IIS)</a>.</li> + <li> + <a href="http://www.php.net" target="_blank">PHP</a> 4.1.0 or higher. The following directives are required to be set correctly in php.ini: + <ul> + <li>the engine directive must be On.</li> + <li>the <a href="http://www.php.net/sybase#ini.magic-quotes-sybase" target="_blank">magic_quotes_sybase</a> directive must be set to Off.</li> + <li>the <a href="http://www.php.net/session#ini.session.save-path" target="_blank">session.save_path</a> directive must be set to a valid directory.</li> + <li>the <a href="http://www.php.net/manual/en/ini.core.php#ini.file-uploads" target="_blank">file_uploads</a> directive must be On.</li> + <li>the <a href="http://www.php.net/manual/en/ini.core.php#ini.upload-tmp-dir" target="_blank">upload_tmp_dir</a> must be set to a valid directory.</li> + </ul> + </li> + <li>Any of the following database systems + <ul> + <li><a href="http://www.mysql.com/" target="_blank">MySQL</a> 4.0.18 or higher.</li> + <li><a href="http://www.postgresql.org/" target="_blank">PostgreSQL</a> 8.0 or higher.</li> + <li><a href="http://www.sqlite.org/" target="_blank">SQLite</a> 1.x - 2.x.</li> + </ul> + </li> + <li>at least 2 megabytes of storage space in the database, although more is highly recommended.</li> + <li>The database user must have at least the following privileges: SELECT, INSERT, UPDATE, DELETE, ALTER, and INDEX.</li> + <li>about 20 megabytes of storage space on the web server, although more is recommended.</li> + </ul> + <p>Recommendations for best performance:</p> + <ul> + <li>Windows, <a href="http://www.linux.org/" target="_blank">Linux</a> or another Unix based operating system.</li> + <li>the <a href="http://aspell.sf.net/" target="_blank">GNU Aspell</a> 0.50 or higher and <a href="http://ftp.gnu.org/gnu/aspell/dict/" target="_blank">its dictionaries</a> for spell checking functionality.</li> + <li><a href="http://httpd.apache.org/" target="_blank">Apache</a> 2.0.0 or higher with <a href="http://httpd.apache.org/docs-2.0/mod/core.html#acceptpathinfo" target="_blank">AcceptPathInfo</a> set to On (Apache 2 and later only) for queryless URL support.</li> + <li> + <a href="http://www.php.net" target="_blank">PHP</a> 5.2.0 or higher, with the following set in php.ini: + <ul> + <li>the <a href="http://www.php.net/manual/en/info.configuration.php#ini.max-input-time" target="_blank">max_input_time</a> directive is set to a value of at least 30.</li> + <li>the <a href="http://www.php.net/manual/en/ini.core.php#ini.post-max-size" target="_blank">post_max_size</a> and <a href="http://www.php.net/configuration.directives#ini.upload-max-filesize" target="_blank">upload_max_filesize</a> directives set to the size of the largest attachments you wish to be able to upload.</li> + <li>the <a href="http://www.php.net/session#ini.session.use-trans-sid" target="_blank">session.use_trans_sid</a> directive set to Off.</li> + <li>the <a href="http://www.php.net/manual/en/ini.core.php#ini.memory-limit" target="_blank">memory_limit</a> directive is set to at least 8M.</li> + <li>the <a href="http://www.php.net/manual/en/info.configuration.php#ini.max-execution-time" target="_blank">max_execution_time</a> directive is set to at least 15.</li> + <li>the <a href="http://www.php.net/manual/en/ini.core.php#ini.register-globals" target="_blank">register_globals</a> directive is set to Off.</li> + <li>the <a href="http://www.php.net/manual/en/features.safe-mode.php" target="_blank">safe mode</a> disabled or <a href="http://httpd.apache.org/docs/2.0/suexec.html" target="_blank">suEXEC</a> ensabled.</li> + <li>the <a href="http://www.php.net/manual/en/book.bc.php" target="_blank">BCMath</a> library enabled for using the <a href="/smf/Registration#Settings" title="Registration">OpenID</a> registration method.</li> + </ul> + </li> + <li>Any of the following database systems + <ul> + <li><a href="http://www.mysql.com/" target="_blank">MySQL</a> 5.0 or higher.</li> + <li><a href="http://www.postgresql.org/" target="_blank">PostgreSQL</a> 8.3.3 or higher.</li> + <li><a href="http://www.sqlite.org/" target="_blank">SQLite</a> 2.8.</li> + </ul> + </li> + <li><a href="http://www.boutell.com/gd/" target="_blank">GD Graphics Library</a> 2.0 or higher.</li> + </ul> + <p>If your server does not meet these requirements, SMF may not work properly.</p> + </div> + <div class="panel"> + <h2 id="backups">Backing up data</h2> + <p>Before starting the upgrade process, a backup of the live database should be taken. This protects the forum from accidental damage and any issues from upgrading. Although all steps are taken, and extensive testing carried out, sometimes issues develop. Therefore, having a backup is crucial. The upgrading tool can backup all database tables before it runs, however the best practice is to have a full backup available. </p> + <h3>Back up a database using SMF</h3> + <p>From SMF, navigate to Forum Maintenance. (Administration Center -&gt; Maintenance -&gt; Forum Maintenance) On the database section, save the data and the structure. Then, compress the file. Select &quot;Download&quot; and wait for the database to complete the download completely. It is recommended if you use this method to verify that the backup is complete by opening the file and checking the last line. If the file is not complete and has an error please try one of the other methods to backup your database.</p> + <h3>Back up a database using PHPMyAdmin</h3> + <p>PHPMyAdmin gives the option to export a database, from the initial page, select the &quot;Export&quot; option and follow the instructions. Select your SMF database. These are different based on host. </p> + <h3>Back up a database using a control panel</h3> + <p>If your hosting service provides a control panel interface, this can be used to back up a database. Selecting the &quot;Backups&quot; or &quot;Backups Wizard&quot; options should take you to a page, prompting you to back up your database. With different hosts, these options may have different titles. </p> + </div> + <div class="panel"> + <h2 id="uploadingftp">Upload files method 1: using FTP</h2> + <p>If your server doesn't support webinstall.php than you can use an FTP client and an FTP access to upload the files to your server.</p> + <p>All you need to do is upload all of the files in this package, excluding this file itself, to your server. You should upload it to the same directory as your previous installation of SMF or YaBB SE. If you are given the option to &quot;resume&quot; uploads, make sure you do not do that - you must upload all of the files. You may wish to make sure that all of the files were uploaded, such as those in <tt>Themes/default/languages</tt>, because some FTP clients have been known to drop files.</p> + <h3>Language files</h3> + <p>If you are using additional languages it will be useful to upload also the updated versions of the language files along with the upgrading packages. Doing so all updated text strings will appear correctly after the upgrade, and will allow the upgrade to run in your selected language.</p> + </div> + <div class="panel"> + <h2 id="Set_File_Permissions">Set file permissions</h2> + <p>After the upgrade archive has been uploaded and extracted, you need to set the files' permissions. This is commonly done by use of the Unix utility <b>CHMOD</b>. The correct CHMOD value for SMF files is either 777, 775 or 755, depending on your hosting service. There are two methods for this step, the method used depends on the hosting service that you use.</p> + <h3>Setting File Permissions With the Upgrader</h3> + <p>The SMF upgrader can set file permissions simply and easily. Navigating to the directory where SMF is located should redirect you to the upgrade.php file and prompt the upgrader. For example: www.yourdomain.com/forum/upgrade.php. If the upgrader detects files that need their permissions adjusted it will prompt for FTP details so it can <b>CHMOD</b> the files it requires for the upgrade. This may not work on some servers. + </p> + <h3>Setting File Permissions With FTP</h3> + <p>Using a control panel or FTP client, file permissions can be changed quickly and easily. Usually, FTP programs will allow permissions to be changed by right-clicking files/directories and selecting &quot;Properties&quot;, &quot;Attributes&quot; or &quot;Permissions&quot;. The desired numerical value can be entered, or if provided, check boxes can be changed.</p> + <p>The following files and directories must be writable. Depending on how your server is set up, this could mean that they must have <b>CHMOD</b> values of 644, 664 or 666 for files, and 755, 775 or 777 for folders:</p> + <ul><li>/attachments + </li><li>/avatars + </li><li>/Packages + </li><li>/Packages/installed.list + </li><li>/Smileys + </li><li>/Themes + </li><li>agreement.txt + </li><li>Settings.php + </li><li>Settings_bak.php + </li><li>upgrade.php + </li></ul> + <p>If the permission on your files or folders does not make them writable, the SMF upgrader will report the problem. In that case, use your FTP client or host panel to reset the permissions for the files or folders the upgrader reports.</p> + </div> + <div class="panel"> + <h2 id="Run_the_upgrading_tool">Run the upgrading tool</h2> + <p>The final step in upgrading SMF, is to run the upgrading tool. Navigate to the directory where SMF is located. It should redirect you to the upgrade.php file and prompt you to run the upgrade. In example: www.yourdomain.com/forum/upgrade.php. </p> + <p>The first page you see may request your FTP information. If you see this screen, it is because the installer found some files or folders with inadequate permissions for SMF to run properly. If you enter your FTP information here, the installer can automatically fix these permissions for you. Please note that the path should be the same path you see in your FTP client. For example, it might be &quot;public_html/forum&quot;. And remember, the installer will not save your FTP password anywhere.</p> + <h3>Upgrade settings</h3> + <dl> + <dt>Backup database with the prefix "backup_"</dt> + <dd>Selecting this option will get the upgrade tool to copy all data in the database before upgrading within the original database.</dd> + <dt>Maintenance Mode</dt> + <dd>Selecting this option will place the forum into maintenance mode while upgrading rather than showing errors, this is highly recommended.</dd> + <dt>Output extra debugging information.</dt> + <dd>The upgrade tool can give detailed information while performing an upgrade by selecting this option, it will aid the support team to solve any errors if they occur while upgrading.</dd> + </dl> + </div> + <div class="panel"> + <h2 id="webinstall">Upload files method 2: using webinstall.php</h2> + <p>Your server may support webinstall.php. This script will automatically download SMF to your server. This may not work on all servers and also may require providing it with FTP details.</p> + <p>The first thing you need to do is upload webinstall.php to the location of where SMF is to exist on your server.</p> + <p>After you have finished uploading the file, point your browser to http://www.yourdomain.tld/forum/webinstall.php - where www.yourdomain.tld/forum is the URL to where you uploaded it. You should then see the webinstall interface.</p> + <p>The first page you see may request your FTP information. If you see this screen, it is because the webinstaller found some files or folders with inadequate permissions for SMF to run properly. If you enter your FTP information here, the webinstaller can automatically fix these permissions for you. Please note that the path should be the same path you see in your FTP client. For example, it might be &quot;public_html/forum&quot;. And remember, the webinstaller will not save your FTP password anywhere.</p> + <p>On the webinstall interface you have an option to login, this is useful for charter members to easily download early releases. You may have an option to select multiple versions of SMF to download. It is up to you to decide which version of SMF you wish to install. Additionally you may have options of additional languages to download.</p> + <p>After specifying these options and agreeing to the agreement, webinstall will attempt to download al SMF files and decompress them in the same folder as webinstall.php. If successful you will be prompted to the SMF upgrade screen. If this fails you will need to follow the process below to upload files.</p> + <p>In most cases, you'll want to have &quot;Put the forum into maintenance mode during upgrade.&quot; checked, because it will ensure that nothing is messed with while the upgrader is working. You may also wish to check &quot;Backup tables in your database...&quot;, which will make a backup of your old information and tables before making any changes in the database.</p> + </div> + <div class="panel"> + <h2 id="finishing">Finishing the upgrade and cleaning up</h2> + <p>Once all parts of the upgrade have completed, check the box to remove the upgrade files from the server. If this does not work, they will need to be deleted via FTP. All upgrade files should be removed from the server once the upgrade process is complete. These files are upgrade.php and the .sql files whose name starts with 'upgrade'. They are a major security risk if they are left on a server unattended. Once SMF has been upgraded, they are no longer needed.</p> + <p>Good luck!<br /> + Simple Machines</p> + </div> + </div> + </div> + </div></div> + <div id="footer_section"><div class="frame"> + <div class="smalltext"><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.</div> + <div class="smalltext"><a href="http://www.simplemachines.org">Simple Machines Forum</a></div> + </div></div> + </body> +</html> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/ssi_examples.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/ssi_examples.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,686 @@ +<?php + +/** + * Simple Machines Forum (SMF) + * + * @package SMF + * @author Simple Machines http://www.simplemachines.org + * @copyright 2011 Simple Machines + * @license http://www.simplemachines.org/about/smf/license.php BSD + * + * @version 2.0 + */ + +// Special thanks to Spaceman-Spiff for his contributions to this page. + +/* Define $ssi_guest_access variable just before including SSI.php to handle guest access to your script. + false: (default) fallback to forum setting + true: allow guest access to the script regardless +*/ +$ssi_guest_access = false; + +// Include the SSI file. +require(dirname(__FILE__) . '/SSI.php'); + +// Viewing the homepage sample? +if (isset($_GET['view']) && $_GET['view'] == 'home1') +{ + template_homepage_sample1('output'); + exit; +} + +// Load the main template. +template_ssi_above(); +?> + + <h2>SMF SSI.php Functions</h2> + <p><strong>Current Version:</strong> 2.0</p> + <p>This file is used to demonstrate the capabilities of SSI.php using PHP include functions. The examples show the include tag, then the results of it.</p> + + <h2>Include Code</h2> + <p>To use SSI.php in your page add at the very top of your page before the &lt;html&gt; tag on line 1 of your php file:</p> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php require(&quot;<?php echo addslashes($user_info['is_admin'] ? realpath($boarddir . '/SSI.php') : 'SSI.php'); ?>&quot;); ?&gt;</code> + + <h2>Some notes on usage</h2> + <p>All the functions have an output method parameter. This can either be &quot;echo&quot; (the default) or &quot;array&quot;</p> + <p>If it is &quot;echo&quot;, the function will act normally - otherwise, it will return an array containing information about the requested task. For example, it might return a list of topics for ssi_recentTopics.</p> + <p onclick="if (getInnerHTML(this).indexOf('Bird') == -1) setInnerHTML(this, getInnerHTML(this) + '<br /><img src=&quot;http://www.simplemachines.org/images/chocobo.jpg&quot; title=&quot;Bird-san&quot; alt=&quot;Chocobo!&quot; />'); return false;">This functionality can be used to allow you to present the information in any way you wish.</p> + + <h2>Additional Guides &amp; FAQ</h2> + <p>Need more information on using SSI.php? Check out <a href="http://docs.simplemachines.org/index.php?topic=400.0">Using SSI.php article</a> or <a href="http://www.simplemachines.org/community/index.php?topic=14906.0">the SSI FAQ</a>.</p> + + <div id="sidenav" class="windowbg"> + <span class="topslice"><span></span></span> + <div class="content"> + <h2 id="functionlist">Function List</h2> + <h3>Recent Items</h3> + <ul> + <li><a href="#" onclick="showSSIBlock('ssi_recentTopics'); return false;">Recent Topics</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_recentPosts'); return false;">Recent Posts</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_recentPoll'); return false;">Recent Poll</a></li> + </ul> + <h3>Top Items</h3> + <ul> + <li><a href="#" onclick="showSSIBlock('ssi_topBoards'); return false;">Top Boards</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_topTopicsViews'); return false;">Top Topics</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_topPoll'); return false;">Top Poll</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_topPoster'); return false;">Top Poster</a></li> + </ul> + <h3>Members</h3> + <ul> + <li><a href="#" onclick="showSSIBlock('ssi_latestMember'); return false;">Latest Member Function</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_randomMember'); return false;">Member of the Day</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_whosOnline'); return false;">Who's Online</a></li> + </ul> + <h3>Authentication</h3> + <ul> + <li><a href="#" onclick="showSSIBlock('ssi_login'); return false;">Welcome, Login &amp; Logout</a></li> + </ul> + <h3>Calendar</h3> + <ul> + <li><a href="#" onclick="showSSIBlock('ssi_todaysCalendar'); return false;">Today's Events</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_recentEvents'); return false;">Recent Events</a></li> + </ul> + <h3>Miscellaneous</h3> + <ul> + <li><a href="#" onclick="showSSIBlock('ssi_boardStats'); return false;">Forum Stats</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_news'); return false;">News</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_boardNews'); return false;">Board News</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_menubar'); return false;">Menubar</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_quickSearch'); return false;">Quick Search Box</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_recentAttachments'); return false;">Recent Attachments</a></li> + </ul> + <?php if ($user_info['is_admin']) { ?> + <h3>Advanced Functions <img class="help" title="Functions that require additional tweaking, not just copy and paste." src="<?php echo $settings['images_url']; ?>/helptopics.gif" alt="" /></h3> + <ul> + <li><a href="#" onclick="showSSIBlock('ssi_showPoll'); return false;">Show Single Poll</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_fetchPosts'); return false;">Show Single Post</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_fetchMember'); return false;">Show Single Member</a></li> + <li><a href="#" onclick="showSSIBlock('ssi_fetchGroupMembers'); return false;">Show Group Members</a></li> + </ul> + <?php } ?> + <h3>Website Samples</h3> + <ul> + <li><a href="#" onclick="showSSIBlock('htmlhome')">Sample 1</a></li> + </ul> + <h2 id="other">Other</h2> + <ul> + <li><a href="#" onclick="toggleVisibleByClass('ssi_preview', false); return false;">Show all examples</a></li> + <li><a href="#" onclick="toggleVisibleByClass('ssi_preview', true); return false;">Hide all examples</a></li> + </ul> + </div> + <span class="botslice"><span></span></span> + </div> + + <div id="preview" class="windowbg2"> + <span class="topslice"><span></span></span> + <div class="content"> + +<!-- RECENT ITEMS --> + <div class="ssi_preview" id="ssi_recentTopics"> + <h2>Recent Topics Function</h2> + <h3>Code (simple mode)</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_recentTopics(); ?&gt;</code> + <h3>Code (advanced mode)</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_recentTopics($num_recent = 8, $exclude_boards = null, $include_boards = null, $output_method = 'echo'); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_recentTopics(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_recentPosts"> + <h2>Recent Posts Function</h2> + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_recentPosts(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_recentPosts(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_recentPoll"> + <h2>Recent Poll Function</h2> + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_recentPoll(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_recentPoll(); flush(); ?></div> + </div> + +<!-- TOP ITEMS --> + <div class="ssi_preview" id="ssi_topBoards"> + <h2>Top Boards Function</h2> + <p>Shows top boards by the number of posts.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_topBoards(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_topBoards(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_topTopicsViews"> + <h2>Top Topics</h2> + <p>Shows top topics by the number of replies or views.</p> + + <h3>Code (show by number of views)</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_topTopicsViews(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_topTopicsViews(); flush(); ?></div> + + <h3>Code (show by number of replies)</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_topTopicsReplies(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_topTopicsReplies(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_topPoll"> + <h2>Top Poll Function</h2> + <p>Shows the most-voted-in poll.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_topPoll(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_topPoll(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_topPoster"> + <h2>Top Poster Function</h2> + Shows the top poster's name and profile link. + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_topPoster(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_topPoster(); flush(); ?></div> + </div> + +<!-- MEMBERS --> + <div class="ssi_preview" id="ssi_latestMember"> + <h2>Latest Member Function</h2> + <p>Shows the latest member's name and profile link.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_latestMember(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_latestMember(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_randomMember"> + <h2>Member of the Day</h2> + <p>Shows one random member of the day. This changes once a day.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_randomMember('day'); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_randomMember('day'); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_whosOnline"> + <h2>Who's Online Function</h2> + <p>This function shows who are online inside the forum.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_whosOnline(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_whosOnline(); flush(); ?></div> + + <h2>Log Online Presence</h2> + <p>This function logs the SSI page's visitor, then shows the Who's Online list. In other words, this function shows who are online inside and outside the forum.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_logOnline(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_logOnline(); flush(); ?></div> + </div> + +<!-- WELCOME, LOGIN AND LOGOUT --> + <div class="ssi_preview" id="ssi_login"> + <h2>Login Function</h2> + <p>Shows a login box only when user is not logged in.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_login(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_login(); flush(); ?></div> + + <h2>Logout Function</h2> + <p>Shows a logout link only when user is logged in.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_logout(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_logout(); flush(); ?></div> + + <h2>Welcome Function</h2> + <p>Greets users or guests, also shows user's messages if logged in.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_welcome(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_welcome(); flush(); ?></div> + </div> + +<!-- CALENDAR --> + <div class="ssi_preview" id="ssi_todaysCalendar"> + <h2>Today's Calendar Function</h2> + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_todaysCalendar(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_todaysCalendar(); flush(); ?></div> + + <h2>Today's Birthdays Function</h2> + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_todaysBirthdays(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_todaysBirthdays(); flush(); ?></div> + + <h2>Today's Holidays Function</h2> + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_todaysHolidays(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_todaysHolidays(); flush(); ?></div> + + <h2>Today's Events Function</h2> + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_todaysEvents(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_todaysEvents(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_recentEvents"> + <h2>Recent Calendar Events Function</h2> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_recentEvents(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_recentEvents(); flush(); ?></div> + </div> + +<!-- MISCELLANEOUS --> + <div class="ssi_preview" id="ssi_boardStats"> + <h2>Forum Stats</h2> + <p>Shows some basic forum stats: total members, posts, topics, boards, etc.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_boardStats(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_boardStats(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_news"> + <h2>News Function</h2> + <p>Shows random forum news.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_news(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_news(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_boardNews"> + <h2>Board News Function</h2> + <p>Shows the latest posts from read only boards, or a specific board.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_boardNews(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_boardNews(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_menubar"> + <h2>Menubar Function</h2> + <p>Displays a menu bar, like one displayed at the top of the forum.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_menubar(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_menubar(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_quickSearch"> + <h2>Quick Search Function</h2> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_quickSearch(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_quickSearch(); flush(); ?></div> + </div> + + <div class="ssi_preview" id="ssi_recentAttachments"> + <h2>Recent Attachments Function</h2> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_recentAttachments(); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><?php ssi_recentAttachments(); flush(); ?></div> + </div> + +<!-- ADVANCED FUNCTIONS --> + <div class="ssi_preview" id="ssi_showPoll"> + <h2>Show Single Poll</h2> + <p>Shows a poll in the specified topic.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_showPoll($topicID); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><i>Not shown because it needs specific topic ID that contains a poll.</i></div> + </div> + + <div class="ssi_preview" id="ssi_fetchPosts"> + <h2>Show Single Post</h2> + <p>Fetches a post with a particular IDs. By default will only show if you have permission to the see + the board in question. This can be overriden by passing the 2nd parameter as <tt>true</tt>.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_fetchPosts($postIDs, $isOverride); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><i>Not shown because it needs a specific post ID.</i></div> + </div> + + <div class="ssi_preview" id="ssi_fetchMember"> + <h2>Show Single Member</h2> + <p>Shows the specified member's name and profile link.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_fetchMember($memberIDs); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><i>Not shown because it needs a specific member ID.</i></div> + </div> + + <div class="ssi_preview" id="ssi_fetchGroupMembers"> + <h2>Show Group Members</h2> + <p>Shows all members in a specified group.</p> + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code">&lt;?php ssi_fetchGroupMembers($groupIDs); ?&gt;</code> + <h3>Result</h3> + <div class="ssi_result"><i>Not shown because it needs specific membergroup IDs.</i></div> + </div> + + <div class="ssi_preview" id="htmlhome"> + <h2>Home Page Sample</h2> + This sample uses the following features: ssi_recentTopics(), ssi_logOnline(), ssi_welcome(), and ssi_boardNews(). + ssi_recentTopics() is fetched using the array method, to allow further customizations on the output. + + <h3>Code</h3> + <div class="codeheader">Code: <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">[Select]</a></div><code class="bbc_code"><?php echo htmlspecialchars(template_homepage_sample1('source')); ?></code> + <h3>Result</h3> + <iframe src="?view=home1" width="99%" height="300"></iframe> + </div> + </div> + <span class="botslice"><span></span></span> + </div> + +<?php + +template_ssi_below(); + +function template_ssi_above() +{ + global $settings, $context, $scripturl; + + echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>SMF 2.0 SSI.php Examples</title> + <link rel="stylesheet" type="text/css" href="', $settings['default_theme_url'], '/css/index.css?fin20" /> + <script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/script.js"></script> + <style type="text/css"> + #wrapper + { + width: 90%; + } + #upper_section .user + { + height: 4em; + } + #upper_section .news + { + height: 80px; + } + #content_section + { + position: relative; + top: -20px; + } + #main_content_section h2 + { + font-size: 1.5em; + border-bottom: solid 1px #d05800; + line-height: 1.5em; + margin: 0.5em 0; + color: #d05800; + } + #liftup + { + position: relative; + top: -70px; + padding: 1em 2em 1em 1em; + line-height: 1.6em; + } + #footer_section + { + position: relative; + top: -20px; + } + #sidenav + { + width: 210px; + float: left; + margin-right: 20px; + } + #sidenav ul + { + margin: 0 0 0 15px; + padding: 0; + list-style: none; + font-size: 90%; + } + #preview + { + margin-left: 230px; + } + .ssi_preview + { + margin-bottom: 1.5em; + } + .ssi_preview h3 + { + margin: 1em 0 0.5em 0; + } + .ssi_result + { + background-color: #fff; + border: 1px solid #99a; + padding: 10px; + overflow: hidden; + } + </style> + <script type="text/javascript"><!-- // --><![CDATA[ + var smf_scripturl = "', $scripturl, '"; + var smf_iso_case_folding = ', $context['server']['iso_case_folding'] ? 'true' : 'false', '; + var smf_charset = "', $context['character_set'], '"; + + // Sets all ssi_preview class to hidden, then shows the one requested. + function showSSIBlock(elementID) + { + toggleVisibleByClass("ssi_preview", true); + document.getElementById(elementID).style.display = "block"; + } + + // Toggle visibility of all sections. + function toggleVisibleByClass(sClassName, bHide) + { + var oSections = document.getElementsByTagName("div"); + for (var i = 0; i < oSections.length; i++) + { + if (oSections[i].className == null || oSections[i].className.indexOf(sClassName) == -1) + continue; + + oSections[i].style.display = bHide ? "none" : "block"; + } + } + // ]]></script> + </head> + <body> + <div id="wrapper"> + <div id="header"><div class="frame"> + <div id="top_section"> + <h1 class="forumtitle">SMF 2.0 SSI.php Examples</h1> + <img id="smflogo" src="Themes/default/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum" /> + </div> + <div id="upper_section" class="middletext" style="overflow: hidden;"> + <div class="user"></div> + <div class="news normaltext"> + </div> + </div> + </div></div> + <div id="content_section"><div class="frame"> + <div id="main_content_section"> + <div id="liftup" class="flow_auto">'; +} + +function template_ssi_below() +{ + global $time_start; + + echo ' + <script type="text/javascript"><!-- // --><![CDATA[ + showSSIBlock("ssi_recentTopics"); + // ]]></script> + </div> + </div> + </div></div> + <div id="footer_section"><div class="frame"> + <div class="smalltext"><a href="http://www.simplemachines.org">Simple Machines Forum</a></div> + </div></div> + </div> + </body> +</html>'; +} + +function template_homepage_sample1($method = 'source') +{ + global $user_info, $boarddir; + + $header = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>SSI.php example for home page</title> + <style type="text/css"> + body { font-family: Arial, Tahoma, sans-serif; font-size: 80%; background: #DFDFDF; color: #FFFFFF; margin: 0 } + ul,ol { padding-left: 19px; margin: 0; } + li { font-size: 11px; } + h1,h2,h3 { margin: 0; padding: 0; } + h3 { font-size: 15px; } + a:link,a:visited { color: #FF9000; text-decoration: none; } + a:hover { text-decoration: underline; } + + #container { background: #52514E; width: 100%; border: 1px solid midnightblue; line-height: 150%; margin: 0; } + #header,#footer { color: lightgray; background-color: #2A2825; clear: both; padding: .5em; } + #leftbar { background: #DF7E00; float: left; width: 160px; margin: 0; padding: 1em; } + #leftbar a { color: #000000; text-decoration: underline; } + #content { margin-left: 190px; padding: 1em; } + #navigation { float: right; } + #navigation a:link,#navigation a:visited { color: #FF9000; } + </style> +</head> +<body> +<div id="container"> + <div id="header"> + <div id="navigation"> + <a href="#">Link</a> | <a href="#">Link</a> | <a href="#">Link</a> | <a href="#">Link</a> | <a href="#">Link</a> + </div> + <h1 class="header">YourWebsite.com</h1> + </div> + <div id="leftbar"> + <h3>Recent Forum Topics</h3> + <ul>'; + + $footer = ' + <div id="footer"> + <a target="_blank" rel="license" href="http://creativecommons.org/licenses/publicdomain/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/publicdomain/88x31.png" /></a> + This sample website layout is dedicated to the <a target="_blank" rel="license" href="http://creativecommons.org/licenses/publicdomain/">Public Domain</a>. + </div> +</div> +</body> +</html>'; + + if ($method == 'source') + { + $header = '<?php require("' . ($user_info['is_admin'] ? addslashes(realpath($boarddir . '/SSI.php')) : 'SSI.php') . '"); ?>' . "\n" . $header; + return $header . template_homepage_sample1_html() . $footer; + } + else + { + echo $header; + template_homepage_sample1_php(); + echo $footer; + } + +} + +function template_homepage_sample1_php() +{ + global $txt; + + $topics = ssi_recentTopics(8, null, null, 'array'); + + foreach ($topics as $topic) + echo ' + <li><a href="', $topic['href'], '">', $topic['subject'], '</a> ', $txt['by'], ' ', $topic['poster']['link'], '</li>'; + + unset($topics); + + echo ' + + </ul><br /> + + <h3>Online Users</h3>'; + ssi_logOnline(); + + echo ' + </div> + + <div id="content">'; + + ssi_welcome(); + echo ' + <br /><br /> + + <h2>News</h2>'; + + ssi_boardNews(); + + echo ' + </div>'; + +} + +function template_homepage_sample1_html() +{ + $result = ' +<?php +// Using array method to show shorter display style. +$topics = ssi_recentTopics(8, null, null, \'array\'); + +foreach ($topics as $topic) +{ + // Uncomment the following code to get a listing of array elements that SMF provides for this function. + // echo \'<pre>\', print_r($topic), \'</pre>\'; + + echo \' + <li><a href=\"\', $topic[\'href\'], \'\">\', $topic[\'subject\'], \'</a> \', $txt[\'by\'], \' \', $topics[$i][\'poster\'][\'link\'], \'</li>\'; +} + +unset($topics); +?> + </ul><br /> + <h3>Online Users</h3> + <?php ssi_logOnline(); ?> + </div> + <div id="content"> + <?php ssi_welcome(); ?><br /><br /> + <h2>News</h2> + <?php ssi_boardNews(); ?> + </div>'; + + return $result; +} + +?> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/ssi_examples.shtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/ssi_examples.shtml Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,162 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <title> &lt;&lt; :: SMF SSI.php 2.0 RC4 :: &gt;&gt; </title> + </head> + <body style="font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10pt;"> + <h1>SMF SSI.php Functions</h1> + Current Version 2.0 RC4<br /> + <br /> + This file is used to demonstrate the capabilities of SSI.php using SHTML include functions.<br /> + The examples the include tag, then the results of it. Examples are separated by horizontal rules.<br /> + + <hr /> + + <h3>Recent Topics Function: &lt;!--#include virtual="./SSI.php?ssi_function=recentTopics" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=recentTopics" --> + + <hr /> + + <h3>Recent Posts Function: &lt;!--#include virtual="./SSI.php?ssi_function=recentPosts" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=recentPosts" --> + + <hr /> + + <h3>Recent Poll Function: &lt;!--#include virtual="./SSI.php?ssi_function=recentPoll" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=recentPoll" --> + + <hr /> + + <h3>Top Boards Function: &lt;!--#include virtual="./SSI.php?ssi_function=topBoards" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=topBoards" --> + + <hr /> + + <h3>Top Topics by View Function: &lt;!--#include virtual="./SSI.php?ssi_function=topTopicsViews" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=topTopicsViews" --> + + <hr /> + + <h3>Top Topics by Replies Function: &lt;!--#include virtual="./SSI.php?ssi_function=topTopicsReplies" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=topTopicsReplies" --> + + <hr /> + + <h3>Top Poll Function: &lt;!--#include virtual="./SSI.php?ssi_function=topPoll" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=topPoll" --> + + <hr /> + + <h3>Top Poster Function: &lt;!--#include virtual="./SSI.php?ssi_function=topPoster" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=topPoster" --> + + <hr /> + + <h3>Topic's Poll Function: &lt;!--#include virtual="./SSI.php?ssi_function=showPoll;ssi_topic=##" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=showPoll;ssi_topic=1" --> + + <hr /> + + <h3>Latest Member Function: &lt;!--#include virtual="./SSI.php?ssi_function=latestMember" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=latestMember" --> + + <hr /> + + <h3>Random Member Function: &lt;!--#include virtual="./SSI.php?ssi_function=randomMember" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=randomMember" --> + + <hr /> + + <h3>Board Stats: &lt;!--#include virtual="./SSI.php?ssi_function=boardStats" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=boardStats" --> + + <hr /> + + <h3>Who's Online Function: &lt;!--#include virtual="./SSI.php?ssi_function=whosOnline" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=whosOnline" --> + + <hr /> + + <h3>Log Online Presence + Who's Online Function: &lt;!--#include virtual="./SSI.php?ssi_function=logOnline" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=logOnline" --> + + <hr /> + + <h3>Welcome Function: &lt;!--#include virtual="./SSI.php?ssi_function=welcome" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=welcome" --> + + <hr /> + + <h3>News Function: &lt;!--#include virtual="./SSI.php?ssi_function=news" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=news" --> + + <hr /> + + <h3>Board News Function: &lt;!--#include virtual="./SSI.php?ssi_function=boardNews" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=boardNews" --> + + <hr /> + + <h3>Menubar Function: &lt;!--#include virtual="./SSI.php?ssi_function=menubar" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=menubar" --> + + <hr /> + + <h3>Quick Search Function: &lt;!--#include virtual="./SSI.php?ssi_function=quickSearch" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=quickSearch" --> + + <hr /> + + <h3>Login Function: &lt;!--#include virtual="./SSI.php?ssi_function=login" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=login" --> + + <hr /> + + <h3>Log Out Function: &lt;!--#include virtual="./SSI.php?ssi_function=logout" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=logout" --> + + <hr /> + + <h3>Today's Birthdays Function: &lt;!--#include virtual="./SSI.php?ssi_function=todaysBirthdays" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=todaysBirthdays" --> + + <hr /> + + <h3>Today's Holidays Function: &lt;!--#include virtual="./SSI.php?ssi_function=todaysHolidays" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=todaysHolidays" --> + + <hr /> + + <h3>Today's Events Function: &lt;!--#include virtual="./SSI.php?ssi_function=todaysEvents" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=todaysEvents" --> + + <hr /> + + <h3>Today's Calendar Function: &lt;!--#include virtual="./SSI.php?ssi_function=todaysCalendar" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=todaysCalendar" --> + + <hr /> + + <h3>Recent Calendar Events Function: &lt;!--#include virtual="./SSI.php?ssi_function=recentEvents" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=recentEvents" --> + + <hr /> + + <h3>Recent Attachments Function &lt;!--#include virtual="./SSI.php?ssi_function=recentAttachments" --&gt;</h3> + <!--#include virtual="./SSI.php?ssi_function=recentAttachments" --> + + <hr /> + + <br /> + <br /> + <span style="font-size: smaller;"> + <a href="http://www.simplemachines.org/" title="Free Forum Software" target="_blank">SMF &copy; 2006&ndash;2010, Simple Machines LLC</a> + </span> + <br /> + <br /> + <span style="font-size: smaller; color: #CCCCCC;"> + *ssi_examples.shtml last modified on <!--#config timefmt="%m/%d/%y" --><!--#echo var="LAST_MODIFIED" --> + </span> + + </body> +</html> \ No newline at end of file diff -r 72f59aa7e503 -r e3e11437ecea forum/subscriptions.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/subscriptions.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,294 @@ +<?php + +/** + * Simple Machines Forum (SMF) + * + * @package SMF + * @author Simple Machines http://www.simplemachines.org + * @copyright 2011 Simple Machines + * @license http://www.simplemachines.org/about/smf/license.php BSD + * + * @version 2.0.2 + */ + +/* + This file is the file which all subscription gateways should call + when a payment has been received - it sorts out the user status. + + void generateSubscriptionError() + // log the error for posterity +*/ + +// Start things rolling by getting SMF alive... +$ssi_guest_access = true; +if (!file_exists(dirname(__FILE__) . '/SSI.php')) + die('Cannot find SSI.php'); + +require_once(dirname(__FILE__) . '/SSI.php'); +require_once($sourcedir . '/ManagePaid.php'); + +// For any admin emailing. +require_once($sourcedir . '/Subs-Admin.php'); + +loadLanguage('ManagePaid'); + +// If there's literally nothing coming in, let's take flight! +if (empty($_POST)) + die($txt['paid_no_data']); + +// I assume we're even active? +if (empty($modSettings['paid_enabled'])) + exit; + +// If we have some custom people who find out about problems load them here. +$notify_users = array(); +if (!empty($modSettings['paid_email_to'])) + foreach (explode(',', $modSettings['paid_email_to']) as $email) + $notify_users[] = array( + 'email' => $email, + 'name' => $txt['who_member'], + 'id' => 0, + ); + +// We need to see whether we can find the correct payment gateway, +// we'll going to go through all our gateway scripts and find out +// if they are happy with what we have. +$txnType = ''; +$gatewayHandles = loadPaymentGateways(); +foreach ($gatewayHandles as $gateway) +{ + $gatewayClass = new $gateway['payment_class'](); + if ($gatewayClass->isValid()) + { + $txnType = $gateway['code']; + break; + } +} + +if (empty($txnType)) + generateSubscriptionError($txt['paid_unknown_transaction_type']); + +// Get the subscription and member ID amoungst others... +@list ($subscription_id, $member_id) = $gatewayClass->precheck(); + +// Integer these just in case. +$subscription_id = (int) $subscription_id; +$member_id = (int) $member_id; + +// This would be bad... +if (empty($member_id)) + generateSubscriptionError($txt['paid_empty_member']); + +// Verify the member. +$request = $smcFunc['db_query']('', ' + SELECT id_member, member_name, real_name, email_address + FROM {db_prefix}members + WHERE id_member = {int:current_member}', + array( + 'current_member' => $member_id, + ) +); +// Didn't find them? +if ($smcFunc['db_num_rows']($request) == 0) + generateSubscriptionError(sprintf($txt['paid_could_not_find_member'], $member_id)); +$member_info = $smcFunc['db_fetch_assoc']($request); +$smcFunc['db_free_result']($request); + +// Get the subscription details. +$request = $smcFunc['db_query']('', ' + SELECT cost, length, name + FROM {db_prefix}subscriptions + WHERE id_subscribe = {int:current_subscription}', + array( + 'current_subscription' => $subscription_id, + ) +); + +// Didn't find it? +if ($smcFunc['db_num_rows']($request) == 0) + generateSubscriptionError(sprintf($txt['paid_count_not_find_subscription'], $member_id, $subscription_id)); + +$subscription_info = $smcFunc['db_fetch_assoc']($request); +$smcFunc['db_free_result']($request); + +// We wish to check the pending payments to make sure we are expecting this. +$request = $smcFunc['db_query']('', ' + SELECT id_sublog, payments_pending, pending_details, end_time + FROM {db_prefix}log_subscribed + WHERE id_subscribe = {int:current_subscription} + AND id_member = {int:current_member} + LIMIT 1', + array( + 'current_subscription' => $subscription_id, + 'current_member' => $member_id, + ) +); +if ($smcFunc['db_num_rows']($request) == 0) + generateSubscriptionError(sprintf($txt['paid_count_not_find_subscription_log'], $member_id, $subscription_id)); +$subscription_info += $smcFunc['db_fetch_assoc']($request); +$smcFunc['db_free_result']($request); + +// Is this a refund etc? +if ($gatewayClass->isRefund()) +{ + // If the end time subtracted by current time, is not greater + // than the duration (ie length of subscription), then we close it. + if ($subscription_info['end_time'] - time() < $subscription_info['length']) + { + // Delete user subscription. + removeSubscription($subscription_id, $member_id); + $subscription_act = time(); + $status = 0; + } + else + { + loadSubscriptions(); + $subscription_act = $subscription_info['end_time'] - $context['subscriptions'][$subscription_id]['num_length']; + $status = 1; + } + + // Mark it as complete so we have a record. + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET end_time = {int:current_time} + WHERE id_subscribe = {int:current_subscription} + AND id_member = {int:current_member} + AND status = {int:status}', + array( + 'current_time' => $subscription_act, + 'current_subscription' => $subscription_id, + 'current_member' => $member_id, + 'status' => $status, + ) + ); + + // Receipt? + if (!empty($modSettings['paid_email']) && $modSettings['paid_email'] == 2) + { + $replacements = array( + 'NAME' => $subscription_info['name'], + 'REFUNDNAME' => $member_info['member_name'], + 'REFUNDUSER' => $member_info['real_name'], + 'PROFILELINK' => $scripturl . '?action=profile;u=' . $member_id, + 'DATE' => timeformat(time(), false), + ); + + emailAdmins('paid_subscription_refund', $replacements, $notify_users); + } + +} +// Otherwise is it what we want, a purchase? +elseif ($gatewayClass->isPayment() || $gatewayClass->isSubscription()) +{ + $cost = unserialize($subscription_info['cost']); + $total_cost = $gatewayClass->getCost(); + $notify = false; + + // For one off's we want to only capture them once! + if (!$gatewayClass->isSubscription()) + { + $real_details = @unserialize($subscription_info['pending_details']); + if (empty($real_details)) + generateSubscriptionError(sprintf($txt['paid_count_not_find_outstanding_payment'], $member_id, $subscription_id)); + // Now we just try to find anything pending. + // We don't really care which it is as security happens later. + foreach ($real_details as $id => $detail) + { + unset($real_details[$id]); + if ($detail[3] == 'payback' && $subscription_info['payments_pending']) + $subscription_info['payments_pending']--; + break; + } + $subscription_info['pending_details'] = empty($real_details) ? '' : serialize($real_details); + + $smcFunc['db_query']('', ' + UPDATE {db_prefix}log_subscribed + SET payments_pending = {int:payments_pending}, pending_details = {string:pending_details} + WHERE id_sublog = {int:current_subscription_item}', + array( + 'payments_pending' => $subscription_info['payments_pending'], + 'current_subscription_item' => $subscription_info['id_sublog'], + 'pending_details' => $subscription_info['pending_details'], + ) + ); + } + + // Is this flexible? + if ($subscription_info['length'] == 'F') + { + $found_duration = 0; + // This is a little harder, can we find the right duration? + foreach ($cost as $duration => $value) + { + if ($duration == 'fixed') + continue; + elseif ((float) $value == (float) $total_cost) + $found_duration = strtoupper(substr($duration, 0, 1)); + } + + // If we have the duration then we're done. + if ($found_duration!== 0) + { + $notify = true; + addSubscription($subscription_id, $member_id, $found_duration); + } + } + else + { + $actual_cost = $cost['fixed']; + // It must be at least the right amount. + if ($total_cost != 0 && $total_cost >= $actual_cost) + { + // Add the subscription. + $notify = true; + addSubscription($subscription_id, $member_id); + } + } + + // Send a receipt? + if (!empty($modSettings['paid_email']) && $modSettings['paid_email'] == 2 && $notify) + { + $replacements = array( + 'NAME' => $subscription_info['name'], + 'SUBNAME' => $member_info['member_name'], + 'SUBUSER' => $member_info['real_name'], + 'SUBEMAIL' => $member_info['email_address'], + 'PRICE' => sprintf($modSettings['paid_currency_symbol'], $total_cost), + 'PROFILELINK' => $scripturl . '?action=profile;u=' . $member_id, + 'DATE' => timeformat(time(), false), + ); + + emailAdmins('paid_subscription_new', $replacements, $notify_users); + } +} + +// In case we have anything specific to do. +$gatewayClass->close(); + +// Log an error then die. +function generateSubscriptionError($text) +{ + global $modSettings, $notify_users, $smcFunc; + + // Send an email? + if (!empty($modSettings['paid_email'])) + { + $replacements = array( + 'ERROR' => $text, + ); + + emailAdmins('paid_subscription_error', $replacements, $notify_users); + } + + // Maybe we can try to give them the post data? + if (!empty($_POST)) + foreach ($_POST as $key => $val) + $text .= '<br />' . $smcFunc['htmlspecialchars']($key) . ': ' . $smcFunc['htmlspecialchars']($val); + + // Then just log and die. + log_error($text); + + exit; +} + +?> \ No newline at end of file