Логирование действий пользователей (всплывающее окно на AJAX) 1.0.0

Добавляет логирование действий пользователей, их точное месторасположение на трекере.

  1. Exile
    Совместимость:
    не проверялся на совместимость
    Изменения в БД:
    не требуются
    Дополнительные требования:
    sqlite3
    Автор:
    frost444
    Общественное достояние:
    да
    Описание: добавляет логирование действий пользователей, их точное месторасположение на трекере.

    Возможности:
    • Запись лога перемещения пользователя по трекеру
    • Живой вывод тех кто смотрит топик или форум
    • Использование AJAX и sqlite3
    Инструкция по установке:
    PHP:
    Автор модаfrost444

    ####[ОТКРЫТЬ]----- config.php
    -----[найти]-----

        
    'bb_login_err'   => array('filecache',   array()),

    -----[
    добавить ниже]-----

        
    'buf_where'      => array('db_sqlite',   array('columns' => 'user_ip INT, username TEXT, user_id INT, user_rank INT, user_opt INT, page_id INT, id INT, title TEXT, mode TEXT, url TEXT, time INT')),

    -----[
    добавить в конец]-----
    $bb_cfg['bots_conf'] = array(
        
    'id' => '-777',
    );
    $bb_cfg['bots'] = array (
        
    'AdsBot-Google' => 'AdsBot [Google]',
        
    'ia_archiver' => 'Alexa [Bot]',
        
    'Scooter/' => 'Alta Vista [Bot]',
        
    'Ask Jeeves' => 'Ask Jeeves [Bot]',
        
    'Baiduspider+(' => 'Baidu [Spider]',
        
    'Exabot/' => 'Exabot [Bot]',
        
    'FAST Enterprise Crawler' => 'FAST Enterprise [Crawler]',
        
    'FAST-WebCrawler/' => 'FAST WebCrawler [Crawler]',
        
    'http://www.neomo.de/' => 'Francis [Bot]',
        
    'Gigabot/' => 'Gigabot [Bot]',
        
    'Mediapartners-Google' => 'Google Adsense [Bot]',
        
    'Google Desktop' => 'Google Desktop',
        
    'Feedfetcher-Google' => 'Google Feedfetcher',
        
    'Googlebot' => 'Google [Bot]',
        
    'heise-IT-Markt-Crawler' => 'Heise IT-Markt [Crawler]',
        
    'heritrix/1.' => 'Heritrix [Crawler]',
        
    'ibm.com/cs/crawler' => 'IBM Research [Bot]',
        
    'ICCrawler - ICjobs' => 'ICCrawler - ICjobs',
        
    'ichiro/' => 'ichiro [Crawler]',
        
    'MJ12bot/' => 'Majestic-12 [Bot]',
        
    'MetagerBot/' => 'Metager [Bot]',
        
    'msnbot-NewsBlogs/' => 'MSN NewsBlogs',
        
    'msnbot/' => 'MSN [Bot]',
        
    'msnbot-media/' => 'MSNbot Media',
        
    'NG-Search/' => 'NG-Search [Bot]',
        
    'http://lucene.apache.org/nutch/' => 'Nutch [Bot]',
        
    'NutchCVS/' => 'Nutch/CVS [Bot]',
        
    'OmniExplorer_Bot/' => 'OmniExplorer [Bot]',
        
    'online link validator' => 'Online link [Validator]',
        
    'psbot/0' => 'psbot [Picsearch]',
        
    'Seekbot/' => 'Seekport [Bot]',
        
    'Sensis Web Crawler' => 'Sensis [Crawler]',
        
    'SEO search Crawler/' => 'SEO Crawler',
        
    'Seoma [SEO Crawler]' => 'Seoma [Crawler]',
        
    'SEOsearch/' => 'SEOSearch [Crawler]',
        
    'Snappy/1.1 ( http://www.urltrends.com/ )' => 'Snappy [Bot]',
        
    'http://www.tkl.iis.u-tokyo.ac.jp/~crawler/' => 'Steeler [Crawler]',
        
    'SynooBot/' => 'Synoo [Bot]',
        
    'crawleradmin.t-info@telekom.de' => 'Telekom [Bot]',
        
    'TurnitinBot/' => 'TurnitinBot [Bot]',
        
    'voyager/1.0' => 'Voyager [Bot]',
        
    'W3 SiteSearch Crawler' => 'W3 [Sitesearch]',
        
    'W3C-checklink/' => 'W3C [Linkcheck]',
        
    'W3C_*Validator' => 'W3C [Validator]',
        
    'http://www.WISEnutbot.com' => 'WiseNut [Bot]',
        
    'yacybot' => 'YaCy [Bot]',
        
    'Yahoo-MMCrawler/' => 'Yahoo MMCrawler [Bot]',
        
    'Yahoo! DE Slurp' => 'Yahoo Slurp [Bot]',
        
    'Yahoo! Slurp' => 'Yahoo [Bot]',
        
    'YahooSeeker/' => 'YahooSeeker [Bot]',
        
    'Yandex/1.01.001 (compatible; Win16; I)' => 'Яндекс БОТ',
        
    'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)' => 'Яндекс [Bot]',
        
    'bingbot/' => 'Bing [Bot]',
    );

    $bb_cfg['where_user'] = true;

    ####[СОХРАНИТЬ_ФАЙЛ]####

    ####[ОТКРЫТЬ]----- ajax.php
    -----[найти]-----

            
    'index_data'        => array('guest'),

    -----[
    добавить ниже]-----

            
    'UserStats'         => array('user'),
           
    -----[
    найти последнюю скобку и перед ней добавить]-----

        function 
    UserStats()
        {
            require(
    AJAX_DIR .'UserStats.php');
        }

    ####[СОХРАНИТЬ_ФАЙЛ]####

    ####[ОТКРЫТЬ]----- lang_main.php
    -----[в конец]-----

    // Online/Offline
    $lang['OFFLINE'] = 'Вне форума';
    $lang['ONLINE'] = 'На форуме';
    $lang['HIDDEN'] = 'Скрыт';
    $lang['ON_OFF_STATUS'] = 'Статус';
    // Online/Offline

    $lang['LAST_TYPE'] = 'Последнее действие';
    $lang['ONLINE_USERS_WHERE'] = '<b>%1$d</b> человек просматривают эту страницу: <b>%2$d</b><sup title="С 1-го аккаунта сидят несколько человек"><b>[%3$d]</b></sup> зарегистрированных, <b>%4$d</b> скрытых, <b>%5$d</b> гостей, <b>%6$d</b> ботов';

    ####[СОХРАНИТЬ_ФАЙЛ]####

    ####[ОТКРЫТЬ]----- index.php
    -----[найти]-----

    $template->assign_vars(array(
        
    'SHOW_FORUMS'           => $forums_count,

    -----[
    добавить выше]-----

    where_user(array('page_id' => PAGE_FORUM'id' => '0''mode' => '0''url' => str_replace($bb_cfg['script_path'], ""$_SERVER['REQUEST_URI']), 'title' => $lang['FORUM']));

    ####[СОХРАНИТЬ_ФАЙЛ]####

    ####[ОТКРЫТЬ]----- viewtopic.php
    -----[найти]-----

    $template->assign_vars(array(
        
    'PAGE_URL'            => $pg_url,
       
    -----[
    добавить выше]-----

    where_user(array('page_id' => PAGE_VIEWTOPIC'id' => $topic_id'mode' => '0''url' => str_replace($bb_cfg['script_path'], ""$_SERVER['REQUEST_URI']), 'title' => $t_data['topic_title']), true);
       
    -----[
    найти]-----

    $template->assign_vars(array(
        
    'PAGE_URL'            => $pg_url,

    -----[
    добавить ниже]-----

        
    'WHO_ONLINE_LIST'      => who_online_list(PAGE_VIEWTOPIC$topic_id),

    ####[СОХРАНИТЬ_ФАЙЛ]####

    ####[ОТКРЫТЬ]----- functions.php
    -----[найти]-----

    function 
    profile_url($data)
    {
        ...
    }

    -----[
    заменить]-----

    function 
    profile_url($data$popupon false)
    {
        global 
    $bb_cfg$lang$datastore;

        if (!
    $ranks $datastore->get('ranks'))
        {
            
    $datastore->update('ranks');
            
    $ranks $datastore->get('ranks');
        }

        
    $user_rank = !empty($data['user_rank']) ? $data['user_rank'] : 0;

        if(isset(
    $ranks[$user_rank]))
        {
            
    $title $ranks[$user_rank]['rank_title'];
            
    $style $ranks[$user_rank]['rank_style'];
        }
        if(empty(
    $title)) $title $lang['USER'];
        if(empty(
    $style)) $style 'colorUser';

        if(!
    $bb_cfg['color_nick']) $style '';

        
    $username = !empty($data['username']) ? $data['username'] : $lang['GUEST'];
        
    $user_id = (!empty($data['user_id']) && $username != $lang['GUEST']) ? $data['user_id'] : ANONYMOUS;
        if(
    $popupon)
        {
            
    $profile "&nbsp;<span onclick=\"user_stats(".$user_id.", 'popup');\" title=\"Просмотр профиля\" class=\"UserPopupsImg clickable\"><span class=\"pad_6\"></span></span>";
            
    $profile '<span title="'$title .'" class="'$style .'">'$username .'</span>'.$profile.'';
        }else{
            
    $profile '<span title="'$title .'" class="'$style .'">'$username .'</span>';
        }
       
        if(!
    in_array($user_id, array(''ANONYMOUSBOT_UID)) && $username && !$popupon)
        {
            
    $popup "&nbsp;<span onclick=\"user_stats(".$user_id.", 'popup');\" title=\"Просмотр профиля\" class=\"UserPopupsImg clickable\"><span class=\"pad_6\"></span></span>";
            
    $profile '<a href="'make_url(PROFILE_URL $user_idtrue) .'">'$profile .'</a>'.$popup.'';
        }

        return 
    $profile;
    }


    -----[
    добавить в конец]-----

    function 
    who_online_list ($page_id$id false$mode false)
    {
        global 
    $bb_cfg$lang$userdata;

        
    $id = ($id) ? "AND id = {$id}'';
        
    $mode = ($mode) ? "AND mode = {$mode}'';
        
    $where_time = (TIMENOW 300);
        
    $sql CACHE('buf_where')->fetch_rowset("
        SELECT username, user_rank, user_opt, user_id, min(user_ip) as user_ip FROM buf_where
            WHERE page_id = 
    {$page_id}
            
    $id
            
    $mode
            AND time > 
    $where_time
            GROUP BY user_ip
            having min(user_ip) = max(user_ip)
        "
    );
        
    $id_user $id_user_hidden $id_anon $id_bot $user $count_all = array();
        foreach (
    $sql as $row)
        {   
            
    $count_all[] = $row['user_ip'];
            if(
    $row['user_id'] != ANONYMOUS && $row['user_id'] != $bb_cfg['bots_conf']['id'])
            {
                
    $id_user[] = $row['user_id'];
                
    $user[] = profile_url(array('username' => $row['username'], 'user_id' => $row['user_id'], 'user_rank' => $row['user_rank']));
            }
            else if(
    $row['user_id'] != ANONYMOUS && $row['user_id'] != $bb_cfg['bots_conf']['id'] && bf($row['user_opt'], 'user_opt''allow_viewonline'))
            {
                
    $id_user_hidden[] = $row['user_id'];
            }
            else if(
    $row['user_id'] == ANONYMOUS)
            {
                
    $id_anon[] = $row['user_id'];
            }
            else if(
    $row['user_id'] == $bb_cfg['bots_conf']['id'])
            {
                
    $id_bot[] = $row['user_id'];
            }
        }
        
    $alls count($count_all);
        
    $users count(array_unique($id_user));
        
    $hiddens count(array_unique($id_user_hidden));
        
    $anons count($id_anon);
        
    $bots count($id_bot);
        
    $duble = ($alls $users $hiddens $anons $bots);
        
    $online_count sprintf($lang['ONLINE_USERS_WHERE'], $alls$users$duble$hiddens$anons$bots);
        
    $online_list $online_count '<br /><br />' join(",\n"array_unique($user));
       
        return 
    $online_list;
    }

    function 
    where_user($data$quest false)
    {
        global 
    $bb_cfg$lang$userdata;

        if(!
    $bb_cfg['where_user']) return;
        if(
    IS_GUEST && !$quest) return;

        
    $where_time = (TIMENOW 300);
        
    $buf CACHE('buf_where')->fetch_row("
                SELECT time FROM buf_where
                WHERE page_id    = 
    {$data['page_id']}
                    AND mode      = '
    {$data['mode']}'
                    AND user_ip  = '"
    USER_IP ."'
                    AND time     > 
    $where_time
                    AND id       = 
    {$data['id']}
                ORDER BY time DESC"
    );
        
    $insert true;
        if(
    $buf$insert false;

        
    $sql['user_ip']    = USER_IP;
        
    $sql['username']   = $userdata['username'];
        
    $sql['user_id']    = $userdata['user_id'];

        if(
    IS_GUEST)
        {
            
    $user_browser = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'not browser';

            foreach (
    $bb_cfg['bots'] as $bot => $name)
            {
                if(
    strstr($user_browser$bot))
                {
                    
    $sql['username'] = $name;
                    
    $sql['user_id']  = $bb_cfg['bots_conf']['id'];
                }
            }
        }

        
    $sql['user_rank']    = $userdata['user_rank'];
        
    $sql['user_opt']    = $userdata['user_opt'];
        
    $sql['time']        = TIMENOW;
        
    $sql['page_id']        = @$data['page_id'];
        
    $sql['id']            = @$data['id'];
        
    $sql['mode']        = @$data['mode'];
        
    $sql['url']            = @$data['url'];
        
    $sql['title']        = @htmlCHR($data['title']);

        
    $sql_insert DB()->build_array('INSERT'$sql);
        if(
    $insertCACHE('buf_where')->query("INSERT INTO buf_where $sql_insert");
    }

    ####[СОХРАНИТЬ_ФАЙЛ]####

    ####[ОТКРЫТЬ]----- init_bb.php
    -----[найти]-----

    define('POST_USERS_URL',  'u');

    -----[
    добавить ниже]-----

    //[START] Where user
    define('PAGE_REGISTER',                -1);
    define('PAGE_LOGIN',                0);
    define('PAGE_INDEX',                1);
    define('PAGE_FORUM',                2);
    define('PAGE_VIEWFORUM',            3);
    define('PAGE_VIEWTOPIC',            4);
    define('PAGE_POSTING',                5);
    define('PAGE_PROFILE',                6);
    define('PAGE_PRIVMSGS',                7);
    define('PAGE_SEARCH',                8);
    define('PAGE_TRACKER',                9);
    define('PAGE_VIEWONLINE',            10);
    define('PAGE_VIEWMEMBERS',            11);
    define('PAGE_GROUPCP',                12);
    define('PAGE_FAQ',                    13);
    define('PAGE_REPORT',                14);
    define('PAGE_PORTAL',                15);
    define('PAGE_ORDER',                16);
    define('PAGE_POINTSCP',                17);
    define('PAGE_QUESTBOOK',            18);
    define('PAGE_RADIO',                19);
    define('PAGE_PRESENTS',                20);
    define('PAGE_VIEWCHEATER',            21);
    define('PAGE_WARNINGS',                22);
    define('PAGE_MYTOP',                22);
    ////(BLOG)(START) Where user
    define('PAGE_BLOG',                    100);
    define('PAGE_BLOG_ALLENTRIES',        101);
    define('PAGE_BLOG_BLOCKED',            102);
    define('PAGE_BLOG_CFG',                103);
    define('PAGE_BLOG_CONTRIBUTORS',    104);
    define('PAGE_BLOG_ENTRY',            105);
    define('PAGE_BLOG_FRIENDS',            106);
    define('PAGE_BLOG_FPOSTING',        107);
    define('PAGE_BLOG_RSS',                108);
    define('PAGE_BLOGS',                109);
    define('PAGE_BLOGS_NEWS',            110);
    ////(BLOG)(END) Where user
    ////(GRABBER)(START) Where user
    define('PAGE_RUTOR',                200);
    define('PAGE_RUTORREGISTER',        201);
    define('PAGE_RELEASE',                202);
    define('PAGE_GRAB_INDEX',            203);
    define('PAGE_QUOTE',                204);
    ////(GRABBER)(END) Where user
    //[END] Where user
               
    ####[СОХРАНИТЬ_ФАЙЛ]####
    ####[ОТКРЫТЬ]----- init_bb.php
    -----[найти]-----

                        case 
    'db_sqlite':
                            if (!isset(
    $this->obj[$cache_name]))
                            {
                                
    $cache_cfg['pconnect']     = $this->cfg['pconnect'];
                                
    $cache_cfg['db_file_path'] = $this->get_db_path($cache_name$cache_cfg'.sqlite.db');
                                
    $cache_cfg['table_name']   = $cache_name;
                                
    $cache_cfg['table_schema'] = $this->get_table_schema($cache_cfg);

                                
    $this->obj[$cache_name] = new sqlite_common($cache_cfg);
                            }
                            
    $this->ref[$cache_name] =& $this->obj[$cache_name];
                            break;

    -----[
    заменить]-----

                        case 
    'db_sqlite':
                            if (!isset(
    $this->obj[$cache_name]))
                            {
                                
    $cache_cfg['pconnect']     = $this->cfg['pconnect'];
                                
    $cache_cfg['db_file_path'] = $this->get_db_path($cache_name$cache_cfg'.sqlite.db');
                                
    $cache_cfg['table_name']   = $cache_name;
                                
    $cache_cfg['table_schema'] = $this->get_table_schema($cache_cfg);
                                
    $cache_cfg['file_name']    = $cache_name.'.sqlite.db';
                                
    $cache_cfg['dir']             = $this->cfg['db_dir'];

                                
    $this->obj[$cache_name] = new sqlite_common($cache_cfg);
                            }
                            
    $this->ref[$cache_name] =& $this->obj[$cache_name];
                            break;
                           
    -----[
    найти]-----

    class 
    sqlite_common extends cache_common
    {
        var 
    $cfg = array(
                
    'db_file_path' => 'sqlite.db',
                
    'table_name'   => 'table_name',
                
    'table_schema' => 'CREATE TABLE table_name (...)',
                
    'pconnect'     => true,
                
    'con_required' => true,
                
    'log_name'     => 'SQLite',
                
    'shard_type'   => 'none',     #  none, string, int (тип перевичного ключа для шардинга)
                
    'shard_val'    => 0,          #  для string - кол. начальных символов, для int - делитель (будет использован остаток от деления)
            
    );
        var 
    $engine    'SQLite';
        var 
    $dbh       null;
        var 
    $connected false;
        var 
    $shard_val false;

        var 
    $table_create_attempts 0;

        function 
    sqlite_common ($cfg)
        {
            
    $this->cfg array_merge($this->cfg$cfg);
            
    $this->dbg_enabled sql_dbg_enabled();
        }

        function 
    connect ()
        {
            
    $this->cur_query = ($this->dbg_enabled) ? ($this->cfg['pconnect'] ? 'p' '') .'connect to: '$this->cfg['db_file_path'] : 'connect';
            
    $this->debug('start');

            
    $connect_type = ($this->cfg['pconnect']) ? 'sqlite_popen' 'sqlite_open';

            if (
    $this->cfg['shard_type'] != 'none' && $this->shard_val === false)
            {
                
    trigger_error("cannot shard: shard_val not defined for {$this->cfg['db_file_path']}"E_USER_ERROR);
            }

            if (@
    $this->dbh $connect_type($this->cfg['db_file_path'], 0666$sqlite_error))
            {
                
    $this->connected true;
            }

            if (!
    $this->connected && $this->cfg['con_required'])
            {
                
    trigger_error($sqlite_errorE_USER_ERROR);
            }

            
    $this->debug('stop');
            
    $this->cur_query null;
        }

        function 
    create_table ()
        {
            
    $this->table_create_attempts++;
            return 
    sqlite_query($this->dbh$this->cfg['table_schema']);
        }

        function 
    shard ($name)
        {
            
    $type $this->cfg['shard_type'];

            if (
    $type == 'none') return;
            if (
    is_array($name))  trigger_error('cannot shard: $name is array'E_USER_ERROR);

            
    // define shard_val
            
    if ($type == 'string')
            {
                
    $shard_val substr($name0$this->cfg['shard_val']);
            }
            else
            {
                
    $shard_val $name $this->cfg['shard_val'];
            }
            
    // все запросы должны быть к одному и тому же шарду
            
    if ($this->shard_val !== false)
            {
                if (
    $shard_val != $this->shard_val)
                {
                    
    trigger_error("shard cannot be reassigned. [{$this->shard_val}$shard_val$name]"E_USER_ERROR);
                }
                else
                {
                    return;
                }
            }
            
    $this->shard_val $shard_val;
            
    $this->cfg['db_file_path'] = str_replace('*'$shard_val$this->cfg['db_file_path']);
        }

        function 
    query ($query)
        {
            if (!
    $this->connected$this->connect();

            
    $this->cur_query $query;
            
    $this->debug('start');

            if (!
    $result = @sqlite_unbuffered_query($this->dbh$querySQLITE_ASSOC))
            {
                if (!
    $this->table_create_attempts && !sqlite_num_rows(sqlite_query($this->dbh"PRAGMA table_info({$this->cfg['table_name']})")))
                {
                    if (
    $this->create_table())
                    {
                        
    $result sqlite_unbuffered_query($this->dbh$querySQLITE_ASSOC);
                    }
                }
                if (!
    $result)
                {
                    
    $this->trigger_error($this->get_error_msg());
                }
            }

            
    $this->debug('stop');
            
    $this->cur_query null;

            
    $this->num_queries++;

            return 
    $result;
        }

        function 
    fetch_row ($query)
        {
            
    $result $this->query($query);
            return 
    is_resource($result) ? sqlite_fetch_array($resultSQLITE_ASSOC) : false;
        }

        function 
    fetch_rowset ($query)
        {
            
    $result $this->query($query);
            return 
    is_resource($result) ? sqlite_fetch_all($resultSQLITE_ASSOC) : array();
        }

        function 
    changes ()
        {
            return 
    is_resource($this->dbh) ? sqlite_changes($this->dbh) : 0;
        }

        function 
    escape ($str)
        {
            return 
    sqlite_escape_string($str);
        }

        function 
    get_error_msg ()
        {
            return 
    'SQLite error #'. ($err_code sqlite_last_error($this->dbh)) .': 'sqlite_error_string($err_code);
        }

        function 
    rm ($name '')
        {
            if (
    $name)
            {
                
    $this->db->shard($this->prefix $name);
                
    $result $this->db->query("DELETE FROM "$this->cfg['table_name'] ." WHERE cache_name = '"sqlite_escape_string($this->prefix $name) ."'");
            }
            else
            {
                
    $result $this->db->query("DELETE FROM "$this->cfg['table_name']);
            }
            return (bool) 
    $result;
        }

        function 
    gc ($expire_time TIMENOW)
        {
            
    $result $this->db->query("DELETE FROM "$this->cfg['table_name'] ." WHERE cache_expire_time < $expire_time");
            return (
    $result) ? sqlite_changes($this->db->dbh) : 0;
        }

        function 
    trigger_error ($msg 'DB Error')
        {
            if (
    error_reporting()) trigger_error($msgE_USER_ERROR);
        }
    }

    -----[
    заменить]-----

    class 
    sqlite_common extends cache_common
    {
        var 
    $cfg = array(
                     
    'db_file_path' => 'sqlite.db',
                     
    'table_name'   => 'table_name',
                     
    'table_schema' => 'CREATE TABLE table_name (...)',
                     
    'pconnect'     => true,
                     
    'con_required' => true,
                     
    'log_name'     => 'SQLite3',
                     
    'shard_type'   => 'none',     #  none, string, int (тип перевичного ключа для шардинга)
                     
    'shard_val'    => 0,          #  для string - кол. начальных символов, для int - делитель (будет использован остаток от деления)
                     
    'file_name'    => 'sqlite.db',
                     
    'dir'            => null,
                   );
        var 
    $engine    'SQLite3';
        var 
    $dbh       null;
        var 
    $connected false;
        var 
    $shard_val false;

        var 
    $table_create_attempts 0;

        function 
    sqlite_common ($cfg)
        {
            
    $this->cfg array_merge($this->cfg$cfg);
            
    $this->dbg_enabled sql_dbg_enabled();
        }

        function 
    connect ()
        {
            
    $this->cur_query = ($this->dbg_enabled) ? ($this->cfg['pconnect'] ? 'p' '') .'connect to: '$this->cfg['db_file_path'] : 'connect';
            
    $this->debug('start');

            if (
    $this->cfg['shard_type'] != 'none' && $this->shard_val === false)
            {
                
    trigger_error("cannot shard: shard_val not defined for {$this->cfg['db_file_path']}"E_USER_ERROR);
            }
            
    $this->dbh = new SQLite3($this->cfg['db_file_path']);

            if (
    $this->dbh)
            {
                
    $this->connected true;
            }
           
            if (!
    $this->connected && $this->cfg['con_required'])
            {
                
    trigger_error('Error'E_USER_ERROR);
            }

            
    $this->debug('stop');
            
    $this->cur_query null;
        }

        function 
    create_table ()
        {
            
    $this->table_create_attempts++;
            return 
    $this->dbh->exec($this->cfg['table_schema']);
        }

        function 
    shard ($name)
        {
            
    $type $this->cfg['shard_type'];

            if (
    $type == 'none') return;
            if (
    is_array($name))  trigger_error('cannot shard: $name is array'E_USER_ERROR);

            
    // define shard_val
            
    if ($type == 'string')
            {
                
    $shard_val substr($name0$this->cfg['shard_val']);
            }
            else
            {
                
    $shard_val $name $this->cfg['shard_val'];
            }
            
    // все запросы должны быть к одному и тому же шарду
            
    if ($this->shard_val !== false)
            {
                if (
    $shard_val != $this->shard_val)
                {
                    
    trigger_error("shard cannot be reassigned. [{$this->shard_val}$shard_val$name]"E_USER_ERROR);
                }
                else
                {
                    return;
                }
            }
            
    $this->shard_val $shard_val;
            
    $this->cfg['pconnect'] = str_replace('*'$shard_val$this->cfg['db_file_path']);
        }

        function 
    query ($query$single false)
        {
            if (!
    $this->connected$this->connect();

            
    $this->cur_query $query;
            
    $this->debug('start');
            
    $queryIns = ($single) ? 'querySingle':'query';
            
    $mode = ($single) ? @$this->dbh->$queryIns($query,true) : @$this->dbh->$queryIns($query);
            if (!
    $result = @$mode)
            {
                if (!
    $this->table_create_attempts && !$this->fetch_column_types($this->cfg['table_name']))
                {
                    if (
    $this->create_table())
                    {
                        
    $result $this->dbh->exec($query);
                    }
                }
            }
            
    $this->debug('stop');
            
    $this->cur_query null;

            
    $this->num_queries++;

            return 
    $result;
        }

        function 
    fetch_row ($query)
        {
            
    $result $this->query($querytrue);
           
            return !empty(
    $result) ? $result false;
        }

        function 
    fetch_rowset ($query)
        {
            
    $rows = array();
            
    $result $this->query($query);
            
    $i 0;
            while (
    $row $result->fetchArray(SQLITE3_ASSOC)) {
                
    $rows[$i]=$row;
                
    $i++;
            }
           
            return !empty(
    $result) ? $rows : array();
        }
           
        function 
    changes ()
        {
            return (
    $this->dbh) ? $this->dbh->changes() : 0;
        }
       
        function 
    prepare($row)
        {
            return (
    $this->dbh) ? $this->dbh->prepare($row) : 0;
        }
       
        function 
    escape ($str)
        {
            return 
    $this->dbh->escapeString($str);
        }

        function 
    get_error_msg ()
        {
            return 
    'SQLite error #'. ($err_code $this->dbh->lastErrorMsg()) .': '$this->dbh->lastErrorCode();
        }

        function 
    rm ($name '')
        {
            if (
    $name)
            {
                
    $this->db->shard($this->prefix $name);
                
    $result $this->db->exec("DELETE FROM "$this->cfg['table_name'] ." WHERE cache_name = '"$this->dbh->prepare($this->prefix $name) ."'");
            }
            else
            {
                
    $result $this->db->exec("DELETE FROM "$this->cfg['table_name']);
            }
            return (bool) 
    $result;
        }

        function 
    gc ($expire_time TIMENOW)
        {
            
    $result $this->db->exec("DELETE FROM "$this->cfg['table_name'] ." WHERE cache_expire_time < $expire_time");
            return (
    $result) ? $result->changes() : 0;
        }

        function 
    trigger_error ($msg 'DB Error')
        {
            if (
    error_reporting()) trigger_error($msgE_USER_ERROR);
        }
       
        function 
    fetch_column_types($table_name)
        {
            
    $col_types = array();
            
    $col_info_res  $this->dbh->query"PRAGMA table_info('"$table_name "')");

            while (
    $col_info $col_info_res->fetchArray(SQLITE3_ASSOC))
            {
                
    $column_name $col_info['name'];
                
    $column_type $col_info['type'];
                
    $col_types[$column_name] = $column_type;
            }
            
    $col_info_res->finalize();
            return 
    $col_types;
        }
    }
       
    ####[СОХРАНИТЬ_ФАЙЛ]####

    ####[ОТКРЫТЬ]----- tpl_config.tpl
    -----[найти]-----

    $_lang $_main basename('lang_'$bb_cfg['default_lang']) .'/';

    -----[
    добавить ниже]-----

    $images['icon_online']           = $_lang .'user-online.png';
    $images['icon_offline']        = $_lang .'user-offline.png';
    $images['icon_hidden']            = $_lang .'user-hidden.png';
       
    ####[СОХРАНИТЬ_ФАЙЛ]####


    ####[ОТКРЫТЬ]----- page_geader.tpl
    -----[найти]-----

    <
    script type="text/javascript" src="{SITE_URL}misc/js/main.js?v={$bb_cfg['js_ver']}"></script>

    -----[
    добавить ниже]-----

    <
    script type="text/javascript" src="{SITE_URL}misc/js/opt.js?v={$bb_cfg['js_ver']}"></script>
    <
    link rel="stylesheet" href="{TPL_DIR}/popup.css?v={$bb_cfg['css_ver']}type="text/css">
    <
    link rel="stylesheet" href="{TPL_DIR}/popup-user.css?v={$bb_cfg['css_ver']}type="text/css">
    <
    link rel="stylesheet" href="{TPL_DIR}/AjaxPopup.css?v={$bb_cfg['css_ver']}type="text/css">

    -----[
    найти]-----

    <
    body>

    -----[
    добавить ниже]-----

    <
    span id="user_popup"></span>

    ####[СОХРАНИТЬ_ФАЙЛ]####

    ####[ОТКРЫТЬ]----- viewtopic.tpl
    -----[найти]-----

    </
    table><!--/pagination-->
       
    -----[
    добавить ниже]-----

    <
    div class="category row1 border bw_TRBL mrg_8 pad_4">{WHO_ONLINE_LIST}</div>
       
    ####[СОХРАНИТЬ_ФАЙЛ]####

    Изображения

    1. Снимок.JPG