/sources/Subs.php
PHP | 4142 lines | 2863 code | 493 blank | 786 comment | 702 complexity | fd7dfb7282fc0705c0cb0c5e04a37a29 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * @name ElkArte Forum
- * @copyright ElkArte Forum contributors
- * @license BSD http://opensource.org/licenses/BSD-3-Clause
- *
- * This software is a derived product, based on:
- *
- * Simple Machines Forum (SMF)
- * copyright: 2011 Simple Machines (http://www.simplemachines.org)
- * license: BSD, See included LICENSE.TXT for terms and conditions.
- *
- * @version 1.0 Alpha
- *
- * This file has all the main functions in it that relate to, well, everything.
- *
- */
- if (!defined('ELKARTE'))
- die('No access...');
- /**
- * Update some basic statistics.
- *
- * 'member' statistic updates the latest member, the total member
- * count, and the number of unapproved members.
- * 'member' also only counts approved members when approval is on, but
- * is much more efficient with it off.
- *
- * 'message' changes the total number of messages, and the
- * highest message id by id_msg - which can be parameters 1 and 2,
- * respectively.
- *
- * 'topic' updates the total number of topics, or if parameter1 is true
- * simply increments them.
- *
- * 'subject' updateds the log_search_subjects in the event of a topic being
- * moved, removed or split. parameter1 is the topicid, parameter2 is the new subject
- *
- * 'postgroups' case updates those members who match condition's
- * post-based membergroups in the database (restricted by parameter1).
- *
- * @param string $type Stat type - can be 'member', 'message', 'topic', 'subject' or 'postgroups'
- * @param mixed $parameter1 = null
- * @param mixed $parameter2 = null
- */
- function updateStats($type, $parameter1 = null, $parameter2 = null)
- {
- global $modSettings, $smcFunc;
- switch ($type)
- {
- case 'member':
- $changes = array(
- 'memberlist_updated' => 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;
- $postgroups = cache_get_data('updateStats:postgroups', 360);
- if ($postgroups === null || $parameter1 === 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 = '';
- $lastMin = 0;
- 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);
- }
- }
- /**
- * Updates the columns in the members table.
- * Assumes the data has been htmlspecialchar'd.
- * this function should be used whenever member data needs to be
- * updated in place of an UPDATE query.
- *
- * id_member is either an int or an array of ints to be updated.
- *
- * data is an associative array of the columns to be updated and their respective values.
- * any string values updated should be quoted and slashed.
- *
- * the value of any column can be '+' or '-', which mean 'increment'
- * and decrement, respectively.
- *
- * if the member's post number is updated, updates their post groups.
- *
- * @param mixed $members An array of integers
- * @param array $data
- */
- 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;
- }
- // 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',
- );
- 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], $knownInts, $knownFloats));
- }
- }
- $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.
- *
- * - updates both the settings table and $modSettings array.
- * - all of changeArray's indexes and values are assumed to have escaped apostrophes (')!
- * - if a variable is already set to what you want to change it to, that
- * variable will be skipped over; it would be unnecessary to reset.
- * - When use_update is true, UPDATEs will be used instead of REPLACE.
- * - when use_update is true, the value can be true or false to increment
- * or decrement it, respectively.
- *
- * @param array $changeArray
- * @param bool $update = false
- * @param bool $debug = false
- */
- 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.
- *
- * - builds the page list, e.g. 1 ... 6 7 [8] 9 10 ... 15.
- * - flexible_start causes it to use "url.page" instead of "url;start=page".
- * - handles any wireless settings (adding special things to URLs.)
- * - very importantly, cleans up the start value passed, and forces it to
- * be a multiple of num_per_page.
- * - checks that start is not more than max_value.
- * - base_url should be the URL without any start parameter on it.
- * - uses the compactTopicPagesEnable and compactTopicPagesContiguous
- * settings to decide how to display the menu.
- *
- * an example is available near the function definition.
- * $pageindex = constructPageIndex($scripturl . '?board=' . $board, $_REQUEST['start'], $num_messages, $maxindex, true);
- *
- * @param string $base_url
- * @param int $start
- * @param int $max_value
- * @param int $num_per_page
- * @param bool $flexible_start = false
- * @param bool $show_prevnext = true
- */
- function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flexible_start = false, $show_prevnext = true)
- {
- global $modSettings, $context, $txt;
- // 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));
- $context['current_page'] = $start / $num_per_page;
- $base_link = '<a class="navPages" href="' . ($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d') . '">%2$s</a> ';
- // Compact pages is off or on?
- if (empty($modSettings['compactTopicPagesEnable']))
- {
- // Show the left arrow.
- $pageindex = $start == 0 ? ' ' : sprintf($base_link, $start - $num_per_page, '<span class="previous_page">' . $txt['prev'] . '</span>');
- // Show all the pages.
- $display_page = 1;
- for ($counter = 0; $counter < $max_value; $counter += $num_per_page)
- $pageindex .= $start == $counter && !$start_invalid ? '<span class="current_page"><strong>' . $display_page++ . '</strong></span> ' : 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, '<span class="next_page">' . $txt['next'] . '</span>');
- }
- else
- {
- // If they didn't enter an odd value, pretend they did.
- $PageContiguous = (int) ($modSettings['compactTopicPagesContiguous'] - ($modSettings['compactTopicPagesContiguous'] % 2)) / 2;
- // Show the "prev page" link. (>prev page< 1 ... 6 7 [8] 9 10 ... 15 next page)
- if (!empty($start) && $show_prevnext)
- $pageindex = sprintf($base_link, $start - $num_per_page, '<span class="previous_page">' . $txt['prev'] . '</span>');
- else
- $pageindex = '';
- // Show the first page. (prev page >1< ... 6 7 [8] 9 10 ... 15)
- if ($start > $num_per_page * $PageContiguous)
- $pageindex .= sprintf($base_link, 0, '1');
- // Show the ... after the first page. (prev page 1 >...< 6 7 [8] 9 10 ... 15 next page)
- if ($start > $num_per_page * ($PageContiguous + 1))
- $pageindex .= '<span class="expand_pages" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . $num_per_page . ', ' . ($start - $num_per_page * $PageContiguous) . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor = \'pointer\';"><strong> ... </strong></span>';
- // Show the pages before the current one. (prev page 1 ... >6 7< [8] 9 10 ... 15 next page)
- 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. (prev page 1 ... 6 7 >[8]< 9 10 ... 15 next page)
- if (!$start_invalid)
- $pageindex .= '<span class="current_page"><strong>' . ($start / $num_per_page + 1) . '</strong></span>';
- else
- $pageindex .= sprintf($base_link, $start, $start / $num_per_page + 1);
- // Show the pages after the current one... (prev page 1 ... 6 7 [8] >9 10< ... 15 next page)
- $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. (prev page 1 ... 6 7 [8] 9 10 >...< 15 next page)
- if ($start + $num_per_page * ($PageContiguous + 1) < $tmpMaxPages)
- $pageindex .= '<span class="expand_pages" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . ($start + $num_per_page * ($PageContiguous + 1)) . ', ' . $tmpMaxPages . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor=\'pointer\';"><strong> ... </strong></span>';
- // Show the last number in the list. (prev page 1 ... 6 7 [8] 9 10 ... >15< next page)
- if ($start + $num_per_page * $PageContiguous < $tmpMaxPages)
- $pageindex .= sprintf($base_link, $tmpMaxPages, $tmpMaxPages / $num_per_page + 1);
- // Show the "next page" link. (prev page 1 ... 6 7 [8] 9 10 ... 15 >next page<)
- if ($start != $tmpMaxPages && $show_prevnext)
- $pageindex .= sprintf($base_link, $start + $num_per_page, '<span class="next_page">' . $txt['next'] . '</span>');
- }
- return $pageindex;
- }
- /**
- * Formats a number.
- * - uses the format of number_format to decide how to format the number.
- * for example, it might display "1 234,50".
- * - caches the formatting data from the setting for optimization.
- *
- * @param float $number
- * @param bool $override_decimal_count = false
- */
- function comma_format($number, $override_decimal_count = false)
- {
- global $txt;
- static $thousands_separator = null, $decimal_separator = null, $decimal_count = null;
- // 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, (float) $number === $number ? ($override_decimal_count === false ? $decimal_count : $override_decimal_count) : 0, $decimal_separator, $thousands_separator);
- }
- /**
- * Format a time to make it look purdy.
- *
- * - returns a pretty formated version of time based on the user's format in $user_info['time_format'].
- * - applies all necessary time offsets to the timestamp, unless offset_type is set.
- * - if todayMod is set and show_today was not not specified or true, an
- * alternate format string is used to show the date with something to show it is "today" or "yesterday".
- * - performs localization (more than just strftime would do alone.)
- *
- * @param int $log_time
- * @param bool $show_today = true
- * @param string $offset_type = false
- */
- 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...
- * Faster than html_entity_decode
- *
- * - removes the base entities ( & " ' < and >. ) from text with htmlspecialchars_decode
- * - additionally converts   with str_replace
- *
- * @param string $string
- * @return the string without entities
- */
- function un_htmlspecialchars($string)
- {
- $string = htmlspecialchars_decode($string, ENT_QUOTES);
- $string = str_replace(' ', ' ', $string);
- return $string;
- }
- /**
- * Shorten a subject + internationalization concerns.
- *
- * - shortens a subject so that it is either shorter than length, or that length plus an ellipsis.
- * - respects internationalization characters and entities as one character.
- * - avoids trailing entities.
- * - returns the shortened string.
- *
- * @param string $subject
- * @param int $len
- */
- 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) . '...';
- }
- /**
- * Gets the current time with offset.
- *
- * - always applies the offset in the time_offset setting.
- *
- * @param bool $use_user_offset = true if use_user_offset is true, applies the user's offset as well
- * @param int $timestamp = null
- * @return int seconds since the unix epoch
- */
- 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;
- }
- /**
- * Calculates all the possible permutations (orders) of array.
- * should not be called on huge arrays (bigger than like 10 elements.)
- * returns an array containing each permutation.
- *
- * @param array $array
- * @return 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.
- *
- * - only parses bbc tags which are not disabled in disabledBBC.
- * - handles basic HTML, if enablePostHTML is on.
- * - caches the from/to replace regular expressions so as not to reload them every time a string is parsed.
- * - only parses smileys if smileys is true.
- * - does nothing if the enableBBC setting is off.
- * - uses the cache_id as a unique identifier to facilitate any caching it may do.
- * -returns the modified message.
- *
- * @param string $message
- * @param bool $smileys = true
- * @param string $cache_id = ''
- * @param array $parse_tags = null
- * @return string
- */
- 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 '';
- // Clean up any cut/paste issues we may have
- $message = sanitizeMSCutPaste($message);
- // If the load average is too high, don't parse the BBC.
- if (!empty($context['load_average']) && !empty($modSettings['bbc']) && $context['load_average'] >= $modSettings['bbc'])
- {
- $context['disabled_parse_bbc'] = true;
- return $message;
- }
- if ($smileys !== null && ($smileys == '1' || $smileys == '0'))
- $smileys = (bool) $smileys;
- if (empty($modSettings['enableBBC']) && $message !== false)
- {
- if ($smileys === true)
- parsesmileys($message);
- return $message;
- }
- // 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();
- }
- // Allow mods access before entering the main parse_bbc loop
- call_integration_hook('integrate_pre_parsebbc', array($message, $smileys, $cache_id, $parse_tags));
- // 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, '<div>$1</div>' elsewise.
- disabled_before: used in place of before when disabled. Defaults
- to '<div>' if block_level, '' if not.
- disabled_after: used in place of after when disabled. Defaults
- to '</div>' 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' => '<abbr title="$1">',
- 'after' => '</abbr>',
- 'quoted' => 'optional',
- 'disabled_after' => ' ($1)',
- ),
- array(
- 'tag' => 'acronym',
- 'type' => 'unparsed_equals',
- 'before' => '<acronym title="$1">',
- 'after' => '</acronym>',
- 'quoted' => 'optional',
- 'disabled_after' => ' ($1)',
- ),
- array(
- 'tag' => 'anchor',
- 'type' => 'unparsed_equals',
- 'test' => '[#]?([A-Za-z][A-Za-z0-9_\-]*)\]',
- 'before' => '<span id="post_$1">',
- 'after' => '</span>',
- ),
- array(
- 'tag' => 'b',
- 'before' => '<strong class="bbc_strong">',
- 'after' => '</strong>',
- ),
- array(
- 'tag' => 'bdo',
- 'type' => 'unparsed_equals',
- 'before' => '<bdo dir="$1">',
- 'after' => '</bdo>',
- 'test' => '(rtl|ltr)\]',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'black',
- 'before' => '<span style="color: black;" class="bbc_color">',
- 'after' => '</span>',
- ),
- array(
- 'tag' => 'blue',
- 'before' => '<span style="color: blue;" class="bbc_color">',
- 'after' => '</span>',
- ),
- array(
- 'tag' => 'br',
- 'type' => 'closed',
- 'content' => '<br />',
- ),
- array(
- 'tag' => 'center',
- 'before' => '<div align="center">',
- 'after' => '</div>',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'code',
- 'type' => 'unparsed_content',
- 'content' => '<div class="codeheader">' . $txt['code'] . ': <a href="javascript:void(0);" onclick="return smfSelectText(this);" class="codeoperation">' . $txt['code_select'] . '</a></div><pre class="bbc_code">$1</pre>',
- // @todo 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("<pre style=\"display: inline;\">\t</pre>", "\t", implode(\'\', $php_parts));
- $data = str_replace("\t", "<span style=\"white-space: pre;\">\t</span>", $data);
- // Recent Opera bug requiring temporary fix. &nsbp; is needed before </code> to avoid broken selection.
- if ($context[\'browser\'][\'is_opera\'])
- $data .= \' \';
- }'),
- 'block_level' => true,
- ),
- array(
- 'tag' => 'code',
- 'type' => 'unparsed_equals_content',
- 'content' => '<div class="codeheader">' . $txt['code'] . ': ($2) <a href="#" onclick="return smfSelectText(this);" class="codeoperation">' . $txt['code_select'] . '</a></div><pre class="bbc_code">$1</pre>',
- // @todo 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("<pre style=\"display: inline;\">\t</pre>", "\t", implode(\'\', $php_parts));
- $data[0] = str_replace("\t", "<span style=\"white-space: pre;\">\t</span>", $data[0]);
- // Recent Opera bug requiring temporary fix. &nsbp; is needed before </code> 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' => '<span style="color: $1;" class="bbc_color">',
- 'after' => '</span>',
- ),
- array(
- 'tag' => 'email',
- 'type' => 'unparsed_content',
- 'content' => '<a href="mailto:$1" class="bbc_email">$1</a>',
- // @todo Should this respect guest_hideContacts?
- 'validate' => create_function('&$tag, &$data, $disabled', '$data = strtr($data, array(\'<br />\' => \'\'));'),
- ),
- array(
- 'tag' => 'email',
- 'type' => 'unparsed_equals',
- 'before' => '<a href="mailto:$1" class="bbc_email">',
- 'after' => '</a>',
- // @todo 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' => (isBrowser('ie') ? '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="$2" height="$3"><param name="movie" value="$1" /><param name="play" value="true" /><param name="loop" value="true" /><param name="quality" value="high" /><param name="AllowScriptAccess" value="never" /><embed src="$1" width="$2" height="$3" play="true" loop="true" quality="high" AllowScriptAccess="never" /><noembed><a href="$1" target="_blank" class="new_win">$1</a></noembed></object>' : '<embed type="application/x-shockwave-flash" src="$1" width="$2" height="$3" play="true" loop="true" quality="high" AllowScriptAccess="never" /><noembed><a href="$1" target="_blank" class="new_win">$1</a></noembed>'),
- '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' => '<a href="$1" target="_blank" class="new_win">$1</a>',
- ),
- array(
- 'tag' => 'font',
- 'type' => 'unparsed_equals',
- 'test' => '[A-Za-z0-9_,\-\s]+?\]',
- 'before' => '<span style="font-family: $1;" class="bbc_font">',
- 'after' => '</span>',
- ),
- array(
- 'tag' => 'ftp',
- 'type' => 'unparsed_content',
- 'content' => '<a href="$1" class="bbc_ftp new_win" target="_blank">$1</a>',
- 'validate' => create_function('&$tag, &$data, $disabled', '
- $data = strtr($data, array(\'<br />\' => \'\'));
- if (strpos($data, \'ftp://\') !== 0 && strpos($data, \'ftps://\') !== 0)
- $data = \'ftp://\' . $data;
- '),
- ),
- array(
- 'tag' => 'ftp',
- 'type' => 'unparsed_equals',
- 'before' => '<a href="$1" class="bbc_ftp new_win" target="_blank">',
- 'after' => '</a>',
- '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' => isBrowser('ie') ? '<table style="border-collapse: collapse; border-spacing: 0;display: inline; vertical-align: middle; font: inherit;"><tr><td style="filter: Glow(color=$1, strength=$2); font: inherit;">' : '<span style="text-shadow: $1 1px 1px 1px">',
- 'after' => isBrowser('ie') ? '</td></tr></table> ' : '</span>',
- ),
- array(
- 'tag' => 'green',
- 'before' => '<span style="color: green;" class="bbc_color">',
- 'after' => '</span>',
- ),
- array(
- 'tag' => 'html',
- 'type' => 'unparsed_content',
- 'content' => '$1',
- 'block_level' => true,
- 'disabled_content' => '$1',
- ),
- array(
- 'tag' => 'hr',
- 'type' => 'closed',
- 'content' => '<hr />',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'i',
- 'before' => '<em>',
- 'after' => '</em>',
- ),
- 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' => '<img src="$1" alt="{alt}"{width}{height} class="bbc_img resized" />',
- 'validate' => create_function('&$tag, &$data, $disabled', '
- $data = strtr($data, array(\'<br />\' => \'\'));
- if (strpos($data, \'http://\') !== 0 && strpos($data, \'https://\') !== 0)
- $data = \'http://\' . $data;
- '),
- 'disabled_content' => '($1)',
- ),
- array(
- 'tag' => 'img',
- 'type' => 'unparsed_content',
- 'content' => '<img src="$1" alt="" class="bbc_img" />',
- 'validate' => create_function('&$tag, &$data, $disabled', '
- $data = strtr($data, array(\'<br />\' => \'\'));
- if (strpos($data, \'http://\') !== 0 && strpos($data, \'https://\') !== 0)
- $data = \'http://\' . $data;
- '),
- 'disabled_content' => '($1)',
- ),
- array(
- 'tag' => 'iurl',
- 'type' => 'unparsed_content',
- 'content' => '<a href="$1" class="bbc_link">$1</a>',
- 'validate' => create_function('&$tag, &$data, $disabled', '
- $data = strtr($data, array(\'<br />\' => \'\'));
- if (strpos($data, \'http://\') !== 0 && strpos($data, \'https://\') !== 0)
- $data = \'http://\' . $data;
- '),
- ),
- array(
- 'tag' => 'iurl',
- 'type' => 'unparsed_equals',
- 'before' => '<a href="$1" class="bbc_link">',
- 'after' => '</a>',
- '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' => '<div style="text-align: left;">',
- 'after' => '</div>',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'li',
- 'before' => '<li>',
- 'after' => '</li>',
- 'trim' => 'outside',
- 'require_parents' => array('list'),
- 'block_level' => true,
- 'disabled_before' => '',
- 'disabled_after' => '<br />',
- ),
- array(
- 'tag' => 'list',
- 'before' => '<ul class="bbc_list">',
- 'after' => '</ul>',
- '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' => '<ul class="bbc_list" style="list-style-type: {type};">',
- 'after' => '</ul>',
- 'trim' => 'inside',
- 'require_children' => array('li'),
- 'block_level' => true,
- ),
- array(
- 'tag' => 'ltr',
- 'before' => '<div dir="ltr">',
- 'after' => '</div>',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'me',
- 'type' => 'unparsed_equals',
- 'before' => '<div class="meaction">* $1 ',
- 'after' => '</div>',
- 'quoted' => 'optional',
- 'block_level' => true,
- 'disabled_before' => '/me ',
- 'disabled_after' => '<br />',
- ),
- array(
- 'tag' => 'move',
- 'before' => '<marquee>',
- 'after' => '</marquee>',
- 'block_level' => true,
- 'disallow_children' => array('move'),
- ),
- array(
- 'tag' => 'nobbc',
- 'type' => 'unparsed_content',
- 'content' => '$1',
- ),
- array(
- 'tag' => 'php',
- 'type' => 'unparsed_content',
- 'content' => '<span class="phpcode">$1</span>',
- '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)~\', \'~\?>((?:</(font|span)>)*)$~\'), \'$1\', $data, 2);
- // Fix the PHP code stuff...
- $data = str_replace("<pre style=\"display: inline;\">\t</pre>", "\t", $data);
- $data = str_replace("\t", "<span style=\"white-space: pre;\">\t</span>", $data);
- }'),
- 'block_level' => false,
- 'disabled_content' => '$1',
- ),
- array(
- 'tag' => 'pre',
- 'before' => '<pre>',
- 'after' => '</pre>',
- ),
- array(
- 'tag' => 'quote',
- 'before' => '<div class="quoteheader"><div class="topslice_quote">' . $txt['quote'] . '</div></div><blockquote>',
- 'after' => '</blockquote><div class="quotefooter"><div class="botslice_quote"></div></div>',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'quote',
- 'parameters' => array(
- 'author' => array('match' => '(.{1,192}?)', 'quoted' => true),
- ),
- 'before' => '<div class="quoteheader"><div class="topslice_quote">' . $txt['quote_from'] . ': {author}</div></div><blockquote>',
- 'after' => '</blockquote><div class="quotefooter"><div class="botslice_quote"></div></div>',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'quote',
- 'type' => 'parsed_equals',
- 'before' => '<div class="quoteheader"><div class="topslice_quote">' . $txt['quote_from'] . ': $1</div></div><blockquote>',
- 'after' => '</blockquote><div class="quotefooter"><div class="botslice_quote"></div></div>',
- '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' => '<div class="quoteheader"><div class="topslice_quote"><a href="' . $scripturl . '?{link}">' . $txt['quote_from'] . ': {author} ' . $txt['search_on'] . ' {date}</a></div></div><blockquote>',
- 'after' => '</blockquote><div class="quotefooter"><div class="botslice_quote"></div></div>',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'quote',
- 'parameters' => array(
- 'author' => array('match' => '(.{1,192}?)'),
- ),
- 'before' => '<div class="quoteheader"><div class="topslice_quote">' . $txt['quote_from'] . ': {author}</div></div><blockquote>',
- 'after' => '</blockquote><div class="quotefooter"><div class="botslice_quote"></div></div>',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'red',
- 'before' => '<span style="color: red;" class="bbc_color">',
- 'after' => '</span>',
- ),
- array(
- 'tag' => 'right',
- 'before' => '<div style="text-align: right;">',
- 'after' => '</div>',
- 'block_level' => true,
- ),
- array(
- 'tag' => 'rtl',
- 'before' => '<div dir="rtl">',
- 'after' => '</div>',
- 'block_level' => true,
- ),
- array(
- 'tag' => 's',
- 'before' => '<del>',
- 'after' => '</del>',
- ),
- array(
- 'tag' => 'shadow',
- 'type' => 'unparsed_commas',
- 'test' => '[#0-9a-zA-Z\-]{3,12},(left|right|top|bottom|[012…
Large files files are truncated, but you can click here to view the full file