<?php
/**
 * This file implements the TemplateCache class.
 *
 * 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/}
 *
 * @package templates
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );

load_class( '_core/model/dataobjects/_dataobjectcache.class.php', 'DataObjectCache' );
load_class( 'templates/model/_template.class.php', 'Template' );

/**
 * Blog Cache Class
 *
 * @package templates
 */
class TemplateCache extends DataObjectCache
{
	/**
	 * Cache by template code
	 *
	 * @var cache_by_code array
	 */
	var $cache_by_code;
	
	var $loaded_contexts = array();


	/**
	 * Constructor
	 *
	 * @param string Name of the order field or NULL to use name field
	 */
	function __construct()
	{
		parent::__construct( 'Template', false, 'T_templates', 'tpl_', 'tpl_ID', 'tpl_name' );
	}


	/**
	 * Add a template to the cache
	 *
	 * @param object Template
	 * @return boolean true if it was added false otherwise
	 */
	function add( $Template )
	{
		$code = $Template->get( 'code' );
		$this->cache_by_code[$code] = & $Template;

		return parent::add( $Template );
	}


	/**
	 * Get Template by given code
	 *
	 * @param string Code of Template
	 * @param boolean true if function should die on error
	 * @param boolean true if function should die on empty/null
	 * @return object|NULL|boolean Reference on cached Template, NULL - if request with empty code, FALSE - if requested Template does not exist
	 */
	function & get_by_code( $code, $halt_on_error = true, $halt_on_empty = true )
	{
		global $DB, $Debuglog;

		if( empty( $code ) )
		{	// Don't allow request with empty code:
			if( $halt_on_empty )
			{
				debug_die( "Requested $this->objtype from $this->dbtablename without code!" );
			}
			$r = NULL;
			return $r;
		}

		if( isset( $this->cache_by_code[ $code ] ) )
		{	// Get Template from cache by code:
			$Debuglog->add( "Accessing <strong>$this->objtype($code)</strong> from cache by code", 'dataobjects' );
			return $this->cache_by_code[ $code ];
		}

		// Load just the requested Template:
		$Debuglog->add( "Loading <strong>$this->objtype($code)</strong>", 'dataobjects' );
		$SQL = $this->get_SQL_object();
		$SQL->WHERE_and( 'tpl_code = '.$DB->quote( $code ) );

		if( $db_row = $DB->get_row( $SQL->get(), OBJECT, 0, 'DataObjectCache::get_by_code()' ) )
		{
			$resolved_ID = $db_row->{$this->dbIDname};
			$Debuglog->add( 'success; ID = '.$resolved_ID, 'dataobjects' );
			if( ! isset( $this->cache[$resolved_ID] ) )
			{	// Object is not already in cache:
				$Debuglog->add( 'Adding to cache...', 'dataobjects' );
				//$Obj = new $this->objtype( $row ); // COPY !!
				if( ! $this->add( $this->new_obj( $db_row ) ) )
				{	// could not add
					$Debuglog->add( 'Could not add() object to cache!', 'dataobjects' );
				}
			}
			if( ! isset( $this->cache_by_code[ $code ] ) )
			{	// Add object in cache by code:
				$this->cache_by_code[ $code ] = $this->new_obj( $db_row );
			}
		}

		if( empty( $this->cache_by_code[ $code ] ) )
		{	// Object does not exist by requested code:
			$Debuglog->add( 'Could not get DataObject by code.', 'dataobjects' );
			if( $halt_on_error )
			{
				debug_die( "Requested $this->objtype does not exist!" );
			}
			$this->cache_by_code[ $code ] = false;
		}

		return $this->cache_by_code[ $code ];
	}


	/**
	 * Returns option array with cache contents
	 *
	 * Load the cache if necessary
	 *
	 * @param string Callback method name
	 * @param array IDs to ignore.
	 * @return string
	 */
	function get_code_option_array( $method = 'get_name', $ignore_IDs = array() )
	{
		if( ! $this->all_loaded && $this->load_all )
		{ // We have not loaded all items so far, but we're allowed to.
			if ( empty( $ignore_IDs ) )
			{	// just load all items
				$this->load_all();
			}
			else
			{	// only load those items not listed in $ignore_IDs
				$this->load_list( $ignore_IDs, true );
			}
		}

		$r = array();

		foreach( $this->cache as $loop_Obj )
		{
			if( in_array( $loop_Obj->code, $ignore_IDs ) )
			{	// Ignore this ID
				continue;
			}

			$r[$loop_Obj->code] = $loop_Obj->$method();
		}

		return $r;
	}


	/**
	 * Get localized Template by given code
	 *
	 * @param string Code of Template
	 * @param boolean true if function should die on error
	 * @param boolean true if function should die on empty/null
	 * @param string Locale, NULL - for current locale
	 * @return object|NULL|boolean Reference on cached Template, NULL - if request with empty code, FALSE - if requested Template does not exist
	 */
	function & get_localized_by_code( $code, $halt_on_error = true, $halt_on_empty = true, $locale = NULL )
	{
		if( ! ( $locale_Template = & $this->get_by_code( $code, $halt_on_error, $halt_on_empty ) ) )
		{	// No template found by code:
			$r = false;
			return $r;
		}

		if( $locale === NULL )
		{	// Use current locale:
			global $current_locale;
			$locale = $current_locale;
		}

		// Check if the template has a child matching the current locale:
		$localized_templates = $locale_Template->get_localized_templates( $locale );
		if( ! empty( $localized_templates ) )
		{	// Use localized template:
			$locale_Template = & $localized_templates[0];
		}

		if( $halt_on_error && ! $locale_Template )
		{	// Halt if no template with locale:
			debug_die( 'No Template found in '.$this->dbtablename.' for locale '.$locale.' by code '.$code.'!' );
		}

		return $locale_Template;
	}


	/**
	 * Load templates for a given context
	 * 
	 * @param string Comma-separated list of contexts to load
	 */
	function load_by_context( $context )
	{
		global $DB;

		if( empty( $context ) )
		{	// Nothing to load:
			return;
		}

		$context = array_map( 'trim', explode( ',', $context ) );

		$context_already_loaded = array_intersect( array_keys( $this->loaded_contexts ), $context );
		if( $this->all_loaded || ( count( $context_already_loaded ) == count( $context ) ) )
		{	// Already loaded
			return false;
		}

		$context_to_load = array_diff( $context, $this->loaded_contexts );
	
		$SQL = new SQL( 'Get templates with context: '.implode( ', ', $context_to_load ) );
		$SQL->SELECT( '*' );
		$SQL->FROM( 'T_templates' );
		$SQL->WHERE( 'tpl_translates_tpl_ID IS NULL' );
		$SQL->WHERE_and( 'tpl_context IN ('.$DB->quote( $context_to_load ).')' );
		$SQL->ORDER_BY( 'tpl_name, tpl_code' );

		$this->load_by_sql( $SQL );

		foreach( $context_to_load as $loop_context )
		{
			$this->loaded_contexts[$loop_context] = true;
		}

		return true;
	}
}
