<?php

// panel.class.php
// -------------------------------------------------------------------------------------------------
// Resolves panel requests according to a set of content versioning rules
//
// Project    > dmABS
// Copyright  > (C) Digital Mail Limited (2005-2010)
// Author     > RAK for DML
// -------------------------------------------------------------------------------------------------

require_once('debug.class.php');

class Panel
{

	var $root;				// Absolute or relative path to content directory (containing '_PANELS/')
	var $current_version;	// The current version
	var $default_version;	// The default version
	var $today;				// GMT date in YYYYMMDD format
	
	var $panel_env;			// Profile of the content directory
	
	var $pan_globals;		// An array containing the names of variables that must be in the scope for panel rendering
	var $js_interface;		// If set, push ID of panel into javascript array named in this variable when rendering
	
	var	$search;			// The panel search
	var	$leg_search;		// The legacy search
	var	$result;			// An array containing possible matches in a version
	var	$history;			// An array containing possible matches for each request (ie. panel in page)
	var	$reqcount;			// The request count (number of requests)
	
	var $panel_file; 		// Array containing info about the chosen panel, ie. the result of the request
	var $panel_next; 		// Array containing info about the the next pending panel
	
	var $debug; // Holds debugging object

	// ---------------------------------------------------------------------------------------------

	function Panel($root, $c_ver, $d_ver = 'english_1', $date = -1)
	{
		// CONSTRUCTOR
		$this->object  			= 'Panel';
		
		$this->root				= $root;
		$this->current_version 	= $c_ver;
		$this->default_version 	= $d_ver;
		$this->today 			= $date == -1 ? gmdate('Ymd') : $date;
		
		$this->pan_globals		= array();
		$this->js_interface		= '';

		$this->debug      		= new Debug(TRUE);

		$this->result 			= array();
		$this->history 			= array();
		$this->reqcount			= 0;

		$this->init_panel_environment();
	}
	
	// *********************************************************************************************
	// ---------------------------------------------------------------------------------------------
	// PUBLIC METHODS
	// ---------------------------------------------------------------------------------------------
	// *********************************************************************************************

	function render($owner, $panel_id, $extra = array())
	{
		// Find the current version of the panel and output it
		//
		$this->debug->heading('render(' . $owner . ', ' . $panel_id . ')');
		$this->request($owner, $panel_id, $extra);
		$this->output_panel();		
	}
	
	// ---------------------------------------------------------------------------------------------

	function render_absolute($panel_path)
	{
		// Render the specified panel
		//
		$this->panel_file['path'] = $this->root . $panel_path;
		$this->output_panel();		
	}

	// ---------------------------------------------------------------------------------------------

	function request($owner, $panel_id, $extra = array())
	{
		$this->debug->msg('<b>request(' . $owner .', ' . $panel_id .')</b>');
		
		$this->owner 	= $owner;
		$this->panel_id = $panel_id;
		$this->extra 	= $extra;

		$this->resolve_panel();
		$this->history[$this->reqcount]['search'] = $this->search;
		$this->history[$this->reqcount]['panels'] = $this->result;

		// If no match found, try the default brand if distinct from the current brand
		if ($this->panel_file === FALSE && $this->panel_env['dm']['current'] != $this->panel_env['dm']['default'])
		{
			$this->resolve_panel('default');
			$this->history[$this->reqcount]['panels'] = array_merge($this->history[$this->reqcount]['panels'], $this->result);
		}
		
		$this->history[$this->reqcount]['choice'] = $this->panel_file;
		$this->history[$this->reqcount]['next']   = $this->panel_next;
		$this->history[$this->reqcount]['final']  = $this->panel_final;
		$this->reqcount++;
		
		return $this->panel_file;
	}
	
	// ---------------------------------------------------------------------------------------------
	// ADMIN AREA METHODS

	function getall($filter = '*')
	{
		// Return an array of all unique panels (disregarding release date and status), sorted by owner
		// If filter is * only panels matching *.* or *_* will be returned
		//
		$a = $b = $c = $d = array();
		
		$a = $this->get_candidate_panels($this->panel_env['dm']['current'], $filter);
		$b = $this->get_candidate_panels($this->panel_env['my']['current'], $filter);
		if ($this->panel_env['dm']['current'] != $this->panel_env['dm']['default'])
		{
			$c = $this->get_candidate_panels($this->panel_env['dm']['default'], $filter);
			$d = $this->get_candidate_panels($this->panel_env['my']['default'], $filter);
		}
		
		$candidates = array_merge($a, $b, $c, $d);
		$candidates = array_unique($candidates);
		
		$pinfo = array();
		foreach($candidates as $c)
		{
			$inf = $this->get_panel_info($c);
			$pinfo[$inf['owner']][] = $inf['panel_id'];
		}
		foreach($pinfo as $owner => $panel_ids_arr)
		{
			$pinfo[$owner] = array_unique($panel_ids_arr);
		}
		return $pinfo;
	}

	// ---------------------------------------------------------------------------------------------

	function approve_panel($panel_path)
	{
		// Changes status of panel from pending (_p) to approved (_a), and save in current version,
		// if not there already
		//
		$this->debug->msg('<b>approve_panel(' . $panel_path . ')</b>');
		$panel_html = file_get_contents($this->root . $panel_path);
		
		$info 	 = $this->get_panel_info($panel_path);
		$prefix  = strpos($panel_path, '_MY_PANELS') !== FALSE ? '_MY' : '';
		$newpath = $this->root . '/' . $prefix . '_PANELS/' . $this->current_version . '/' . 
				   $info['owner'] . '--' . $info['panel_id'] . '_' . $info['release_date'] . '_a.html';
		$this->panel_file = $this->get_panel_info($newpath);
		if ($this->save_panel($panel_html))
		{
			unlink($this->root . $panel_path); // Delete old pending panel
			return $this->panel_file;
		}
		return FALSE;
	}

	// ---------------------------------------------------------------------------------------------

	function publish_panel($panel_path)
	{
		// Changes release date of panel to the current 'today', and saves in current version,
		// if not there already
		//
		$this->debug->msg('<b>publish_panel(' . $panel_path . ')</b>');
		$panel_html = file_get_contents($this->root . $panel_path);
		
		$info 	 = $this->get_panel_info($panel_path);
		$prefix  = strpos($panel_path, '_MY_PANELS') !== FALSE ? '_MY' : '';
		$newpath = $this->root . '/' . $prefix . '_PANELS/' . $this->current_version . '/' . 
				   $info['owner'] . '--' . $info['panel_id'] . '_' . $this->today . '_' . $info['status'] . '.html';
		$this->panel_file = $this->get_panel_info($newpath);
		if ($this->save_panel($panel_html))
		{
			unlink($this->root . $panel_path); // Delete old version of panel
			return $this->panel_file;
		}
		return FALSE;
	}

	// ---------------------------------------------------------------------------------------------

	function save_edited_panel($panel_name, $panel_html)
	{
		// Save a dealer edited panel in /_MY_PANELS/$this->current_version/
		// with the 'current' date and approved status (_a)
		//
		$this->debug->msg('<b>save_edited_panel(' . $panel_name . ', &lt;HTML&gt;)</b>');

		$dir  = $this->root . '/_MY_PANELS/' . $this->current_version . '/';
		if (!is_dir($dir))
		{
			if (!@mkdir($dir, 0777)) return FALSE;
		}
		if (preg_match('/\w+--[\w-]+_\d{8}_[aep]/', $panel_name))
		{
			 // Knock off old date and status
			$newpath = $dir . substr($panel_name, 0, -11);
		}
		else
		{
			// Undated panels
			$newpath = $dir . $panel_name;
		}
		// END CODE SLATED FOR EVENTUAL REMOVAL
		$newpath .= '_' . $this->today . '_a.html';
		$this->panel_file = $this->get_panel_info($newpath);
		return $this->save_panel($panel_html);
	}

	// *********************************************************************************************
	// ---------------------------------------------------------------------------------------------
	// PRIVATE METHODS
	// ---------------------------------------------------------------------------------------------
	// *********************************************************************************************

	function resolve_panel($ver = 'current')
	{
		$this->flush_panel(); // Rid ourselves of the last result
		
		$this->searches 	= array();
		$search				= $this->owner . '--' . $this->panel_id;
		$this->searches[] 	= $search;
		if (!empty($this->extra))
		{
			foreach($this->extra as $extra)
			{
				$search .= '-' . $extra;
				$this->searches[] = $search;
			}
		}
		
		while ($search = array_pop($this->searches))
		{
			$this->search = $search;
					
			// FIND THE CANDIDATE PANELS IN THE VERSION
			$this->debug->msg('Looking in ' . $ver . ': ' . $this->search);
			if ($this->panel_env['dm'][$ver])
			{
				$candidate = $this->get_candidate_panels($this->panel_env['dm'][$ver], $this->search);
				if ($candidate === FALSE) $candidate = array();
				foreach($candidate as $candy) $this->result[] = $this->get_panel_info($candy);
			}
			if ($this->panel_env['my'][$ver])
			{
				$candidate = $this->get_candidate_panels($this->panel_env['my'][$ver], $this->search);
				if ($candidate === FALSE) $candidate = array();
				foreach($candidate as $candy) $this->result[] = $this->get_panel_info($candy);
			}
		
			$this->apply_rules();
			if ($this->panel_file)
			{
				$this->debug->msg('Found: <b>' . $this->panel_file['path'] . '</b>');
				return($this->panel_file);
			}
			else
			{
				$this->debug->msg('Not Found');
			}
		}
	}
	
	// ---------------------------------------------------------------------------------------------

	function apply_rules()
	{
		// Decide which version of a panel to use, and check for pending versions
		//
		usort($this->result, 'compare_reldate'); // Sort candidates in order of release date

		$choice = FALSE;
		$num_matches = count($this->result);
			
		// Find latest (released) approved or essential panel
		$latest = 0;
		for($i=0; $i<$num_matches; $i++)
		{
			if ($this->result[$i]['status'] == 'a' || $this->result[$i]['status'] == 'e')
			{
				if ($this->result[$i]['release_date'] > $latest && $this->result[$i]['release_date'] <= $this->today)
				{
					$latest = $this->result[$i]['release_date'];
					$choice = $i;
				}
			}
		}
				
		// If no match found, try the latest (released) pending panel
		if ($choice === FALSE)
		{
			for($i=0; $i<$num_matches; $i++)
			{
				if ($this->result[$i]['status'] == 'p')
				{
					if ($this->result[$i]['release_date'] > $latest && $this->result[$i]['release_date'] <= $this->today)
					{
						$latest = $this->result[$i]['release_date'];
						$choice = $i;
					}
				}
			}
		}
		
		// Otherwise try a panel without a date and status
		if ($choice === FALSE)
		{
			for($i=0; $i<$num_matches; $i++)
			{
				if ($this->result[$i]['release_date'] == 0 && $this->result[$i]['status'] == '')
				{
					$latest = $this->result[$i]['release_date'];
					$choice = $i;
				}
			}
		}

		$this->panel_file = $choice !== FALSE ? $this->result[$choice] : FALSE;
		
		// Are there any pending panels?
		if ($choice === FALSE) $choice = -1;
		
		if (isset($this->result[$choice + 1])) 	$this->panel_next	= $this->result[$choice + 1];
		if ($choice < $num_matches - 2)			$this->panel_final	= $this->result[$num_matches - 1];

	}
	
	// ---------------------------------------------------------------------------------------------
	
	function get_panel_info($panel_path)
	{
		// Break filename down into owner, panel_id, release_date and status
		$panel_name = substr($panel_path, strrpos($panel_path, DIRECTORY_SEPARATOR) + 1);
		preg_match('/(.*)--(.*?)(?(?=_\d{8}_\w)_(\d{8})_(\w))\.\w*/u', $panel_name, $match);
		$info = array(
			'path' 			=> $panel_path,
			'name' 			=> $panel_name,
			'owner' 		=> isset($match[1]) ? $match[1] : '',
			'panel_id' 		=> isset($match[2]) ? $match[2] : '',
			'release_date' 	=> isset($match[3]) ? (int) $match[3] : 0,
			'status' 		=> isset($match[4]) ? $match[4] : ''
		);
		return $info;
	}
	
	// ---------------------------------------------------------------------------------------------
	// ---------------------------------------------------------------------------------------------
	// ---------------------------------------------------------------------------------------------

	function init_panel_environment()
	{
		// Get version directories
		//
		$env['dm']['base'] = $this->root . '/_PANELS';
		$env['my']['base'] = $this->root . '/_MY_PANELS';
		
		$env['dm']['versions'] = $this->get_candidate_directories($env['dm']['base']);	
		$env['my']['versions'] = $this->get_candidate_directories($env['my']['base']);
		
		// Get _PANELS vesrion directory
		if (in_array($env['dm']['base'] . DIRECTORY_SEPARATOR . $this->current_version, $env['dm']['versions']))
		{
			$env['dm']['current'] = $env['dm']['base'] . DIRECTORY_SEPARATOR . $this->current_version;
		}
		else if (in_array($env['dm']['base'] . DIRECTORY_SEPARATOR . $this->default_version, $env['dm']['versions']))
		{
			$env['dm']['current'] = $env['dm']['base'] . DIRECTORY_SEPARATOR . $this->default_version;
		}
		else
		{
			$env['dm']['current'] = NULL;
		}
		
		// Get _MY_PANELS version directory
		if (in_array($env['my']['base'] . DIRECTORY_SEPARATOR . $this->current_version, $env['my']['versions']))
		{
			$env['my']['current'] = $env['my']['base'] . DIRECTORY_SEPARATOR . $this->current_version;
		}
		else
		{
			$env['my']['current'] = NULL;
		}
		
		// Get _PANELS default version
		if (in_array($env['dm']['base'] . DIRECTORY_SEPARATOR . $this->default_version, $env['dm']['versions']))
		{
			$env['dm']['default'] = $env['dm']['base'] . DIRECTORY_SEPARATOR . $this->default_version;
		}
		else
		{
			$env['dm']['default'] = NULL;
		}
	
		// Get _MY_PANELS default version
		if (in_array($env['my']['base'] . DIRECTORY_SEPARATOR . $this->default_version, $env['my']['versions']))
		{
			$env['my']['default'] = $env['my']['base'] . DIRECTORY_SEPARATOR . $this->default_version;
		}
		else
		{
			$env['my']['default'] = NULL;
		}
		
		$this->panel_env = $env;
		
		// Debugging
		if (!$this->debug->debug) return;
		$this->debug->heading('Initialising panel environment (places to look)');
		if ($env['my']['current'] || $env['dm']['current'])
		{
			$this->debug->msg('<b>Current versions:</b>');
			if ($env['my']['current']) 	  $this->debug->msg($env['my']['current']);
			if ($env['dm']['current']) 	  $this->debug->msg($env['dm']['current']);
		}
		else
		{
			$this->debug->msg('No current versions found');
		}
		if ($env['my']['default'] || $env['dm']['default'])
		{
			$this->debug->msg('<b>Default versions:</b>');
			if ($env['my']['default']) $this->debug->msg($env['my']['default']);
			if ($env['dm']['default']) $this->debug->msg($env['dm']['default']);
		}
		else
		{
			$this->debug->msg('No default versions found');
		}

	}
	
	// ---------------------------------------------------------------------------------------------
	
	function get_candidate_directories($directory, $search = '')
	{
		// Match directories whose names contain $search in the directory specified by $directory
		//
		if (!$directory) return array();
		
		$candidates = glob($directory . DIRECTORY_SEPARATOR . $search . '*', GLOB_ONLYDIR);
		if ($candidates === FALSE) $candidates = array();
		return $candidates;
	}

	// ---------------------------------------------------------------------------------------------

	function get_candidate_panels($directory, $search = '')
	{
		// Match files whose names contain $search in the directory specified by $directory
		//
		if (!$directory) return array();

		$c1 = glob($directory . DIRECTORY_SEPARATOR . $search . '_*');  // Dated
		$c2 = glob($directory . DIRECTORY_SEPARATOR . $search . '.*');	// Un-dated
		if ($c1 === FALSE) $c1 = array();
		if ($c2 === FALSE) $c2 = array();
		$candidates = array_merge($c1, $c2);
		return $candidates;
	}

	// ---------------------------------------------------------------------------------------------

	function output_panel()
	{
		foreach($this->pan_globals as $g) global $$g; // Bring variables needed by the panel into scope
		
		$id = str_replace('.html', '', str_replace($this->root, '', $this->panel_file['path'])); // . '-' . $this->reqcount;
		
		echo('<div id="' . $id . '" name="editable">' . "\n");
		if (!@include($this->panel_file['path']))
		{
			echo('<p>Call to non-existant panel: ' . $this->search . '</p>');
		}
		if ($this->js_interface)
		{
			echo("\n");
			echo('<script language="javascript" type="text/javascript">' . "\n");
			echo($this->js_interface . '.push(\'' . $id . '\');' . "\n");
			echo('</script>' . "\n");
		}
		echo('</div>' . "\n");
	}

	// ---------------------------------------------------------------------------------------------

	function save_panel($panel_html)
	{
		// Saves '$this->panel_file'
		//
		$this->debug->msg('<b>save_panel(&lt;HTML&gt;)</b>');

		// ASSUME DIRECTORY EXISTS FOR THE MOMENT !

		$fp = @fopen($this->panel_file['path'], 'w');
		if ($fp)
		{
			fputs($fp, $panel_html);
			fclose($fp);
			@chmod($this->panel_file['path'], 0777); // We can try, but doesn't fowk if SAFE_MODE is on
			$this->debug->msg('Saved: ' . $this->panel_file['path']);
			$this->debug->msg();
			return TRUE;
		}
		else
		{
			// Filesystem not writable
			$this->debug->msg('Saving failed: ' . $this->panel_file['path']);
			$this->debug->msg();
			return FALSE;
		}
	}

	// ---------------------------------------------------------------------------------------------
	// ADMIN AREA FUNCTIONS

	function create_empty_version($version)
	{
		// Create a new version
		//
		// EXPERIMENTAL...
		//
		$dir  = $this->root . '/_PANELS/' . $version . '/';
		if (!is_dir($dir))
		{
			if (!@mkdir($dir, 0777)) return FALSE;
		}
	}

	// ---------------------------------------------------------------------------------------------
	
	function create_duplicate_version($ver_to_duplicate, $new_version)
	{
		// Duplicate an existing version, giving it a new name
		//
		// EXPERIMENTAL...
		//
		require_once('zip.class.php');
		$zipfile = new zipfile;
		$zipfile->create_dir("."); // Add a subdirectory - must be done. If a subdirectory is not wanted, just simply add one named "." as shown here
		$zipfile->create_file(file_get_contents(__FILE__), "zip.txt"); // Add the data of the file that is wanted, and the full path to it in the zip.
		echo('<pre>' . print_r($zipfile, 1) . '</pre>');
	}
	
	// ---------------------------------------------------------------------------------------------
	// ---------------------------------------------------------------------------------------------
	// ---------------------------------------------------------------------------------------------

	function flush_panel()
	{
		$this->result 		= array();
		$this->panel_file 	= FALSE;
		$this->panel_next 	= FALSE;
		$this->panel_final 	= FALSE;
	}

	// ---------------------------------------------------------------------------------------------

}

// END CLASS
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------

// Callback functions declared outside the class 

function compare_reldate($a, $b)
{
	return($a['release_date'] > $b['release_date']);
}

?>