<?php
/**
* @version	$Id: statistics_tag_processor.php 15152 2012-03-04 09:26:24Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

	defined('FULL_PATH') or die('restricted access!');

	class StatisticsTagProcessor extends kDBTagProcessor {

		var $TagCache = Array(); // parsed tag (in sql queries only) values are cached

		var $CurrentSQL = ''; // sql query being currently processed
		var $PostFormatting = false; // apply formatting to sql query results
		var $PostFormattingParams = Array(); // post formatting params if any

		function CalculateValue($params)
	    {
	    	$object = $this->getObject($params);
	        $this->CurrentSQL = $object->GetDBField($params['field']);

	        // 1. replace prefix to actual one
			$this->CurrentSQL = str_replace("<%prefix%>", TABLE_PREFIX, $this->CurrentSQL);

			// 2. replace all pseudo-tags found in sql with their values
			while ( ($tag = $this->FindTag()) != false ) {
				$this->CurrentSQL = str_replace('<%'.$tag.'%>', $this->ProcessStatisticTag($tag), $this->CurrentSQL);
			}

			// 3. query sql and process gathered data
	        $values = $this->Conn->GetCol($this->CurrentSQL);
	        if (!$values) return '';
	        if (!$this->PostFormatting) return array_shift($values);


    		switch ($this->PostFormatting) {
    			case 'number':
    				// simple-specific postformatting
    				$lang = $this->Application->recallObject('lang.current');
					/* @var $lang LanguagesItem */

    				$value = $lang->formatNumber($value, $this->PostFormattingParams['precision']);
    				break;

    			case 'COUNT':
    				// extended postformatting
    				$value = count($values);
    				break;

    			case 'SUM':
    				$value = 0;
    				foreach ($values as $cur_value) {
    					$value += $cur_value;
    				}

    				if ($this->PostFormattingParams['format_as'] == 'file') {
    					$value = size($value);
    				}
    				break;

    			// other type of information (not from db)
    			case 'SysFileSize':
    				$value = size( dir_size(FULL_PATH.'/') );
    				break;

    			default: // simple-default postformatting
    				$value = adodb_date($this->PostFormatting, array_shift($values));
    				break;
    		}
    		$this->PostFormatting = false;
    		$this->PostFormattingParams = Array();


	        return $value;

	    }

	    function FindTag()
		{
			// finds tag in current sql & returns it if found, false otherwise
			$tagOpen = '<%'; $tagClose = '%>';	$tagOpenLen = strlen($tagOpen);
			$startPos = strpos($this->CurrentSQL, $tagOpen);
			if( $startPos !== false )
			{
				$endPos = strpos($this->CurrentSQL, $tagClose, $startPos);
				return ($endPos > $startPos) ? substr($this->CurrentSQL, $startPos + $tagOpenLen, $endPos - $startPos - $tagOpenLen) : false;
			}
			return false;
		}

		function ProcessStatisticTag($tag)
		{
			$tag = trim($tag);
			if (isset($this->TagCache[$tag])) {
				return $this->TagCache[$tag];
			}

			$object = $this->getObject();
			/* @var $object kDBItem */

			list($tag_name, $tag_params) = explode(' ', $tag, 2); // 1st - function, 2nd .. nth - params
			preg_match_all('/([\${}a-zA-Z0-9_.-]+)=(["\']{1,1})(.*?)(?<!\\\)\\2/s', $tag_params, $rets, PREG_SET_ORDER);

			$tag_params = Array();
			foreach ($rets AS $key => $val){
				$tag_params[$val[1]] = str_replace(Array('\\' . $val[2], '+'), Array($val[2], ' '), $val[3]);
			}

			switch ($tag_name) {
				case 'm:config':
					// m:config name="<variable_name>"
					return $this->Application->ConfigValue($tag_params['name']);
					break;

				case 'm:post_format':
					// m:post_format field="<field_name>" type="<formatting_type>" precision="2"
					$lang = $this->Application->recallObject('lang.current');
					/* @var $lang LanguagesItem */

					switch ($tag_params['type']) {
						case 'date':
							$this->PostFormatting = $lang->GetDBField('DateFormat');
							break;

						case 'time':
							$this->PostFormatting = $lang->GetDBField('TimeFormat');
							break;

						case 'currency':
							$this->PostFormatting = 'number';
							$this->PostFormattingParams['precision'] = $tag_params['precision'];
							break;
					}
					return $tag_params['field'];
					break;

				case 'm:custom_action':
					// m:custom_action sql="empty" action="SysFileSize"
					$this->PostFormatting = $tag_params['action'];
					return ($tag_params['sql'] == 'empty') ? 'SELECT 1' : $tag_params['sql'];
					break;

				case 'modules:get_current':
					return $object->GetDBField('Module');
					break;

				case 'm:sql_action':
					//m:sql_action sql="SHOW TABLES" action="COUNT" field="*"
					$this->PostFormatting = $tag_params['action'];
					$this->PostFormattingParams = $tag_params;
					return $tag_params['sql'];
					break;

				case 'link:hit_count':
					if ($tag_params['type'] == 'top') {// by now only top is supported
						$top_links_count = $this->Application->ConfigValue('Link_TopCount');

						$sql = 'SELECT Hits
								FROM '.TABLE_PREFIX.'Link
								ORDER BY Hits DESC LIMIT 0, '.$top_links_count;
						return $this->getLastRecord($sql, 'Hits');
					}
					break;

				case 'article:hit_count':
					if ($tag_params['type'] == 'top') {// by now only top is supported
						$top_articles_count = $this->Application->ConfigValue('News_VotesToHot');
						$min_votes = $this->Application->ConfigValue('News_MinVotes');

						$sql = 'SELECT CachedRating
								FROM '.TABLE_PREFIX.'News
								WHERE CachedVotesQty > '.$min_votes.'
								ORDER BY CachedRating DESC LIMIT 0, '.$top_articles_count;
						return $this->getLastRecord($sql, 'CachedRating');
					}
					break;

				case 'topic:hit_count':
					if ($tag_params['type'] == 'top') {// by now only top is supported
						$top_posts_count = $this->Application->ConfigValue('Topic_PostsToPop');

						$sql = 'SELECT Views
								FROM '.TABLE_PREFIX.'Topic
								ORDER BY Views DESC LIMIT 0, '.$top_posts_count;
						return $this->getLastRecord($sql, 'Views');
					}
					break;


			}

			return '';
		}

		function getLastRecord($sql, $field)
		{
			$records = $this->Conn->GetCol($sql);
			return count($records) ? array_pop($records) : 0;
		}

		/**
		 * Allows to get pending item count for prefix
		 *
		 * @param Array $params
		 * @return int
		 */
		function CountPending($params)
		{
			$prefix = $params['prefix'];
			$cache_key = 'statistics.pending[%' . $this->Application->incrementCacheSerial($prefix, null, false) . '%]';
			$value = $this->Application->getCache($cache_key);

			if ($value === false) {
				$statistics_info = $this->Application->getUnitOption($prefix.'.pending', 'StatisticsInfo');
				if (!$statistics_info) {
					return 0;
				}

				$table = $this->Application->getUnitOption($prefix, 'TableName');
				$status_field = array_shift( $this->Application->getUnitOption($prefix, 'StatusField') );
				$this->Conn->nextQueryCachable = true;
				$sql = 'SELECT COUNT(*)
						FROM '.$table.'
						WHERE '.$status_field.' = '.$statistics_info['status'];
				$value = $this->Conn->GetOne($sql);
				$this->Application->setCache($cache_key, $value);
			}

			return $value;
		}

		function GetTotalPending()
		{
			$prefixes = $this->getPendingPrefixes();

			$sum = 0;
			foreach ($prefixes as $prefix) {
				$sum += $this->CountPending( Array('prefix' => $prefix) );
			}
			return $sum;
		}

		/**
		 * Get prefixes, that are allowed to have pending items (check module licenses too)
		 *
		 * @return Array
		 */
		function getPendingPrefixes()
		{
			$modules_helper = $this->Application->recallObject('ModulesHelper');
			/* @var $modules_helper kModulesHelper */

			$licensed_modules = array_map('strtolower', $modules_helper->_GetModules());

			$sql = 'SELECT LOWER(Module), Prefix
					FROM '.TABLE_PREFIX.'ItemTypes it
					LEFT JOIN '.TABLE_PREFIX.'Modules m ON m.Name = it.Module
					WHERE (m.Loaded = 1) AND (LENGTH(it.ClassName) > 0)';
			$prefixes = $this->Conn->GetCol($sql, 'Prefix');

			$module_names = array_map('strtolower', array_unique(array_values($prefixes)));
			$module_names = array_intersect($module_names, $licensed_modules);

			$ret = Array ();
			foreach ($prefixes as $prefix => $module_name) {
				if (in_array($module_name, $module_names)) {
					$ret[] = $prefix;
				}
			}

			return $ret;

		}

		function PrintPendingStatistics($params)
		{
			$check_prefixes = $this->getPendingPrefixes();
			if (!$check_prefixes) {
				return '';
			}

			$ret = '';
			$columns = $params['columns'];
			$block_params = $this->prepareTagParams( Array('name' => $this->SelectParam($params, 'render_as,block') ) );

			$prefixes = Array();
			foreach ($check_prefixes as $prefix) {
				$statistics_info = $this->Application->getUnitOption($prefix.'.pending', 'StatisticsInfo');
				if ($statistics_info) {
					$prefixes[] = $prefix;
				}
			}

			$row_number = 0;

			foreach ($prefixes as $i => $prefix) {
				$block_params['prefix'] = $prefix;
				$statistics_info = $this->Application->getUnitOption($prefix.'.pending', 'StatisticsInfo');

				if ($i % $columns == 0) {
					$column_number = 1;
					$ret .= '<tr>';
				}

				$block_params['column_number'] = $column_number;
				$block_params['is_first'] = $i < $columns ? 1 : 0;
				$template = $statistics_info['url']['t'];
				unset($statistics_info['url']['t']);
				$url = $this->Application->HREF($template, '', $statistics_info['url']);
				if ($statistics_info['js_url'] != '#url#') {
					$statistics_info['js_url'] = 'javascript:'.$statistics_info['js_url'];
				}

				$block_params['url'] = str_replace('#url#', $url, $statistics_info['js_url']);
				$block_params['icon'] = $statistics_info['icon'];
				$block_params['label'] = $statistics_info['label'];
				$ret .= $this->Application->ParseBlock($block_params);
				$column_number++;

				if (($i+1) % $columns == 0) {
					$ret .= '</tr>';
				}
			}
			return $ret;
		}

	}