<?php

/* 

 * phpMyVisites : website statistics and audience measurements

 * Copyright (C) 2002 - 2006

 * http://www.phpmyvisites.net/ 

 * phpMyVisites is free software (license GNU/GPL)

 * Authors : phpMyVisites team

*/



// $Id: Archive.class.php 102 2006-10-01 16:28:30Z matthieu_ $





$GLOBALS['test'] = ", sum(case total_pages when 1 then 1 else 0 end) as onepage," .

				" sum(total_pages) as sumpage, count(*) as s, sum(total_time) as sumtime";



require_once INCLUDE_PATH."/core/include/Date.class.php";

require_once INCLUDE_PATH."/core/include/Str.class.php";

require_once INCLUDE_PATH."/core/include/Archive.functions.php";

require_once INCLUDE_PATH."/core/include/functions.php";



/**

 * Base class used by other ArchiveClass to assign some standard attributes

 * 

 */

class Archive

{

 	/**

 	 * @var object Site

 	 */	

	var $site;

	

	/**

	 * @var array fieldName => fieldValue to save in database

	 */

	var $toRecord; // array 'field_name' => value to record

	

	/**

	 * @var int Current idarchives of the archive process

	 */

	var $idArchives; 

	

	/**

	 * @var int State of the current archive process (Must be DB_ARCHIVES_*)

	 */

	var $state; // DB_x

	

	/**

	 * @var object Date

	 */

	var $date;

	var $date2;

	

	/**

	 * @var int Type of the current archive period (Must be DB_ARCHIVES_PERIOD_*)

	 */

	var $periodType;

	

	

	// int value

	/**

	 * @var array integer arrays to sum conserving keys array( 3, 4, 6 ); 

	 */

	var $intValuesToSum;

	

	// key => int value

	var $arrayOneDimToSum;

	

	// key => array('pmv_sum' => X, 'iddetail1' => Y, 'iddetail2' => Z);

	var $arrayPmvSumToSum;

	

	var $arrayIntToSum;

	var $archiveOk; // flag to known if finished, used when using current archive twice

	

	/**

	 * @var string literal date corresponding to $this->date

	 */

	var $literalDate;

	

	/**

	 * @param object $site

	 */

	function Archive($site)

	{

		$this->site = $site;

		$this->state = DB_ARCHIVES_DONE;

		$this->toRecord = array();

		

		$this->archiveOk = false;

		

		$this->intValuesToSum = array(

		'nb_vis', 'nb_pag', 'nb_uniq_pag', 'nb_vis_1pag', 'sum_vis_lth',

		'nb_direct', 'nb_search_engine', 'nb_site', 'nb_newsletter', 'nb_partner',

		'nb_vis_returning', 'nb_pag_returning', 'nb_vis_1pag_returning', 'sum_vis_lth_returning'

		);

		

		// key => int value

		$this->arrayOneDimToSum = array(

		'vis_st', 'vis_lt', 'pag_st', 'pag_lt', 'vis_lth', 'vis_nb_pag', 

		'vis_country', 'vis_continent', 'vis_provider', 'vis_config', 'vis_os', 'vis_browser', 'vis_browser_type',

		'vis_resolution', 'vis_plugin', 

		'vis_newsletter',

		'vis_nb_vis'

		);

		

		// key => array('pmv_sum' => X, 'iddetail1' => Y, 'iddetail2' => Z);

		$this->arrayPmvSumToSum = array(

		'vis_search_engine', 'vis_keyword', 'vis_site', 'vis_partner'

		);

		

		$this->arrayIntToSum = array(

		'int_lt', 'int_st', 'int_referer_type', 'int_search_engine', 'int_keyword', 'int_site', 'int_partner',

		'int_newsletter', 'int_country', 'int_continent', 'int_os', 'int_browser', 'int_resolution'

		);



	}	

	

	/**

	 * Update database, set var state 

	 */

	function updateDbState()

	{

		updateLine(

			T_ARCHIVES, 

			array( 'done' => $this->state, 'idarchives' => $this->idArchives), 

			'idarchives');

	}

	

	/**

	 * saves var toRecord values in the database, serializing arrays

	 */

	function saveDb()

	{

		if( 

			// day + simple day archive, means array empty, do NOT compress

		//	($this->periodType === DB_ARCHIVES_PERIOD_DAY && !CURRENT_DAY_SIMPLE_ARCHIVE) 

		//	|| 

		//	($this->periodType !== DB_ARCHIVES_PERIOD_DAY && !CURRENT_PERIOD_SIMPLE_ARCHIVE)

			COMPRESS_DB_DATA 

			&& function_exists('gzcompress'))

		{

			$this->toRecord['compressed'] = 1;

		}

		else

		{

			$this->toRecord['compressed'] = 0;

		}

		/*

		print("<pre>");

		var_dump($this->toRecord);

		print("<br><br><br>");

		*/

		

		foreach($this->toRecord as $fieldName => $a_value)

		{

			if(is_array($a_value))

			{

				$a_value = databaseEscape(compress(serialize($a_value), $this->toRecord['compressed']));	

			}

			//print("<br>update $fieldName data = ".strlen(serialize($a_value)));

			

			updateLine(

				T_ARCHIVES, 

				array( $fieldName => $a_value, 'idarchives' => $this->idArchives), 

				'idarchives');

		}

		

		$this->updateDbState();

	}

	

	/**

	 * set periodType

	 * 

	 * @param int $type

	 */

	function setPeriodType($type)

	{

		$this->periodType = $type;

		

	}

	

	function getLiteralDate()

	{

		return $this->literalDate;

	}

	function setLiteralDate()

	{

		switch($this->periodType)

		{

			

			case DB_ARCHIVES_PERIOD_DAY:

				$typeDateDisplay = 1;

			break;

			

			case DB_ARCHIVES_PERIOD_WEEK:

				$typeDateDisplay = 6;				

			break;

				

			case DB_ARCHIVES_PERIOD_MONTH:

				$typeDateDisplay = 4;

				

			break;

				

			case DB_ARCHIVES_PERIOD_YEAR:

				$typeDateDisplay = 11;

			break;

		}

		

		$this->literalDate = getDateDisplay($typeDateDisplay, $this->date);

	}

	/**

	 * init the database, inserting a new row for the current archive process

	 * assigns var idArchives

	 * 

	 * @return true

	 */

	function initDb()

	{

		$r2 = query("INSERT INTO ".T_ARCHIVES." (idsite, done, period, date1, date2)" .

				" VALUES (".$this->site->getId().", " .

						" ". DB_ARCHIVES_FAIL."," .

						 $this->periodType .", ".

						" '".$this->date->get()."'," .

						" '".$this->date2->get()."')"

				);



		$this->idArchives = mysql_insert_id();

		return true;

	}

		

	function deleteOldRecords()

	{

		// delete failed archives

		// delete temp archives older than today

		// date2 <> yesterday because of monthly and weekly archive for months/week not finished yet

		// once on 10 calls

		if( time() % 10 == 0 )

		{

			$r = query("SELECT max(idarchives)

						FROM ".T_ARCHIVES

						);

			$r = mysql_fetch_row($r);

			

			// max id - 50 is the max id we want to delete, else it would be dangerous 

			$maxId = (int)$r[0] - 100;

			if($maxId > 1)

			{				

				// we delete only old tries for archived

				// because if someone asked for an archive 10min ago, we don't delete it!

				$query = "DELETE FROM ".T_ARCHIVES."

						WHERE (done = ".DB_ARCHIVES_FAIL."

							OR done = ".DB_ARCHIVES_TEMP.") 

							AND date1 <> '".date("Y-m-d")."'

							AND date2 <> '".date("Y-m-d")."'

							AND date2 <> '".date("Y-m-d", time()-86400)."' 

							AND idarchives < $maxId

						";

				$r = query($query);

				//print("deleted records= ".mysql_affected_rows() ."<br> $query <br>");

			}

		}

		if( time() % 30 == 0)

		{

			$r = query("OPTIMIZE TABLE ".T_ARCHIVES);

			$r = query("OPTIMIZE TABLE ".T_VISIT);

		}



		// delete doubled archives

		

		// delete for days

		// double archive is when concat(idsite, date1) is the same

		// for period = DB_DAYS and done = DONE

		//$this->deleteDoubleArchives(true);

		

		// delete for other period than days (week/month/year)

		// double archive is when concat(idsite, date1, date2) is the same

		// for period <> DB_DAYS and done = DONE

		$this->deleteDoubleArchives(false);		

	}

	

	function deleteDoubleArchives( $dayPeriod )

	{

		if($dayPeriod)

		{

			$concatSQL = 'CONCAT( idsite , period,  date1)';

			$periodTest = '='; 

		}

		else

		{

			$concatSQL = 'CONCAT( idsite , period,  date1, date2)';

			$periodTest = '<>'; 

			

		}

		$r = query("SELECT count(*) as c, idsite, date1, date2, $concatSQL as ac

					FROM ".T_ARCHIVES."

					WHERE period ".$periodTest." ".DB_ARCHIVES_PERIOD_DAY."

						AND done = ".DB_ARCHIVES_DONE."

					GROUP BY ac

					HAVING c > 1"

			);

		while($l = mysql_fetch_assoc($r))

		{

			//print($l['c'] . ": ". $l['idsite']." for date " . $l['date1']."<>".$l['date2']." ( ".$l['ac'].")<br>");

			

			$concatToLookFor = $l['ac'];

			// for the date select the archives to keep

			$r2 = query("SELECT idarchives as id

						FROM ".T_ARCHIVES."

						WHERE $concatSQL = '".$concatToLookFor."'

							AND done = ".DB_ARCHIVES_DONE."

						ORDER BY idarchives DESC

						LIMIT 1");

			$l2 = mysql_fetch_assoc($r2);

			$idToKeep = $l2['id'];

			

			$query = "DELETE 

						FROM  ".T_ARCHIVES."

						WHERE $concatSQL = '".$concatToLookFor."'

							AND idarchives <> $idToKeep

							AND done = ".DB_ARCHIVES_DONE."

						";

			$r3 = query($query);

		}

	}

	/**

	 * offset the date, considering the date of the first log record for the current site

	 * and considering today date. It gives a date which is between these 2 dates.

	 * 

	 * @param object $o_date

	 */

	function offsetDate($o_date, $minDay = null)

	{

		$o_dateR = new Date($o_date->get());

		if(is_null($minDay)) 

			$minDay = $this->site->getMinDay();

		

		// Manage with future

		if($o_date->getTimestamp() > time())

		{

			$o_dateR = new Date(getDateFromTimestamp(time()));

		}

		

				

		// manage with past

		if($o_date->getTimestamp() <  $minDay->getTimestamp())

		{

			$o_dateR = $minDay;

		}

		return $o_dateR;

	}

	

	/**

	 * Returns limit of the period periodType containing $s_date

	 * Works with weeks, month, years, and misc periods (in this case, minDate is the first day where there are logs)

	 * 

	 * @param string $s_date 

	 */

	function getPeriodDatesLimit($s_date)

	{

		// objects

		$date = new Date($s_date);

		$minDate = $this->site->getMinDay();

		

		$date = $this->offsetDate($date);

		

		//print("MinDate is ".$minDate->get()."<br>Date asked is $s_date<br>Date after Offset is ".$date->get()."<br>");

		

		switch ($this->periodType) 

		{

			

			case DB_ARCHIVES_PERIOD_MONTH:

				// detect min day for the month

				// if the minimum date month is different, we surely take the whole month 

				if($minDate->getMonth() == $date->getMonth() 

					&& $minDate->getYear() == $date->getYear())

				{

					$minDateForSure = $minDate->get();

				}

				else

				{

					$minDateForSure = $date->getYear()."-".$date->getMonth()."-01";

				}

				

				// detect max date for the month

				// if month asked is the current month then max day is today, else its the last day of the month

				if($date->getMonth() == date("m") && $date->getYear() == date("Y")) // >= because if month in future, take today

				{

					

					// if today is the first day of the month, don't say 

					// "yesterday is the last day of the month"

					// but "today is the only day of the month"

					if( date("j",time() ) == 1)

					{	

						$maxDateForSure = getDateFromTimestamp(time());

					}

					else

					{

						$maxDateForSure = getDateFromTimestamp(time()-86400);						

					}

				}

				else

				{

					$maxDateForSure = $date->getYear()."-".$date->getMonth()."-".date("t", $date->getTimestamp());

				}

				break;

			case DB_ARCHIVES_PERIOD_WEEK:

				

				$time = $date->getTimestamp();

				

				// detect beginning of the week

				while(date("W", $time-86400) == $date->getWeek()

				&& $time-86400 >= $minDate->getTimestamp()

				)

				{

					$time -= 86400;

				}

				

				$minDateForSure = getDateFromTimestamp($time);

				

				// end of week

				while(date("W", $time+86400) == $date->getWeek()

				&& $time+86400 <= time()

				)

				{

					$time += 86400;

				}

				$maxDateForSure = getDateFromTimestamp($time);

				

				break;

			case DB_ARCHIVES_PERIOD_MISC_PERIOD:

				$minDateForSure = $minDate->get();

				$maxDateForSure = $date->get();

				break;

				

			case DB_ARCHIVES_PERIOD_YEAR:

			

				// min is minDate if year common 

				if($date->getYear() == date("Y", $minDate->getYear()))

				{

					$minDateForSure = $minDate->get();

				}

				else

				{

					$minDateForSure = $date->getYear()."-01-01";

				}

				

				// max date is today if year is current year

				if($date->getYear() == date("Y"))

				{

					$maxDateForSure = getDateFromTimestamp(time());

	

				}

				else

				{

					$maxDateForSure = $date->getYear()."-12-31";

				}

				break;

				

				default:

				break;

		}

		

		$this->date = new Date($minDateForSure);

		$this->date2 = new Date($maxDateForSure);

		

		

		//print("max=$maxDateForSure <br> ".$this->date->get().",". $this->date2->get()."<br>");

		$this->date = $this->offsetDate($this->date);

		$this->date2 = $this->offsetDate($this->date2);

	}	

	

	

	function sortAndLimitToRecord()

	{

		$doNotSort = array('vis_st', 'vis_lt', 'pag_st', 'pag_lt', 'vis_lth', 'vis_nb_pag', 'vis_nb_vis');



		foreach ($this->arrayOneDimToSum as $name) 

		{

			if( !in_array($name, $doNotSort))

			{

				arsort($this->toRecord[$name]);

			}

			$this->toRecord[$name] = getArrayOffsetLimit($this->toRecord[$name], 0, MAX_DISTINCT_ELEMENTS);

		}

		

		foreach($this->arrayPmvSumToSum as $name)

		{

			uasort($this->toRecord[$name], "sortingPmv"); 

			$this->toRecord[$name] = getArrayOffsetLimit($this->toRecord[$name], 0, MAX_DISTINCT_ELEMENTS);

		}

		

		foreach($this->arrayIntToSum as $name)

		{

			uasort($this->toRecord[$name], "sortingInterest");

			$this->toRecord[$name] = getArrayOffsetLimit($this->toRecord[$name], 0, MAX_DISTINCT_INTEREST_ELEMENTS);

		}

		

	}

}





?>