Вход Регистрация
Файл: library/XenForo/Template/Helper/Core.php
Строк: 2055
<?php

/**
* Helper methods for the core template functions/tags.
*
* @package XenForo_Template
*/
class XenForo_Template_Helper_Core
{
    
/**
     * Holds data about style properties available to the templates.
     * The first dimension is a group of properties; the second represents
     * the actual rules. Rules should be keyed by the CSS property name
     * so they can be output directly. Prefix non-direct CSS vars with
     * an underscore.
     *
     * @var array
     */
    
protected static $_styleProperties = array();

    
/**
     * A list of invalid property accesses. If an invalid group is accessed:
     * [group] => true; if an invalid property in a valid group is accessed:
     * [group][property] => true.
     *
     * @var array
     */
    
protected static $_invalidStylePropertyAccess = array();

    
/**
     * List of display styles for user title/name markup.
     *
     * @var array [user group id] => [username_css, user_title]
     */
    
protected static $_displayStyles = array();

    
/**
     * List of thread prefixes for marking up threads.
     *
     * @var array [prefix id] => css class name
     */
    
protected static $_threadPrefixes = array();

    
/**
     * List of user titles in ladder.
     *
     * @var array
     */
    
protected static $_userTitles = array();

    
/**
     * List of user banners, highest priority to lowest.
     *
     * @var array
     */
    
protected static $_userBanners = array();

    
/**
     * Default language to send to functions that vary per language. Null
     * uses the visitor's language.
     *
     * @var array|null
     */
    
protected static $_language null;

    
/**
     * Array to cache model objects
     *
     * @var array
     */
    
protected static $_modelCache = array();

    
/**
     * Array to cache user custom field info
     *
     * @var array
     */
    
protected static $_userFieldsInfo = array();

    
/**
     * Array to cache user custom field values
     *
     * @var array
     */
    
protected static $_userFieldsValues = array();

    
/**
     * List of callbacks for the "helper" template tag. Maps the helper name (key)
     * to a callback (value).
     *
     * Data received by this callback is not escaped!
     *
     * @var array
     */
    
public static $helperCallbacks = array(
        
'avatar'            => array('self''helperAvatarUrl'),
        
'avatarcropcss'     => array('self''helperAvatarCropCss'),
        
'username'          => array('self''helperUserName'),
        
'usertitle'         => array('self''helperUserTitle'),
        
'userbanner'        => array('self''helperUserBanner'),
        
'richusername'      => array('self''helperRichUserName'),
        
'userblurb'         => array('self''helperUserBlurb'),
        
'sortarrow'         => array('self''helperSortArrow'),
        
'json'              => array('self''helperJson'),
        
'clearfix'          => array('XenForo_ViewRenderer_Css''helperClearfix'),
        
'cssimportant'      => array('XenForo_ViewRenderer_Css''helperCssImportant'),
        
'snippet'           => array('self''helperSnippet'),
        
'bodytext'          => array('self''helperBodyText'),
        
'bbcode'            => array('self''helperBbCode'),
        
'highlight'         => array('XenForo_Helper_String''highlightSearchTerm'),
        
'striphtml'         => array('self''helperStripHtml'),
        
'linktitle'         => array('XenForo_Link''buildIntegerAndTitleUrlComponent'),
        
'wrap'              => array('self''helperWrap'),
        
'wordtrim'          => array('self''helperWordTrim'),
        
'pagenumber'        => array('self''helperPageNumber'),
        
'dump'              => array('self''helperDump'),
        
'type'              => array('self''helperType'),
        
'strlen'            => array('self''helperStrlen'),
        
'implode'           => array('self''helperImplode'),
        
'rgba'                => array('XenForo_Helper_Color''rgba'),
        
'unrgba'            => array('XenForo_Helper_Color''unrgba'),
        
'fullurl'           => array('XenForo_Link''convertUriToAbsoluteUri'),
        
'ismemberof'        => array('self''helperIsMemberOf'),
        
'twitterlang'       => array('self''helperTwitterLanguage'),
        
'listitemid'        => array('XenForo_Template_Helper_Admin''getListItemId'),
        
'threadprefix'      => array('self''helperThreadPrefix'),
        
'threadprefixgroup' => array('self''helperThreadPrefixGroup'),
        
'ignoredcss'        => array('self''helperIgnoredCss'),
        
'isignored'         => array('self''helperIsIgnored'),
        
'nodeclasses'       => array('self''helperNodeClasses'),
        
'ip'                => array('self''helperIp'),
        
'copyright'         => array('XenForo_Application''getCopyrightHtml'),

        
'avatarhtml'        => array('self''helperAvatarHtml'),
        
'datetimehtml'      => array('self''helperDateTimeHtml'),
        
'followhtml'        => array('self''helperFollowHtml'),
        
'likeshtml'         => array('self''helperLikesHtml'),
        
'pagenavhtml'       => array('self''helperPageNavHtml'),
        
'usernamehtml'      => array('self''helperUserNameHtml'),

        
'userfieldtitle'    => array('self''helperUserFieldTitle'),
        
'userfieldvalue'    => array('self''helperUserFieldValue'),

        
'javascripturl'     => array('self''helperJavaScriptUrl'),

        
'uniqueid'          => array('self''helperUniqueId')
    );

    
/**
     * List of callbacks for the "string" template tag. To support chaining, the
     * called function should tag the string as the first argument and only require
     * one argument. If neither constraint is true, the function must be called on its own.
     *
     * Data received by this call back may be escaped!
     *
     * @var array
     */
    
public static $stringCallbacks = array(
        
'repeat'   => 'str_repeat',
        
'nl2br'    => 'nl2br',
        
'trim'     => 'trim',
        
'strtoupper' => 'utf8_strtoupper',
        
'strtolower' => 'utf8_strtolower',
        
'censor'   => array('XenForo_Helper_String''censorStringTemplateHelper'),
        
'wordtrim' => array('XenForo_Helper_String''wholeWordTrim'),
        
'autolink' => array('XenForo_Helper_String''autoLinkPlainText'),
        
'wrap'     => array('XenForo_Helper_String''wordWrapString')
    );

    
/**
    * Private constructor. Don't instantiate this object. Use it statically.
    */
    
private function __construct() {}

    
/**
     * Appends bread crumb entries to the existing list of bread crumbs.
     *
     * @param array $existing Existing entries
     * @param array $new New entries; if this is invalid, nothing will be appended
     *
     * @return array Existing entries with new entries appended
     */
    
public static function appendBreadCrumbs(array $existing$new)
    {
        if (!
is_array($new))
        {
            return 
$existing;
        }

        foreach (
$new AS $breadCrumb)
        {
            if (isset(
$breadCrumb['value']))
            {
                
$breadCrumb['value'] = htmlspecialchars($breadCrumb['value']);
            }

            
$existing[] = $breadCrumb;
        }

        return 
$existing;
    }

    
/**
     * Returns the content in its raw format only if it meets the specified condition.
     *
     * @param mixed $data
     * @param string $condition "Object" to return any object raw, otherwise a class name
     *
     * @return mixed Data escaped if needed
     */
    
public static function rawCondition($data$condition 'object')
    {
        if (!
$condition || !is_string($condition))
        {
            return 
$data;
        }

        switch (
$condition)
        {
            case 
'object':
                if (
is_object($data))
                {
                    return 
$data;
                }
                break;

            default:
                if (
$data instanceof $condition)
                {
                    return 
$data;
                }
                break;
        }

        return 
htmlspecialchars($data);
    }

    
/**
    * Date formatting. Format represents a string name of a format.
    *
    * @param integer Unix timestamp to format
    * @param string  Named format (name options TBD)
    *
    * @return string
    */
    
public static function date($timestamp$format null)
    {
        return 
XenForo_Locale::date($timestamp$formatself::$_language);
    }

    
/**
    * Time formatting. Format represents a string name of a format.
    *
    * @param integer Unix timestamp to format
    * @param string  Named format (name options TBD)
    *
    * @return string
    */
    
public static function time($timestamp$format null)
    {
        return 
XenForo_Locale::time($timestamp$formatself::$_language);
    }

    
/**
    * Date and time formatting. Format represents a string name of a format.
    *
    * @param integer Unix timestamp to format
    * @param string  Named format (name options TBD)
    *
    * @return string
    */
    
public static function dateTime($timestamp$format null)
    {
        if (
$format == 'html')
        {
            return 
self::callHelper('datetimehtml', array($timestamp));
        }
        else
        {
            return 
XenForo_Locale::dateTime($timestamp$formatself::$_language);
        }
    }

    
/**
     * Returns an <abbr> tag with a date suitable for Javascript refreshing
     *
     * @param integer $timestamp
     * @param array $attributes
     *
     * @return string <abbr class="DateTime" data-unixtime="$timestamp"...
     */
    
public static function helperDateTimeHtml($timestamp$attributes = array())
    {
        
$class = (empty($attributes['class']) ? '' ' ' htmlspecialchars($attributes['class']));

        unset(
$attributes['time'], $attributes['class']);

        
$attribs self::getAttributes($attributes);

        
$time XenForo_Locale::dateTime($timestamp'separate'self::$_language);

        if (
$time['relative'])
        {
            
$tag 'abbr';
            
$data ' data-time="' $timestamp '" data-diff="' . (XenForo_Application::$time $timestamp)
                . 
'" data-datestring="' $time['date'] . '" data-timestring="' $time['time'] . '"';
            
$value $time['string'];
        }
        else
        {
            
$tag 'span';
            if (!isset(
$attributes['title']))
            {
                
$data ' title="' $time['string'] . '"'// empty this to remove tooltip from non-relative dates
            
}
            else
            {
                
$data '';
            }
            
$value $time['date'];
        }

        
$rtlPrefix XenForo_Locale::getRtlDateTimeMarker(self::$_language);

        return 
"<{$tag} class="DateTime{$class}"{$attribs}{$data}>$rtlPrefix{$value}</{$tag}>";
    }

    
/**
     * Returns a hyperlink to follow or unfollow a user. Designed to work with the XenForo.FollowLink javascript.
     *
     * @param array $user
     * @param array $attributes
     * @param string $wrapTag
     *
     * @return string
     */
    
public static function helperFollowHtml(array $user, array $attributes$wrapTag '')
    {
        
$visitor XenForo_Visitor::getInstance();

        if (!
$visitor['user_id'] || $visitor['user_id'] == $user['user_id'])
        {
            return 
'';
        }

        if (
$visitor->isFollowing($user['user_id']))
        {
            
$action 'unfollow';
        }
        else
        {
            
$action 'follow';
        }

        
$link =  XenForo_Link::buildPublicLink("members/$action"$user, array('_xfToken' => $visitor['csrf_token_page']));

        
$class = (empty($attributes['class']) ? '' ' ' htmlspecialchars($attributes['class']));

        unset(
$attributes['user'], $attributes['class']);

        if (!isset(
$attributes['title']) && isset($user['following']))
        {
            if (
self::_getModelFromCache('XenForo_Model_User')->isFollowing($visitor['user_id'], $user))
            {
                
$attributes['title'] = new XenForo_Phrase('user_is_following_you', array('user' => $user['username']));
            }
            else
            {
                
$attributes['title'] = new XenForo_Phrase('user_is_not_following_you', array('user' => $user['username']));
            }
        }

        if (!empty(
$attributes['tag']))
        {
            
$tag $attributes['tag'];
            unset(
$attributes['tag']);
        }

        
$attribs self::getAttributes($attributes);

        
$link "<a href="{$link}" class="FollowLink{$class}"  {$attribs}>" . new XenForo_Phrase($action) . '</a>';

        if (!empty(
$tag))
        {
            return 
"<$tag>$link</$tag>";
        }
        else
        {
            return 
$link;
        }
    }

    
/**
     * Returns an HTML string declaring who likes something
     *
     * @param integer Total number of likes
     * @param string Link to page showing all users who liked this content
     * @param integer Timestamp at which the visitor liked this content
     * @param array Array of up to 3 users who liked this content - user_id, username required.
     *
     * @return string
     */
    
public static function helperLikesHtml($number$likesLink$likeDate 0, array $users = array())
    {
        
$number intval($number);

        if (empty(
$users))
        {
            if (
$number 1)
            {
                return new 
XenForo_Phrase('likes_x_people_like_this', array(
                    
'likes' => self::numberFormat($number),
                    
'likesLink' => $likesLink
                
));
            }
            else
            {
                return new 
XenForo_Phrase('likes_1_person_likes_this', array(
                    
'likes' => self::numberFormat($number),
                    
'likesLink' => $likesLink
                
));
            }
        }

        
$userCount count($users);
        if (
$userCount && $number $userCount// indicates some users are deleted
        
{
            for (
$i 0$i $number$i++)
            {
                if (empty(
$users[$i]))
                {
                    
$users[$i] = array(
                        
'user_id' => 0,
                        
'username' => new XenForo_Phrase('deleted_user_parentheses'// costs a query, but edge case
                    
);
                }
            }
        }

        if (
$likeDate)
        {
            
$youLikeThis true;

            
$visitorId XenForo_Visitor::getUserId();
            foreach (
$users AS $key => $user)
            {
                if (
$user['user_id'] == $visitorId)
                {
                    unset(
$users[$key]);
                    break;
                }
            }

            
$users array_values($users);

            if (
count($users) == 3)
            {
                unset(
$users[2]);
            }
        }
        else
        {
            
$youLikeThis false;
        }

        
$user1 $user2 $user3 '';

        if (
$users[0])
        {
            
$user1 self::callHelper('username', array($users[0]));

            if (
$users[1])
            {
                
$user2 self::callHelper('username', array($users[1]));

                if (
$users[2])
                {
                    
$user3 self::callHelper('username', array($users[2]));
                }
            }
        }

        
$phraseParams = array(
            
'user1' => $user1,
            
'user2' => $user2,
            
'user3' => $user3,
            
'others' => self::numberFormat($number 3),
            
'likesLink' => $likesLink
        
);

        switch (
$number)
        {
            case 
1: return new XenForo_Phrase(($youLikeThis
                
'likes_you_like_this'
                
'likes_user1_likes_this'), $phraseParamsfalse);

            case 
2: return new XenForo_Phrase(($youLikeThis
                
'likes_you_and_user1_like_this'
                
'likes_user1_and_user2_like_this'), $phraseParamsfalse);

            case 
3: return new XenForo_Phrase(($youLikeThis
                
'likes_you_user1_and_user2_like_this'
                
'likes_user1_user2_and_user3_like_this'), $phraseParamsfalse);

            case 
4: return new XenForo_Phrase(($youLikeThis
                
'likes_you_user1_user2_and_1_other_like_this'
                
'likes_user1_user2_user3_and_1_other_like_this'), $phraseParamsfalse);

            default: return new 
XenForo_Phrase(($youLikeThis
                
'likes_you_user1_user2_and_x_others_like_this'
                
'likes_user1_user2_user3_and_x_others_like_this'), $phraseParamsfalse);
        }
    }

    
/**
     * Sets the default language for language-specific calls. This should
     * be unset after the specific context is complete.
     *
     * @param array|null $language
     */
    
public static function setDefaultLanguage(array $language null)
    {
        
self::$_language $language;
    }

    
/**
     * Gets the default language.
     *
     * @return array|null
     */
    
public static function getDefaultLanguage()
    {
        return 
self::$_language;
    }

    
/**
    * Escape a string for use within JavaScript. The context represents whether
    * it is within a double- or single-quoted string. This function does not
    * support outputs in other contexts!
    *
    * @param string String to escape
    * @param string Context (double or single)
    *
    * @return string
    */
    
public static function jsEscape($string$context 'double')
    {
        
$quote = ($context == 'double' '"' "'");

        
$string str_replace(
            array(
'\',   $quote,        "r",  "n",  '</'),
            array('
\\', '\' . $quote, "\r", "\n", '<\/'),
            $string
        );

        $string = preg_replace('
/-(?=-)/', '-\', $string);

        return $string;
    }

    /**
     * Generates a link to the specified type of public data.
     *
     * @param string $type Type of data to link to. May also include a specific action.
     * @param mixed $data Primary data about this link
     * @param array $extraParams Extra named params. Unhandled params will become the query string
     * @param callback|false $escapeCallback Callback method for escaping the link
     *
     * @return string
     */
    public static function link($type, $data = null, array $extraParams = array(), $escapeCallback = '
htmlspecialchars')
    {
        $link = XenForo_Link::buildPublicLink($type, $data, $extraParams);
        if ($escapeCallback == '
htmlspecialchars')
        {
            $link = htmlspecialchars($link, ENT_QUOTES, '
UTF-8');
        }
        else if ($escapeCallback)
        {
            $link = call_user_func($escapeCallback, $link);
        }

        return $link;
    }

    /**
     * Generates an link to the specified type of admin data.
     *
     * @param string $type Type of data to link to. May also include a specific action.
     * @param mixed $data Primary data about this link
     * @param array $extraParams Extra named params. Unhandled params will become the query string
     * @param callback|false $escapeCallback Callback method for escaping the link
     *
     * @return string
     */
    public static function adminLink($type, $data = null, array $extraParams = array(), $escapeCallback = '
htmlspecialchars')
    {
        $link = XenForo_Link::buildAdminLink($type, $data, $extraParams);
        if ($escapeCallback)
        {
            $link = call_user_func($escapeCallback, $link);
        }

        return $link;
    }

    /**
     * Wrapper function for handling pagenav template tags (not functions). This simply
     * ensures that some of the data is of the expected type.
     *
     * @param string $callType Type of page to render: admin or public
     * @param integer $perPage Items to display per page
     * @param integer $totalItems Total number of items
     * @param integer $page Current page number
     * @param string $linkType Type of link to create
     * @param mixed $linkData Data for the link
     * @param array $linkParams List of key value params for the link; page will be set as needed
     * @param string|false $unreadLink URL for to jump to the first unread
     * @param array $options Options to control the building
     *
     * @return string
     */
    public static function helperPageNavHtml($callType, $perPage, $totalItems, $page, $linkType,
        $linkData = false, $linkParams = false, $unreadLink = false, $options = false)
    {
        if (!is_array($linkParams))
        {
            $linkParams = array();
        }
        if (!is_array($options))
        {
            $options = array();
        }

        if ($unreadLink)
        {
            $options['
unreadLink'] = $unreadLink;
        }

        if ($callType == '
admin')
        {
            return self::adminPageNav($perPage, $totalItems, $page, $linkType, $linkData, $linkParams, $options);
        }
        else
        {
            return self::pageNav($perPage, $totalItems, $page, $linkType, $linkData, $linkParams, $options);
        }
    }

    /**
     * Gets the page navigation for a public page.
     *
     * @param integer $perPage Items to display per page
     * @param integer $totalItems Total number of items
     * @param integer $page Current page number
     * @param string $linkType Type of link to create
     * @param mixed $linkData Data for the link
     * @param array $linkParams List of key value params for the link; page will be set as needed
     * @param array $options Options to control the building
     *
     * @return string|XenForo_Template_Public
     */
    public static function pageNav($perPage, $totalItems, $page, $linkType,
        $linkData = null, array $linkParams = array(), array $options = array()
    )
    {
        return self::_getPageNav('
XenForo_Template_Public', 'link', $perPage, $totalItems, $page,
            $linkType, $linkData, $linkParams, $options
        );
    }

    /**
     * Gets the page navigation for an admin page.
     *
     * @param integer $perPage Items to display per page
     * @param integer $totalItems Total number of items
     * @param integer $page Current page number
     * @param string $linkType Type of link to create
     * @param mixed $linkData Data for the link
     * @param array $linkParams List of key value params for the link; page will be set as needed
     * @param array $options Options to control the building
     *
     * @return string|XenForo_Template_Public
     */
    public static function adminPageNav($perPage, $totalItems, $page, $linkType,
        $linkData = null, array $linkParams = array(), array $options = array()
    )
    {
        return self::_getPageNav('
XenForo_Template_Admin', 'adminLink', $perPage, $totalItems, $page,
            $linkType, $linkData, $linkParams, $options
        );
    }

    /**
     * Helper to get page navigation (all pages, for scrolling pagenav version).
     *
     * @param string $templateClass Name of the template class to instantiate
     * @param string $linkFunction Name of the linking function to call (in this class)
     * @param integer $perPage Items to display per page
     * @param integer $totalItems Total number of items
     * @param integer $currentPage Current page number
     * @param string $linkType Type of link to create
     * @param mixed $linkData Data for the link
     * @param array $linkParams List of key value params for the link; page will be set as needed
     * @param array $options Options to control the building
     *
     * @return string|XenForo_Template_Abstract
     */
    protected static function _getPageNav($templateClass, $linkFunction, $perPage, $totalItems, $currentPage,
        $linkType, $linkData = null, array $linkParams = array(), array $options = array()
    )
    {
        // abort if there are insufficient items to make multiple pages
        if ($totalItems < 1 || $perPage < 1)
        {
            return '';
        }

        $options = array_merge(
            array(
                '
unreadLink' => '',
                '
template' => 'page_nav',
                '
displayRange' => 2 //TODO: make this come from an option?
            ),
            $options
        );
        $unreadLinkHtml = htmlspecialchars($options['
unreadLink'], ENT_COMPAT, 'utf-8', false);

        $pageTotal = ceil($totalItems / $perPage);

        // abort if there is only one page
        if ($pageTotal <= 1)
        {
            if (!empty($options['
unreadLink']))
            {
                return new $templateClass($options['
template'], array(
                    '
unreadLinkHtml' => $unreadLinkHtml,
                    '
pageTotal' => $pageTotal
                ));
            }

            return '';
        }

        $currentPage = min(max($currentPage, 1), $pageTotal);

        // number of pages either side of the current page
        $range = $options['
displayRange'];
        $scrollSize = 1 + 2 * $range;
        $scrollThreshold = $scrollSize + 2;

        if ($pageTotal > $scrollThreshold)
        {
            $startPage = max(2, $currentPage - $range);
            $endPage = min($pageTotal, $startPage + $scrollSize);

            $extraPages = $scrollSize - ($endPage - $startPage);
            if ($extraPages > 0)
            {
                $startPage -= $extraPages;
            }
        }
        else
        {
            $startPage = 2;
            $endPage = $pageTotal;
        }

        if ($endPage > $startPage)
        {
            $endPage--;
            $pages = range($startPage, $endPage);
        }
        else
        {
            $pages = array();
        }

        if (isset($linkParams['
_params']) && is_array($linkParams['_params']))
        {
            $tempParams = $linkParams['
_params'];
            unset($linkParams['
_params']);
            $linkParams = array_merge($tempParams, $linkParams);
        }

        $templateVariables = array(
            '
pageTotal' => intval($pageTotal),
            '
currentPage' => $currentPage,

            '
pages' => $pages,
            '
range' => $range,
            '
scrollThreshold' => $scrollThreshold,

            '
startPage' => $startPage,
            '
endPage' => $endPage,

            '
prevPage' => ($currentPage > 1 ? ($currentPage - 1) : false),
            '
nextPage' => ($currentPage < $pageTotal ? ($currentPage + 1) : false),

            '
pageNumberSentinel' => XenForo_Application::$integerSentinel,

            '
linkType' => $linkType,
            '
linkData' => $linkData,
            '
linkParams' => $linkParams,

            '
maxDigits' => strlen($pageTotal),

            '
unreadLinkHtml' => $unreadLinkHtml
        );

        $template = new $templateClass($options['
template'], $templateVariables);

        return $template;
    }

    /**
     * Formats a number based on current user'
s languageBehaves like PHP's number_format.
     *
     * @param mixed $number
     * @param integer $decimals
     *
     * @return string
     */
    public static function numberFormat($number, $decimals = 0)
    {
        return XenForo_Locale::numberFormat($number, $decimals, self::$_language);
    }

    /**
     * Calls a general helper as listed in the helper callbacks.
     *
     * @param string $helper Name of helper
     * @param array $args All arguments passed to the helper.
     *
     * @return string
     */
    public static function callHelper($helper, array $args)
    {
        $helper = strtolower(strval($helper));
        if (!isset(self::$helperCallbacks[$helper]))
        {
            return '';
        }

        return call_user_func_array(self::$helperCallbacks[$helper], $args);
    }

    /**
     * Helper to get the user title for the specified user.
     *
     * @param array $user
     * @param boolean $allowCustomTitle Allows the user title to come from the custom title
     * @param boolean $withBanner Set to true if being displayed next to a user banner; title will be hidden if necessary
     *
     * @return string
     */
    public static function helperUserTitle($user, $allowCustomTitle = true, $withBanner = false)
    {
        if (!is_array($user) || !array_key_exists('
display_style_group_id', $user))
        {
            return '';
        }

        if ($allowCustomTitle && !empty($user['
custom_title']))
        {
            return htmlspecialchars($user['
custom_title']);
        }

        if (empty($user['
user_id']))
        {
            $user['
display_style_group_id'] = XenForo_Model_User::$defaultGuestGroupId;
            $user['
user_group_id'] = XenForo_Model_User::$defaultGuestGroupId;
            $user['
secondary_group_ids'] = '';
        }

        $bannerOpt = XenForo_Application::getOptions()->userBanners;
        if ($withBanner && $bannerOpt['
hideUserTitle'])
        {
            if (!empty($user['
is_staff']) && !empty($bannerOpt['showStaff']))
            {
                return '';
            }

            $memberGroupIds = array($user['
user_group_id']);
            if (!empty($user['
secondary_group_ids']))
            {
                $memberGroupIds = array_merge($memberGroupIds, explode('
,', $user['secondary_group_ids']));
            }

            foreach (self::$_userBanners AS $groupId => $banner)
            {
                if (!in_array($groupId, $memberGroupIds))
                {
                    continue;
                }

                return '';
            }
        }

        if (isset($user['
display_style_group_id']) && isset(self::$_displayStyles[$user['display_style_group_id']]))
        {
            $style = self::$_displayStyles[$user['
display_style_group_id']];
            if ($style['
user_title'] !== '')
            {
                return $style['
user_title'];
            }
        }

        if (empty($user['
user_id']) || !isset($user['trophy_points']))
        {
            return ''; // guest user title or nothing
        }

        foreach (self::$_userTitles AS $points => $title)
        {
            if ($user['
trophy_points'] >= $points)
            {
                return $title;
            }
        }

        return '';
    }

    /**
     * Sets the user titles in the ladder.
     *
     * @param array $userTitles
     */
    public static function setUserTitles(array $userTitles)
    {
        self::$_userTitles = $userTitles;
    }

    /**
     * Helper to get the user banner for the specified user.
     *
     * @param array $user
     * @param boolean $allowCustomTitle Allows the user title to come from the custom title
     * @param boolean $disableStacking If true, stacking is always disabled (highest priority title used)
     *
     * @return string
     */
    public static function helperUserBanner($user, $extraClass = '', $disableStacking = false)
    {
        if (!is_array($user) || !array_key_exists('
user_group_id', $user) || !array_key_exists('secondary_group_ids', $user))
        {
            return '';
        }

        if (empty($user['
user_id']))
        {
            $user['
user_group_id'] = XenForo_Model_User::$defaultGuestGroupId;
            $user['
secondary_group_ids'] = '';
        }

        $banners = array();
        $opt = XenForo_Application::getOptions()->userBanners;

        if (!empty($user['
is_staff']) && !empty($opt['showStaff']))
        {
            $p = new XenForo_Phrase('
staff_member');
            $banners['
staff'] = '<em class="userBanner bannerStaff ' . $extraClass . '" itemprop="title"><span class="before"></span><strong>' . $p . '</strong><span class="after"></span></em>';
        }

        $memberGroupIds = array($user['
user_group_id']);
        if (!empty($user['
secondary_group_ids']))
        {
            $memberGroupIds = array_merge($memberGroupIds, explode('
,', $user['secondary_group_ids']));
        }

        foreach (self::$_userBanners AS $groupId => $banner)
        {
            if (!in_array($groupId, $memberGroupIds))
            {
                continue;
            }

            $banners[$groupId] = '
<em class="' . $banner['class'] . ' ' . $extraClass . '" itemprop="title"><span class="before"></span><strong>' . $banner['text'] . '</strong><span class="after"></span></em>';
        }

        if (!$banners)
        {
            return '';
        }

        if ($opt['
displayMultiple'] && !$disableStacking)
        {
            return implode("n", $banners);
        }
        else if ($opt['
showStaffAndOther'] && isset($banners['staff']) && count($banners) >= 2)
        {
            $staffBanner = $banners['
staff'];
            unset($banners['
staff']);
            return $staffBanner . "n" . reset($banners);
        }
        else
        {
            return reset($banners);
        }
    }

    /**
     * Sets the user banners, highest priority to lowest.
     *
     * @param array $banners
     */
    public static function setUserBanners(array $banners)
    {
        self::$_userBanners = $banners;
    }

    /**
     * Outputs the necessary HTML for a rich username (includes the display style markup class).
     *
     * @param array $user
     * @param string Alternative username HTML
     *
     * @return string
     */
    public static function helperRichUserName(array $user, $usernameHtml = '')
    {
        if (!is_array($user) || (!isset($user['
username']) && $usernameHtml === ''))
        {
            return '';
        }

        if ($usernameHtml === '')
        {
            $usernameHtml = htmlspecialchars($user['
username']);
        }

        if (empty($user['
user_id']))
        {
            $user['
display_style_group_id'] = XenForo_Model_User::$defaultGuestGroupId;
        }

        $classes = array();

        if (isset($user['
display_style_group_id']) && isset(self::$_displayStyles[$user['display_style_group_id']]))
        {
            $style = self::$_displayStyles[$user['
display_style_group_id']];
            if ($style['
username_css'])
            {
                $classes[] = '
style' . $user['display_style_group_id'];
            }
        }

        if (!empty($user['
is_banned']) && XenForo_Visitor::getInstance()->hasPermission('general', 'bypassUserPrivacy'))
        {
            $classes[] = '
banned';
        }

        return $classes
            ? '
<span class="' . implode(' ', $classes) . '">' . $usernameHtml . '</span>'
            : $usernameHtml;
    }

    /**
     * Helper, for the user blurb "Title, gender, age, from location".
     *
     * @param array $user
     * @param boolean Include user title in blurb
     *
     * @return string
     */
    public static function helperUserBlurb(array $user, $includeUserTitle = true)
    {
        if (!is_array($user) || empty($user['
user_id']))
        {
            return '';
        }

        $parts = array();

        if ($includeUserTitle && $userTitle = self::callHelper('
usertitle', array($user)))
        {
            $parts[] = '
<span class="userTitle" itemprop="title">' . $userTitle . '</span>';
        }

        if (!empty($user['
gender']))
        {
            $parts[] = new XenForo_Phrase($user['
gender']);
        }

        if (!isset($user['
age']) && !empty($user['show_dob_year']) && !empty($user['dob_year']))
        {
            $user['
age'] = self::_getModelFromCache('XenForo_Model_UserProfile')->getUserAge($user);
        }

        if (!empty($user['
age']))
        {
            $parts[] = $user['
age'];
        }

        if (!empty($user['
location']))
        {
            $user['
locationCensored'] = XenForo_Helper_String::censorString($user['location']);

            $location = '
<a href="'
                . XenForo_Link::buildPublicLink('misc/location-info', '', array('location' => 
$user['locationCensored']))
                . '" 
class="concealed" target="_blank" rel="nofollow">'
                . htmlspecialchars($user['
locationCensored'])
                . '
</a>';

            $parts[] = new XenForo_Phrase('
from_x_location', array('location' => $location), false);
        }

        return implode('
', $parts);
    }

    /**
     * Sets the display styles.
     *
     * @param array $displayStyles
     */
    public static function setDisplayStyles(array $displayStyles)
    {
        self::$_displayStyles = $displayStyles;
    }

    /**
     * Helper to print out the sort arrow.
     *
     * @param string $order Name of the current ordering field
     * @param string $direction Direction (asc, desc)
     * @param string $fieldName Name of the field we'
re looking at
     
* @param string $descOutput HTML to output for descending
     
* @param string $ascOutput HTML to output for ascending
     
*
     * @return 
string
     
*/
    public static function 
helperSortArrow($order$direction$fieldName$descOutput ' &darr;'$ascOutput ' &uarr;')
    {
        if (
$order != $fieldName)
        {
            return 
'';
        }
        else if (
$direction == 'desc')
        {
            return 
$descOutput;
        }
        else
        {
            return 
$ascOutput;
        }
    }

    
/**
     * Strips BB Code from a string and word-trims it to a given max length around an optional search term
     *
     * @param string $string Input text (bb code)
     * @param integer $maxLength
     * @param array $options Key-value options
     *
     * @return string HTML
     */
    
public static function helperSnippet($string$maxLength 0, array $options = array())
    {
        
$options array_merge(array(
            
'term' => '',
            
'fromStart' => 0,
            
'emClass' => '',
            
'stripQuote' => false,
            
'stripHtml' => false,
            
'stripPlainTag' => false
        
), $options);

        if (
$options['stripHtml'])
        {
            
$string strip_tags($string);
        }
        else if (
$options['stripPlainTag'])
        {
            
$string preg_replace(
                
'#(?<=^|s|[](,]|--|@)@[(d+):('|"|&quot;|)(.*)\2]#iU',
                '\3',
                
$string
            );
        }
        else
        {
            
$string = XenForo_Helper_String::bbCodeStrip($string$options['stripQuote']);
        }

        if (
$maxLength)
        {
            if (
$options['fromStart'])
            {
                
$string = XenForo_Helper_String::wholeWordTrim($string$maxLength);
            }
            else
            {
                
$string = XenForo_Helper_String::wholeWordTrimAroundSearchTerm($string$maxLength$options['term']);
            }
        }

        
$string = trim($string);
        
$string = XenForo_Helper_String::censorString($string);

        if (
$options['term'] && $options['emClass'])
        {
            return XenForo_Helper_String::highlightSearchTerm(
$string$options['term'], $options['emClass']);
        }
        else
        {
            return htmlspecialchars(
$string);
        }
    }

    /**
     * Prepares simple body text with word wrap, censoring, and nl2br.
     * HTML/BB code is not parsed within string.
     *
     * @param string 
$string
     *
     * @return string
     */
    public static function helperBodyText(
$string)
    {
        
$string = XenForo_Helper_String::censorString($string);
        
$string = htmlspecialchars($string);
        
$string = XenForo_Helper_String::autoLinkPlainText($string);
        
$string = XenForo_Helper_String::linkTaggedPlainText($string);

        return nl2br(
$string);
    }

    /**
     * Helper to render the specified text as BB code.
     *
     * @param XenForo_BbCode_Parser 
$parser
     * @param string 
$text
     *
     * @return string
     */
    public static function helperBbCode(
$parser$text)
    {
        if (!(
$parser instanceof XenForo_BbCode_Parser))
        {
            trigger_error('BB code parser not specified correctly.', E_USER_WARNING);
            return '';
        }
        else
        {
            return 
$parser->render($text);
        }
    }

    /**
     * Strips HTML from the text and then HTML escapes it without double encoding.
     *
     * @param string 
$string
     * @param string 
$allowedTags List of allowed tags for strip_tags
     *
     * @return string String with HTML removed or escaped if not removed.
     */
    public static function helperStripHtml(
$string$allowedTags = '')
    {
        return htmlspecialchars(strip_tags(
$string$allowedTags), ENT_COMPAT, null, false);
    }

    /**
     * Wraps and HTML escapes the given string.
     *
     * @param string 
$string
     * @param integer|null 
$breakLength
     */
    public static function helperWrap(
$string$breakLength)
    {
        return htmlspecialchars(XenForo_Helper_String::wordWrapString(
$string$breakLength));
    }

    /**
     * Word trims and HTML escapes the given string.
     *
     * @param string 
$string
     * @param integer 
$trimLength
     */
    public static function helperWordTrim(
$string$trimLength)
    {
        return htmlspecialchars(XenForo_Helper_String::wholeWordTrim(
$string$trimLength));
    }

    /**
     * Returns the string ' | Page 
$page' if $page is greater than one.
     *
     * @param integer 
$page
     *
     * @return string
     */
    public static function helperPageNumber(
$page)
    {
        
$page = intval($page);

        if (
$page > 1)
        {
            return htmlspecialchars(new XenForo_Phrase('page_x', array('page' => 
$page)));
        }
    }

    public static function helperDump(
$data)
    {
        return Zend_Debug::dump(
$data, null, false);
    }

    public static function helperType(
$data)
    {
        return gettype(
$data);
    }

    public static function helperStrlen(
$string$unicode = false)
    {
        return 
$unicode ? utf8_strlen($string) : strlen($string);
    }

    public static function helperImplode(
$data$glue = ' ')
    {
        if (is_array(
$glue))
        {
            
$temp = $data;
            
$data = $glue;
            
$glue = $temp;
        }

        
$data = array_map('htmlspecialchars', $data);
        return implode(
$glue$data);
    }

    public static function helperIsMemberOf(array 
$user$userGroupId$multipleIds = null)
    {
        if (!is_null(
$multipleIds))
        {
            // check multiple groups
            
$userGroupId = array_slice(func_get_args(), 1);
        }

        return self::_getModelFromCache('XenForo_Model_User')->isMemberOfUserGroup(
$user$userGroupId);
    }

    public static function helperTwitterLanguage(
$locale)
    {
        
$lang = strtolower(substr($locale, 0, 2));

        switch (
$lang)
        {
            case 'en':
            case 'fr':
            case 'de':
            case 'it':
            case 'es':
            case 'ko':
            case 'ja':
                return 
$lang;

            default:
                return 'en';
        }
    }

    /**
     * Performs string manipulation functions. Function list is a string
     * that may be delimited by " " (eg, 'nl2br trim'). Chained functions will
     * be run from left to right. Chaining can only work when a single argument
     * is provided. Functions requiring multiple args need separate calls.
     *
     * @param string 
$functionList
     * @param array 
$args
     *
     * @return string
     */
    public static function string(
$functionList, array $args)
    {
        
$functions = explode(' ', strval($functionList));
        if (count(
$functions) > 1 && count($args) > 1)
        {
            return '';
        }

        foreach (
$functions AS $function)
        {
            
$function = strtolower(trim($function));
            if (!isset(self::
$stringCallbacks[$function]))
            {
                continue;
            }

            
$args = array(call_user_func_array(self::$stringCallbacks[$function]$args));
        }

        return 
$args[0];
    }

    /**
     * Outputs a style property or a group of style properties. See
     * {@link 
$_styleProperties} for more information on the format.
     * The property name may be in format "
group" or "group.rule". Scalar
     * properties cannot have a rule.
     *
     * If no rule is specified, an entire group will be outputted, including
     * rule names. If a rule is specified, only the value will be output.
     *
     * @param string 
$propertyName
     *
     * @return string
     */
    public static function styleProperty(
$propertyName)
    {
        
$props = self::$_styleProperties;

        
$parts = explode('.', $propertyName, 2);
        if (!empty(
$parts[1]))
        {
            
$propertyName = $parts[0];
            
$propertyComponent = $parts[1];
        }
        else
        {
            
$propertyComponent = '';
        }

        if (!isset(
$props[$propertyName]))
        {
            self::
$_invalidStylePropertyAccess[$propertyName] = true;
            return '';
        }

        
$property = $props[$propertyName];
        if (!is_array(
$property))
        {
            // scalar property ...
            if (
$propertyComponent)
            {
                // ... with unknown sub component
                self::
$_invalidStylePropertyAccess[$propertyName][$propertyComponent] = true;
                return '';
            }
            else
            {
                // ... in total
                return 
$property;
            }
        }

        // css properties now
        if (
$propertyComponent)
        {
            if (isset(
$property[$propertyComponent]))
            {
                return 
$property[$propertyComponent];
            }
            else if (preg_match('#^border-.*-(radius|width|style|color)$#', 
$propertyComponent$regexMatch))
            {
                
$alternative = 'border-' . $regexMatch[1];
                if (isset(
$property[$alternative]))
                {
                    return 
$property[$alternative];
                }
            }
            else if (preg_match('#^(padding|margin)-#', 
$propertyComponent$regexMatch))
            {
                
$alternative = $regexMatch[1] . '-all';
                if (isset(
$property[$alternative]))
                {
                    return 
$property[$alternative];
                }
            }

            return '';
        }
        else
        {
            
$output = '';
            foreach (array('font', 'background', 'padding', 'margin', 'border', 'extra') AS 
$component)
            {
                if (isset(
$property[$component]) && $property[$component] !== '')
                {
                    
$output .= $property[$component] . "n";
                }
            }
            if (isset(
$property['width']) && $property['width'] !== '')
            {
                
$output .= "width$property[width];n";
            }
            if (isset(
$property['height']) && $property['height'] !== '')
            {
                
$output .= "height$property[height];n";
            }

            return 
$output;
        }
    }

    /**
     * Helper to set the available style properties.
     *
     * @param array 
$properties Style properties
     * @param boolean 
$merge True to merge with existing set
     */
    public static function setStyleProperties(array 
$properties$merge = false)
    {
        if (
$merge)
        {
            self::
$_styleProperties = array_merge(self::$_styleProperties$properties);
        }
        else
        {
            self::
$_styleProperties = $properties;
        }
    }

    /**
     * Returns a list of invalid style property accesses.
     * @see 
$_invalidStylePropertyAccess
     *
     * @return array
     */
    public static function getInvalidStylePropertyAccessList()
    {
        return self::
$_invalidStylePropertyAccess;
    }

    /**
     * Resets invalid style property accesses.
     */
    public static function resetInvalidStylePropertyAccessList()
    {
        self::
$_invalidStylePropertyAccess = array();
    }

    /**
     * Converts a URL into hidden inputs, for use in a GET form when the
     * action may have a query string. Note that the non-query string part
     * of the URL will not be output in any way.
     *
     * @param string 
$url
     *
     * @return string String of hidden form inputs
     */
    public static function getHiddenInputsFromUrl(
$url)
    {
        
$converted = self::convertUrlToActionAndNamedParams($url);
        return self::getHiddenInputs(
$converted['params']);
    }

    /**
     * Gets hidden inputs from a list of key-value params.
     *
     * @param array 
$params
     *
     * @return string
     */
    public static function getHiddenInputs(array 
$params)
    {
        
$inputs = '';
        foreach (
$params AS $name => $value)
        {
            
$inputs .= '<input type="hidden" name="' . htmlspecialchars($name)
                . '" value="' . htmlspecialchars($value) . '" />' . "
n";
        }

        return 
$inputs;
    }

    /**
     * Constructs ' href="
link-to-user"' if appropriate
     *
     * @param array 
$user
     * @param array 
$attributes
     *
     * @return string ' href="
members/example-user.234"' or empty
     */
    public static function getUserHref(array 
$user, array $attributes = array())
    {
        if (empty(
$attributes['href']))
        {
            if (
$user['user_id'])
            {
                
$href = self::link('members', $user);
            }
            else
            {
                
$href = '';
            }
        }
        else
        {
            
$href = htmlspecialchars($attributes['href']);
        }

        return (
$href ? " href="{$href}"" : '');
    }

    /**
     * Builds a string of attributes for insertion into an HTML tag.
     *
     * @param array 
$attributes
     *
     * @return string ' attr1="
abc" attr2="def" attr3="ghi"'
     */
    public static function getAttributes(array 
$attributes = array())
    {
        
$attributesHtml = '';

        foreach (
$attributes AS $attribute => $value)
        {
            
$attributesHtml .= ' ' . htmlspecialchars($attribute) . "="{$value}"";
        }

        return 
$attributesHtml;
    }

    /**
     * Converts a URL that may have a route/action and named params to a form
     * action (script name, things before query string) and named params. A route
     * in the query string is converted to a named param "
_".
     *
     * @param string 
$url
     *
     * @return array Format: [action] => form action, [params] => key-value param pairs
     */
    public static function convertUrlToActionAndNamedParams(
$url)
    {
        
$params = array();

        if ((
$questPos = strpos($url, '?')) !== false)
        {
            
$queryString = htmlspecialchars_decode(substr($url$questPos + 1));
            
$url = substr($url, 0, $questPos);

            if (preg_match('/^([^=&]*)(&|$)/', 
$queryString$queryStringUrl))
            {
                
$route = $queryStringUrl[1];
                
$queryString = substr($queryString, strlen($queryStringUrl[0]));
            }
            else
            {
                
$route = '';
            }


            if (
$route !== '')
            {
                
$params['_'] = $route;
            }

            if (
$queryString)
            {
                
$params = array_merge($params, XenForo_Application::parseQueryString($queryString));
            }
        }

        return array(
            'action' => htmlspecialchars(
$url),
            'params' => 
$params
        );
    }

    // -------------------------------------------------
    // Username link method

    /**
     * Produces a <a href="
members/username.123" class="username">Username</a> snippet
     *
     * @param array 
$user
     * @param string 
$username Used to override the username from $user
     * @param boolean Render rich username markup
     * @param array Attributes for the <a> tag
     *
     * @return string
     */
    public static function helperUserNameHtml(array 
$user$username = '', $rich = false, array $attributes = array())
    {
        if (
$username == '')
        {
            
$username = htmlspecialchars($user['username']);
        }

        if (
$rich)
        {
            
$username = self::callHelper('richusername', array($user$username));
        }
        else if (!empty(
$user['is_banned']) && XenForo_Visitor::getInstance()->hasPermission('general', 'bypassUserPrivacy'))
        {
            
$username = '<span class="banned">' . $username . '</span>';
        }

        
$href = self::getUserHref($user$attributes);

        
$class = (empty($attributes['class']) ? '' : ' ' . htmlspecialchars($attributes['class']));

        unset(
$attributes['href'], $attributes['class']);

        
$attribs = self::getAttributes($attributes);

        return "
<a{$href} class="username{$class}"{$attribs}>{$username}</a>";
    }

    public static function helperUserName(array 
$user$class = '', $rich = false)
    {
        return self::callHelper('usernamehtml', array(
$user, '', $rich, array('class' => $class)));
    }

    // -------------------------------------------------
    // Avatar-related methods

    /**
     * Returns an <a> tag for use as a user avatar
     *
     * @param array 
$user
     * @param boolean If true, use an <img> tag, otherwise use a block <span> with the avatar as a background image
     * @param array Extra tag attributes
     * @param string Additional tag contents (inserted after image element)
     */
    public static function helperAvatarHtml(array 
$user$img, array $attributes = array(), $content = '')
    {
        if (!empty(
$attributes['size']))
        {
            
$size = strtolower($attributes['size']);

            switch (
$size)
            {
                case 'l':
                case 'm':
                case 's':
                    break;

                default:
                    
$size = 'm';
            }
        }
        else
        {
            
$size = 'm';
        }

        
$forceType = (isset($attributes['forcetype']) ? $attributes['forcetype'] : null);

        
$canonical = (isset($attributes['canonical']) && self::attributeTrue($attributes['canonical']));

        
$src = call_user_func(self::$helperCallbacks['avatar'], $user$size$forceType$canonical);

        
$href = self::getUserHref($user$attributes);
        unset(
$attributes['href']);

        if (
$img)
        {
            
$username = htmlspecialchars($user['username']);
            
$dimension = XenForo_Model_Avatar::getSizeFromCode($size);

            
$image = "<img src="{$src}width="{$dimension}height="{$dimension}alt="{$username}/>";
        }
        else
        {
            
$text = (empty($attributes['text']) ? '' : htmlspecialchars($attributes['text']));

            
$image = "<span class="img {$size}style="background-image: url('{$src}')">{$text}</span>";
        }

        
$class = (empty($attributes['class']) ? '' : ' ' . htmlspecialchars($attributes['class']));

        unset(
$attributes['user'], $attributes['size'], $attributes['img'], $attributes['text'], $attributes['class']);

        
$attribs = self::getAttributes($attributes);

        if (
$content !== '')
        {
            
$content = " {$content}";
        }

        return "
<a{$href} class="avatar Av{$user['user_id']}{$size}{$class}"{$attribsdata-avatarhtml="true">{$image}{$content}</a>";
    }

    /**
     * Helper to fetch the URL of a user's avatar.
     *
     * @param array 
$user User info
     * @param string 
$size Size code
     * @param boolean Serve the default gender avatar, even if the user has a custom avatar
     * @param boolean Serve the full canonical URL
     *
     * @return string Path to avatar
     */
    public static function helperAvatarUrl(
$user$size$forceType = null, $canonical = false)
    {
        if (!is_array(
$user))
        {
            
$user = array();
        }

        if (
$forceType)
        {
            switch (
$forceType)
            {
                case 'default':
                case 'custom':
                    break;

                default:
                    
$forceType = null;
                    break;
            }
        }

        
$url = self::getAvatarUrl($user$size$forceType);

        if (
$canonical)
        {
            
$url = XenForo_Link::convertUriToAbsoluteUri($url, true);
        }

        return htmlspecialchars(
$url);
    }

    /**
     * Returns an array containing the URLs for each avatar size available for the given user
     *
     * @param array 
$user
     *
     * @return array [
$sizeCode => $url$sizeCode => $url...]
     */
    public static function getAvatarUrls(array 
$user)
    {
        
$urls = array();

        foreach (XenForo_Model_Avatar::getSizes() AS 
$sizeCode => $maxDimensions)
        {
            
$urls[$sizeCode] = self::getAvatarUrl($user$sizeCode);
        }

        return 
$urls;
    }

    /**
     * Returns the URL to the appropriate avatar type for the given user
     *
     * @param array 
$user
     * @param string 
$size (s,m,l)
     * @param string Force 'default' or 'custom' type
     *
     * @return string
     */
    public static function getAvatarUrl(array 
$user$size$forceType = '')
    {
        if (!empty(
$user['user_id']) && $forceType != 'default')
        {
            if (
$user['gravatar'] && $forceType != 'custom')
            {
                return self::_getGravatarUrl(
$user$size);
            }
            else if (!empty(
$user['avatar_date']))
            {
                return self::_getCustomAvatarUrl(
$user$size);
            }
        }

        return self::_getDefaultAvatarUrl(
$user$size);
    }

    /**
     * Returns the default gender-specific avatar URL
     *
     * @param string 
$gender - male / female / other
     * @param string 
$size (s,m,l)
     *
     * @return string
     */
    protected static function _getDefaultAvatarUrl(array 
$user$size)
    {
        if (!isset(
$user['gender']))
        {
            
$user['gender'] = '';
        }

        switch (
$user['gender'])
        {
            case 'male':
            case 'female':
                
$gender = $user['gender'] . '_';
                break;

            default:
                
$gender = '';
                break;
        }

        if (!
$imagePath = self::styleProperty('imagePath'))
        {
            
$imagePath = 'styles/default';
        }

        return "
{$imagePath}/xenforo/avatars/avatar_{$gender}{$size}.png";
    }

    /**
     * Returns the URL to a user's custom avatar
     *
     * @param array 
$user
     * @param string 
$size (s,m,l)
     *
     * @return string
     */
    protected static function _getCustomAvatarUrl(array 
$user$size)
    {
        
$group = floor($user['user_id'] / 1000);
        return XenForo_Application::
$externalDataUrl . "/avatars/$size/$group/$user[user_id].jpg?$user[avatar_date]";
    }

    /**
     * Returns a Gravatar URL for the user
     *
     * @param array 
$user
     * @param string|integer 
$size (s,m,l)
     * @param string Override default (useful to use '404')
     */
    protected static function _getGravatarUrl(array 
$user$size$default = '')
    {
        
$md5 = md5(strtolower(trim($user['gravatar'])));

        if (
$default === '')
        {
            
$default = '&d=' . urlencode(XenForo_Application::get('options')->boardUrl . '/' . self::_getDefaultAvatarUrl($user$size));
        }
        else if (!empty(
$default))
        {
            
$default = '&d=' . urlencode($default);
        }

        if (is_string(
$size))
        {
            
$size = XenForo_Model_Avatar::getSizeFromCode($size);
        }

        return (XenForo_Application::
$secure ? 'https://secure' : 'http://www')
            . "
.gravatar.com/avatar/{$md5}?s={$size}{$default}";
    }

    /**
     * Helper to fetch the CSS rules to crop a user's avatar to their chosen square aspect
     *
     * @param array 
$user
     */
    public static function helperAvatarCropCss(
$user)
    {
        if (!is_array(
$user)                             // not a valid user
            || empty(
$user['avatar_date'])               // no custom avatar
            || !array_key_exists('avatar_crop_x', 
$user) // no x crop info
            || !array_key_exists('avatar_crop_y', 
$user) // no y crop info
            || !empty(
$user['gravatar'])                 // using Gravatar, which is always square
        )
        {
            return '';
        }

        
$css = '';

        foreach (XenForo_ViewPublic_Helper_User::getAvatarCropCss(
$user) AS $property => $value)
        {
            
$css .= "$property$value";
        }

        return 
$css;
    }

    public static function helperThreadPrefixGroup(
$prefixGroupId)
    {
        return self::_getPhraseText('thread_prefix_group_' . 
$prefixGroupId);
    }

    /**
     * Helper to display a thread prefix for the specified prefix ID/thread. Can take an array.
     *
     * @param integer|array 
$prefixId Prefix ID or array with key of prefix_id
     * @param string 
$outputType Type of output; options are html (marked up), plain (plain text), escaped (plain text escaped)
     * @param string|null 
$append Value to append if there is a prefix (eg, a space); if null, defaults to space (html) or dash (plain)
     *
     * @return string
     */
    public static function helperThreadPrefix(
$prefixId$outputType = 'html', $append = null)
    {
        if (is_array(
$prefixId))
        {
            if (!isset(
$prefixId['prefix_id']))
            {
                return '';
            }

            
$prefixId = $prefixId['prefix_id'];
        }

        
$prefixId = intval($prefixId);
        if (!
$prefixId || !isset(self::$_threadPrefixes[$prefixId]))
        {
            return '';
        }

        
$text = self::_getPhraseText('thread_prefix_' . $prefixId);
        if (
$text === '')
        {
            return '';
        }

        switch (
$outputType)
        {
            case 'html':
                
$text = '<span class="' . htmlspecialchars(self::$_threadPrefixes[$prefixId]) . '">'
                    . htmlspecialchars(
$text) . '</span>';
                if (
$append === null)
                {
                    
$append = ' ';
                }
                break;

            case 'plain':
                break; // ok as is

            default:
                
$text = htmlspecialchars($text); // just be safe and escape everything else
        }

        if (
$append === null)
        {
            
$append = ' - ';
        }

        return 
$text . $append;
    }

    /**
     * Fetches the text of the specified phrase
     *
     * @param string 
$phraseName
     *
     * @return string
     */
    protected static function _getPhraseText(
$phraseName)
    {
        if (self::
$_language && isset(self::$_language['phrase_cache']))
        {
            
$cache = self::$_language['phrase_cache'];
            return (isset(
$cache[$phraseName]) ? $cache[$phraseName] : '');
        }
        else
        {
            
$phrase = new XenForo_Phrase($phraseName);
            return 
$phrase->render(false);
        }
    }

    /**
     * Sets the thread prefixes.
     *
     * @param array 
$prefixes [prefix id] => class name
     */
    public static function setThreadPrefixes(
$prefixes)
    {
        self::
$_threadPrefixes = $prefixes;
    }

    /**
     * Encodes the incoming data as JSON
     *
     * @param mixed 
$data
     *
     * @return string JSON
     */
    public static function helperJson(
$data)
    {
        return XenForo_ViewRenderer_Json::jsonEncodeForOutput(
$data, false);
    }

    public static function helperIgnoredCss(array 
$ignoredUsers)
    {
        if (empty(
$ignoredUsers))
        {
            return '';
        }

        
$selectors = array();
        foreach (
$ignoredUsers AS $username)
        {
            
$selectors[] = '*[data-author="' . htmlspecialchars($username) . '"]';
        }

        return '';//<style id="
ignoredUserCss">' . implode(', ', $selectors) . ' { display: none; } </style>';
    }

    /**
     * Determines whether or not the given user ID / username is being ignored by the visiting user
     *
     * @param integer|string 
$user
     *
     * @return boolean
     */
    public static function helperIsIgnored(
$user, array &$ignoredNames = array())
    {
        
$visitor = XenForo_Visitor::getInstance();

        if (!
$visitor['user_id'] || empty($visitor['ignoredUsers']))
        {
            return false;
        }

        
$ignoredUser = self::_getModelFromCache('XenForo_Model_User')->isUserIgnored($visitor->toArray(), $user);

        if (
$ignoredUser !== false)
        {
            
$ignoredNames[$ignoredUser[0]] = $ignoredUser[1];

            return 
$ignoredUser;
        }

        return false;
    }

    /**
     * Returns a string of CSS class names, identifying the current node and all its ancestors.
     *
     * @param array 
$nodeBreadCrumbs
     * @param array 
$currentNode
     *
     * @return string 'node5 node3 node1'
     */
    public static function helperNodeClasses(array 
$nodeBreadCrumbs, array $currentNode = array())
    {
        
$nodeClasses = array();

        if (array_key_exists('node_id', 
$currentNode))
        {
            
$nodeClasses[] = "node{$currentNode['node_id']}";
        }

        foreach (array_keys(
$nodeBreadCrumbs) AS $nodeId)
        {
            
$nodeClasses[] = "node{$nodeId}";
        }

        return implode(' ', array_unique(
$nodeClasses));
    }

    public static function helperIp(
$ip)
    {
        
$res = XenForo_Helper_Ip::convertIpBinaryToString($ip);
        return htmlspecialchars(
$res ? $res : $ip, ENT_COMPAT, 'utf-8');
    }

    /**
     * Helper to fetch the title of a custom user field from its ID
     *
     * @param string 
$field
     *
     * @return XenForo_Phrase
     */
    public static function helperUserFieldTitle(
$fieldId)
    {
        return new XenForo_Phrase(
            self::_getModelFromCache('XenForo_Model_UserField')->getUserFieldTitlePhraseName(
$fieldId));
    }

    /**
     * Helper to fetch the HTML value of a custom user field
     *
     * @param array|string 
$field Either the field info array for a field, or just its field_id
     * @param array 
$user User to whom the field belongs
     * @param mixed 
$fieldValue Value of the field for $user
     *
     * @return string|boolean
     */
    public static function helperUserFieldValue(
$field, array $user = array(), $fieldValue = null)
    {
        if (empty(
$user['user_id']))
        {
            return false;
        }

        if (!is_array(
$field))
        {
            if (empty(self::
$_userFieldsInfo))
            {
                self::
$_userFieldsInfo = XenForo_Application::get('userFieldsInfo');
            }

            if (!isset(self::
$_userFieldsInfo[$field]))
            {
                return false;
            }

            
$field = self::$_userFieldsInfo[$field];
        }

        
$fieldId = $field['field_id'];

        if (isset(self::
$_userFieldsValues[$user['user_id']][$fieldId]))
        {
            return self::
$_userFieldsValues[$user['user_id']][$fieldId];
        }

        if (is_null(
$fieldValue))
        {
            
$fieldValue = $field['field_value'];
        }

        
$value = XenForo_ViewPublic_Helper_User::getUserFieldValueHtml($field$fieldValue);

        if (is_array(
$value))
        {
            
$value = implode(', ', $value);
        }

        self::
$_userFieldsValues[$user['user_id']][$fieldId] = $value;
        return 
$value;
    }

    public static function helperJavaScriptUrl(
$scriptUrl)
    {
        
$jsUrl = XenForo_Application::$javaScriptUrl;

        switch (XenForo_Application::get('options')->uncompressedJs)
        {
            case 1:
                return str_replace("
$jsUrl/xenforo/", "$jsUrl/xenforo/full/", $scriptUrl);
            case 2:
                return str_replace("
$jsUrl/xenforo/", "$jsUrl/xenforo/min/", $scriptUrl);
        }

        return 
$scriptUrl;
    }

    protected static 
$_uniqueId = 0;

    public static function helperUniqueId()
    {
        return '_xfServerUniqueId' . (++self::
$_uniqueId);
    }

    /**
     * Helper to determine if an attribute value should be treated as true
     *
     * @param mixed 
$attribute
     *
     * @return boolean
     */
    public static function attributeTrue(
$attribute)
    {
        if (!empty(
$attribute))
        {
            switch ((string)strtolower(
$attribute))
            {
                case 'on':
                case 'yes':
                case 'true':
                case '1':
                    return true;
            }
        }

        return false;
    }

    /**
     * Adds a CSS class to a class list if it is not already there, otherwise removes it
     *
     * @param string Class to add / remove
     * @param string Existing class defininition
     *
     * @return string
     */
    public static function toggleClass(
$class, &$classList = '')
    {
        if (
$classList == '')
        {
            // empty class list - add
            
$classList = $class;
        }
        else if (
$classList == $class)
        {
            // class list contains only class - remove
            
$classList = '';
        }
        else if (strpos(" 
$classList ", " $class ") === false)
        {
            // class list does not include class - add
            
$classList = "$classList $class";
        }
        else
        {
            // class list includes class - remove
            
$classList = array_keys(preg_split('/s+/', $classList, -1, PREG_SPLIT_NO_EMPTY));
            unset(
$classList[$class]);

            
$classList = implode($classList, ' ');
        }

        return 
$classList;
    }

    /**
     * Adds a CSS class to a class list if it's not already there
     *
     * @param string Class to add / remove
     * @param string Existing class defininition
     *
     * @return string
     */
    public static function addClass(
$class, &$classList = '')
    {
        if (
$classList == '')
        {
            
$classList = $class;
        }
        else if (strpos(" 
$classList ", " $class ") === false)
        {
            
$classList = "$classList $class";
        }

        return 
$classList;
    }

    /**
     * Fetches a model object from the local cache
     *
     * @param string 
$modelName
     *
     * @return XenForo_Model
     */
    protected static function _getModelFromCache(
$modelName)
    {
        if (!isset(self::
$_modelCache[$modelName]))
        {
            self::
$_modelCache[$modelName] = XenForo_Model::create($modelName);
        }

        return self::
$_modelCache[$modelName];
    }
}
Онлайн: 0
Реклама