Seditio Source
Root |
./othercms/b2evolution_7.2.3/inc/sessions/model/_hitlog.funcs.php
<?php
/**
 * This file implements functions for logging of hits and extracting stats.
 *
 * NOTE: the refererList() and stats_* functions are not fully functional ATM. I'll transform them into the Hitlog object during the next days. blueyed.
 *
 * This file is part of the evoCore framework - {@link http://evocore.net/}
 * See also {@link https://github.com/b2evolution/b2evolution}.
 *
 * @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
 *
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
 * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
 *
 * {@internal Origin:
 * This file was inspired by N C Young's Referer Script released in
 * the public domain on 07/19/2002. {@link http://ncyoung.com/entry/57}.
 * See also {@link http://ncyoung.com/demo/referer/}.
 * }}
 *
 * @package evocore
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );


/**
 * Display hits results table
 */
function hits_results_block( $params = array() )
{
    if( !
is_logged_in() )
    {
// Only logged in users can access to this function
       
return;
    }

    global
$blog, $sec_ID;

    if(
$blog == 0 )
    {
        if( !
check_user_perm( 'stats', 'view' ) )
        {
// Current user has no permission to view all stats (aggregated stats)
           
return;
        }
    }
    else
    {
        if( !
check_user_perm( 'stats', 'list', false, $blog ) )
        {
// Current user has no permission to view the stats of the selected blog
           
return;
        }
    }

   
/**
     * View funcs
     */
   
load_funcs('sessions/views/_stats_view.funcs.php');

    global
$blog, $admin_url, $rsc_url;
    global
$Session, $UserSettings, $DB;

    global
$datestartinput, $datestart, $datestopinput, $datestop;
    global
$preset_referer_type, $preset_agent_type;

   
$tab = param( 'tab', 'string', 'summary', true );
   
$tab3 = param( 'tab3', 'string', '', true );

    switch(
$tab )
    {
        case
'other':
           
$preset_results_title = T_('Direct browser hits');
           
$preset_referer_type = 'direct';
           
$preset_agent_type = 'browser';
           
$preset_filter_all_url = '?ctrl=stats&amp;tab=referers&amp;blog='.$blog;
           
$hide_columns = 'referer';
            break;

        case
'referers':
           
$preset_results_title = T_('Refered browser hits');
           
$preset_referer_type = 'referer';
           
$preset_agent_type = 'browser';
           
$preset_filter_all_url = '?ctrl=stats&amp;tab=referers&amp;blog='.$blog;
            break;

        case
'refsearches':
            if(
$tab3 == 'hits' )
            {
               
$preset_results_title = T_('Search hits');
               
$preset_referer_type = 'search';
               
$preset_agent_type = 'browser';
               
$preset_filter_all_url = '?ctrl=stats&amp;tab=refsearches&amp;tab3=hits&amp;blog='.$blog;
            }
            break;
    }

    if(
param_date( 'datestartinput', T_('Invalid date'), false,  NULL ) !== NULL )
    {
// We have a user provided localized date:
       
memorize_param( 'datestart', 'string', NULL, trim( form_date( $datestartinput ) ) );
       
memorize_param( 'datestartinput', 'string', NULL, empty( $datestartinput ) ? NULL : date( locale_datefmt(), strtotime( $datestartinput ) ) );
    }
    else
    {
// We may have an automated param transmission date:
       
param( 'datestart', 'string', '', true );
    }
    if(
param_date( 'datestopinput', T_('Invalid date'), false, NULL ) !== NULL )
    {
// We have a user provided localized date:
       
memorize_param( 'datestop', 'string', NULL, trim( form_date( $datestopinput ) ) );
       
memorize_param( 'datestopinput', 'string', NULL, empty( $datestopinput ) ? NULL : date( locale_datefmt(), strtotime( $datestopinput ) ) );
    }
    else
    {
// We may have an automated param transmission date:
       
param( 'datestop', 'string', '', true );
    }

   
$exclude = param( 'exclude', 'integer', 0, true );
   
$sess_ID = param( 'sess_ID', 'integer', NULL, true );
   
$remote_IP = param( 'remote_IP', 'string', NULL, true );
   
$referer_type = isset( $preset_referer_type ) ? $preset_referer_type : param( 'referer_type', 'string', NULL, true );
   
$agent_type = isset( $preset_agent_type ) ? $preset_agent_type : param( 'agent_type', 'string', NULL, true );
   
$device = param( 'device', 'string', NULL, true );
   
$hit_type = param( 'hit_type', 'string', NULL, true );
   
$reqURI = param( 'reqURI', 'string', NULL, true );
   
$resp_code = param( 'resp_code', 'integer', NULL, true );

   
// Create result set:

   
$SQL = new SQL();
   
$SQL->SELECT( 'SQL_NO_CACHE hit_ID, sess_ID, sess_device, hit_datetime, hit_type, hit_referer_type, hit_uri, hit_disp, hit_ctrl, hit_action, hit_coll_ID, hit_referer, hit_remote_addr,'
       
.'user_login, hit_agent_type, blog_shortname, dom_name, goal_name, hit_keyphrase, hit_serprank, hit_response_code, hit_method, hit_agent_ID' );
   
$SQL->FROM( 'T_hitlog LEFT JOIN T_basedomains ON dom_ID = hit_referer_dom_ID'
       
.' LEFT JOIN T_sessions ON hit_sess_ID = sess_ID'
       
.' LEFT JOIN T_blogs ON hit_coll_ID = blog_ID'
       
.' LEFT JOIN T_users ON sess_user_ID = user_ID'
       
.' LEFT JOIN T_track__goalhit ON hit_ID = ghit_hit_ID'
       
.' LEFT JOIN T_track__goal ON ghit_goal_ID = goal_ID' );

   
$count_SQL = new SQL();
   
$count_SQL->SELECT( 'SQL_NO_CACHE COUNT(hit_ID)' );
   
$count_SQL->FROM( 'T_hitlog' );

   
$operator = ( $exclude ? ' <> ' : ' = ' );

    if( ! empty(
$sess_ID ) )
    {
// We want to filter on the session ID:
       
$filter = 'hit_sess_ID'.$operator.$sess_ID;
       
$SQL->WHERE( $filter );
       
$count_SQL->WHERE( $filter );
    }
    elseif( ! empty(
$remote_IP ) ) // TODO: allow combine
   
{ // We want to filter on the goal name:
       
$filter = 'hit_remote_addr'.$operator.$DB->quote( $remote_IP );
       
$SQL->WHERE( $filter );
       
$count_SQL->WHERE( $filter );
    }

    if( ! empty(
$referer_type ) )
    {
       
$filter = 'hit_referer_type = '.$DB->quote( $referer_type );
       
$SQL->WHERE_and( $filter );
       
$count_SQL->WHERE_and( $filter );
    }

    if( ! empty(
$agent_type ) )
    {
       
$filter = 'hit_agent_type = '.$DB->quote( $agent_type );
       
$SQL->WHERE_and( $filter );
       
$count_SQL->WHERE_and( $filter );
    }

    if( ! empty(
$device ) )
    {
        if(
$device == 'other' )
        {
// Unknown device
           
$device = '';
        }
       
$filter = 'sess_device = '.$DB->quote( $device );
       
$SQL->WHERE_and( $filter );
       
$count_SQL->WHERE_and( $filter );
       
$count_SQL->FROM_add( 'LEFT JOIN T_sessions ON hit_sess_ID = sess_ID' );
    }

    if( ! empty(
$hit_type ) )
    {
       
$filter = 'hit_type = '.$DB->quote( $hit_type );
       
$SQL->WHERE_and( $filter );
       
$count_SQL->WHERE_and( $filter );
    }

    if( ! empty(
$reqURI ) )
    {
       
$filter = 'hit_uri LIKE '.$DB->quote( $reqURI );
       
$SQL->WHERE_and( $filter );
       
$count_SQL->WHERE_and( $filter );
    }

    if( ! empty(
$resp_code ) )
    {
       
$filter = 'hit_response_code = ' .$DB->quote( $resp_code );
       
$SQL->WHERE_and( $filter );
       
$count_SQL->WHERE_and( $filter );
    }

    if( ! empty(
$datestart ) )
    {
       
$SQL->WHERE_and( 'hit_datetime >= '.$DB->quote( $datestart.' 00:00:00' ) );
       
$count_SQL->WHERE_and( 'hit_datetime >= '.$DB->quote( $datestart.' 00:00:00' ) );
    }
    if( ! empty(
$datestop ) )
    {
       
$SQL->WHERE_and( 'hit_datetime <= '.$DB->quote( $datestop.' 23:59:59' ) );
       
$count_SQL->WHERE_and( 'hit_datetime <= '.$DB->quote( $datestop.' 23:59:59' ) );
    }


    if( ! empty(
$sec_ID ) )
    {    
// Filter by selected section:
       
$filter = 'blog_sec_ID = '.$DB->escape( $sec_ID );
       
$SQL->WHERE_and( $filter );
       
$count_SQL->FROM_add( 'LEFT JOIN T_blogs ON hit_coll_ID = blog_ID' );
       
$count_SQL->WHERE_and( $filter );
    }

    if( ! empty(
$blog ) )
    {    
// Filter by selected collection:
       
$filter = 'hit_coll_ID = '.$DB->escape( $blog );
       
$SQL->WHERE_and( $filter );
       
$count_SQL->WHERE_and( $filter );
    }

   
$resuts_param_prefix = 'hits_';
    if( ! empty(
$preset_referer_type ) )
    {
       
$resuts_param_prefix = substr( $preset_referer_type, 0, 8 ).'_'.$resuts_param_prefix;
    }

   
$default_order = '--D';

   
$SQL->ORDER_BY( '*, hit_ID' );

   
$Results = new Results( $SQL->get(), $resuts_param_prefix, $default_order, $UserSettings->get( 'results_per_page' ), $count_SQL->get(), true, 100000 );

   
// Initialize Results object
   
hits_results( $Results, array( 'default_order' => $default_order ) );

    if(
is_ajax_content() )
    {
// init results param by template name
       
if( !isset( $params[ 'skin_type' ] ) || ! isset( $params[ 'skin_name' ] ) )
        {
           
debug_die( 'Invalid ajax results request!' );
        }
       
$Results->init_params_by_skin( $params[ 'skin_type' ], $params[ 'skin_name' ] );
    }

   
// Display results:
   
$Results->display();

    if( !
is_ajax_content() )
    {
// Create this hidden div to get a function name for AJAX request
       
echo '<div id="'.$resuts_param_prefix.'ajax_callback" style="display:none">'.__FUNCTION__.'</div>';
    }
}


/**
 * Displays keywords used for search leading to this page
 */
function stats_search_keywords( $keyphrase, $length = 45 )
{
    global
$evo_charset;

    if(
$keyphrase === NULL || $keyphrase === '' )
    {
        return
'<span class="note">['./* TRANS: "Not Available" */ T_('N/A').']</span>';
    }

   
// Save original string
   
$keyphrase_orig = $keyphrase;

   
$keyphrase = strmaxlen( $keyphrase, $length, '...', 'raw' );

   
// Convert keyword encoding, some charsets are supported only in PHP 4.3.2 and later.
    // This fixes encoding problem for Cyrillic keywords
    // See http://forums.b2evolution.net/viewtopic.php?t=17431
   
$keyphrase = htmlentities( $keyphrase, ENT_COMPAT, $evo_charset );

    return
'<span title="'.format_to_output( $keyphrase_orig, 'htmlattr' ).'">'.$keyphrase.'</span>';
}


/**
 * Generate a random ip
 *
 * @return string ip
 */
function generate_random_ip()
{
    return
mt_rand( 0, 255 ).'.'.mt_rand( 0, 255 ).'.'.mt_rand( 0, 255 ).'.'.mt_rand( 0, 255 );
}


/**
 * Generate fake hit statistics
 *
 * @param integer the number of days to generate statistics
 * @param integer min interval between hits in seconds
 * @param integer max interval between hits in seconds
 * @param boolean TRUE to display the process dots during generating of the hits
 * @return integer count of inserted hits
 */
function generate_hit_stat( $days, $min_interval, $max_interval, $display_process = false )
{
    global
$baseurlroot, $admin_url, $user_agents, $DB, $htsrv_url, $is_api_request;

   
load_class( 'items/model/_itemlistlight.class.php', 'ItemListLight' );
   
load_class( 'sessions/model/_hit.class.php', 'Hit' );

   
$links = array();

   
$BlogCache = & get_BlogCache();

   
$blogs_id = $BlogCache->load_public();

    foreach(
$blogs_id as $blog_id )
    {    
// Handle all public blogs:
       
$listBlog = & $BlogCache->get_by_ID( $blog_id );
        if( empty(
$listBlog ) )
        {
            continue;
        }

       
$ItemList = new ItemListLight( $listBlog );
       
$filters = array();

       
# This is the list of categories to restrict the linkblog to (cats will be displayed recursively)
        # Example: $linkblog_cat = '4,6,7';
       
$linkblog_cat = '';

       
# This is the array if categories to restrict the linkblog to (non recursive)
        # Example: $linkblog_catsel = array( 4, 6, 7 );
       
$linkblog_catsel = array(); // $cat_array;
        // Compile cat array stuff:
       
$linkblog_cat_array = array();
       
$linkblog_cat_modifier = '';

       
compile_cat_array( $linkblog_cat, $linkblog_catsel, /* by ref */ $linkblog_cat_array, /* by ref */ $linkblog_cat_modifier, $listBlog->ID );

       
$filters['cat_array'] = $linkblog_cat_array;
       
$filters['cat_modifier'] = $linkblog_cat_modifier;


       
$ItemList->set_default_filters( $filters );

       
// Get the items list of current blog
       
$ItemList->query();

        if( !
$ItemList->result_num_rows )
        {    
// Nothing to display:
           
continue;
        }

        while(
$Item = & $ItemList->get_category_group() )
        {
           
// Open new cat:
           
$Chapter = & $Item->get_main_Chapter();
            while(
$Item = & $ItemList->get_item() )
            {
               
$links[] = array(
                   
'link' => '/'.$listBlog->siteurl.'/'.$Chapter->get_url_path().$Item->urltitle, // trim( $Chapter->get_permanent_url( NULL ,' ' ) ).
                   
'blog_id' => $blog_id
               
);
            }
        }

       
// add search links for all blogs
       
$links[] = array(
           
'link' => url_add_param( '/'.$listBlog->siteurl, 's=$keywords$&disp=search&submit=Search', '&' ),
           
'blog_id' => $blog_id
       
);

       
$links[] = array(
           
'link' => url_add_param( '/'.$listBlog->siteurl, 'disp=users', '&' ),
           
'blog_id' => $blog_id,
           
'disp' => 'users'
       
);

       
$links[] = array(
           
'link' => url_add_param( '/'.$listBlog->siteurl, 'disp=user&user_ID=1', '&' ),
           
'blog_id' => $blog_id,
           
'disp' => 'users'
       
);

       
$links[] = array(
           
'link' => url_add_param( '/'.$listBlog->siteurl, 'disp=threads', '&' ),
           
'blog_id' => $blog_id,
           
'disp' => 'threads'
       
);

       
$links[] = array(
           
'link' => url_add_param( '/'.$listBlog->siteurl, 'disp=profile', '&' ),
           
'blog_id' => $blog_id,
           
'disp' => 'profile'
       
);

       
$links[] = array(
           
'link' => $htsrv_url.'anon_async.php',
           
'blog_id' => $blog_id
       
);

       
$links[] = array(
           
'link' => '/api/v1/collections/'.$listBlog->urlname.'/posts',
           
'blog_id' => $blog_id
       
);

       
$links[] = array(
           
'link' => '/api/v1/collections/'.$listBlog->urlname.'/search/post',
           
'blog_id' => $blog_id
       
);

       
$links[] = array(
           
'link' => '/xmlsrv/xmlrpc.php?blog='.$listBlog->ID,
           
'blog_id' => $blog_id
       
);
    }

   
$links[] = array(
           
'link' => '/api/v1/collections',
        );

   
$links[] = array(
           
'link' => '/xmlsrv/xmlrpc.php'
       
);

   
$referes = array('http://www.fake-referer1.com',
       
'http://www.fake-referer2.com',
       
'http://www.fake-referer3.com',
       
'http://www.fake-referer4.com',
       
'http://www.fake-referer5.com',
       
'http://www.fake-referer6.com',
       
'http://www.fake-referer7.com',
       
'http://www.fake-referer8.com',
       
'http://www.fake-referer9.com',
       
'http://www.mail.google.com/fake/referer',
       
'http://www.webmail.aol.com/fake/referer',
       
'http://www.mail.yahoo.com/fake/referer',
       
'http://bloglines.com/fake/referer',
       
'http://www.fake-refer-online-casino1.com',
       
'http://www.fake-refer-online-casino2.com',
       
'http://www.fake-refer-online-casino3.com',
       
'http://www.google.com/url?sa=t&rct=j&q=$keywords$&source=web&cd=4',
       
'http://www.bing.com/search?q=$keywords$&src=IE-SearchBox&FORM=IE8SRC'
   
);

   
$devices = array(
           
'iphone',
           
'ipad',
           
'andrtab',
           
'android',
           
'berrytab',
           
'blkberry',
           
'winphone',
           
'wince',
           
'palm',
           
'gendvice'
       
);

   
$request_methods = array( 'GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'unknown' );
   
$request_methods_count = count( $request_methods ) - 1;

   
$robots = array();
    foreach(
$user_agents as $lUserAgent )
    {
        if(
$lUserAgent[0] == 'robot' )
        {
           
$robots[] = $lUserAgent[1];
        }
    }

   
$robots_count = count( $robots ) - 1;

   
$ref_count = count( $referes ) - 1;

   
$admin_link = array(
           
'link' => $admin_url,
           
'blog_id' => NULL
       
);

   
$links_count = count( $links );

    if( empty(
$links_count ) )
    {
        global
$Messages;
       
$Messages->add( 'Cannot generate statistics without collection links.' );
        return;
    }

   
// generate users id array

   
$users_array = $DB->get_results('
                    SELECT user_ID
                      FROM T_users
                      WHERE user_status IN ( "activated", "autoactivated", "manualactivated" )
                      LIMIT 10'
                   
, ARRAY_A );

   
$users_count = count( $users_array );
   
$devices_count = count( $devices );

    if( empty(
$users_count ) )
    {
       
$Messages->add( 'Cannot generate statistics without valid users.' );
        return;
    }

   
// Calculate the period of testing
   
$cur_time = time();
   
$past_time = mktime( date( 'H' ), date( 'i' ), date( 's' ), date( 'm' ), date( 'd' ) - $days, date( 'Y' ) );

   
$insert_data = '';
   
$insert_data_count = 0;

   
// create session array for testing
   
$sessions = array();
   
mt_srand( crc32( microtime() ) );
    for(
$i = 0; $i <= $users_count - 1; $i++ )
    {
       
$sessions[] = array(
               
'sess_ID'          => -1,
               
'sess_key'         => generate_random_key( 32 ),
               
'sess_start_ts'    => 0,
               
'sess_lastseen_ts' => 0,
               
'sess_ipaddress'   => generate_random_ip(),
               
'sess_user_ID'     => $users_array[$i]['user_ID'],
               
'sess_device'      => $devices[ mt_rand( 0, $devices_count - 1 ) ],
               
'pervios_link'     => '',
               
'robot'            => ''
           
);
    }

   
// main cycle of generation
    //mt_srand(crc32(microtime()));
   
for( $time_shift = $past_time; $cur_time > $time_shift; $time_shift += mt_rand( $min_interval, $max_interval ) )
    {
       
//mt_srand(crc32(microtime()));
       
$insert_data_count = $insert_data_count + 1;

       
$rand_i = mt_rand( 0, $users_count - 1 );
       
$rand_link = mt_rand( 0, $links_count - 1 );
       
$cur_session = $sessions[$rand_i];
       
$rand_request_method = $request_methods[ mt_rand( 0, $request_methods_count - 1 ) ];


        if(
strstr( $links[$rand_link]['link'], '$keywords$' ) )
        {
// check if the current search link is selected randomly.
            // If yes, generate search link and add it to DB
            //mt_srand(crc32(microtime()+ $time_shift));
           
$keywords = 'fake search '.mt_rand( 0, 9 );
           
$links[$rand_link]['link'] = str_replace( '$keywords$', urlencode( $keywords ), $links[$rand_link]['link'] );
            if(
strstr( $links[$rand_link]['link'], 's=' ) )
            {
               
$links[$rand_link]['s'] = $keywords;
            }
        }

        if(
$cur_session['sess_ID'] == -1 )
        {
// This session needs initialization:
           
$cur_session['sess_start_ts'] = $time_shift - 1;
           
$cur_session['sess_lastseen_ts'] = $time_shift;

           
$DB->query( 'INSERT INTO T_sessions ( sess_key, sess_start_ts, sess_lastseen_ts, sess_ipaddress, sess_user_ID, sess_device )
                    VALUES (
                        '
.$DB->quote( $cur_session['sess_key'] ).',
                        '
.$DB->quote( date( 'Y-m-d H:i:s', $cur_session['sess_start_ts'] ) ).',
                        '
.$DB->quote( date( 'Y-m-d H:i:s', $cur_session['sess_lastseen_ts'] ) ).',
                        '
.$DB->quote( $cur_session['sess_ipaddress'] ).',
                        '
.$DB->quote( $cur_session['sess_user_ID'] ).',
                        '
.$DB->quote( $cur_session['sess_device'] ).'
                    )'
);

           
$cur_session['sess_ID'] = $DB->insert_id;
           
$sessions[$rand_i] = $cur_session;

           
// Check if current url is api request:
           
$is_api_request = ( strpos( $links[$rand_link]['link'], '/api/v1' ) === 0 || strpos( $links[$rand_link]['link'], '/xmlsrv/xmlrpc.php' ) === 0 );

           
$Test_hit = new Hit( '', $cur_session['sess_ipaddress'], $cur_session['sess_ID'], $cur_session['sess_lastseen_ts'], 1, $links[$rand_link] );
           
$Test_hit->method = $rand_request_method;
           
$Test_hit->log();
        }
        else
        {
            if( (
$time_shift - $cur_session['sess_lastseen_ts'] ) > 3000 || ! empty( $cur_session['robot'] ) )
            {
// This session last updated more than 3000 sec ago. Instead of this session create a new session.
               
$cur_session = array(
                   
'sess_ID'          => -1,
                   
'sess_key'         => generate_random_key( 32 ),
                   
'sess_start_ts'    => 0,
                   
'sess_lastseen_ts' => 0,
                   
'sess_ipaddress'   => generate_random_ip(),
                   
'sess_user_ID'     => $users_array[ mt_rand( 0, $users_count - 1 ) ]['user_ID'],
                   
'sess_device'      => $devices[ mt_rand( 0, $devices_count - 1 ) ],
                   
'pervios_link'     => '',
                   
'robot'            => ''
               
);

               
$cur_session['sess_start_ts'] = $time_shift - 1;
               
$cur_session['sess_lastseen_ts'] = $time_shift;
               
$r_num = mt_rand( 0, 100 );
                if(
$r_num > 40 )
                {
// Create anonymous user and make double insert into hits.
                   
$cur_session['sess_user_ID'] = -1;
                   
$DB->query( 'INSERT INTO T_sessions ( sess_key, sess_start_ts, sess_lastseen_ts, sess_ipaddress, sess_device )
                            VALUES (
                                '
.$DB->quote( $cur_session['sess_key'] ).',
                                '
.$DB->quote( date( 'Y-m-d H:i:s', $cur_session['sess_start_ts'] ) ).',
                                '
.$DB->quote( date( 'Y-m-d H:i:s', $cur_session['sess_lastseen_ts'] ) ).',
                                '
.$DB->quote( $cur_session['sess_ipaddress'] ).',
                                '
.$DB->quote( $cur_session['sess_device'] ).'
                            )'
);

                    if(
$r_num >= 80 )
                    {
// Create robot hit
                       
$cur_session['robot'] = $robots[ mt_rand( 0, $robots_count ) ];
                    }
                }
                else
                {
                   
$DB->query(    'INSERT INTO T_sessions( sess_key, sess_start_ts, sess_lastseen_ts, sess_ipaddress, sess_user_ID, sess_device )
                            VALUES (
                                '
.$DB->quote( $cur_session['sess_key'] ).',
                                '
.$DB->quote( date( 'Y-m-d H:i:s', $cur_session['sess_start_ts'] ) ).',
                                '
.$DB->quote( date( 'Y-m-d H:i:s', $cur_session['sess_lastseen_ts'] ) ).',
                                '
.$DB->quote( $cur_session['sess_ipaddress'] ).',
                                '
.$DB->quote( $cur_session['sess_user_ID'] ).',
                                '
.$DB->quote( $cur_session['sess_device'] ).'
                            )'
);
                }

               
$cur_session['sess_ID'] = $DB->insert_id;

                if(
mt_rand( 0, 100 ) > 20 )
                {
                   
//$ref_count
                   
$ref_link = $referes[ mt_rand( 0, $ref_count ) ];
                    if(
strstr( $ref_link, '$keywords$' ) )
                    {
// check if the current search link is selected randomly.
                       
$keywords = 'fake search '.mt_rand( 0, 9 );
                       
$ref_link = str_replace( '$keywords$', urlencode( $keywords ), $ref_link );
                    }
                }
                else
                {
                   
$ref_link = '';
                }

                if(
$cur_session['sess_user_ID'] == -1 )
                {
                    if( empty(
$cur_session['robot'] ) )
                    {
                       
$link = array(
                           
'link'    => '/htsrv/login.php',
                           
'blog_id' => 1
                       
);

                       
// This is NOT api request:
                       
$is_api_request = false;

                       
$Test_hit = new Hit( $ref_link, $cur_session['sess_ipaddress'], $cur_session['sess_ID'], $cur_session['sess_lastseen_ts'], 1, $link );

                       
$Test_hit->method = $rand_request_method;

                       
$Test_hit->log();

                       
$link = array(
                           
'link'    => '/htsrv/login.php?redirect_to=fake_stat',
                           
'blog_id' => 1
                       
);

                       
$Test_hit = new Hit( $baseurlroot, $cur_session['sess_ipaddress'], $cur_session['sess_ID'], $cur_session['sess_lastseen_ts'] + 3, 1, $link );

                       
$Test_hit->method = $rand_request_method;

                       
$Test_hit->log();

                       
$cur_session['pervios_link'] = $baseurlroot.$link['link'];
                    }
                    else
                    {
                       
// Check if current url is api request:
                       
$is_api_request = ( strpos( $links[$rand_link]['link'], '/api/v1' ) === 0 || strpos( $links[$rand_link]['link'], '/xmlsrv/xmlrpc.php' ) === 0 );

                        if(
mt_rand( 0, 100 ) < 50 )
                        {
// robot hit
                           
$Test_hit = new Hit( '', $cur_session['sess_ipaddress'], $cur_session['sess_ID'], $cur_session['sess_lastseen_ts'], 1, $links[$rand_link], $cur_session['robot'] );
                        }
                        else
                        {
// rss/atom hit
                           
$Test_hit = new Hit( '', $cur_session['sess_ipaddress'], $cur_session['sess_ID'], $cur_session['sess_lastseen_ts'], 1, $links[$rand_link], NULL, NULL, 1 );
                        }
                       
$Test_hit->method = $rand_request_method;
                       
$Test_hit->log();
                    }
                }
                else
                {
                    if(
mt_rand( 0, 100 ) < 10 )
                    {    
// Test hit to admin page:

                        // This is NOT api request:
                       
$is_api_request = false;

                       
$Test_hit = new Hit( '', $cur_session['sess_ipaddress'], $cur_session['sess_ID'], $cur_session['sess_lastseen_ts'], 1, $admin_link, NULL, 1 );
                       
$Test_hit->method = $rand_request_method;
                       
$Test_hit->log();
                       
$cur_session['pervios_link'] = $admin_url;
                    }
                    else
                    {
                       
// Check if current url is api request:
                       
$is_api_request = ( strpos( $links[$rand_link]['link'], '/api/v1' ) === 0 || strpos( $links[$rand_link]['link'], '/xmlsrv/xmlrpc.php' ) === 0 );

                       
$Test_hit = new Hit( $ref_link, $cur_session['sess_ipaddress'], $cur_session['sess_ID'], $cur_session['sess_lastseen_ts'], 1, $links[$rand_link] );
                       
$Test_hit->method = $rand_request_method;
                       
$Test_hit->log();
                       
$cur_session['pervios_link'] = $baseurlroot.$links[$rand_link]['link'];
                    }
                }
            }
            else
            {
               
// Update session
               
$cur_session['sess_lastseen_ts'] = $time_shift;

               
// Check if current url is api request:
               
$is_api_request = ( strpos( $links[$rand_link]['link'], '/api/v1' ) === 0 || strpos( $links[$rand_link]['link'], '/xmlsrv/xmlrpc.php' ) === 0 );

               
$Test_hit = new Hit( $cur_session['pervios_link'], $cur_session['sess_ipaddress'], $cur_session['sess_ID'], $cur_session['sess_lastseen_ts'], 1, $links[$rand_link] );
               
$Test_hit->method = $rand_request_method;
               
$Test_hit->log();

               
$DB->query( 'UPDATE T_sessions
                      SET sess_lastseen_ts = '
.$DB->quote( date( 'Y-m-d H:i:s', $cur_session['sess_lastseen_ts'] ) ).'
                    WHERE sess_ID = '
.$DB->quote( $cur_session['sess_ID'] ),
                   
'Update session' );

               
$cur_session['pervios_link'] = $baseurlroot.$links[$rand_link]['link'];

               
$sessions[$rand_i] = $cur_session;
            }
        }

        if( isset(
$Test_hit ) && ! empty( $Test_hit->ID ) && rand( 0, 2 ) == 2 )
        {    
// Insert sample goal hits per random new Hit:
           
$DB->query( 'INSERT INTO T_track__goalhit ( ghit_goal_ID, ghit_hit_ID )
                SELECT goal_ID, '
.$Test_hit->ID.' FROM T_track__goal
                 ORDER BY RAND()
                 LIMIT 1'
);
        }

       
$sessions[$rand_i] = $cur_session;

        if(
$display_process )
        {
            if(
$insert_data_count % 100 == 0 )
            {
// Display a process of creating by one dot for 100 hits
               
echo ' .';
               
evo_flush();
            }
        }
    }

   
// Reset this back to from test values:
   
$is_api_request = false;

    return
$insert_data_count;
}


/**
 * Get domain type titles
 *
 * @param boolean TRUE to escape quotes in titles
 * @return array Domain titles
 */
function stats_dom_type_titles( $escape_quotes = false )
{
    return array(
           
'unknown'    => $escape_quotes ? TS_('Unknown') : T_('Unknown'),
           
'normal'     => $escape_quotes ? TS_('Referer') : T_('Referer'),
           
'searcheng'  => $escape_quotes ? TS_('Search referer') : T_('Search referer'),
           
'aggregator' => $escape_quotes ? TS_('Aggregator referer') : T_('Aggregator referer'),
           
'email'      => $escape_quotes ? TS_('Email provider') : T_('Email provider'),
        );
}


/**
 * Get domain status titles
 *
 * @param boolean TRUE to escape quotes in titles
 * @return array Domain titles
 */
function stats_dom_status_titles( $escape_quotes = false )
{
    return array(
           
'trusted' => $escape_quotes ? TS_('Trusted') : T_('Trusted'),
           
'unknown' => $escape_quotes ? TS_('Unknown') : T_('Unknown'),
           
'suspect' => $escape_quotes ? TS_('Suspect') : T_('Suspect'),
           
'blocked' => $escape_quotes ? TS_('Blocked') : T_('Blocked'),
        );
}


/**
 * Get status colors of domain
 *
 * @return array Color values
 */
function stats_dom_status_colors()
{
    return array(
           
'trusted' => '00CC00',
           
'unknown' => '999999',
           
'suspect' => 'FFAA00',
           
'blocked' => 'FF0000',
        );
}


/**
 * Get array of status icons for domains
 *
 * @return array Status icons
 */
function stats_dom_status_icons()
{
    return array(
           
'trusted' => get_icon( 'bullet_green', 'imgtag', array( 'title' => aipr_status_title( 'trusted' ) ) ),
           
'unknown' => get_icon( 'bullet_white', 'imgtag', array( 'title' => aipr_status_title( 'unknown' ) ) ),
           
'suspect' => get_icon( 'bullet_orange', 'imgtag', array( 'title' => aipr_status_title( 'suspect' ) ) ),
           
'blocked' => get_icon( 'bullet_red', 'imgtag', array( 'title' => aipr_status_title( 'blocked' ) ) )
        );
}


/**
 * Get domain type title by value
 *
 * @param string Domain type value
 * @param boolean TRUE to escape quotes in titles
 * @return string Domain type title
 */
function stats_dom_type_title( $dom_type, $escape_quotes = false )
{
   
$dom_type_titles = stats_dom_type_titles( $escape_quotes );
    return isset(
$dom_type_titles[ $dom_type ] ) ? $dom_type_titles[ $dom_type ] : $dom_type;
}


/**
 * Get domain status title by value
 *
 * @param string Domain status value
 * @param boolean TRUE to escape quotes in titles
 * @return string Domain status title
 */
function stats_dom_status_title( $dom_status, $escape_quotes = false )
{
   
$dom_status_titles = stats_dom_status_titles( $escape_quotes );
    return isset(
$dom_status_titles[ $dom_status ] ) ? $dom_status_titles[ $dom_status ] : $dom_status;
}


/**
 * Get domain status color by value
 *
 * @param string Domain status value
 * @return string Domain status color
 */
function stats_dom_status_color( $dom_status )
{
   
$dom_status_colors = stats_dom_status_colors();
    return isset(
$dom_status_colors[ $dom_status ] ) ? '#'.$dom_status_colors[ $dom_status ] : 'none';
}


/**
 * Get domain status icon by value
 *
 * @param string Domain status value
 * @return string Domain status icon
 */
function stats_dom_status_icon( $dom_status )
{
   
$dom_status_icons = stats_dom_status_icons();
    return isset(
$dom_status_icons[ $dom_status ] ) ? $dom_status_icons[ $dom_status ] : '';
}


/**
 * Get top existing Domain object by subdomain name
 *
 * @param string Subdomain name
 * @return onject Domain object
 */
function & get_Domain_by_subdomain( $subdomain_name )
{
   
$DomainCache = & get_DomainCache();

   
$subdomain_name = explode( '.', $subdomain_name );

   
// Store in this var top existing domain even with unknown status,
    // This domain will be returned when all other subdomains have unknown status:
   
$first_existing_Domain = NULL;

    for(
$i = 0; $i < count( $subdomain_name ); $i++ )
    {
       
$domain_name = implode( '.', array_slice( $subdomain_name, $i ) );
       
$domain_names = array( $domain_name, '.'.$domain_name );
        foreach(
$domain_names as $domain_name )
        {
            if(
$Domain = & $DomainCache->get_by_name( $domain_name, false, false ) )
            {    
// If domain exists in DB:
               
if( $first_existing_Domain === NULL )
                {    
// Store first top existing domain with any status:
                   
$first_existing_Domain = $Domain;
                }
                if(
$Domain->get( 'status' ) != 'unknown' )
                {    
// Use this domain because it exists with not unknown status:
                   
return $Domain;
                }
            }
        }
    }

   
// Return either NULL or first top existing domain with unknown status:
   
return $first_existing_Domain;
}


/**
 * Get Domain object by url
 *
 * @param string URL
 * @return onject Domain object
 */
function & get_Domain_by_url( $url )
{
   
// Exctract domain name from url:
   
$domain_name = url_part( $url, 'host' );

   
$Domain = & get_Domain_by_subdomain( $domain_name );

    return
$Domain;
}


/**
 * Get user agent name by ID
 *
 * @param integer Agent ID
 * @param string Agent name or Agent ID if agent is not found
 */
function get_hit_agent_name_by_ID( $agent_ID )
{
    global
$user_agents;

    if( isset(
$user_agents[ $agent_ID ] ) && ! empty( $user_agents[ $agent_ID ][2] ) )
    {
// Agent is found with given ID
       
return $user_agents[ $agent_ID ][2];
    }
    else
    {
// No agent, Return ID
       
return $agent_ID;
    }
}


/**
 * Extract keyphrases from the hitlog
 *
 * @param boolean|string TRUE to print out messages, 'cron_job' - to log messages for cron job
 * @return integer Number of new inserted key phrases,
 *         string Error message if the process is already running and not allowed to run
 */
function extract_keyphrase_from_hitlogs( $display_messages = true )
{
    global
$DB, $Messages, $Timer;

   
// Set lock name based on the database name, table name and process name
   
$lock_name = $DB->dbname.'.T_track__keyphrase.extract_keyphrase';

    if(
$DB->get_var( 'SELECT IS_FREE_LOCK( '.$DB->quote( $lock_name ).' )' ) === '0' )
    {
// The "exctract_keyphrase" process is already running on a different request, do not start it again
        // Do not translate.
       
return 'Keyphrase extraction is already in progress in a different process. This new request would duplicate the effort so it is aborted.';
    }

   
$inserted_keyphrases_num = 0;

   
// Important: If a two or more different simultanious process will arrive to this point at the same time, only one of them will acquire the lock!
    // The other processes have to wait until the one who acquired the lock will release it. After that the other process will get it one by one.

    // Get lock with a 20 seconds timeout
   
$DB->get_var( 'SELECT GET_LOCK( '.$DB->quote( $lock_name ).', 20 )' );

   
// Look for unextracted keyphrases:
   
$Timer->start( 'extract_keyphrase_number' );
   
$SQL = new SQL( 'Get number of hitlog records and  min/max of hit IDs that have a keyphrase that was not processed yet' );
   
$SQL->SELECT( 'COUNT( hit_ID ) AS hits_num, COUNT( DISTINCT hit_keyphrase ) AS keyphrases_num, MIN( hit_ID ) as min, MAX( hit_ID ) as max' );
   
$SQL->FROM( 'T_hitlog' );
   
$SQL->WHERE( 'hit_keyphrase IS NOT NULL' );
   
$SQL->WHERE_and( 'hit_keyphrase != ""' );
   
$SQL->WHERE_and( 'hit_keyphrase_keyp_ID IS NULL' );  // Keyphrase that has not been extracted yet!
   
$ids = $DB->get_row( $SQL, ARRAY_A );
   
$Timer->stop( 'extract_keyphrase_number' );

    if(
$display_messages )
    {    
// Display info:
       
$log_message = sprintf( TB_('Number of hitlog records that have a keyphrase that were not processed yet: %d'), intval( $ids['hits_num'] ) );
       
$log_message .= ' <span class="note">(keyphrases='.$ids['keyphrases_num'].', min='.$ids['min'].', max='.$ids['max'].')('.sprintf( TB_('Time: %s seconds'), $Timer->get_duration( 'extract_keyphrase_number' ) ).')</span>';
        if(
$display_messages === 'cron_job' )
        {    
// Log a message for cron job:
           
cron_log_append( $log_message );
        }
        else
        {    
// Print out a message:
           
echo '<p>'.$log_message.'</p>';
           
evo_flush();
        }
    }

    if( ! empty(
$ids['min'] ) && ! empty( $ids['max'] ) )
    {    
// Extract keyphrases if needed:

       
$Timer->start( 'extract_keyphrase_insert' );

       
// External keyphrases
       
$sql = 'INSERT INTO T_track__keyphrase(keyp_phrase, keyp_count_refered_searches)
                    SELECT h.hit_keyphrase, 1
                    FROM T_hitlog as h
                    WHERE
                        (h.hit_ID >= '
.$ids['min'].' AND h.hit_ID <= '.$ids['max'].')
                        AND h.hit_keyphrase IS NOT NULL
                        AND h.hit_keyphrase != ""
                        AND h.hit_keyphrase_keyp_ID IS NULL
                        AND h.hit_referer_type = "search"
                ON DUPLICATE KEY UPDATE
                T_track__keyphrase.keyp_count_refered_searches = T_track__keyphrase.keyp_count_refered_searches + 1'
;
       
$DB->query( $sql, ' Insert/Update external keyphrase' );

       
// Internal keyphrases
       
$sql = 'INSERT INTO T_track__keyphrase(keyp_phrase, keyp_count_internal_searches)
                    SELECT h.hit_keyphrase, 1
                    FROM T_hitlog as h
                    WHERE
                        (h.hit_ID >= '
.$ids['min'].' AND h.hit_ID <= '.$ids['max'].')
                        AND h.hit_keyphrase IS NOT NULL
                        AND h.hit_keyphrase != ""
                        AND h.hit_keyphrase_keyp_ID IS NULL
                        AND h.hit_referer_type != "search"
                ON DUPLICATE KEY UPDATE
                T_track__keyphrase.keyp_count_internal_searches = T_track__keyphrase.keyp_count_internal_searches + 1'
;
       
$DB->query( $sql, 'Insert/Update internal keyphrase' );

       
$Timer->stop( 'extract_keyphrase_insert' );

        if(
$display_messages )
        {    
// Display info:
           
$log_message = sprintf( TB_('%d key phrases have been inserted or updated.'), intval( $ids['keyphrases_num'] ) );
           
$log_message .= ' <span class="note">('.sprintf( TB_('Time: %s seconds'), $Timer->get_duration( 'extract_keyphrase_insert' ) ).')</span>';
            if(
$display_messages === 'cron_job' )
            {    
// Log a message for cron job:
               
cron_log_append( $log_message );
            }
            else
            {    
// Print out a message:
               
echo '<p>'.$log_message.'</p>';
               
evo_flush();
            }
        }

       
$chunk = 1000;
       
$number_of_chunks = ceil( ( $ids['max'] - $ids['min'] ) / $chunk );
        if(
$display_messages )
        {    
// Display info:
           
$log_message = TB_('Updating hit logs table...').' ('.$number_of_chunks.' chunks)';
            if(
$display_messages === 'cron_job' )
            {    
// Log a message for cron job:
               
cron_log_append( $log_message, NULL, '' );
// TODO: We must FORCE writing the log to the DB here
           
}
            else
            {    
// Print out a message:
               
echo '<p>'.$log_message;
               
evo_flush();
            }
        }

       
$Timer->start( 'extract_keyphrase_update' );
// TODO: this still takes too much time!
// Let's try updating 1000 lines at a time.
       
$start = $ids['min'];
       
$i = 1;
        while(
$start <= $ids['max'] )
        {
           
$stop = min( $start+$chunk-1, $ids['max'] ); // don't go over max

           
$log_message = 'Doing chunk #'.$i.' from '.$start.' to '.$stop.'...';
            if(
$display_messages === 'cron_job' )
            {    
// Log a message for cron job:
               
cron_log_append( $log_message );
            }
            else
            {    
// Print out a message:
               
echo '<br/>'.$log_message;
               
evo_flush();
            }

           
$sql = 'UPDATE T_hitlog as h, T_track__keyphrase as k
                    SET h.hit_keyphrase_keyp_ID = k.keyp_ID
                    WHERE    h.hit_keyphrase = k.keyp_phrase
                        AND h.hit_keyphrase != ""
                        AND ( h.hit_ID >= '
.$start.' )
                        AND ( h.hit_ID <= '
.$stop.' )
                        AND ( h.hit_keyphrase_keyp_ID IS NULL )'
;
           
$updated_keyphrases_num = $DB->query( $sql, 'Update hitlogs keyphrase id' );

           
$log_message = sprintf( TB_('%d records'), $updated_keyphrases_num );
            if(
$display_messages === 'cron_job' )
            {    
// Log a message for cron job:
               
cron_log_append( $log_message );
            }
            else
            {    
// Print out a message:
               
echo $log_message;
            }

           
$start += $chunk;
           
$i++;
        }


       
$Timer->stop( 'extract_keyphrase_update' );
        if(
$display_messages )
        {    
// Display info:
           
$log_message = 'Finished <span class="note">('.sprintf( TB_('Time: %s seconds'), $Timer->get_duration( 'extract_keyphrase_update' ) ).')</span>';
            if(
$display_messages === 'cron_job' )
            {    
// Log a message for cron job:
               
cron_log_append( $log_message );
            }
            else
            {    
// Print out a message:
               
echo '</p><p>'.$log_message.'</p>';
               
evo_flush();
            }
        }
    }

   
$DB->get_var( 'SELECT RELEASE_LOCK( '.$DB->quote( $lock_name ).' )' );

    return
intval( $ids['keyphrases_num'] );
}


/**
 * Parse extra params of goal hit (E.g. 'item_ID=123')
 *
 * @param string Value of extra params
 * @param string
 */
function stats_goal_hit_extra_params( $ghit_params )
{
    if(
preg_match( '/^item_ID=([0-9]+)$/i', $ghit_params, $matches ) )
    {
// Parse item ID
       
$ItemCache = & get_ItemCache();
        if(
$Item = & $ItemCache->get_by_ID( intval( $matches[1] ), false, false ) )
        {
// Display a link to view with current item title
           
if( check_user_perm( 'item_post!CURSTATUS', 'edit', false, $Item ) )
            {
// Link to admin view
               
return $Item->get_title( array( 'link_type' => 'admin_view' ) );
            }
            else
            {
// Link to permament url (it is allowed for current post type)
               
return $Item->get_title();
            }
        }
    }

    return
htmlspecialchars( $ghit_params );
}


/**
 * Display panel with buttons to control a view of hits summary pages:
 *     - Two buttons to toggle between type of hits summary data(Live or Aggregate)
 *     - Button to aggregate hits and sessions right now
 *
 * @param array Diagram columns: key - code of column, value - title of column
 */
function display_hits_summary_panel( $diagram_columns = array() )
{
    global
$ReqURL;

   
$hits_summary_mode = get_hits_summary_mode();

   
$current_url = preg_replace( '/(\?|&)hits_summary_mode=([^&]+|$)/', '', $ReqURL );

    echo
'<div class="btn-group pull-left">';

   
// Button to switch to view the live hits:
   
echo '<a href="'.url_add_param( $current_url, 'hits_summary_mode=live' ).'"'
       
.' class="btn btn-default'.( $hits_summary_mode == 'live' ? ' active' : '' ).'">'
       
.T_('Live data')
        .
'</a>';

   
// Button to switch to view the aggregated hits data:
   
echo '<a href="'.url_add_param( $current_url, 'hits_summary_mode=aggregate' ).'"'
       
.' class="btn btn-default'.( $hits_summary_mode == 'aggregate' ? ' active' : '' ).'">'
       
.T_('Aggregate data')
        .
'</a>';

    echo
'</div>';

    if(
$hits_summary_mode == 'aggregate' || ! empty( $diagram_columns ) )
    {
       
display_hits_filter_form( 'filter', $diagram_columns, $hits_summary_mode == 'aggregate' );
    }

    if(
check_user_perm( 'stats', 'edit' ) )
    {    
// Display button to aggregate hits right now only if current user has a permission to edit hits:
       
echo '<a href="'.url_add_param( $current_url, 'action=aggregate&'.url_crumb( 'aggregate' ) ).'"'
           
.' class="btn btn-default pull-right">'
           
.T_('Aggregate Now')
            .
'</a>';
    }

    echo
'<div class="clear"></div>';
}


function
display_hits_filter_form( $mode, $diagram_columns, $display_filter_period = true )
{
    global
$UserSettings, $tab3;

    switch(
$mode )
    {
        case
'filter':
           
$period_selector_title = T_('Show');
           
$submit_button_text = T_('Filter');
           
$prefix = 'agg_';
           
$action = 'filter_hits_diagram';
           
$display_filter_diagram_cols = true;
           
$block_style = '';
           
$period_options = array(
               
'last_30_days'   => sprintf( T_('Last %d days'), 30 ),
               
'last_60_days'   => sprintf( T_('Last %d days'), 60 ),
               
'current_month'  => T_( 'Current Month to date' ),
               
'specific_month' => T_( 'Specific Month:' ),
            );
           
$years_length = 20;
            break;

        case
'compare':
           
$period_selector_title = T_('Compare to');
           
$submit_button_text = T_('Compare');
           
$prefix = 'aggcmp_';
           
$action = 'compare_hits_diagram';
           
$display_filter_diagram_cols = false;
           
$block_style = ' style="padding-left:0"';
           
$period_options = array(
               
'prev_30_days'   => sprintf( T_('Previous %d days'), 30 ),
               
'prev_60_days'   => sprintf( T_('Previous %d days'), 60 ),
               
'prev_month'  => T_( 'Previous Month' ),
               
'specific_month' => T_( 'Specific Month:' ),
            );
           
$years_length = 21;
            break;
    }

    echo
'<div class="evo_filter_diagram_hits"'.$block_style.'>';
   
$Form = new Form();
   
$Form->hidden( 'ctrl', 'stats' );
   
$Form->hidden( 'from_ctrl', get_param( 'ctrl' ) );
   
$Form->hidden( 'tab', get_param( 'tab' ) );
   
$Form->hidden( 'tab3', get_param( 'tab3' ) );
   
$Form->hidden( 'blog', get_param( 'blog' ) );
   
$Form->hidden( 'sec_ID', get_param( 'sec_ID' ) );
   
$Form->hidden( 'action', $action );
   
$Form->add_crumb( 'filterhitsdiagram' );

   
$Form->switch_layout( 'none' );

   
$Form->begin_form();

    if(
$display_filter_period )
    {    
// Filter the aggregated data by date period:
       
$Form->select_input_array( $prefix.'period', $UserSettings->get( $prefix.'period' ), $period_options, $period_selector_title );

       
$months_years_params = array( 'force_keys_as_values' => true );
        if(
$UserSettings->get( $prefix.'period' ) != 'specific_month' )
        {
           
$months_years_params['style'] = 'display:none';
        }
       
$months = array();
        for(
$m = 1; $m <= 12; $m++ )
        {
           
$months[ $m ] = T_( date( 'F', mktime( 0, 0, 0, $m ) ) );
        }
       
$month = $UserSettings->get( $prefix.'month' );
       
$Form->select_input_array( $prefix.'month', ( empty( $month ) ? date( 'n' ) : $month ), $months, '', NULL, $months_years_params );

       
$years = array();
        for(
$y = date( 'Y' ) - $years_length; $y <= date( 'Y' ); $y++ )
        {
           
$years[ $y ] = $y;
        }
       
$year = $UserSettings->get( $prefix.'year' );
       
$Form->select_input_array( $prefix.'year', ( empty( $year ) ? date( 'Y' ) : $year ), $years, '', NULL, $months_years_params );

        echo
'<script>
            jQuery( "#'
.$prefix.'period" ).change( function()
            {
                if( jQuery( this ).val() == "specific_month" )
                {
                    jQuery( "#'
.$prefix.'month, #'.$prefix.'year" ).show();
                }
                else
                {
                    jQuery( "#'
.$prefix.'month, #'.$prefix.'year" ).hide();
                }
            } );
            </script>'
;
    }

    if(
$display_filter_diagram_cols )
    {
       
$filter_hits_diagram_cols = $UserSettings->get( 'filter_hits_diagram_cols' );
        foreach(
$diagram_columns as $diagram_column_key => $diagram_column_data )
        {    
// Filter hits by type:
           
$Form->checkbox_basic_input( 'filter_types[]',
                ( ! isset(
$filter_hits_diagram_cols[ $tab3 ] ) || empty( $filter_hits_diagram_cols[ $tab3 ] ) || in_array( $diagram_column_key, $filter_hits_diagram_cols[ $tab3 ] ) ), // Is checked?
               
'<span style="color:#'.$diagram_column_data['color'].'">'.$diagram_column_data['title'].'</span>', // Colored title
               
array( 'value' => $diagram_column_key ) ); // Value
       
}
    }

   
$Form->end_form( array( array( 'submit', 'submit', $submit_button_text, 'btn-info' ) ) );

    echo
'</div>';

    if(
$mode == 'compare' )
    {
        echo
'<div class="clear"></div>';
    }
}


/**
 * Get dates for filter the aggregated hits
 *
 * @param string Data mode: 'aggregate', 'compare'
 * @return array Array with two items: 0 - start date, 1 - end date
 */
function get_filter_aggregated_hits_dates( $mode = 'aggregate' )
{
    global
$DB, $UserSettings;

    if(
$mode == 'compare' )
    {    
// Use a filter for comparing data:
       
$period = $UserSettings->get( 'aggcmp_period' );
    }
    else
    {    
// Use a filter for aggregate data:
       
$period = $UserSettings->get( 'agg_period' );
    }

   
$start_date = $end_date = array(
       
'm' => date( 'm' ),
       
'd' => date( 'd' ),
       
'Y' => date( 'Y' ),
    );

    switch(
$period )
    {
        case
'prev_60_days':
           
$start_date['d'] -= 60; // Date of 120 days ago
           
$end_date['d'] -= 59; // Date of 61 days ago
       
case 'last_60_days':
           
$start_date['d'] -= 59; // Date of 60 days ago
           
$end_date['d'] -= 1; // Yesterday
           
break;

        case
'prev_month':
           
$start_date['m'] -= 1; // First day of previous month
           
$end_date['d'] = 1; // Last day of previous month
       
case 'current_month':
           
$start_date['d'] = 1; // First day of current month
           
$end_date['d'] -= 1; // Yesterday
           
break;

        case
'specific_month':
           
$agg_month = ( $mode == 'compare' ? $UserSettings->get( 'aggcmp_month' ) : $UserSettings->get( 'agg_month' ) );
           
$agg_year = ( $mode == 'compare' ? $UserSettings->get( 'aggcmp_year' ) : $UserSettings->get( 'agg_year' ) );
            if( ! empty(
$agg_month ) )
            {    
// Use month from setting:
               
$start_date['m'] = $agg_month;
               
$end_date['m'] = $agg_month + 1;
            }
            if( ! empty(
$agg_year ) )
            {    
// Use year from setting:
               
$start_date['Y'] = $end_date['Y'] = $agg_year;
            }
           
$start_date['d'] = 1; // First day of the selected month
           
$end_date['d'] = 0; // Last day of the selected month
           
break;

        case
'prev_30_days':
           
$start_date['d'] -= 30; // Date of 60 days ago
           
$end_date['d'] -= 29; // Date of 31 days ago
       
case 'last_30_days':
        default:
           
$start_date['d'] -= 29; // Date of 30 days ago
           
$end_date['d'] -= 1; // Yesterday
           
break;
    }

    return array(
           
date( 'Y-m-d', mktime( 0, 0, 0, $start_date['m'], $start_date['d'], $start_date['Y'] ) ),
           
date( 'Y-m-d', mktime( 0, 0, 0, $end_date['m'], $end_date['d'], $end_date['Y'] ) )
        );
}


/**
 * Get dates for filter the aggregated hits
 *
 * @param array All possible hits diagram columns
 * @return array Filtered hits diagram columns
 */
function get_filtered_hits_diagram_columns( $diagram_type, $diagram_columns )
{
    global
$UserSettings;

    if( empty(
$diagram_type ) )
    {    
// Unknown diagram type:
       
return $diagram_columns;
    }

   
$filter_hits_diagram_cols = $UserSettings->get( 'filter_hits_diagram_cols' );

    if( ! isset(
$filter_hits_diagram_cols[ $diagram_type ] ) )
    {    
// No filter is defiend yet:
       
return $diagram_columns;
    }

    if( empty(
$filter_hits_diagram_cols[ $diagram_type ] ) )
    {    
// If all options are unchecked then check them all:
       
return $diagram_columns;
    }

   
$filtered_cols = array();
    foreach(
$filter_hits_diagram_cols[ $diagram_type ] as $filter_diagram_col )
    {
        if( isset(
$diagram_columns[ $filter_diagram_col ] ) )
        {
           
$filtered_cols[ $filter_diagram_col ] = $diagram_columns[ $filter_diagram_col ];
        }
    }

    return
$filtered_cols;
}


/**
 * Get mode of hits summary data
 *
 * @return string Mode: 'live' or 'aggregate'
 */
function get_hits_summary_mode()
{
    global
$Session;

   
$hits_summary_mode = $Session->get( 'hits_summary_mode' );
    if( empty(
$hits_summary_mode ) )
    {    
// Set mode to display the aggregated data by default:
       
$hits_summary_mode = 'aggregate';
    }

    return
$hits_summary_mode;
}


/**
 * Find the dates without hits and fill them with 0 to display on graph and table
 *
 * @param array Source hits data
 * @param string Start date of hits log in format 'YYYY-mm-dd'
 * @param string End date of hits log in format 'YYYY-mm-dd'
 * @return array Fixed hits data
 */
// erwin> replaced with a more generic fill_empty_days() in _misc.funcs.php
function fill_empty_hit_days( $hits_data, $start_date, $end_date )
{
   
$fixed_hits_data = array();

    if( empty(
$hits_data ) )
    {
        return
$fixed_hits_data;
    }

   
// Get additional fields which must be exist in each array item of new filled empty day below:
   
$additional_fields = array_diff_key( $hits_data[0], array( 'hits' => 0, 'year' => 0, 'month' => 0, 'day' => 0 ) );

   
// Check if hits data array contains start and end dates:
   
$start_date_is_contained = empty( $start_date );
   
$end_date_is_contained = empty( $end_date );
    if( !
$start_date_is_contained || ! $end_date_is_contained )
    {
        foreach(
$hits_data as $hit )
        {
           
$this_date = $hit['year'].'-'.$hit['month'].'-'.$hit['day'];
            if(
$this_date == $start_date )
            {    
// The start date is detected:
               
$start_date_is_contained = true;
            }
            if(
$this_date == $end_date )
            {    
// The start date is detected:
               
$end_date_is_contained = true;
            }
            if(
$start_date_is_contained && $end_date_is_contained )
            {    
// Stop array searching here because we have found the dates:
               
break;
            }
        }
    }

    if( !
$start_date_is_contained )
    {    
// Add item to array with 0 for start date if stats has no data for the date:
       
array_push( $hits_data, array(
               
'hits'     => 0,
               
'year'     => date( 'Y', strtotime( $start_date ) ),
               
'month'    => date( 'n', strtotime( $start_date ) ),
               
'day'      => date( 'j', strtotime( $start_date ) ),
            ) +
$additional_fields );
    }
    if( !
$end_date_is_contained )
    {    
// Add item to array with 0 for end date if stats has no data for the date:
       
array_unshift( $hits_data, array(
               
'hits'     => 0,
               
'year'     => date( 'Y', strtotime( $end_date ) ),
               
'month'    => date( 'n', strtotime( $end_date ) ),
               
'day'      => date( 'j', strtotime( $end_date ) ),
            ) +
$additional_fields );
    }

    foreach(
$hits_data as $hit )
    {
       
$this_date = $hit['year'].'-'.$hit['month'].'-'.$hit['day'];

        if( isset(
$prev_date ) && $prev_date != $this_date )
        {    
// If hits are from another day:
           
$prev_time = strtotime( $prev_date ) - 86400;
           
$this_time = strtotime( $this_date );

            if(
$prev_time != $this_time )
            {    
// If previous date is not previous day(it means some day has no hits):
               
$empty_days = ( $prev_time - $this_time ) / 86400;
                for(
$d = 0; $d <= $empty_days; $d++ )
                {    
// Add each empty day to array with 0 hits count:
                   
$empty_day = $prev_time - $d * 86400;
                   
$fixed_hits_data[] = array(
                           
'hits'     => 0,
                           
'year'     => date( 'Y', $empty_day ),
                           
'month'    => date( 'n', $empty_day ),
                           
'day'      => date( 'j', $empty_day ),
                        ) +
$additional_fields;
                }
            }
        }

       
$prev_date = $hit['year'].'-'.$hit['month'].'-'.$hit['day'];
       
$fixed_hits_data[] = $hit;
    }

    return
$fixed_hits_data;
}


/**
 * Get full URL from hit URI and collection ID
 *
 * @param string Hit URI
 * @param integer Hit collection ID
 * @return string Full hit URL
 */
function get_hit_full_url( $hit_uri, $hit_coll_ID )
{
   
$hit_host = '';

    if( ! empty(
$hit_coll_ID ) )
    {    
// Try to get a collection if it was a hit from collection page:
       
$BlogCache = & get_BlogCache();
        if(
$Blog = & $BlogCache->get_by_ID( $hit_coll_ID, false, false ) )
        {    
// Get collection host:
           
$hit_host = $Blog->get_baseurl_root();
        }
    }

    return
$hit_host.$hit_uri;
}


/**
 * Get the search engine parameter definitions.
 *
 * Based on search engine detections YAML list maintained and used by Matomo - {@link https://github.com/matomo-org/searchengine-and-social-list}
 *
 * @return array of search engine definitions
 */
function get_search_engine_params()
{
    global
$search_engine_params, $inc_path;

    if( empty(
$search_engine_params ) )
    {
       
$search_engine_params = array();

       
// Load search engine definitions:
       
$search_engine_definitions = json_decode( file_get_contents( $inc_path.'_ext/matomo/SearchEngines.json' ) );

        foreach(
$search_engine_definitions as $name => $info )
        {
            if( empty(
$info ) || !is_array( $info ) )
            {
                continue;
            }

            foreach(
$info as $url_definitions )
            {
               
$url_definitions = (array) $url_definitions;
                foreach(
$url_definitions['urls'] as $url )
                {
                   
$search_engine_data = $url_definitions;
                    unset(
$search_engine_data['urls'] );
                   
$search_engine_data['name'] = $name;
                   
$search_engine_params[$url] = $search_engine_data;
                }
            }
        }
    }

    return
$search_engine_params;
}
?>